1.0.5 • Published 4 years ago

jrfws2 v1.0.5

Weekly downloads
1
License
MIT
Repository
github
Last release
4 years ago

jrfws2

Deprecated. New version jrf-ws-3

jrfws2 is async/await package for creating real-time api based on websockets. It is a wrapper over easy and fast ws. It can work independently or in conjunction with koa.

readme_ru

jrfwslogo

Server

Start server

const {JRFWSServer} = require('jrfws2');  
const jrfwsServer = new JRFWSServer();  
  
async function initServer() {

  /// any code	
  
  let opt = {  
  port: 3003  
  };  
  /// default port: 3001  
  await jrfwsServer.startServer(opt);  
}  
  
initServer();

You can read more about opt servers from the official documentation ws

Start server with Koa

const Koa = require('koa');    
const app = new Koa();  
const {JRFWSServer} = require('jrfws2');  
const jrfwsServer = new JRFWSServer();  

/// any code

/// http
jrfwsServer.attach(app);
app.listen(3001);

/// or https
jrfwsServer.attach(app, true, {
  key: fs.readFileSync(...),
  cert: fs.readFileSync(...),
  ca: fs.readFileSync(...)
});
app.listen(3001);

/// app.jrfws

Client

To establish a connection, you must specify the address url. You can specify the parameter reconnect - true try to reconnect to the server, after a break, false - do not restore the connection.

const {JRFWSClient} = require('jrfws2');
const jrfwsClient = new JRFWSClient();
await jrfwsClient.startClient({url: `ws://localhost:${PORT}`});

import {JRFWSBrowserClient} from 'jrfws2';
const jrfwsClient = new JRFWSBrowserClient();
await jrfwsClient.startClient({url: `ws://localhost:${PORT}`});

hook - this.onOpen = async (args) => {}; fulfills when connecting to the server.

hook - this.onError = async (args) => {}; works if a connection to the server fails.

hook - this.onClose = async (args) => {}; executes when the connection to the server is closed.

hook - this.onMessage = async (args) => {}; executes when a message arrives from the server.

Routing

To process incoming messages, you need to configure routing.

await jrfws.route(route, act, func(data, stop));
paramtyperequireddescription
routestringPath
actstringAction on the path
funcfunctiontrueReceiving asynchronous function: data - received data, stop - asynchronous function, the call of which will suspend the subsequent routing

data consists of

paramtypedescription
uidstringid of the incoming message. Filled when the sender is waiting for a response to the message
isResbooleantrue - the message is a response to the sent message
userobjectUser who sent the message
dataanyAny type of data sent in a message
routestringMessage path
actstringAction on the message path
systemobjectSystem Information Messages
clientobjectThe client (client connection) that sent the message. The internal client object ws, to which the sendMes method has been added. This option is available only on the server.
fromobjectMessage initiator data. Only populated with broadcast.

An example of adding routing handlers. Handlers are executed in the order in which they were added. The exception is the system handler not found, which is always executed last.

  // Will process all messages since route and act are not set
  await jrfwsSrv.route({
          func: async ({data, stop}) => {
            
          }
        });
    
  // Will process all messages with route === 'users'
  // Stops further routing stop ()
  // If the sender expects a response to the message, then it will return 'users'
  await jrfwsSrv.route({
          route: 'users',
          func: async ({data, stop}) => {
            stop();
            return 'users';
          }
        });
    
  // Will process all messages with route === 'users' and act === 'add'
  // Send a message to all clients about adding a new user to: {}
  // If the sender expects a response to the message, then the added user will be returned
  await jrfwsSrv.route({
          route: 'users',
          act: 'add',
          func: async ({data, stop}) => {
            const user = addUser({user: data.data.user});
            await jrfwsSrv.sendMes({route: 'users', act: 'add', data: {user}, to: {}});
            return user;
          }
        });

  // Will process all messages with route === 'users' and act === 'get'
  // Such a handler is possible only on the server since used by data.client.sendMes
  // The handler will send a message to the client of the processed message
  await jrfwsSrv.route({
          route: 'users',
          act: 'get',
          func: async ({data, stop}) => {
            const users = await getUsers({filter: data.data.filter});
            await data.client.sendMes({route: 'users', act: 'get', data: {users}});
          }
        });

  // Will process all messages for which routing has not been suspended
  // previous handlers since route and act are not set
  await jrfwsSrv.route({
          func: async ({data, stop}) => {
            
          }
        });

  // Special handler. Will process messages for which there are no handlers
  // by route and (or) act
  await jrfwsSrv.route({
          route: 'not found',
          func: async ({data, stop}) => {

          }
        });

Message

A message consists of various data.

paramtypedescription
uidstringid of the incoming message. Filled when the sender is waiting for a response to the message
isResbooleantrue - the message is a response to the sent message
userobjectUser who sent the message
dataanyAny type of data sent in a message
routestringMessage path
actstringAction on the message path
systemobjectSystem Information Messages
clientobjectThe client (client connection) that sent the message. The internal client object ws, to which the sendMes method has been added. This option is available only on the server.
fromobjectMessage initiator data. Only populated with broadcast.

user - The user consists of fields specified in jrfws.user.

Default.

this.user = {
      username: '',
      email: '',
      token: '',
      rights: null,
      phone: '',
      uid: this._generateId()
    };

system - System information. Contains an array of the message history system.history. The user’s history removes the confidential fields listed in this.userExcludeFields = ['rights', 'token'];

Each element of the story contains.

paramtypedescription
userobjectThe user who processed the message
datedateDate message processing time
system: {
  history: [{
    user:
      {
        email: 'rick@rick.rc',
        phone: 'rick phone',
        username: 'rick',
        uid: 'FcEKAIwnuvZYUcJ16112019215436216'
      },
    date: '2019-12-16T18:54:37.750Z'
  },
    {
      user:
        {
          email: '',
          phone: '',
          username: 'server',
          uid: 'IaaVTHnmRANpDum16112019215220182'
        },
      date: '2019-12-16T18:54:37.752Z'
    },
    {
      user:
        {
          email: 'mrshitman@shitman.sh',
          phone: 'mr. shitman phone',
          username: 'mr. shitman',
          uid: 'UbHtILtgKDmknAO16112019215436217'
        },
      date: '2019-12-16T18:54:37.754Z'
    }]
},

from - Data of the initiator of the message. Only populated with broadcast. The user deletes the confidential fields listed in this.userExcludeFields = ['rights', 'token'];

Comprises

paramtypedescription
fromUserobjectThe user who sent the message
groupsarrayArray of names of groups of message recipients
from: {
  fromUser:
    {
      email: 'rick@rick.rc',
      phone: 'rick phone',
      username: 'rick',
      uid: 'PahjwMXZfaWLEPN16112019214327265'
    },
  groups: ['megaBrainzzz', 'space', 'waterWorld']
}

Send message

A message is sent using the jrfws.sendMes ({params}) method.

Parameters.

paramtypedescription
routestringWay
actstringAction on the way
dataanyData of any type
awaitResbooleanWait for reply to message
callbackfunctionExecute callback when the message comes back
optionsobjectAdditional message options. Currently there is only one option timeout for parameters awaitRes and callback
toobjectParameters broadcast
clientobjectThe client (one of jrfws.wss.clients) to whom you want to send a message. This option is available only on the server.

Simple send

The message is simply sent.

await jrfws.sendMes({route: 'users'});

await jrfws.sendMes({route: 'users', act: 'get'});

