notify

package
v0.6.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 16, 2020 License: AGPL-3.0 Imports: 31 Imported by: 0

README

Push notifications service

The push notifications service allows organizations to send pop up messages to their users devices. Organizations can send notifications at any time and their users do not have to be in the app or using their devices to receive them.

Currenty the push notifications system used is the Google Firebase Cloud Messassing but any other provider can be easily integrated.

Each user can be subscribed to any organization she wants and be notified for one or more events.

Event subscription is managed with a topic approach, currently supporting the following topics:

  • <entityID>_default_post-new
  • <entityID>_default_process-new

Index

  1. Ethereum
  2. IPFS
  3. Notifications service
  4. API
1. Ethereum

Given that the Vocdoni platform uses different smart contracts deployed into an EVM compatible chain as the source of truth, it is a requirement to have access to an Ethereum JSON-RPC compatible endpoint in order to interact with these contracts. Interacting with this contracts means to read, write and listen for events.

Some examples:

  • New voting process creation
  • A voting process is ended
  • An ended voting process have the results available
  • ...

Currently supported EVM logs: NewProcess(bytes32 processId, uint16 namespace)

2. IPFS

Another fundamental component of the notifications service is the IPFS node. Data is not stored in Ethereum but in IPFS, and given so, have access to an IPFS node is required in order to retrieve and store data in this distributed file system.

This data can be:

  • Voting process metadata (type, mode, questions ...)
  • Organization metadata (name, email, ID ...)
  • Prefered organization bootnodes and gateways to connect with

The actual URI of this data is stored on Ethereum and changes every time any of the data is changed. This is an IPFS property which is a content-addressed system.

Currently supported file changes: News feed modifications on organizations metadata

3. Notifications service

The notifications service, in short, has two main different missions:

  1. To track theProcess smart contract events

    Example: An organization created a new process and wants to inform its members

  2. To track IPFS files changes given its URI stored in Ethereum

    Example: An organization updated its news feed entry and wants to notify its members that a new post is added

Is composed by the following components:


How it works
  • Private database

    This database is used to get the organizations list and be able to track each ones metadata.

  • Ethereum

    The Ethereum JSON-RPC endpoint is used to interact with the Ethereum network, the Process contract and the Ethereum Name Service (ENS) are those the notifications service uses.

    • The ENS smart contracts are used to fetch the canonical Process smart contract address under the voting-process.vocdoni.eth domain.
    • Once the Process contract address is fetched a component called ethEvents starts to listen for logs on the contract.
    • Each time a new process is created (the case currently supported) an Ethereum smart contract event is triggered and the ethEvents component handles it.
    • The event will be processed and a notification anouncing the creation of a process will be created and sended to Firebase.
    • The users subscribed to the corresponding topic will receive the notification
  • IPFS

    The IPFS file tracking also relies on Ethereum as a source of truth and uses Ethereum to get the latest IPFS URI in the ENS Text List Record entry of an organization. This IPFS URI points to the entity metadata. As said, this metadata is stored on IPFS and its content is univocally identified by a hash.

    The file tracking mechanism works as follows:

    • Get the organizations list from the private database.
    • Get each entity IPFS metadata URI from the ENS Text list record corresponding entry.
    • Fetch the file from IPFS:
      • If the organization was not tracked before, just store the file and its hash.
      • Else the current hash is compared with the previously stored ones and if it is different the content is also different.
    • In case of the metadata was updated the current version checks if the news feed field is different.
    • Each time the news feed is modified a new Firebase notification is created.
    • The users subscribed to the corresponding topic will be notified that a new post is created or updated.

    This process runs forever and is repeated every scheduled time.

4. API

The notifications API is used to generate user tokens that can be useful for certain circunstances.

Available by default under /notifications

register
  • Request

    {
     "id": "req-12345678",
     "request": {
       "method": "register",
       "timestamp": 1234567890
     },
     "signature": "0x12345"
    }
    
  • Response:

    {
      "response": {
        "ok": true,
        "request": "req-12345678",  
        "timestamp": 1556110671,
        "token": "xxx-yyy-zzz"
      },
      "id": "req-12345678",
      "signature": "0x12345"
    }
    

Build and run

  • Build
go build cmd/dvotenotif/dvotenotif.go
  • Run
go run cmd/dvotenotif/dvotenotif.go

## or after build

