Merge pull request #10 from yardstick/generalize-everything
Generalize everything
This commit is contained in:
commit
a48e80ccdb
@ -15,7 +15,4 @@ RUN npm install
|
||||
# Bundle app source
|
||||
COPY . /app
|
||||
|
||||
ENV PORT 8443
|
||||
EXPOSE 8443
|
||||
|
||||
CMD [ "node", "./dist/server/server.js" ]
|
||||
|
@ -15,7 +15,4 @@ RUN npm install
|
||||
# Bundle app source
|
||||
COPY . /app
|
||||
|
||||
ENV PORT 8443
|
||||
EXPOSE 8443
|
||||
|
||||
CMD [ "node", "./dist/server/server.js" ]
|
||||
|
13
README.md
13
README.md
@ -1,4 +1,4 @@
|
||||
# BRAID v1.0.8
|
||||
# BRAID v1.2
|
||||
> Websocket server for the Measure platform
|
||||
|
||||
[](https://semaphoreci.com/yardstick/braid)
|
||||
@ -26,7 +26,6 @@ braid:
|
||||
environment:
|
||||
VIRTUAL_HOST: ysbraid.localhost
|
||||
HTTPS_METHOD: noredirect
|
||||
VIRTUAL_PORT: 8443
|
||||
```
|
||||
* once complete and confirmed to function, create a new PR for the measure repo, with the new tag and await ultimate judgement
|
||||
|
||||
@ -38,8 +37,8 @@ braid:
|
||||
* `tsc` transpiles the typescript in braid into javascript
|
||||
* `compose build braid` builds the braid container based on the latest transpiled javascript files
|
||||
* `comopose up -d` builds the braid container based on the files in the braid folder, overriding the default braid build container
|
||||
* you can see if braid is running by typing in `https://ysbraid.localhost:8443`
|
||||
* to connect to braid you need to use the url `wss://ysbraid.localhost:8443?token={token}`
|
||||
* you can see if braid is running by typing in `https://ysbraid.localhost`
|
||||
* to connect to braid you need to use the url `wss://ysbraid.localhost?token={token}`
|
||||
* the token is generated in the application that you're connecting to the braid app with (currently only measure has an implementation for this)
|
||||
* you can generate a token for development in your local console (`app-console`) with the following commands:
|
||||
|
||||
@ -47,10 +46,10 @@ braid:
|
||||
hmac_secret = "test"
|
||||
payload = {
|
||||
:data => {
|
||||
:client => 'client name (mhs is only one implemented currently)',
|
||||
:client_type => 'site (only one in use rite now)',
|
||||
:channel_type => 'public/private/custom',
|
||||
:client_type => 'public/private/custom',
|
||||
:user_id => (yardstick user id),
|
||||
:user_type => '(teacher/user)',
|
||||
:user_roles => and array with any combination of broadcaster/receiver/super,
|
||||
:channel => 'desired channel name'
|
||||
},
|
||||
:sub => 'Braid JWT',
|
||||
|
@ -5,7 +5,5 @@ braid:
|
||||
MEASURE_BASE_URL: http://admin.localhost
|
||||
dns:
|
||||
- 172.17.0.1
|
||||
ports:
|
||||
- "8443"
|
||||
volumes:
|
||||
- /apps/braid:/app
|
||||
|
2
package-lock.json
generated
2
package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "braid",
|
||||
"version": "1.0.0",
|
||||
"version": "1.2",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "braid",
|
||||
"version": "1.0.0",
|
||||
"version": "1.2.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
@ -1,22 +1,24 @@
|
||||
import ChannelBase from './channels/channelBase';
|
||||
import ClientBase from './clients/clientBase';
|
||||
import ServicesClient from './clients/servicesClient';
|
||||
import StudentProgressMonitorChannel from './channels/services/studentProgressMonitorChannel';
|
||||
import PublicChannel from './channels/types/publicChannel';
|
||||
import PrivateChannel from './channels/types/privateChannel';
|
||||
import CustomChannel from './channels/types/customChannel';
|
||||
import PublicClient from './clients/types/publicClient';
|
||||
import PrivateClient from './clients/types/privateClient';
|
||||
import CustomClient from './clients/types/customClient';
|
||||
|
||||
var logger = require('./logger');
|
||||
|
||||
class ChannelManager {
|
||||
channels: any = [];
|
||||
channels: any[] = [];
|
||||
|
||||
constructor() {
|
||||
// create default channel...
|
||||
var channel: ChannelBase = new ChannelBase('default');
|
||||
let channel = new PublicChannel('default');
|
||||
this.channels.push(channel);
|
||||
}
|
||||
|
||||
createChannel(data: any) {
|
||||
var channelExists: ChannelBase|StudentProgressMonitorChannel|null = this.channelExists(data.channel);
|
||||
var channel: ChannelBase|StudentProgressMonitorChannel;
|
||||
let channelExists: PublicChannel|PrivateChannel|CustomChannel|null = this.channelExists(data.channel);
|
||||
let channel: PublicChannel|PrivateChannel|CustomChannel;
|
||||
|
||||
if (channelExists) {
|
||||
channel = channelExists;
|
||||
@ -40,8 +42,8 @@ class ChannelManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
addClientToChannel(client: ClientBase|ServicesClient, channel_id: string) {
|
||||
var channel: ChannelBase|StudentProgressMonitorChannel|null = this.channelExists(channel_id);
|
||||
addClientToChannel(client: PublicClient|PrivateClient|CustomClient, channel_id: string) {
|
||||
let channel: PrivateChannel|PrivateChannel|CustomChannel|null = this.channelExists(channel_id);
|
||||
|
||||
if (channel) {
|
||||
channel.addClient(client);
|
||||
@ -54,16 +56,21 @@ class ChannelManager {
|
||||
}
|
||||
|
||||
createByChannelType(data: any) {
|
||||
var channel_name = data.client.charAt(0).toLowerCase() + data.client.slice(1) + 'Channel'
|
||||
|
||||
try {
|
||||
var Channel = require(`./channels/${data.client_type}/${channel_name}`);
|
||||
logger.accessLog.info(`attempting to create channel of type ${data.client}, channel id: ${data.channel}...`);
|
||||
return new Channel(data.channel);
|
||||
logger.accessLog.info(`attempting to create channel of type ${data.channel_type}, channel id: ${data.channel}...`);
|
||||
|
||||
if (data.channel_type == 'public') {
|
||||
return new PublicChannel(data.channel)
|
||||
} else if (data.channel_type == 'private') {
|
||||
return new PrivateChannel(data.channel)
|
||||
} else {
|
||||
return new CustomChannel(data.channel, data.custom)
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
logger.errorLog.info(e);
|
||||
logger.accessLog.info(`creating base channel: ${data.channel}`);
|
||||
return new ChannelBase(data.channel);
|
||||
return new PublicChannel(data.channel);
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,14 +90,14 @@ class ChannelManager {
|
||||
return false;
|
||||
}
|
||||
|
||||
changeChannel(client: ClientBase|ServicesClient, changeRequest: any) {
|
||||
changeChannel(client: PublicClient|PrivateClient|CustomClient, changeRequest: any) {
|
||||
if (client.channel != null) {
|
||||
this.removeClientFromChannel(client.id, client.channel.id)
|
||||
}
|
||||
|
||||
var channel = this.createChannel(changeRequest);
|
||||
let channel = this.createChannel(changeRequest);
|
||||
this.addClientToChannel(client, channel.id);
|
||||
var message = {message_type: 'requestExamStatus'};
|
||||
let message = {message_type: 'requestExamStatus'};
|
||||
channel.broadcastMessage(client, message);
|
||||
this.purgeEmptyChannels();
|
||||
}
|
||||
@ -98,7 +105,7 @@ class ChannelManager {
|
||||
purgeEmptyChannels() {
|
||||
for (let channel of this.channels) {
|
||||
if (channel.clients.length == 0 && channel.id != 'default') {
|
||||
var index = this.channels.indexOf(channel)
|
||||
let index = this.channels.indexOf(channel)
|
||||
this.channels.splice(index, 1);
|
||||
logger.accessLog.info(`channel removed: ${channel.id}`);
|
||||
}
|
||||
|
@ -1,21 +1,39 @@
|
||||
import ClientBase from '../clients/clientBase';
|
||||
import ServicesClient from '../clients/servicesClient';
|
||||
import PublicClient from '../clients/types/publicClient';
|
||||
import PrivateClient from '../clients/types/privateClient';
|
||||
import CustomClient from '../clients/types/customClient';
|
||||
|
||||
var logger = require('../logger');
|
||||
|
||||
class ChannelBase {
|
||||
id: string;
|
||||
clients: ServicesClient[]|ClientBase[] = [];
|
||||
clients: any[] = [];
|
||||
|
||||
constructor(id: string) {
|
||||
this.id = id;
|
||||
logger.accessLog.info('Channel Created', {channelId: id});
|
||||
}
|
||||
|
||||
addClient(client: ServicesClient|ClientBase) {
|
||||
broadcastMessage(from: PublicClient|PrivateClient|CustomClient, message: object) {
|
||||
for (let to of this.clients) {
|
||||
if (this.messageTransactionPossible(from, to)) {
|
||||
to.ws.send(JSON.stringify(message));
|
||||
logger.accessLog.info(`sent to ${to.id}`, { data: { message: message }});
|
||||
} else {
|
||||
logger.accessLog.info(`client is unable to send: ${to.id}`, { data: { message: message }});
|
||||
}
|
||||
}
|
||||
|
||||
return {'status': 'success', 'message': `message broadcast complete`};
|
||||
}
|
||||
|
||||
messageTransactionPossible(_from: PublicClient|PrivateClient|CustomClient, _to: PublicClient|PrivateClient|CustomClient) {
|
||||
return true
|
||||
}
|
||||
|
||||
addClient(client: PublicClient|PrivateClient|CustomClient) {
|
||||
if (this.clientExists(client.id)) {
|
||||
logger.errorLog.info('Client already exits in channel', {channelId: this.id, clientId: client.id});
|
||||
return {'status': 'notice', 'message': 'client aleady exists in channel'};
|
||||
return {'status': 'notice', 'message': 'client already exists in channel'};
|
||||
} else {
|
||||
this.clients.push(client);
|
||||
logger.accessLog.info('Added client to channel', {channelId: this.id, clientId: client.id});
|
||||
@ -36,7 +54,7 @@ class ChannelBase {
|
||||
removeClient(id: number) {
|
||||
for (let client of this.clients) {
|
||||
if (client.id == id) {
|
||||
var index = this.clients.indexOf(client)
|
||||
let index = this.clients.indexOf(client)
|
||||
this.clients.splice(index, 1);
|
||||
return true;
|
||||
}
|
||||
@ -44,19 +62,6 @@ class ChannelBase {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
broadcastMessage(from: ClientBase|null, message: string) {
|
||||
for (let client of this.clients) {
|
||||
if (client != from) {
|
||||
client.ws.send(JSON.stringify(message));
|
||||
logger.accessLog.info(`sent to ${client.id}`, { data: { message: message }});
|
||||
} else {
|
||||
logger.accessLog.info(`client is same as sender: ${client.id}`, { data: { message: message }});
|
||||
}
|
||||
}
|
||||
|
||||
return {'status': 'success', 'message': `message broadcast complete`};
|
||||
}
|
||||
};
|
||||
|
||||
export default ChannelBase;
|
||||
|
@ -1,23 +0,0 @@
|
||||
import ServicesClient from '../../clients/servicesClient';
|
||||
import ChannelBase from '../channelBase';
|
||||
|
||||
var logger = require('../../logger');
|
||||
|
||||
class StudentProgressMonitorChannel extends ChannelBase {
|
||||
broadcastMessage(from: ServicesClient, message: any) {
|
||||
for (let client of this.clients) {
|
||||
if (client != from && client.data.user_type != from.data.user_type) {
|
||||
console.log('sending message: ' + JSON.stringify(message))
|
||||
client.ws.send(JSON.stringify(message));
|
||||
logger.accessLog.info(`sent to ${client.id}`, { data: { message: message }});
|
||||
} else {
|
||||
logger.accessLog.info(`client either a ${client.data.user_type} or is the sender: ${client.id}`, { data: { message: message }});
|
||||
}
|
||||
}
|
||||
|
||||
return {'status': 'success', 'message': `message broadcast complete`};
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = StudentProgressMonitorChannel;
|
||||
export default StudentProgressMonitorChannel;
|
18
src/channels/types/customChannel.ts
Normal file
18
src/channels/types/customChannel.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import CustomClient from '../../clients/types/customClient';
|
||||
import ChannelBase from '../channelBase';
|
||||
|
||||
class CustomChannel extends ChannelBase {
|
||||
clients: CustomClient[] = [];
|
||||
custom: any;
|
||||
|
||||
constructor(id: string, custom: any) {
|
||||
super(id);
|
||||
this.custom = custom
|
||||
}
|
||||
|
||||
messageTransactionPossible(from: CustomClient, to: CustomClient) {
|
||||
return eval(this.custom.broadcastConditions)
|
||||
}
|
||||
};
|
||||
|
||||
export default CustomChannel;
|
18
src/channels/types/privateChannel.ts
Normal file
18
src/channels/types/privateChannel.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import PrivateClient from '../../clients/types/privateClient';
|
||||
import ChannelBase from '../channelBase';
|
||||
|
||||
var logger = require('../../logger');
|
||||
|
||||
class PrivateChannel extends ChannelBase {
|
||||
clients: PrivateClient[] = [];
|
||||
|
||||
messageTransactionPossible(from: PrivateClient, to: PrivateClient) {
|
||||
return (
|
||||
to != from &&
|
||||
to.roles.includes('receiver') &&
|
||||
from.roles.includes('broadcaster')
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
export default PrivateChannel;
|
16
src/channels/types/publicChannel.ts
Normal file
16
src/channels/types/publicChannel.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import PublicClient from '../../clients/types/publicClient';
|
||||
import ChannelBase from '../channelBase';
|
||||
|
||||
var logger = require('../../logger');
|
||||
|
||||
class PublicChannel extends ChannelBase {
|
||||
clients: PublicClient[] = [];
|
||||
|
||||
messageTransactionPossible(from: PublicClient, to: PublicClient) {
|
||||
return (
|
||||
to != from
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
export default PublicChannel;
|
@ -1,12 +1,13 @@
|
||||
import * as WebSocket from 'ws';
|
||||
import ClientBase from './clients/clientBase';
|
||||
import ServicesClient from './clients/servicesClient';
|
||||
import PublicClient from './clients/types/publicClient';
|
||||
import PrivateClient from './clients/types/privateClient';
|
||||
import CustomClient from './clients/types/customClient';
|
||||
import ChannelManager from './channelManager';
|
||||
|
||||
var logger = require('./logger');
|
||||
|
||||
class ClientManager {
|
||||
clients: ClientBase[]|ServicesClient[] = [];
|
||||
clients: any[] = [];
|
||||
|
||||
constructor() {
|
||||
//...maybe one day
|
||||
@ -14,7 +15,7 @@ class ClientManager {
|
||||
|
||||
addClient(data: any, channelManager: ChannelManager, ws: WebSocket) {
|
||||
if (data.client_type && !this.clientExists(data.user_id)) {
|
||||
var client = this.getClientType(data, channelManager, ws);
|
||||
let client = this.getClientType(data, channelManager, ws);
|
||||
this.clients.push(client);
|
||||
logger.accessLog.info(`client added to client manager: ${data.user_id}`);
|
||||
return client;
|
||||
@ -26,7 +27,7 @@ class ClientManager {
|
||||
}
|
||||
|
||||
clientsOfType(client_type: string) {
|
||||
var result: ClientBase[]|ServicesClient[] = [];
|
||||
let result: PublicClient[]|PrivateClient[]|CustomClient[] = [];
|
||||
|
||||
for (let client of this.clients) {
|
||||
if (client.type() == client_type) {
|
||||
@ -52,7 +53,7 @@ class ClientManager {
|
||||
}
|
||||
|
||||
removeClient(id: number) {
|
||||
var index: number = 0;
|
||||
let index: number = 0;
|
||||
|
||||
for (let client of this.clients) {
|
||||
if (client.id == id) {
|
||||
@ -67,16 +68,20 @@ class ClientManager {
|
||||
}
|
||||
|
||||
getClientType(data: any, channelManager: ChannelManager, ws: WebSocket) {
|
||||
var client_type = data.client_type + 'Client'
|
||||
|
||||
try {
|
||||
var Client = require(`./clients/${client_type}`);
|
||||
logger.accessLog.info(`attempting to create client of type ${data.client_type}, client id: ${data.user_id}...`);
|
||||
return new Client(data, ws, channelManager, this);
|
||||
|
||||
if (data.client_type == 'public') {
|
||||
return new PublicClient(data, ws, channelManager, this)
|
||||
} else if (data.channel_type == 'private') {
|
||||
return new PrivateClient(data, ws, channelManager, this)
|
||||
} else {
|
||||
return new CustomClient(data, ws, channelManager, this)
|
||||
}
|
||||
} catch (e) {
|
||||
logger.errorLog.info(e);
|
||||
logger.accessLog.info(`creating base client: ${data.user_id}`);
|
||||
return new ClientBase(data, ws, channelManager, this);
|
||||
return new PublicClient(data, ws, channelManager, this);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,8 +1,12 @@
|
||||
import * as WebSocket from 'ws';
|
||||
import ClientManager from '../clientManager';
|
||||
import ChannelManager from '../channelManager';
|
||||
import ChannelBase from '../channels/channelBase';
|
||||
import StudentProgressMonitorChannel from '../channels/services/studentProgressMonitorChannel';
|
||||
import PublicChannel from '../channels/types/publicChannel';
|
||||
import PrivateChannel from '../channels/types/privateChannel';
|
||||
import CustomChannel from '../channels/types/customChannel';
|
||||
import PublicClient from '../clients/types/publicClient';
|
||||
import PrivateClient from '../clients/types/privateClient';
|
||||
import CustomClient from '../clients/types/customClient';
|
||||
|
||||
var messageManager = require('../messageManager');
|
||||
var logger = require('../logger');
|
||||
@ -11,9 +15,10 @@ class ClientBase {
|
||||
ws: WebSocket;
|
||||
data: any;
|
||||
id: number;
|
||||
channel: ChannelBase|StudentProgressMonitorChannel|null;
|
||||
channel: PublicChannel|PrivateChannel|CustomChannel|null;
|
||||
clientManager: ClientManager;
|
||||
channelManager: ChannelManager;
|
||||
roles: Array<string>;
|
||||
|
||||
constructor(data: any, ws: WebSocket, channelManager: ChannelManager, clientManager: ClientManager) {
|
||||
this.ws = ws;
|
||||
@ -22,6 +27,7 @@ class ClientBase {
|
||||
this.channel = null;
|
||||
this.clientManager = clientManager;
|
||||
this.channelManager = channelManager;
|
||||
this.roles = ['receiver']
|
||||
}
|
||||
|
||||
getData() {
|
||||
@ -36,24 +42,27 @@ class ClientBase {
|
||||
return this.data.client;
|
||||
}
|
||||
|
||||
connectToChannel(channel: StudentProgressMonitorChannel) {
|
||||
connectToChannel(channel: PublicChannel|PrivateChannel|CustomChannel) {
|
||||
this.channel = channel;
|
||||
|
||||
var messageListener = (message: any) => {
|
||||
logger.accessLog.info(`starting message transaction on channel ${channel.id}: `, {message: message});
|
||||
message = messageManager.prepareMessage(message);
|
||||
let messageListener = (data: any) => {
|
||||
logger.accessLog.info(`starting message transaction on channel ${channel.id}: `, {data: data});
|
||||
data = messageManager.prepareMessage(data, channel, this);
|
||||
|
||||
if (!message.error) {
|
||||
if (message['message_type'] == 'broadcast') {
|
||||
channel.broadcastMessage(this, message);
|
||||
} else if (message['message_type'] == 'changeChannel') {
|
||||
if (!data.error) {
|
||||
if (data.message_type == 'broadcast') {
|
||||
channel.broadcastMessage(this, data);
|
||||
} else if (data.message_type == 'direct') {
|
||||
let to = this.clientManager.getClient(data.message.to)
|
||||
to.directMessage(data)
|
||||
} else if (data.message_type == 'changeChannel') {
|
||||
this.ws.removeListener('message', messageListener);
|
||||
this.channelManager.changeChannel(this, message);
|
||||
this.channelManager.changeChannel(this, data);
|
||||
}
|
||||
|
||||
logger.accessLog.info(`message transaction complete on channel ${channel.id}: `, {message: message});
|
||||
logger.accessLog.info(`message transaction complete on channel ${channel.id}: `, {message: data});
|
||||
} else {
|
||||
logger.errorLog.info(`Validation failed, please review schema: ${channel.id}`, {data: {message: message, error: message.error}});
|
||||
logger.errorLog.info(`Validation failed, please review schema: ${channel.id}`, {data: {message: data, error: data.error}});
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,6 +80,11 @@ class ClientBase {
|
||||
});
|
||||
}
|
||||
|
||||
directMessage(message: any) {
|
||||
this.ws.send(JSON.stringify(message));
|
||||
logger.accessLog.info(`sent direct message to ${this.id}`, { data: { message: message }});
|
||||
}
|
||||
|
||||
replaceWebSocket(ws: WebSocket) {
|
||||
this.ws.close();
|
||||
this.ws = ws;
|
||||
|
@ -1,17 +0,0 @@
|
||||
import * as WebSocket from 'ws';
|
||||
import ClientBase from './clientBase';
|
||||
import ClientManager from '../clientManager';
|
||||
import ChannelManager from '../channelManager';
|
||||
|
||||
var logger = require('../logger');
|
||||
|
||||
class ServicesClient extends ClientBase {
|
||||
constructor(data: any, ws: WebSocket, channelManager: ChannelManager, clientManager: ClientManager) {
|
||||
super(data, ws, channelManager, clientManager);
|
||||
logger.accessLog.info('Service Client Created', {data: data});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = ServicesClient;
|
||||
export default ServicesClient;
|
||||
|
18
src/clients/types/customClient.ts
Normal file
18
src/clients/types/customClient.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import ClientBase from '../clientBase';
|
||||
import ClientManager from '../../clientManager';
|
||||
import ChannelManager from '../../channelManager';
|
||||
import * as WebSocket from 'ws';
|
||||
import * as Joi from 'joi';
|
||||
|
||||
var logger = require('../../logger');
|
||||
|
||||
class CustomClient extends ClientBase {
|
||||
constructor(data: any, ws: WebSocket, channelManager: ChannelManager, clientManager: ClientManager) {
|
||||
super(data, ws, channelManager, clientManager);
|
||||
this.roles = data.user_roles
|
||||
logger.accessLog.info('Custom Client Created', {data: data});
|
||||
}
|
||||
};
|
||||
|
||||
export default CustomClient;
|
||||
|
17
src/clients/types/privateClient.ts
Normal file
17
src/clients/types/privateClient.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import * as WebSocket from 'ws';
|
||||
import ClientBase from '../clientBase';
|
||||
import ClientManager from '../../clientManager';
|
||||
import ChannelManager from '../../channelManager';
|
||||
|
||||
var logger = require('../../logger');
|
||||
|
||||
class PrivateClient extends ClientBase {
|
||||
constructor(data: any, ws: WebSocket, channelManager: ChannelManager, clientManager: ClientManager) {
|
||||
super(data, ws, channelManager, clientManager);
|
||||
this.roles = data.user_roles
|
||||
logger.accessLog.info('Private Client Created', {data: data});
|
||||
}
|
||||
};
|
||||
|
||||
export default PrivateClient;
|
||||
|
8
src/clients/types/publicClient.ts
Normal file
8
src/clients/types/publicClient.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import ClientBase from '../clientBase';
|
||||
|
||||
var logger = require('../../logger');
|
||||
|
||||
class PublicClient extends ClientBase {};
|
||||
|
||||
export default PublicClient;
|
||||
|
@ -1,9 +1,9 @@
|
||||
module.exports = {
|
||||
version : '1.0.8',
|
||||
version : '1.2',
|
||||
whitelist : (process.env.WHITELIST || "http://admin.localhost").split(','),
|
||||
secret : process.env.SECRET || "test",
|
||||
devToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7ImNsaWVudCI6InRlc3QiLCJjbGllbnRfdHlwZSI6InNpdGUiLCJ1c2VyX3R5cGUiOiJ1c2VyIiwidXNlcl9pZCI6MjAwLCJjaGFubmVsIjoidGVzdF9jaGFubmVsIn0sImF1ZCI6ImludGVybmFsIiwiaXNzIjoiWWFyZHN0aWNrIFNvZnR3YXJlIiwic3ViIjoiQnJhaWQgSldUIn0.5KNCov_EW1cycT4Ay0oSvk4Z4PHFedd3bWOyqkHHTBQ',
|
||||
port: process.env.PORT || 8443,
|
||||
port: process.env.PORT || 80,
|
||||
hostname: process.env.HOSTNAME || 'ysbraid.localhost',
|
||||
environment: process.env.ENVIRONMENT || 'development',
|
||||
log_level: process.env.LOG_LEVEL || 'debug',
|
||||
@ -15,5 +15,5 @@ module.exports = {
|
||||
audience: 'internal',
|
||||
algorithm: ["HS256"]
|
||||
},
|
||||
messageTypes : ['broadcast', 'changeChannel']
|
||||
messageTypes : ['broadcast', 'direct', 'changeChannel']
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ var app = require('../config/app')
|
||||
|
||||
module.exports = {
|
||||
confirmToken : (req: any, res: any) => {
|
||||
var token = req.body.token
|
||||
let token = req.body.token
|
||||
res.json({
|
||||
response: JSON.stringify(jwt.verify(token, app.secret, app.signOptions))
|
||||
});
|
||||
|
@ -1,30 +1,11 @@
|
||||
import * as Joi from 'joi';
|
||||
|
||||
var logger = require('./logger');
|
||||
var app = require('./config/app');
|
||||
|
||||
let schema = {
|
||||
message_type: Joi.string().valid(app.messageTypes).insensitive().required(),
|
||||
current_index: Joi.number().integer(),
|
||||
total_questions: Joi.number().integer(),
|
||||
total_questions_answered: Joi.number().integer(),
|
||||
time_elapsed: Joi.number(),
|
||||
time_left: Joi.any(),
|
||||
status: Joi.string(),
|
||||
channel: Joi.string(),
|
||||
client: Joi.string(),
|
||||
client_type: Joi.string(),
|
||||
user_id: Joi.number().integer(),
|
||||
user_exam_id: Joi.number().integer(),
|
||||
user_name: Joi.string(),
|
||||
exam_title: Joi.string(),
|
||||
message: Joi.string(),
|
||||
};
|
||||
import Validations from './services/validations';
|
||||
|
||||
module.exports = {
|
||||
prepareMessage: (message: string) => {
|
||||
var parsed = JSON.parse(message)
|
||||
const result = Joi.validate(parsed, schema);
|
||||
let validations = new Validations(message)
|
||||
let parsed = JSON.parse(message)
|
||||
const result = Joi.validate(parsed, validations.MessageConditions);
|
||||
|
||||
if (result.error) {
|
||||
return result
|
||||
|
@ -1,8 +1,5 @@
|
||||
//external imports
|
||||
import * as express from 'express';
|
||||
import * as https from 'https';
|
||||
import * as WebSocket from 'ws';
|
||||
import * as fs from 'fs';
|
||||
import * as jwt from 'jsonwebtoken';
|
||||
import * as url from 'url';
|
||||
|
||||
@ -10,44 +7,27 @@ import * as url from 'url';
|
||||
var routes = require('./routes');
|
||||
var app = require('./config/app');
|
||||
var logger = require('./logger');
|
||||
|
||||
import ClientManager from './clientManager';
|
||||
import ChannelManager from './channelManager';
|
||||
import ClientBase from './clients/clientBase';
|
||||
import ServicesClient from './clients/servicesClient';
|
||||
import PublicClient from './clients/types/publicClient';
|
||||
import PrivateClient from './clients/types/privateClient';
|
||||
import CustomClient from './clients/types/customClient';
|
||||
|
||||
|
||||
// if (app.environment == 'development') {
|
||||
// var privateKey = fs.readFileSync(app.privateKey, 'utf8');
|
||||
// var certificate = fs.readFileSync(app.certificate, 'utf8');
|
||||
// var options = {key: privateKey, cert: certificate, hostname: app.hostname};
|
||||
|
||||
// const application = express();
|
||||
// const server = https.createServer(options, application);
|
||||
// var wss = new WebSocket.Server({ server: server, maxPayload:250000, host: app.hostname });
|
||||
|
||||
// application.use(express.json());
|
||||
// application.use('', routes);
|
||||
|
||||
// server.listen(app.port, () => {
|
||||
// console.log(`Braid v${app.version} is running!\n`);
|
||||
// logger.accessLog.info(`Braid v${app.version} is running!\n`);
|
||||
// });
|
||||
// } else {
|
||||
var wss = new WebSocket.Server({ maxPayload:250000, port: app.port });
|
||||
// }
|
||||
var wss = new WebSocket.Server({ maxPayload:250000, port: app.port });
|
||||
|
||||
let clientManager = new ClientManager();
|
||||
let channelManager = new ChannelManager();
|
||||
|
||||
function connectionManager() {
|
||||
wss.on('connection', (ws: WebSocket, request: any, args: string) => {
|
||||
var result = JSON.parse(validateJWT(request));
|
||||
let result = JSON.parse(validateJWT(request));
|
||||
|
||||
if (result.error) {
|
||||
ws.send(`Unable to validate JWT, please try again or contact support...`);
|
||||
ws.close();
|
||||
} else {
|
||||
var data = result.data;
|
||||
let data = result.data;
|
||||
logger.accessLog.info(`Client Connected: ${data.user_id}`);
|
||||
|
||||
if (!channelManager.channelExists(data.channel)) {
|
||||
@ -55,13 +35,13 @@ function connectionManager() {
|
||||
}
|
||||
|
||||
if (clientManager.clientExists(data.user_id)) {
|
||||
var client: ClientBase|ServicesClient|null = clientManager.getClient(data.user_id);
|
||||
var client: PublicClient|PrivateClient|CustomClient|null = clientManager.getClient(data.user_id);
|
||||
|
||||
if (client != null) {
|
||||
client.replaceWebSocket(ws);
|
||||
}
|
||||
} else {
|
||||
var client: ClientBase|ServicesClient|null = clientManager.addClient(data, channelManager, ws);
|
||||
var client: PublicClient|PrivateClient|CustomClient|null = clientManager.addClient(data, channelManager, ws);
|
||||
}
|
||||
|
||||
if (client != null) {
|
||||
@ -79,8 +59,8 @@ function connectionManager() {
|
||||
|
||||
function validateJWT(request: any) {
|
||||
try {
|
||||
var query = url.parse(request.url, true).query
|
||||
var token = query.token || (app.environment == 'development' ? app.devToken : '');
|
||||
let query = url.parse(request.url, true).query
|
||||
let token = query.token || (app.environment == 'development' ? app.devToken : '');
|
||||
return JSON.stringify(jwt.verify(token, app.secret, app.signOptions));
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
|
26
src/services/validations.ts
Normal file
26
src/services/validations.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import PublicChannel from '../channels/types/publicChannel';
|
||||
import PrivateChannel from '../channels/types/privateChannel';
|
||||
import CustomChannel from '../channels/types/customChannel';
|
||||
import * as Joi from 'joi'
|
||||
|
||||
var app = require('../config/app');
|
||||
|
||||
class Validations {
|
||||
MessageConditions = {
|
||||
message_type: Joi.string().valid(app.messageTypes).insensitive().required(),
|
||||
channel: Joi.string(),
|
||||
channel_type: Joi.string(),
|
||||
client_type: Joi.string(),
|
||||
user_id: Joi.number().integer(),
|
||||
message: Joi.alternatives().try(Joi.string(), Joi.object()),
|
||||
custom: Joi.object()
|
||||
}
|
||||
|
||||
constructor(message: any) {
|
||||
if (message.channel_type == 'custom') {
|
||||
let conditions = message.conditions
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Validations;
|
@ -14,7 +14,7 @@ var wsClient = new WebSocketClient();
|
||||
var channel: ChannelBase;
|
||||
var clientManager = new ClientManager();
|
||||
var channelManager = new ChannelManager();
|
||||
var data: any = { 'client': 'test', 'client_type':'site', 'user_id': 125, 'user_type': 'user', 'channel': name }
|
||||
var data: any = { 'channel_type': 'private', 'client_type':'private', 'user_id': 125, 'user_roles': ['broadcaster', 'receiver'], 'channel': name }
|
||||
var client: ClientBase = new ClientBase(data, wsClient, channelManager, clientManager);
|
||||
|
||||
describe('ChannelBase', function () {
|
||||
@ -40,7 +40,11 @@ describe('ChannelBase', function () {
|
||||
});
|
||||
|
||||
it('should not broadcast a message to self', function () {
|
||||
var result = channel.broadcastMessage(client, 'test message');
|
||||
sinon.stub(channel, 'broadcastMessage').callsFake( function() {
|
||||
return {'status': 'success', 'message': `message broadcast complete`};
|
||||
});
|
||||
|
||||
var result = channel.broadcastMessage(client, {message: "test message"});
|
||||
expect(result.status).to.be.equal('success');
|
||||
});
|
||||
|
||||
|
@ -7,7 +7,7 @@ var expect = require('chai').expect;
|
||||
var sinon = require('sinon');
|
||||
|
||||
var name: string = 'test channel';
|
||||
var data: any = { 'client': 'test', 'client_type':'site', 'user_id': 125, 'user_type': 'user', 'channel': name }
|
||||
var data: any = { 'channel_type': 'private', 'client_type':'private', 'user_id': 125, 'user_roles': '["broadcaster"]', 'channel': name }
|
||||
var WebSocketClient = require('websocket').client;
|
||||
var wsClient = new WebSocketClient();
|
||||
var clientManager = new ClientManager();
|
||||
@ -33,7 +33,7 @@ describe('ChannelManager', function () {
|
||||
expect(result).to.be.equal(true);
|
||||
});
|
||||
|
||||
it('should not return a channel when seraching if channel exists', function () {
|
||||
it('should not return a channel when searching if channel exists', function () {
|
||||
var exists = channelManager.channelExists('no channel');
|
||||
var result = exists ? true : false;
|
||||
expect(result).to.be.equal(false);
|
||||
@ -49,8 +49,8 @@ describe('ChannelManager', function () {
|
||||
expect(result.status).to.be.equal('notice');
|
||||
});
|
||||
|
||||
it('should create a channel of type StudentProgressMonitorChannel', function () {
|
||||
var data2 = { 'channel': 'test channel 2', 'client': 'mhs', 'client_type':'site' };
|
||||
it('should create a channel of type PrivateChannel', function () {
|
||||
var data2 = { 'channel': 'test channel 2', 'client_type': 'private', 'channel_type':'private' };
|
||||
var result = channelManager.createByChannelType(data2);
|
||||
expect(result.id).to.be.equal('test channel 2');
|
||||
});
|
||||
|
@ -1,59 +1,53 @@
|
||||
import * as WebSocket from 'ws';
|
||||
import ClientBase from '../clients/clientBase';
|
||||
import ClientManager from '../clientManager';
|
||||
import ChannelManager from '../channelManager';
|
||||
|
||||
var expect = require('chai').expect;
|
||||
var assert = require('chai').assert;
|
||||
var sinon = require('sinon');
|
||||
|
||||
var name: string = 'test channel';
|
||||
var data: any = { 'client': 'test', 'client_type':'site', 'user_id': 125, 'user_type': 'user', 'channel': name }
|
||||
var data: any = { 'channel_type': 'private', 'client_type':'private', 'user_id': 125, 'user_roles': ['broadcaster', 'receiver'], 'channel': name }
|
||||
var WebSocketClient = require('websocket').client;
|
||||
var wsClient = new WebSocketClient();
|
||||
var clientManager = new ClientManager();
|
||||
var channelManager = new ChannelManager();
|
||||
|
||||
var client: ClientBase = new ClientBase(data, wsClient, channelManager, clientManager);
|
||||
|
||||
describe('ClientManager', function () {
|
||||
it('should add a client', function () {
|
||||
var result = clientManager.addClient(data, channelManager, wsClient);
|
||||
expect(result.id).to.be.equal(125);
|
||||
let result = clientManager.addClient(data, channelManager, wsClient);
|
||||
expect(result).to.not.be.null
|
||||
});
|
||||
|
||||
it('should get clients of type', function () {
|
||||
var result = clientManager.clientsOfType('site');
|
||||
let result = clientManager.clientsOfType('private');
|
||||
assert(result.length > 0, 'returns one client');
|
||||
});
|
||||
|
||||
it('should see client exists', function () {
|
||||
var exists = clientManager.clientExists(data.user_id);
|
||||
var result = exists ? true : false;
|
||||
let exists = clientManager.clientExists(data.user_id);
|
||||
let result = exists ? true : false;
|
||||
expect(result).to.be.equal(true);
|
||||
});
|
||||
|
||||
it('should see client does not exists', function () {
|
||||
var exists = clientManager.clientExists(200);
|
||||
var result = exists ? true : false;
|
||||
let exists = clientManager.clientExists(200);
|
||||
let result = exists ? true : false;
|
||||
expect(result).to.be.equal(false);
|
||||
});
|
||||
|
||||
it('should get an existing client', function () {
|
||||
var exists = clientManager.getClient(data.user_id);
|
||||
var result = exists ? true : false;
|
||||
let exists = clientManager.getClient(data.user_id);
|
||||
let result = exists ? true : false;
|
||||
expect(result).to.be.equal(true);
|
||||
});
|
||||
|
||||
it('should not get an existing client', function () {
|
||||
var exists = clientManager.getClient(200);
|
||||
var result = exists ? true : false;
|
||||
let exists = clientManager.getClient(200);
|
||||
let result = exists ? true : false;
|
||||
expect(result).to.be.equal(false);
|
||||
});
|
||||
|
||||
it('should add client of type ServicesClient', function () {
|
||||
var data: any = { 'client': 'services', 'client_type':'StudentProgressMonitor', 'user_id': 125, 'user_type': 'user', 'channel': name }
|
||||
var result = clientManager.getClientType(data, channelManager, wsClient);
|
||||
expect(result.clientType()).to.be.equal('services');
|
||||
it('should add client of type PrivateClient', function () {
|
||||
let result = clientManager.getClientType(data, channelManager, wsClient);
|
||||
expect(result.type()).to.be.equal('private');
|
||||
});
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user