diff --git a/.docker-compose/Dockerfile b/.docker-compose/Dockerfile index 40e04ba..c5fa404 100755 --- a/.docker-compose/Dockerfile +++ b/.docker-compose/Dockerfile @@ -15,7 +15,4 @@ RUN npm install # Bundle app source COPY . /app -ENV PORT 8443 -EXPOSE 8443 - CMD [ "node", "./dist/server/server.js" ] diff --git a/Dockerfile b/Dockerfile index 2728f8d..f489647 100755 --- a/Dockerfile +++ b/Dockerfile @@ -15,7 +15,4 @@ RUN npm install # Bundle app source COPY . /app -ENV PORT 8443 -EXPOSE 8443 - CMD [ "node", "./dist/server/server.js" ] diff --git a/README.md b/README.md index e38e7d2..3676aac 100755 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# BRAID v1.0.8 +# BRAID v1.2 > Websocket server for the Measure platform [![Build Status](https://semaphoreci.com/api/v1/projects/7767f0f3-4da6-4c84-9167-4db5402a3262/2573412/badge.svg)](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', diff --git a/docker-compose.yml b/docker-compose.yml index 98f581d..a688d37 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,7 +5,5 @@ braid: MEASURE_BASE_URL: http://admin.localhost dns: - 172.17.0.1 - ports: - - "8443" volumes: - /apps/braid:/app diff --git a/package-lock.json b/package-lock.json index 08f0605..ad74586 100755 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "braid", - "version": "1.0.0", + "version": "1.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index bc125f4..a620085 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "braid", - "version": "1.0.0", + "version": "1.2.0", "description": "", "main": "index.js", "scripts": { diff --git a/rc b/rc index 6917759..f4b4dd2 100755 --- a/rc +++ b/rc @@ -6,4 +6,4 @@ if [ -f .user_rc ]; then . .user_rc fi -. .vagrantrc +source .vagrantrc diff --git a/src/channelManager.ts b/src/channelManager.ts index 55c4ca2..745fac8 100644 --- a/src/channelManager.ts +++ b/src/channelManager.ts @@ -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}`); } diff --git a/src/channels/channelBase.ts b/src/channels/channelBase.ts index 9e76470..453aa7a 100644 --- a/src/channels/channelBase.ts +++ b/src/channels/channelBase.ts @@ -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; diff --git a/src/channels/services/studentProgressMonitorChannel.ts b/src/channels/services/studentProgressMonitorChannel.ts deleted file mode 100644 index d0744f4..0000000 --- a/src/channels/services/studentProgressMonitorChannel.ts +++ /dev/null @@ -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; diff --git a/src/channels/types/customChannel.ts b/src/channels/types/customChannel.ts new file mode 100644 index 0000000..9c768a1 --- /dev/null +++ b/src/channels/types/customChannel.ts @@ -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; diff --git a/src/channels/types/privateChannel.ts b/src/channels/types/privateChannel.ts new file mode 100644 index 0000000..7dc6b03 --- /dev/null +++ b/src/channels/types/privateChannel.ts @@ -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; diff --git a/src/channels/types/publicChannel.ts b/src/channels/types/publicChannel.ts new file mode 100644 index 0000000..07daaa1 --- /dev/null +++ b/src/channels/types/publicChannel.ts @@ -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; diff --git a/src/clientManager.ts b/src/clientManager.ts index cba1fed..592ec53 100644 --- a/src/clientManager.ts +++ b/src/clientManager.ts @@ -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); } } }; diff --git a/src/clients/clientBase.ts b/src/clients/clientBase.ts index 1705090..f9e997b 100644 --- a/src/clients/clientBase.ts +++ b/src/clients/clientBase.ts @@ -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; 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; diff --git a/src/clients/servicesClient.ts b/src/clients/servicesClient.ts deleted file mode 100644 index 5451df2..0000000 --- a/src/clients/servicesClient.ts +++ /dev/null @@ -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; - diff --git a/src/clients/types/customClient.ts b/src/clients/types/customClient.ts new file mode 100644 index 0000000..2732b70 --- /dev/null +++ b/src/clients/types/customClient.ts @@ -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; + diff --git a/src/clients/types/privateClient.ts b/src/clients/types/privateClient.ts new file mode 100644 index 0000000..616838f --- /dev/null +++ b/src/clients/types/privateClient.ts @@ -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; + diff --git a/src/clients/types/publicClient.ts b/src/clients/types/publicClient.ts new file mode 100644 index 0000000..f66c3f4 --- /dev/null +++ b/src/clients/types/publicClient.ts @@ -0,0 +1,8 @@ +import ClientBase from '../clientBase'; + +var logger = require('../../logger'); + +class PublicClient extends ClientBase {}; + +export default PublicClient; + diff --git a/src/config/app.ts b/src/config/app.ts index f2887a5..9d50ae3 100644 --- a/src/config/app.ts +++ b/src/config/app.ts @@ -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'] } diff --git a/src/controllers/authController.ts b/src/controllers/authController.ts index da42ae7..2f3c9ad 100644 --- a/src/controllers/authController.ts +++ b/src/controllers/authController.ts @@ -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)) }); diff --git a/src/messageManager.ts b/src/messageManager.ts index f70c0b5..6566fa2 100644 --- a/src/messageManager.ts +++ b/src/messageManager.ts @@ -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 diff --git a/src/server.ts b/src/server.ts index 0221806..5e56bc2 100755 --- a/src/server.ts +++ b/src/server.ts @@ -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); diff --git a/src/services/validations.ts b/src/services/validations.ts new file mode 100644 index 0000000..3c09c7d --- /dev/null +++ b/src/services/validations.ts @@ -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; \ No newline at end of file diff --git a/src/test/channelBase.spec.ts b/src/test/channelBase.spec.ts index 806fa30..9a71ab0 100644 --- a/src/test/channelBase.spec.ts +++ b/src/test/channelBase.spec.ts @@ -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'); }); diff --git a/src/test/channelManager.spec.ts b/src/test/channelManager.spec.ts index 5c45820..690442b 100644 --- a/src/test/channelManager.spec.ts +++ b/src/test/channelManager.spec.ts @@ -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'); }); diff --git a/src/test/clientManager.spec.ts b/src/test/clientManager.spec.ts index 962602c..29ffa4d 100644 --- a/src/test/clientManager.spec.ts +++ b/src/test/clientManager.spec.ts @@ -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'); }); }); diff --git a/src/test/mhsChannel.spec.ts b/src/test/mhsChannel.spec.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/test/mhsClient.spec.ts b/src/test/mhsClient.spec.ts deleted file mode 100644 index e69de29..0000000