diff --git a/package.json b/package.json index a79810b..a620085 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "braid", - "version": "1.2", + "version": "1.2.0", "description": "", "main": "index.js", "scripts": { diff --git a/src/channelManager.ts b/src/channelManager.ts index d1d6a08..890689b 100644 --- a/src/channelManager.ts +++ b/src/channelManager.ts @@ -1,7 +1,9 @@ -import ChannelBase from './channels/channelBase'; -import ClientBase from './clients/clientBase'; -import PrivateClient from './clients/types/privateClient'; +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'); @@ -10,13 +12,13 @@ class ChannelManager { constructor() { // create default channel... - var channel: ChannelBase = new ChannelBase('default'); + var channel: PublicChannel = new PublicChannel('default'); this.channels.push(channel); } createChannel(data: any) { - var channelExists: ChannelBase|PrivateChannel|null = this.channelExists(data.channel); - var channel: ChannelBase|PrivateChannel; + var channelExists: PublicChannel|PrivateChannel|CustomChannel|null = this.channelExists(data.channel); + var channel: PublicChannel|PrivateChannel|CustomChannel; if (channelExists) { channel = channelExists; @@ -40,8 +42,8 @@ class ChannelManager { return null; } - addClientToChannel(client: ClientBase|PrivateClient, channel_id: string) { - var channel: ChannelBase|PrivateChannel|null = this.channelExists(channel_id); + addClientToChannel(client: PublicClient|PrivateClient|CustomClient, channel_id: string) { + var channel: PrivateChannel|PrivateChannel|CustomChannel|null = this.channelExists(channel_id); if (channel) { channel.addClient(client); @@ -63,7 +65,7 @@ class ChannelManager { } catch (e) { logger.errorLog.info(e); logger.accessLog.info(`creating base channel: ${data.channel}`); - return new ChannelBase(data.channel); + return new PublicChannel(data.channel); } } @@ -83,7 +85,7 @@ class ChannelManager { return false; } - changeChannel(client: ClientBase|PrivateClient, changeRequest: any) { + changeChannel(client: PublicClient|PrivateClient|CustomClient, changeRequest: any) { if (client.channel != null) { this.removeClientFromChannel(client.id, client.channel.id) } diff --git a/src/channels/channelBase.ts b/src/channels/channelBase.ts index 68a2a7d..908d9f6 100644 --- a/src/channels/channelBase.ts +++ b/src/channels/channelBase.ts @@ -1,21 +1,44 @@ -import ClientBase from '../clients/clientBase'; +import PublicClient from '../clients/types/publicClient'; import PrivateClient from '../clients/types/privateClient'; +import CustomClient from '../clients/types/customClient'; +import * as Joi from 'joi'; var logger = require('../logger'); class ChannelBase { id: string; - clients: PrivateClient[]|ClientBase[] = []; + clients: PrivateClient[]|PublicClient[]|CustomClient[] = []; constructor(id: string) { this.id = id; logger.accessLog.info('Channel Created', {channelId: id}); } - addClient(client: PrivateClient|ClientBase) { + validations() { + return {} + } + + 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}); @@ -44,19 +67,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/types/customChannel.ts b/src/channels/types/customChannel.ts index e69de29..b4a6fbb 100644 --- a/src/channels/types/customChannel.ts +++ b/src/channels/types/customChannel.ts @@ -0,0 +1,18 @@ +import CustomClient from '../../clients/types/customClient'; +import ChannelBase from '../channelBase'; +import * as Joi from 'joi'; + +class CustomChannel extends ChannelBase { + validations() { + return { + test: Joi.alternatives().try(Joi.string(), Joi.object()) + } + } + + messageTransactionPossible(from: CustomClient, to: CustomClient) { + return true + } +}; + +module.exports = CustomChannel; +export default CustomChannel; diff --git a/src/channels/types/privateChannel.ts b/src/channels/types/privateChannel.ts index 0215b24..c668b7a 100644 --- a/src/channels/types/privateChannel.ts +++ b/src/channels/types/privateChannel.ts @@ -4,18 +4,12 @@ import ChannelBase from '../channelBase'; var logger = require('../../logger'); class PrivateChannel extends ChannelBase { - broadcastMessage(from: PrivateClient, message: any) { - for (let client of this.clients) { - if (client != from && client.roles.includes('receiver') && from.roles.includes('broadcaster')) { - 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 is either just a broadcaster or is the sender: ${client.id}`, { data: { message: message }}); - } - } - - return {'status': 'success', 'message': `message broadcast complete`}; + messageTransactionPossible(from: PrivateClient, to: PrivateClient) { + return ( + to != from && + to.roles.includes('receiver') && + from.roles.includes('broadcaster') + ) } }; diff --git a/src/channels/types/publicChannel.ts b/src/channels/types/publicChannel.ts index e69de29..abdc902 100644 --- a/src/channels/types/publicChannel.ts +++ b/src/channels/types/publicChannel.ts @@ -0,0 +1,15 @@ +import PublicClient from '../../clients/types/publicClient'; +import ChannelBase from '../channelBase'; + +var logger = require('../../logger'); + +class PublicChannel extends ChannelBase { + messageTransactionPossible(from: PublicClient, to: PublicClient) { + return ( + to != from + ) + } +}; + +module.exports = PublicChannel; +export default PublicChannel; diff --git a/src/clientManager.ts b/src/clientManager.ts index 7910d66..be5ce06 100644 --- a/src/clientManager.ts +++ b/src/clientManager.ts @@ -1,12 +1,13 @@ import * as WebSocket from 'ws'; -import ClientBase from './clients/clientBase'; +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[]|PrivateClient[] = []; + clients: PublicClient[]|PrivateClient[]|CustomClient[] = []; constructor() { //...maybe one day @@ -26,7 +27,7 @@ class ClientManager { } clientsOfType(client_type: string) { - var result: ClientBase[]|PrivateClient[] = []; + var result: PublicClient[]|PrivateClient[]|CustomClient[] = []; for (let client of this.clients) { if (client.type() == client_type) { @@ -76,7 +77,7 @@ class ClientManager { } 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 e0e1ad5..966c5fe 100644 --- a/src/clients/clientBase.ts +++ b/src/clients/clientBase.ts @@ -1,8 +1,9 @@ import * as WebSocket from 'ws'; import ClientManager from '../clientManager'; import ChannelManager from '../channelManager'; -import ChannelBase from '../channels/channelBase'; -import Private from '../channels/types/privateChannel'; +import PublicChannel from '../channels/types/publicChannel'; +import PrivateChannel from '../channels/types/privateChannel'; +import CustomChannel from '../channels/types/customChannel'; var messageManager = require('../messageManager'); var logger = require('../logger'); @@ -11,7 +12,7 @@ class ClientBase { ws: WebSocket; data: any; id: number; - channel: ChannelBase|Private|null; + channel: PublicChannel|PrivateChannel|CustomChannel|null; clientManager: ClientManager; channelManager: ChannelManager; roles: Array; @@ -26,6 +27,10 @@ class ClientBase { this.roles = ['receiver'] } + validations() { + return {} + } + getData() { return this.data; } @@ -38,12 +43,12 @@ class ClientBase { return this.data.client; } - connectToChannel(channel: Private) { + 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); + message = messageManager.prepareMessage(message, channel, this); if (!message.error) { if (message['message_type'] == 'broadcast') { diff --git a/src/clients/types/customClient.ts b/src/clients/types/customClient.ts index e69de29..19b938b 100644 --- a/src/clients/types/customClient.ts +++ b/src/clients/types/customClient.ts @@ -0,0 +1,17 @@ +import ClientBase from '../clientBase'; +import * as Joi from 'joi'; + +class CustomClient extends ClientBase { + // validations() { + // return { + // test: Joi.alternatives().try(Joi.string(), Joi.object()) + // } + // } + validations() { + return { what: "test"} + } +}; + +module.exports = CustomClient; +export default CustomClient; + diff --git a/src/clients/types/privateClient.ts b/src/clients/types/privateClient.ts index 78d43fe..a5fec6c 100644 --- a/src/clients/types/privateClient.ts +++ b/src/clients/types/privateClient.ts @@ -6,7 +6,6 @@ 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 diff --git a/src/clients/types/publicClient.ts b/src/clients/types/publicClient.ts index e69de29..a97e585 100644 --- a/src/clients/types/publicClient.ts +++ b/src/clients/types/publicClient.ts @@ -0,0 +1,9 @@ +import ClientBase from '../clientBase'; + +var logger = require('../../logger'); + +class PublicClient extends ClientBase {}; + +module.exports = PublicClient; +export default PublicClient; + diff --git a/src/messageManager.ts b/src/messageManager.ts index 906c7d6..294b356 100644 --- a/src/messageManager.ts +++ b/src/messageManager.ts @@ -1,21 +1,31 @@ import * as Joi from 'joi'; +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'); var app = require('./config/app'); -let schema = { - 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()), +let schema = (channel: PublicChannel|PrivateChannel|CustomChannel, client: PublicClient|PrivateClient|CustomClient) => { + let validations = { + 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()) + } + + return {...validations, ...channel.validations, ...client.validations} }; module.exports = { - prepareMessage: (message: string) => { + prepareMessage: (message: string, channel: PublicChannel|PrivateChannel|CustomChannel, client: PublicClient|PrivateClient|CustomClient) => { var parsed = JSON.parse(message) - const result = Joi.validate(parsed, schema); + const result = Joi.validate(parsed, schema(channel, client)); if (result.error) { return result diff --git a/src/test/channelBase.spec.ts b/src/test/channelBase.spec.ts index 806fa30..a302093 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 () { diff --git a/src/test/clientManager.spec.ts b/src/test/clientManager.spec.ts index 6afd07d..4e8a418 100644 --- a/src/test/clientManager.spec.ts +++ b/src/test/clientManager.spec.ts @@ -8,7 +8,7 @@ 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(); @@ -23,7 +23,7 @@ describe('ClientManager', function () { }); it('should get clients of type', function () { - var result = clientManager.clientsOfType('site'); + var result = clientManager.clientsOfType('private'); assert(result.length > 0, 'returns one client'); }); @@ -52,7 +52,7 @@ describe('ClientManager', function () { }); it('should add client of type PrivateClient', function () { - var data: any = { 'channel_type': 'private', 'client_type':'private', 'user_id': 125, 'user_roles': '["broadcaster", "receiver"]', 'channel': name } + var data: any = { 'channel_type': 'private', 'client_type': 'private', 'user_id': 125, 'user_roles': ['broadcaster', 'receiver'], 'channel': name } var result = clientManager.getClientType(data, channelManager, wsClient); expect(result.clientType()).to.be.equal('private'); });