await jrfws.sendMes({route: 'users', act: 'add', data: {
  user: {username: 'rick', email: 'rick@rick.rc'}
});

Wait for a response to the message

The message is sent, the program waits for a response to the message.

If the wait time is longer than this.timeout = 10000, then the answer will be error: {message: 'timeout: 10000'} and will execute the this.onTimeout handler, which will   passed the parameter data

paramtypedescription
uidstringmessage id
userobjectUser who sent the message
routestringMessage path
actstringAction on the message path
systemobjectSystem Information Messages
timeoutnumberMaximum time to wait for a response
resobjectMessage reply. Will contain error
// set default timeout; default = 10000
jrfws.timeout = 5000;

jrfws.onTimeout = async ({data}) => {
      console.error(`Timeout: ${data.timeout} ms; uid: ${data.uid}; route: ${data.route}; act: ${data.act}`);
    };

// default timeout
const res = await jrfws.sendMes({route: 'users', awaitRes: true});

// custom timeout
const res = await jrfws.sendMes({route: 'users', awaitRes: true, options: {timeout: 1000}});

Callback

A message is being sent. Callback expects a data response.

paramtypedescription
uidstringid of the incoming message. Filled when the sender is waiting for a response to the message
isResbooleantrue - the message is a response to the sent message
userobjectUser who sent the message
dataanyAny type of data sent in a message
routestringMessage path
actstringAction on the message path
systemobjectSystem Information Messages

If the wait time is longer than this.timeout = 10000, then the answer will be error: {message: 'timeout: 10000'} and will execute the this.onTimeout handler, which will   passed the parameter data

paramtypedescription
uidstringmessage id
userobjectUser who sent the message
routestringMessage path
actstringAction on the message path
systemobjectSystem Information Messages
timeoutnumberMaximum time to wait for a response
resobjectMessage reply. Will contain error
// set default timeout; default = 10000
jrfws.timeout = 5000;

jrfws.onTimeout = async ({data}) => {
      console.error(`Timeout: ${data.timeout} ms; uid: ${data.uid}; route: ${data.route}; act: ${data.act}`);
    };

async function callback({data}) {
  console.log(JSON.stringify(data));
}

// default timeout
const res = await jrfws.sendMes({route: 'users', callback});

// custom timeout
const res = await jrfws.sendMes({route: 'users', callback, options: {timeout: 1000}});

Send broadcast

For broadcast messages, the to parameter must be passed. If the parameter is empty, This message will be sent to all users. If the recipient user multiple client connections, then all user clients will receive a message. The user identification for this.user occurs by the unique fields ofthis.userSearchFields. In the message sent to the client, the confidential fields this.userExcludeFields = ['rights', 'token']; will be deleted from all user objects.

paramtypedescription
usersstringThe user who will receive the message. The string value of one of the unique fields of the user this.userSearchFields = ['username', 'email', 'phone']
usersarrayUsers who receive the message. There can be string values ​​of unique user fields, as well as user objects
excludeUsersstringExclude user from recipients. The string value of one of the unique fields of the user this.userSearchFields = ['username', 'email', 'phone']
excludeUsersarrayExclude users from recipients. There can be string values ​​of unique user fields, as well as user objects
groupsstringThe name of the group whose users will receive the message
groupsarrayThe groups whose users will receive messages. There can be both lines and group objects
excludeGroupsstringName of group to exclude from recipients
excludeGroupsarrayGroups to be excluded from recipients. There can be both lines and group objects
meeTobooleantrue - send message to yourself
// send all users
await jrfws.sendMes({data: 'hello', to: {}});

await jrfws.sendMes({
  route: 'chat',
  act: 'say',
  data: 'hello',
  to: {
    users: 'rick'
  }
});

await jrfws.sendMes({
  route: 'chat',
  act: 'say',
  data: 'hello',
  to: {
    users: ['rick', {email: 'morty@morty.mr'}]
  }
});

await jrfws.sendMes({
  route: 'chat',
  act: 'say',
  data: 'hello',
  to: {
    excludUsers: ['rick', {email: 'morty@morty.mr'}]
  }
});

await jrfws.sendMes({
  route: 'chat',
  act: 'say',
  data: 'hello',
  to: {
    groups: ['space', {name: 'waterLand'}],
    excludUsers: ['rick', {email: 'morty@morty.mr'}]
  }
});

hook - this.onBeforeSendMesTo executes before sending a message from the server to the client. If returns true, then the message is sent. Parameters client, from, to, mes.

hook - this.onAfterSendMesTo executes after sending a message from the server to the client. Parameters client, from, to, mes.

Hooks params

paramtypedescription
clientobjectThe client connection to which the message is sent
fromobjectThe user who sent the message fromUser. Groups for which the message groups
toobjectBroadcast options
mesobjectMessage to customer

Groups

Groups contain users to whom broadcast messages will be sent.

The add, get, del methods are available on the server and client. The groups themselves are stored on the server, hooks are also available on the server.

addGroup

Add group(s) will return true orfalse. Accepts the group parameter contain an array of groups.

paramtypedescription
namestringThe name of the group. must be unique
descriptionanyGroup Description
const res = await jrfws.addGroup({group: {name: 'space'}});

const res = await jrfws.addGroup({group: [{name: 'space'}, {name: 'waterWorld'}]});

hook - this.onBeforeAddGroup fulfills before adding a group. If returns true, then the group is added. Parameters mes, group, mesSendServer.

hook - this.onAfterAddGroup executes after adding a group. If it returns not true, then the group is deleted. Parameters mes, group, mesSendServer.

delGroup

Delete group(s) will return true orfalse. Takes the group parameter, maybe contain an array of groups.

paramtypedescription
groupstringGroup name
groupobjectGroup Object
grouparrayArray of groups
const res = await jrfws.delGroup({group: 'space'});

const res = await jrfws.delGroup({group: {name: 'space'}});

const res = await jrfws.delGroup({group: ['space', {name: 'waterWorld'}]});

hook - this.onBeforeDelGroup fulfills before deleting the group. If returns true, then the group is deleted. Parameters mes, group, mesSendServer.

hook - this.onAfterDelGroup completes the deletion of the group. Parameters mes, group, mesSendServer.

getGroups

Get groups.

const groups = await jrfws.getGroups();

hook - this.onBeforeGetGroup fulfills before receiving groups. If returns true, then a list of groups will be formed. Parameters mes, mesSendServer.

hook - this.onAfterGetGroup executes after receiving groups. Parameters mes, groups, mesSendServer.

Hooks params

paramtypedescription
mesobjectMessage action initiator
grouparray/string/objectGroup
groupsobjectGroups {group1: {name: 'group1', description: '', users: []}, group2: {name: 'group2', description: '', users: []}}
mesSendServerbooleantrue - initiator of the server action,false - initiator of the client

addUserToGroup

Add user to group. Will return true orfalse. Accepts the parameters group anduser.

paramtypedescription
userstring/objectWhose user you want to add to the group
groupstring/objectGroup to which user is added

hook - this.onBeforeAddUserToGroup fulfills before adding a user. If returns true, then the user is added to the group. Parameters mes, group, user, clients, mesSendServer.

hook - this.onAfterAddUserToGroup executes after adding a user. Parameters mes, group, user, clients, mesSendServer.

delUserToGroup

Remove user from group. Will return true orfalse. Accepts the parameters group anduser.

paramtypedescription
userstring / objectThe user whose you want to remove from the group
groupstring / objectThe group from which the user is deleted

hook - this.onBeforeDelUserToGroup fulfills before deleting the user. If returns true, then the user is removed from the group. Parameters mes, group, user, clients, mesSendServer.

hook - this.onAfterDelUserToGroup executes after deleting the user. Parameters mes, group, user, clients, mesSendServer.

Hooks params

paramtypedescription
mesobjectMessage action initiator
groupstring/objectGroup
userstring/objectUser
clientssetClient Connection Collection this.wss.clients
mesSendServerbooleantrue - initiator of the server action,false - initiator of the client

User

User is a user object that identifies the server and client connection. One user can have several client connections at the same time. For example, connections from different devices.

By default, the user has the following fields. But they can be changed.

this.user = {
      username: '',
      email: '',
      token: '',
      rights: null,
      phone: '',
      uid: this._generateId()
    };

To identify the user in client connections. Unique fields are set.

Default.

this.userSearchFields = ['username', 'email', 'phone'];

To send user information to a message. You can define a list of fields.

Default.

this.userIncludeFields = ['email', 'phone', 'username', 'uid'];

In order not to transmit confidential information in messages. You can define a list of fields.

Default.

this.userExcludeFields = ['rights', 'token'];

User Authentication

When a message arrives, it is checked that the user is mapped to a client connection. If the user is not mapped, the user this.defaultClientUser is assigned to the client connection.

Next, the authentication function this.auth is executed, if one is defined. After fulfills the routing of the message.

this.auth = async ({user, data, client, action}) => {};

user - The user of the client connection (on the server side)user is a copy of client.user

data - This is a message received from the client.

data.user - Client connection user (on the client side). You can, for example, check user.token anddata.user.token.

client - Client connection. Which has a method of client.sendMes. Associated user client.user.

action - Authentication actionsobject {stop: false, terminate: false}. action.stop = true - stop message routing, client connection is not disconnected. action.terminate = true - break the client connection.

jrfwsSrv.auth = async ({user, client, data, action}) => {

if (data.route !== 'auth') {

  if (!data.user || !data.user.token) {

    action.stop = true;
    return;

  } else {

    action.stop = !data.user.token.includes('token');
    return;

  }

}

if (!data.user || !data.user.email) {
  action.stop = false;
  return;
}

client.user = data.user;

}

Logging

By default, all logs are displayed in console. The output to console can be undone byjrfws.consoleLog = false.

You can set the log processing function jrfws.onConsoleLog = async ({log}) => {}.

logclient / serverdescription
WebSocket connection establishedclientServer connection established
WebSocket errorclientError connecting to server
No clientserverAttempting to send a message without specifying a client connection
No dataallTrying to send a message without specifying data, route, act
Error broadcast object to: $ {to}allUnsuccessful attempt to broadcast
Error sendMes $ {e}allError sending message