./dvotenotif
  • Options (.yaml config file is also created)
  --dataDir string                    directory where data is stored (default "/home/t480/.dvotenotif")
  --dbHost string                     DB server address (default "127.0.0.1")
  --dbName string                     DB database name (default "vocdoni")
  --dbPassword string                 DB password (default "vocdoni")
  --dbPort int                        DB server port (default 5432)
  --dbSslmode string                  DB postgres sslmode (default "prefer")
  --dbUser string                     DB Username (default "vocdoni")
  --apiRoute string                   API route (default "/api")
  --apiListenHost string              API host to listen on (default "127.0.0.1") 
  --apiListenPort int                 API port to listen on (default 8000)
  --sslDomain string                  API SSL domain (default "")
  --ethBootNodes stringArray          Ethereum p2p custom bootstrap nodes (enode://<pubKey>@<ip>[:port])
  --ethCensusSync                     automatically import new census published on the smart contract
  --ethChain string                   Ethereum blockchain to use: [mainnet goerli xdai xdaistage sokol] (default "sokol")
  --ethChainLightMode                 synchronize Ethereum blockchain in light mode
  --ethNoWaitSync                     do not wait for Ethereum to synchronize (for testing only)
  --ethNodePort int                   Ethereum p2p node port to use (default 30303)
  --ethProcessDomain string           voting contract ENS domain (default "voting-process.vocdoni.eth")
  --ethSigningKey string              signing private Key (if not specified the Ethereum keystore will be used)
  --ethSubscribeOnly                  only subscribe to new ethereum events (do not read past log)
  --ethTrustedPeers stringArray       Ethereum p2p trusted peer nodes (enode://<pubKey>@<ip>[:port])
  --ipfsNoInit                        disables inter planetary file system support
  --ipfsSyncKey string                enable IPFS cluster synchronization using the given secret key
  --ipfsSyncPeers stringArray         use custom ipfsSync peers/bootnodes for accessing the DHT
  --logErrorFile string               Log errors and warnings to a file
  --logLevel string                   Log level (debug, info, warn, error, fatal) (default "info")
  --logOutput string                  Log output (stdout, stderr or filepath) (default "stdout")
  --metricsEnabled                    enable prometheus metrics (default true)
  --metricsRefreshInterval int        metrics refresh interval in seconds (default 10)
  --pushNotificationsKeyFile string   path to notifications service private key file
  --pushNotificationsService int      push notifications service, 1: Firebase (default 1)
  --saveConfig                        overwrites an existing config file with the CLI provided flags
  --w3Enabled                         if true, a web3 public endpoint will be enabled (default true)
  --w3External string                 use an external web3 endpoint instead of the local one. Supported protocols: http(s)://, ws(s):// and IPC filepath
  --w3RPCHost string                  web3 RPC host (default "127.0.0.1")
  --w3RPCPort int                     web3 RPC port (default 9091)
  --w3Route string                    web3 endpoint API route (default "/web3")

Documentation

Index

Constants

View Source
const (
	TopicMessage = iota + 1
	ConditionMessage
	TokenMessage
	GroupMessage
)

message types by receiver

View Source
const (
	PlatformAndroid = iota + 1
	PlatformIos
	PlatformWeb
	PlatformAll
)

supported platforms

View Source
const (
	RetrieveTimeout = 1 * time.Minute
	RefreshTime     = 30 * time.Second
)

RetrieveTimeout the maximum duration the import queue will wait for retreiving a remote file

View Source
const (
	Firebase = iota + 1
)

supported Push notifications service

Variables

View Source
var (
	HashLogProcessCreated   = crypto.Keccak256Hash(logProcessCreated)
	HashLogResultsPublished = crypto.Keccak256Hash(logResultsPublished)
)

Functions

func DefaultFirebasePushNotificationAndroidConfig

func DefaultFirebasePushNotificationAndroidConfig(
	data map[string]string,
	notification *messaging.AndroidNotification) *messaging.AndroidConfig

DefaultFirebasePushNotificationAndroidConfig creates an android notification config with default config opts

func DefaultFirebasePushNotificationIosConfig

func DefaultFirebasePushNotificationIosConfig(
	headers map[string]string,
	payloadAps *messaging.Aps,
	payloadCustom map[string]interface{}) *messaging.APNSConfig

DefaultFirebasePushNotificationIosConfig creates an ios notification config with default config opts

func DefaultFirebasePushNotificationWebConfig

func DefaultFirebasePushNotificationWebConfig(
	headers, data map[string]string,
	notification *messaging.WebpushNotification,
	link string) *messaging.WebpushConfig

DefaultFirebasePushNotificationWebConfig creates a web notification config with default config opts

func ProcessMeta

func ProcessMeta(ctx context.Context, contractABI *abi.ABI, eventData []byte, ph *chain.ProcessHandle) (*types.NewProcessTx, error)

ProcessMeta returns the info of a newly created process from the event raised and ethereum storage

Types

type API

type API struct {
	Router       *router.Router
	PushNotifier PushNotifier
	MetricsAgent *metrics.Agent
}

API wraps the push notifications API

func NewAPI

func NewAPI(r *router.Router, pn PushNotifier, ma *metrics.Agent) *API

NewAPI creates a new push notifications handler for the Router

func (*API) RegisterMethods

func (n *API) RegisterMethods(path string) error

RegisterMethods registers all registry methods behind the given path

type BasePushNotification

type BasePushNotification struct {
	// this are the most common fields among many push notifications services
	ID       string
	Action   string
	Body     string
	Data     Data
	Date     time.Time
	Image    string
	Message  Message
	Platform int
	Priority string
	Title    string
	Token    string
	Topic    string
}

BasePushNotification is a base notification wrapper

type Data

type Data interface{}

Data represents the notification data

type FirebaseAdmin

type FirebaseAdmin struct {
	*firebase.App
	Client *auth.Client
	Key    string
	IPFS   *IPFSFileTracker

	Routes [2]string // routes for each env
}

FirebaseAdmin wraps the firebase admin SDK App struct and a reference to the IPFS file tracker

func NewFirebaseAdmin

func NewFirebaseAdmin(key, env string, ft *IPFSFileTracker) *FirebaseAdmin

NewFirebaseAdmin returns a pointer to a fresh FirebaseAdmin

func (*FirebaseAdmin) Check

func (fa *FirebaseAdmin) Check(notification Notification) bool

Check checks a firebase push notification format

func (*FirebaseAdmin) CreateUser

func (fa *FirebaseAdmin) CreateUser(userData User) (User, error)

CreateUser creates a user with the given user info

func (*FirebaseAdmin) DeleteUser

func (fa *FirebaseAdmin) DeleteUser(uid string) error

DeleteUser deletes a user with the given UID

func (*FirebaseAdmin) DeleteUserBulk

func (fa *FirebaseAdmin) DeleteUserBulk(uids []string) (*auth.DeleteUsersResult, error)

DeleteUserBulk deletes a list of users giving its ids

func (*FirebaseAdmin) GenerateToken

func (fa *FirebaseAdmin) GenerateToken(uid string) (string, error)

GenerateToken returns a custom Firebase FCM token given an UID

func (*FirebaseAdmin) GetUser

func (fa *FirebaseAdmin) GetUser(uid string) (User, error)

GetUser retrieves user's data

func (*FirebaseAdmin) GetUserByEmail

func (fa *FirebaseAdmin) GetUserByEmail(email string) (User, error)

GetUserByEmail returns user's data from the user matching the given email

func (*FirebaseAdmin) HandleEthereum

func (fa *FirebaseAdmin) HandleEthereum(ctx context.Context, event *ethtypes.Log, e *ethevents.EthereumEvents) error

HandleEthereum handles an Ethereum event

func (*FirebaseAdmin) HandleIPFS

func (fa *FirebaseAdmin) HandleIPFS()

HandleIPFS handles an IPFS file content change

func (*FirebaseAdmin) Init

func (fa *FirebaseAdmin) Init() (err error)

Init initializes the Firebase Admin instance

func (*FirebaseAdmin) Send

func (fa *FirebaseAdmin) Send(pn Notification) error

Send sends a push notification

func (*FirebaseAdmin) Service

func (fa *FirebaseAdmin) Service() int

Service returns an integer representing the Firebase push notifications service

func (*FirebaseAdmin) SubscribeTopic

func (fa *FirebaseAdmin) SubscribeTopic(tokens []string, topic string) error

SubscribeTopic subscribes a list of users to a given topic

func (*FirebaseAdmin) UnsubscribeTopic

func (fa *FirebaseAdmin) UnsubscribeTopic(tokens []string, topic string) error

UnsubscribeTopic unsubscribes a list of users to a given topic

func (*FirebaseAdmin) UpdateUser

func (fa *FirebaseAdmin) UpdateUser(uid string, userData User) (User, error)

UpdateUser updates a user given its UID and the info to update

type FirebasePushNotification

type FirebasePushNotification struct {
	Upstream BasePushNotification
	FCM      *messaging.Message
}

FirebasePushNotification wraps a FCM notification

func NewFirebasePushNotification

func NewFirebasePushNotification(
	data map[string]string,
	notification *messaging.Notification,
	androidConfig *messaging.AndroidConfig,
	iosConfig *messaging.APNSConfig,
	webpushConfig *messaging.WebpushConfig,
	FCMOpts *messaging.FCMOptions,
	token, topic, condition string) *FirebasePushNotification

NewFirebasePushNotification returns an initialized message struct with all its data filled

func (*FirebasePushNotification) Action

func (fpn *FirebasePushNotification) Action() string

Action returns the base push notification action

func (*FirebasePushNotification) Body

func (fpn *FirebasePushNotification) Body() string

Body returns the base push notification body

func (*FirebasePushNotification) Data

func (fpn *FirebasePushNotification) Data() Data

Data returns the base push notification data

func (*FirebasePushNotification) Date

func (fpn *FirebasePushNotification) Date() time.Time

Date returns the base push notification date

func (*FirebasePushNotification) ID

func (fpn *FirebasePushNotification) ID() string

ID returns the base push notification id

func (*FirebasePushNotification) Image

func (fpn *FirebasePushNotification) Image() string

Image returns the base push notification image

func (*FirebasePushNotification) Message

func (fpn *FirebasePushNotification) Message() Message

Message returns the base push notification message

func (*FirebasePushNotification) Platform

func (fpn *FirebasePushNotification) Platform() int

Platform returns the base push notification platform

func (*FirebasePushNotification) Priority

func (fpn *FirebasePushNotification) Priority() string

Priority returns the base push notification priority

func (*FirebasePushNotification) Title

func (fpn *FirebasePushNotification) Title() string

Title returns the base push notification title

func (*FirebasePushNotification) Token

func (fpn *FirebasePushNotification) Token() string

Token returns the base push notification token

func (*FirebasePushNotification) Topic

func (fpn *FirebasePushNotification) Topic() string

Topic returns the base push notification topic

type FirebaseUser

FirebaseUser wraps a firebase user

func (FirebaseUser) UID

func (fu FirebaseUser) UID() string

UID returns a FirebaseUser UID

type IPFSFile added in v0.6.1

type IPFSFile struct {
	Hash string
	// used for fast comparation on the news feed
	// NewsFeed:map[default:ipfs://<hash>]
	OuterMap map[string]string
	// the news feed pointed content
	NewsFeed *types.NewsFeed
}

IPFSFile holds the ipfs hash of the entity metadata and the relevant content

type IPFSFileTracker added in v0.6.1

type IPFSFileTracker struct {
	IPFS                   *data.IPFSHandle
	IPFSConfig             *config.IPFSCfg
	FileContentList        *sync.Map         // [eID]IPFSFile
	UpdatedFilesQueue      chan *UpdatedFile // read only receiver
	EntitiesTrackingStatus *sync.Map
	// contains filtered or unexported fields
}

IPFSFileTracker contains all the components of a IPFSFileTracker

func NewIPFSFileTracker added in v0.6.1

func NewIPFSFileTracker(config *config.IPFSCfg, ma *metrics.Agent, db database.Database, ensRegistry, w3endpoint string) *IPFSFileTracker

NewIPFSFileTracker creates a new IPFSFileTracker

func (*IPFSFileTracker) Start added in v0.6.1

func (ft *IPFSFileTracker) Start(ctx context.Context, refreshTime time.Duration) error

Start initializes the file tracker IPFS node and starts the file tracker

type Message

type Message interface{}

Message represents a notification message

type Notification

type Notification interface {
	ID() string
	Action() string
	Body() string
	Data() Data
	Date() time.Time
	Image() string
	Message() Message
	Platform() int
	Priority() string
	Title() string
	Token() string
	Topic() string
}

Notification is the interface wrapping the methods that any Notification must implement

type PushNotifier

type PushNotifier interface {
	// Service returns the push notifications selected service
	Service() int
	// topic subscription
	SubscribeTopic(tokens []string, topic string) error
	UnsubscribeTopic(tokens []string, topic string) error
	// messaging
	Check(notification Notification) bool
	Send(notification Notification) error
	// ethereum
	HandleEthereum(ctx context.Context, event *ethtypes.Log, e *ethevents.EthereumEvents) error
	// ipfs
	HandleIPFS()
	// user management
	GetUser(uid string) (User, error)
	CreateUser(userData User) (User, error)
	UpdateUser(uid string, userData User) (User, error)
	DeleteUser(uid string) error
	// tokens
	GenerateToken(uid string) (string, error)

	Init() error
}

PushNotifier contains the methos that all push notification services should implement

type UpdatedFile added in v0.6.1

type UpdatedFile struct {
	*IPFSFile
	// contains filtered or unexported fields
}

UpdatedFile wraps the IPFS updated file and the creator entity

type User

type User interface {
	UID() string
}

User wraps any user type

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL