Compare commits

..

20 Commits

Author SHA1 Message Date
057476ae66 1.0.51 2019-06-10 17:46:07 +02:00
cb80e4dc2e fix(core): update 2019-06-10 17:46:06 +02:00
8410e09a4d 1.0.50 2019-06-07 11:49:10 +02:00
eb04abddbf fix(core): update 2019-06-07 11:49:10 +02:00
57809d9b53 1.0.49 2019-06-06 23:23:37 +02:00
bee5231d47 fix(core): update 2019-06-06 23:23:37 +02:00
df45287026 1.0.48 2019-06-06 22:22:45 +02:00
b5b6ca81cf fix(core): update 2019-06-06 22:22:45 +02:00
dc80e3b48d 1.0.47 2019-04-30 19:16:04 +02:00
043d795013 fix(core): update 2019-04-30 19:16:03 +02:00
29c0c8dc23 1.0.46 2019-04-28 12:42:09 +02:00
8157f2a56b fix(core): update 2019-04-28 12:42:08 +02:00
2f528d1812 1.0.45 2019-04-24 23:27:58 +02:00
139c71a451 fix(core): update 2019-04-24 23:27:57 +02:00
4678e44d16 1.0.44 2019-04-24 18:20:32 +02:00
af9f590349 fix(core): update 2019-04-24 18:20:31 +02:00
d43ad80784 1.0.43 2019-04-23 00:28:57 +02:00
b1017121ea fix(core): update 2019-04-23 00:28:57 +02:00
a8a91b4db2 1.0.42 2019-04-22 23:23:36 +02:00
67c4b06c4d fix(core): update 2019-04-22 23:23:36 +02:00
20 changed files with 771 additions and 524 deletions

20
.gitignore vendored
View File

@ -1,6 +1,22 @@
.nogit/ .nogit/
node_modules/
dist/ # artifacts
coverage/ coverage/
public/ public/
pages/ pages/
# installs
node_modules/
# caches
.yarn/
.cache/
.rpt2_cache
# builds
dist/
dist_web/
dist_serve/
dist_ts_web/
# custom

View File

@ -1,4 +1,4 @@
# gitzone standard # gitzone ci_default
image: hosttoday/ht-docker-node:npmci image: hosttoday/ht-docker-node:npmci
cache: cache:
@ -78,19 +78,11 @@ release:
# ==================== # ====================
codequality: codequality:
stage: metadata stage: metadata
image: docker:stable
allow_failure: true allow_failure: true
services:
- docker:stable-dind
script: script:
- export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/') - npmci command npm install -g tslint typescript
- docker run - npmci npm install
--env SOURCE_CODE="$PWD" - npmci command "tslint -c tslint.json ./ts/**/*.ts"
--volume "$PWD":/code
--volume /var/run/docker.sock:/var/run/docker.sock
"registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code
artifacts:
paths: [codeclimate.json]
tags: tags:
- docker - docker
- priv - priv
@ -109,10 +101,10 @@ pages:
image: hosttoday/ht-docker-node:npmci image: hosttoday/ht-docker-node:npmci
stage: metadata stage: metadata
script: script:
- npmci command npm install -g typedoc typescript - npmci command npm install -g @gitzone/tsdoc
- npmci npm prepare - npmci npm prepare
- npmci npm install - npmci npm install
- npmci command typedoc --module "commonjs" --target "ES2016" --out public/ ts/ - npmci command tsdoc
tags: tags:
- docker - docker
- notpriv - notpriv

4
.snyk Normal file
View File

@ -0,0 +1,4 @@
# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
version: v1.13.5
ignore: {}
patch: {}

852
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "@pushrocks/smartuniverse", "name": "@pushrocks/smartuniverse",
"version": "1.0.41", "version": "1.0.51",
"private": false, "private": false,
"description": "messaging service for your micro services", "description": "messaging service for your micro services",
"main": "dist/index.js", "main": "dist/index.js",
@ -9,15 +9,16 @@
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"test": "(tstest test/)", "test": "(tstest test/)",
"testManual": "(tsrun test/test.ts)",
"build": "(tsbuild)", "build": "(tsbuild)",
"format": "(gitzone format)" "format": "(gitzone format)"
}, },
"devDependencies": { "devDependencies": {
"@gitzone/tsbuild": "^2.1.8", "@gitzone/tsbuild": "^2.1.11",
"@gitzone/tstest": "^1.0.20", "@gitzone/tstest": "^1.0.24",
"@pushrocks/tapbundle": "^3.0.9", "@pushrocks/tapbundle": "^3.0.9",
"@types/node": "^11.13.4", "@types/node": "^12.0.7",
"tslint": "^5.15.0", "tslint": "^5.17.0",
"tslint-config-prettier": "^1.18.0" "tslint-config-prettier": "^1.18.0"
}, },
"peerDependencies": { "peerDependencies": {
@ -26,14 +27,24 @@
"dependencies": { "dependencies": {
"@pushrocks/lik": "^3.0.5", "@pushrocks/lik": "^3.0.5",
"@pushrocks/smartdelay": "^2.0.3", "@pushrocks/smartdelay": "^2.0.3",
"@pushrocks/smartexpress": "^3.0.18", "@pushrocks/smartexpress": "^3.0.21",
"@pushrocks/smartfile": "^7.0.2", "@pushrocks/smartfile": "^7.0.2",
"@pushrocks/smarthash": "^2.0.4", "@pushrocks/smarthash": "^2.0.4",
"@pushrocks/smartpromise": "^3.0.2", "@pushrocks/smartpromise": "^3.0.2",
"@pushrocks/smartrequest": "^1.1.14", "@pushrocks/smartrequest": "^1.1.15",
"@pushrocks/smartrx": "^2.0.3", "@pushrocks/smartrx": "^2.0.3",
"@pushrocks/smartsocket": "^1.1.27", "@pushrocks/smartsocket": "^1.1.38",
"@pushrocks/smarttime": "^3.0.7", "@pushrocks/smarttime": "^3.0.7",
"@pushrocks/smartunique": "^3.0.1" "@pushrocks/smartunique": "^3.0.1"
} },
"files": [
"ts/*",
"ts_web/*",
"dist/*",
"dist_web/*",
"assets/*",
"cli.js",
"npmextra.json",
"readme.md"
]
} }

View File

@ -1,16 +1,13 @@
# @pushrocks/smartuniverse # @pushrocks/smartuniverse
messaging service for micro services messaging service for micro services
## Availabililty and Links ## Availabililty and Links
* [npmjs.org (npm package)](https://www.npmjs.com/package/@pushrocks/smartuniverse)
- [npmjs.org (npm package)](https://www.npmjs.com/package/@pushrocks/smartuniverse) * [gitlab.com (source)](https://gitlab.com/pushrocks/smartuniverse)
- [gitlab.com (source)](https://gitlab.com/pushrocks/smartuniverse) * [github.com (source mirror)](https://github.com/pushrocks/smartuniverse)
- [github.com (source mirror)](https://github.com/pushrocks/smartuniverse) * [docs (typedoc)](https://pushrocks.gitlab.io/smartuniverse/)
- [docs (typedoc)](https://pushrocks.gitlab.io/smartuniverse/)
## Status for master ## Status for master
[![build status](https://gitlab.com/pushrocks/smartuniverse/badges/master/build.svg)](https://gitlab.com/pushrocks/smartuniverse/commits/master) [![build status](https://gitlab.com/pushrocks/smartuniverse/badges/master/build.svg)](https://gitlab.com/pushrocks/smartuniverse/commits/master)
[![coverage report](https://gitlab.com/pushrocks/smartuniverse/badges/master/coverage.svg)](https://gitlab.com/pushrocks/smartuniverse/commits/master) [![coverage report](https://gitlab.com/pushrocks/smartuniverse/badges/master/coverage.svg)](https://gitlab.com/pushrocks/smartuniverse/commits/master)
[![npm downloads per month](https://img.shields.io/npm/dm/@pushrocks/smartuniverse.svg)](https://www.npmjs.com/package/@pushrocks/smartuniverse) [![npm downloads per month](https://img.shields.io/npm/dm/@pushrocks/smartuniverse.svg)](https://www.npmjs.com/package/@pushrocks/smartuniverse)
@ -28,6 +25,7 @@ Use TypeScript for best in class instellisense.
Think WhatsApp, but for your microservices architecture. It allows your services to securely talk to each other in **private, shielded channels** without having to expose anything to the outside world. This allows the use of **reactive programming across your entire stack**. Think WhatsApp, but for your microservices architecture. It allows your services to securely talk to each other in **private, shielded channels** without having to expose anything to the outside world. This allows the use of **reactive programming across your entire stack**.
### Server side ### Server side
every universe has a server that manages messages. every universe has a server that manages messages.
Think Kafka, but without Kafka. Think Kafka, but without Kafka.
@ -46,11 +44,12 @@ myUniverse.start(8765); // start the server and provide the port on which to lis
``` ```
### Client side ### Client side
All your microservices represents clients in the universe that may talk to each other using the universe server. All your microservices represents clients in the universe that may talk to each other using the universe server.
For further information read the linked docs at the top of this readme. For further information read the linked docs at the top of this readme.
> MIT licensed | **©** [Lossless GmbH](https://lossless.gmbh) > MIT licensed | **©** [Lossless GmbH](https://lossless.gmbh)
> | By using this npm module you agree to our [privacy policy](https://lossless.gmbH/privacy.html) | By using this npm module you agree to our [privacy policy](https://lossless.gmbH/privacy.html)
[![repo-footer](https://pushrocks.gitlab.io/assets/repo-footer.svg)](https://maintainedby.lossless.com) [![repo-footer](https://pushrocks.gitlab.io/assets/repo-footer.svg)](https://maintainedby.lossless.com)

View File

@ -6,8 +6,13 @@ import { Observable } from 'rxjs';
let testUniverse: smartuniverse.Universe; let testUniverse: smartuniverse.Universe;
let testClientUniverse: smartuniverse.ClientUniverse; let testClientUniverse: smartuniverse.ClientUniverse;
let testClientUniverse2: smartuniverse.ClientUniverse;
let testClientChannel: smartuniverse.ClientUniverseChannel; let testClientChannel: smartuniverse.ClientUniverseChannel;
const testServerData = {
serverAddress: 'http://localhost:8765'
};
const testChannelData = { const testChannelData = {
channelName: 'awesomeTestChannel', channelName: 'awesomeTestChannel',
channelPass: 'awesomeChannelPAss' channelPass: 'awesomeChannelPAss'
@ -26,13 +31,17 @@ tap.test('add a message to the SmartUniverse', async () => {
// testing message handling // testing message handling
tap.test('create smartuniverse client', async () => { tap.test('create smartuniverse client', async () => {
testClientUniverse = new smartuniverse.ClientUniverse({ testClientUniverse = new smartuniverse.ClientUniverse({
serverAddress: 'http://localhost:8765' serverAddress: testServerData.serverAddress
}); });
expect(testClientUniverse).to.be.instanceof(smartuniverse.ClientUniverse); expect(testClientUniverse).to.be.instanceof(smartuniverse.ClientUniverse);
}); });
tap.test('should add a channel to the universe', async () => { tap.test('should add a channel to the universe', async () => {
await testUniverse.addChannel('testChannel', 'testPassword'); await testUniverse.addChannel(testChannelData.channelName, testChannelData.channelPass);
});
tap.test('should add the same channel to the client universe in the same way', async () => {
await testClientUniverse.addChannel(testChannelData.channelName, testChannelData.channelPass);
}); });
tap.test('should get a observable correctly', async () => { tap.test('should get a observable correctly', async () => {
@ -43,10 +52,22 @@ tap.test('should get a observable correctly', async () => {
tap.test('should send a message correctly', async () => { tap.test('should send a message correctly', async () => {
await testClientUniverse.sendMessage({ await testClientUniverse.sendMessage({
messageText: 'hello', messageText: 'hello',
targetChannelName: 'channel1' targetChannelName: testChannelData.channelName
}); });
}); });
tap.test('universe should contain the sent message', async () => {
expect(testUniverse.universeCache.messageMap.getArray()[0].messageText).to.equal('hello');
});
tap.test('a second client should be able to subscibe', async () => {
testClientUniverse2 = new smartuniverse.ClientUniverse({
serverAddress: testServerData.serverAddress
});
testClientUniverse2.addChannel(testChannelData.channelName, testChannelData.channelPass);
});
tap.test('should receive a message correctly', async () => {}); tap.test('should receive a message correctly', async () => {});
tap.test('should disconnect the client correctly', async () => { tap.test('should disconnect the client correctly', async () => {

View File

@ -1,3 +1,4 @@
export * from './http.interfaces'; export * from './http.interfaces';
export * from './universechannel.interfaces'; export * from './universechannel.interfaces';
export * from './universemessage.interfaces'; export * from './universemessage.interfaces';
export * from './universeactions.interfaces';

View File

@ -0,0 +1,13 @@
export type IServerCallActions = 'subscribe' | 'sendmessage' | 'unsubscribe';
/**
* the interface for a subscription
*/
export interface IServerCallSubscribeActionPayload {
name: string;
passphrase: string;
}
export interface IServerUnsubscribeActionPayload {
name: string;
}

View File

@ -0,0 +1,4 @@
export interface IAuthenticationRequest {
channelName: string;
password: string;
}

View File

@ -7,10 +7,8 @@ import * as url from 'url';
import * as interfaces from './interfaces'; import * as interfaces from './interfaces';
import { import { ClientUniverseChannel, UniverseMessage } from './';
ClientUniverseChannel, import { ClientUniverseCache } from './smartuniverse.classes.clientuniversecache';
UniverseMessage
} from './';
export interface IClientOptions { export interface IClientOptions {
serverAddress: string; serverAddress: string;
@ -22,10 +20,11 @@ export interface IClientOptions {
*/ */
export class ClientUniverse { export class ClientUniverse {
public options; public options;
public socketClient: plugins.smartsocket.SmartsocketClient; public smartsocketClient: plugins.smartsocket.SmartsocketClient;
public observableIntake: plugins.smartrx.ObservableIntake<UniverseMessage>; public observableIntake: plugins.smartrx.ObservableIntake<UniverseMessage>;
public channelCache = new Objectmap<ClientUniverseChannel>(); public channelStore = new Objectmap<ClientUniverseChannel>();
public clientUniverseCache = new ClientUniverseCache();
constructor(optionsArg: IClientOptions) { constructor(optionsArg: IClientOptions) {
this.options = optionsArg; this.options = optionsArg;
@ -35,19 +34,15 @@ export class ClientUniverse {
* adds a channel to the channelcache * adds a channel to the channelcache
* TODO: verify channel before adding it to the channel cache * TODO: verify channel before adding it to the channel cache
*/ */
public async addChannel (channelNameArg: string, passphraseArg: string) { public async addChannel(channelNameArg: string, passphraseArg: string) {
const existingChannel = this.getChannel(channelNameArg); const existingChannel = await this.getChannel(channelNameArg);
if (existingChannel) { if (existingChannel) {
throw new Error('channel exists'); throw new Error('channel exists');
} }
// lets create the channel // lets create the channel
ClientUniverseChannel.createClientUniverseChannel( ClientUniverseChannel.createClientUniverseChannel(this, channelNameArg, passphraseArg);
this,
channelNameArg,
passphraseArg
);
} }
/** /**
@ -57,29 +52,39 @@ export class ClientUniverse {
*/ */
public async getChannel(channelName: string): Promise<ClientUniverseChannel> { public async getChannel(channelName: string): Promise<ClientUniverseChannel> {
await this.checkConnection(); await this.checkConnection();
const clientUniverseChannel = this.channelCache.find(channel => { const clientUniverseChannel = this.channelStore.find(channel => {
return channel.name === channelName; return channel.name === channelName;
}) });
return clientUniverseChannel; return clientUniverseChannel;
} }
/**
* remove a a achannel
* @param messageArg
*/
public removeChannel(channelNameArg, notifyServer = true) {
const clientUniverseChannel = this.channelStore.findOneAndRemove(channelItemArg => {
return channelItemArg.name === channelNameArg;
});
}
/**
* sends a message towards the server
* @param messageArg
*/
public async sendMessage(messageArg: interfaces.IMessageCreator) { public async sendMessage(messageArg: interfaces.IMessageCreator) {
await this.checkConnection();
const requestBody: interfaces.IUniverseMessage = { const requestBody: interfaces.IUniverseMessage = {
id: plugins.smartunique.shortId(), id: plugins.smartunique.shortId(),
timestamp: Date.now(), timestamp: Date.now(),
passphrase: (await this.getChannel(messageArg.targetChannelName)).passphrase, passphrase: (await this.getChannel(messageArg.targetChannelName)).passphrase,
...messageArg, ...messageArg
}; };
const requestBodyString = JSON.stringify(requestBody);
// TODO: User websocket connection if available // TODO: User websocket connection if available
const response = await plugins.smartrequest.postJson(`${this.options.serverAddress}/sendmessage` , {
requestBody: requestBodyString
});
} }
public close() { public close() {
this.socketClient.disconnect(); this.smartsocketClient.disconnect();
} }
/** /**
@ -87,17 +92,36 @@ export class ClientUniverse {
* since password validation is done through other means, a connection should always be possible * since password validation is done through other means, a connection should always be possible
*/ */
private async checkConnection(): Promise<void> { private async checkConnection(): Promise<void> {
if (!this.socketClient && !this.observableIntake) { if (!this.smartsocketClient && !this.observableIntake) {
const parsedURL = url.parse(this.options.serverAddress); const parsedURL = url.parse(this.options.serverAddress);
this.socketClient = new SmartsocketClient({ const socketConfig: plugins.smartsocket.ISmartsocketClientOptions = {
alias: process.env.SOCKET_ALIAS || 'someclient', alias: process.env.SOCKET_ALIAS || 'someclient',
password: 'UniverseClient', password: 'UniverseClient',
port: parseInt(parsedURL.port, 10), port: parseInt(parsedURL.port, 10),
role: 'UniverseClient', role: 'UniverseClient',
url: parsedURL.hostname url: parsedURL.protocol + '//' + parsedURL.hostname
}); };
console.log(socketConfig);
this.smartsocketClient = new SmartsocketClient(socketConfig);
this.observableIntake = new plugins.smartrx.ObservableIntake(); this.observableIntake = new plugins.smartrx.ObservableIntake();
this.socketClient.connect();
// lets define some basic actions
/**
* should handle a forced unsubscription by the server
*/
const unsubscribe = new plugins.smartsocket.SocketFunction({
funcName: 'unsubscribe',
allowedRoles: [],
funcDef: async (data: interfaces.IServerUnsubscribeActionPayload) => {}
});
/**
* should handle a message reception
*/
const receiveMessage = async () => {};
await this.smartsocketClient.connect();
} }
} }
} }

View File

@ -0,0 +1,8 @@
import * as plugins from './smartuniverse.plugins';
/**
* a cache for clients
* keeps track of which messages have already been received
* good for deduplication in mesh environments
*/
export class ClientUniverseCache {}

View File

@ -18,8 +18,12 @@ export class ClientUniverseChannel implements interfaces.IUniverseChannel {
channelNameArg: string, channelNameArg: string,
passphraseArg: string passphraseArg: string
): Promise<ClientUniverseChannel> { ): Promise<ClientUniverseChannel> {
const clientChannel = new ClientUniverseChannel(clientUniverseArg, channelNameArg, passphraseArg); const clientChannel = new ClientUniverseChannel(
clientUniverseArg.channelCache.add(clientChannel); clientUniverseArg,
channelNameArg,
passphraseArg
);
clientUniverseArg.channelStore.add(clientChannel);
await clientChannel.subscribe(); await clientChannel.subscribe();
return clientChannel; return clientChannel;
} }
@ -46,6 +50,11 @@ export class ClientUniverseChannel implements interfaces.IUniverseChannel {
* tells the universe about this instances interest into a channel * tells the universe about this instances interest into a channel
*/ */
public async subscribe() { public async subscribe() {
this.clientUniverse.socketClient; const serverCallActionName: interfaces.IServerCallActions = 'subscribe';
const serverCallActionPayload: interfaces.IServerCallSubscribeActionPayload = {
name: this.name,
passphrase: this.passphrase
};
this.clientUniverse.smartsocketClient.serverCall(serverCallActionName, serverCallActionPayload);
} }
} }

View File

@ -1,32 +1,34 @@
import * as plugins from './smartuniverse.plugins'; import * as plugins from './smartuniverse.plugins';
import * as interfaces from './interfaces'; import * as interfaces from './interfaces';
import { IUniverseMessage } from './interfaces';
export class ClientUniverseMessage implements interfaces.IUniverseMessage { export class ClientUniverseMessage implements interfaces.IUniverseMessage {
// ====== // ======
// STATIC // STATIC
// ====== // ======
public static createMessageFromPayload(messageDescriptor: interfaces.IUniverseMessage) { public static createMessageFromPayload(messageDescriptor: interfaces.IUniverseMessage) {}
};
// ======== // ========
// INSTANCE // INSTANCE
// ======== // ========
// properties
public id: string; public id: string;
public timestamp: number; public timestamp: number;
public smartTimestamp: plugins.smarttime.TimeStamp; public smartTimestamp: plugins.smarttime.TimeStamp;
public messageText: string; public messageText: string;
public passphrase: string; public passphrase: string;
public payload: any; public payload: any;
public payloadStringType; public payloadStringType;
public targetChannelName: string; public targetChannelName: string;
constructor(messageArg, payloadArg) {}
getAsJsonForPayload () {
constructor(messageArg: IUniverseMessage, payloadArg) {
for (const key of Object.keys(messageArg)) {
this[key] = messageArg[key];
} }
}
getAsJsonForPayload() {}
} }

View File

@ -6,42 +6,24 @@ import { UniverseCache, UniverseChannel, UniverseMessage } from './';
import * as paths from './smartuniverse.paths'; import * as paths from './smartuniverse.paths';
import * as interfaces from './interfaces'; import * as interfaces from './interfaces';
import { UniverseConnectionManager } from './smartuniverse.classes.universeconnectionmanager';
import { UniverseConnection } from './smartuniverse.classes.universeconnection';
export interface ISmartUniverseConstructorOptions { export interface ISmartUniverseConstructorOptions {
messageExpiryInMilliseconds: number; messageExpiryInMilliseconds: number;
} }
/** /**
* main class that setsup a Universe * main class that setsup a Universe
*/ */
export class Universe { export class Universe {
// subinstances // subinstances
public universeCache: UniverseCache; public universeCache: UniverseCache;
public universeConnectionManager: UniverseConnectionManager;
// options // options
private options: ISmartUniverseConstructorOptions; private options: ISmartUniverseConstructorOptions;
/**
* stores the version of the universe server running
* this is done since the version is exposed through the api and multiple fs actions are avoided this way.
*/
private universeVersionStore: string;
/**
* get the currently running version of smartuniverse
*/
public get universeVersion() {
if (this.universeVersionStore) {
return this.universeVersionStore;
} else {
const packageJson = plugins.smartfile.fs.toObjectSync(paths.packageJson);
this.universeVersionStore = packageJson.version;
return this.universeVersionStore;
}
}
/** /**
* the smartexpress server used * the smartexpress server used
*/ */
@ -55,6 +37,26 @@ export class Universe {
constructor(optionsArg: ISmartUniverseConstructorOptions) { constructor(optionsArg: ISmartUniverseConstructorOptions) {
this.options = optionsArg; this.options = optionsArg;
this.universeCache = new UniverseCache(this.options.messageExpiryInMilliseconds); this.universeCache = new UniverseCache(this.options.messageExpiryInMilliseconds);
this.universeConnectionManager = new UniverseConnectionManager();
}
/**
* stores the version of the universe server running
* this is done since the version is exposed through the api and multiple fs actions are avoided this way.
*/
private universeVersionStore: string;
/**
* get the currently running version of smartuniverse
*/
public getUniverseVersion() {
if (this.universeVersionStore) {
return this.universeVersionStore;
} else {
const packageJson = plugins.smartfile.fs.toObjectSync(paths.packageJson);
this.universeVersionStore = packageJson.version;
return this.universeVersionStore;
}
} }
/** /**
@ -67,53 +69,51 @@ export class Universe {
/** /**
* initiates a server * initiates a server
*/ */
public async start(portArg: number | string) { public async start(portArg: number) {
// lets create the base smartexpress server // lets create the base smartexpress server
this.smartexpressServer = new plugins.smartexpress.Server({ this.smartexpressServer = new plugins.smartexpress.Server({
cors: true, cors: true,
defaultAnswer: async () => { defaultAnswer: async () => {
return `smartuniverse server ${this.universeVersion}`; return `smartuniverse server ${this.getUniverseVersion()}`;
}, },
forceSsl: false, forceSsl: false,
port: portArg port: portArg
}); });
// lets create the http request route
this.smartexpressServer.addRoute('/sendmessage', new Handler('POST', async (req, res) => {
const universeMessageInstance = new UniverseMessage(req.body);
this.universeCache.addMessage(universeMessageInstance);
}));
// add websocket upgrade // add websocket upgrade
this.smartsocket = new plugins.smartsocket.Smartsocket({ this.smartsocket = new plugins.smartsocket.Smartsocket({});
port: 12345 // fix this within smartsocket
});
// add a role for the clients // add a role for the clients
const ClientRole = new plugins.smartsocket.SocketRole({ const ClientRole = new plugins.smartsocket.SocketRole({
name: 'clientuniverse', name: 'UniverseClient',
passwordHash: 'clientuniverse' // authentication happens on another level passwordHash: plugins.smarthash.sha256FromStringSync('UniverseClient') // authentication happens on another level
}); });
// add the role to smartsocket // add the role to smartsocket
this.smartsocket.addSocketRoles([ClientRole]); this.smartsocket.addSocketRoles([ClientRole]);
const SubscriptionSocketFunction = new plugins.smartsocket.SocketFunction({ const SubscriptionSocketFunction = new plugins.smartsocket.SocketFunction({
allowedRoles: [ClientRole], allowedRoles: [ClientRole], // there is only one client role, Authentication happens on another level
funcName: 'channelSubscription', funcName: 'channelSubscription',
funcDef: () => { funcDef: async (dataArg: interfaces.IServerCallSubscribeActionPayload, socketConnectionArg) => {
console.log('a client connected'); // run in "this context" of this class
} // TODO: implement an action upon connection of clients (() => {
// TODO: properly add the connection
const universeConnection = new UniverseConnection({
socketConnection: socketConnectionArg,
authenticationRequests: []
})
this.universeConnectionManager.addConnection(universeConnection);
})();
}
}); });
// add smartsocket to the running smartexpress app // add smartsocket to the running smartexpress app
this.smartsocket.setExternalServer('express', this.smartexpressServer as any); this.smartsocket.setExternalServer('smartexpress', this.smartexpressServer as any);
// start everything
// start the socket
this.smartsocket.start();
// start the smartexpress instance
await this.smartexpressServer.start(); await this.smartexpressServer.start();
await this.smartsocket.start();
console.log('started universe');
} }
/** /**

View File

@ -52,7 +52,10 @@ export class UniverseCache {
/** /**
* Read a message from the UniverseCache * Read a message from the UniverseCache
*/ */
public readMessagesYoungerThan(unixTimeArg?: number): Observable<UniverseMessage> { public readMessagesYoungerThan(
unixTimeArg?: number,
channelName?: string
): Observable<UniverseMessage> {
const messageObservable = from(this.messageMap.getArray()).pipe( const messageObservable = from(this.messageMap.getArray()).pipe(
filter(messageArg => { filter(messageArg => {
return messageArg.smartTimestamp.isYoungerThanMilliSeconds(this.destructionTime); return messageArg.smartTimestamp.isYoungerThanMilliSeconds(this.destructionTime);

View File

@ -0,0 +1,35 @@
import * as plugins from './smartuniverse.plugins';
import { UniverseChannel } from './smartuniverse.classes.universechannel';
/**
* represents a connection to the universe
*/
export class UniverseConnection {
public terminatedDeferred = plugins.smartpromise.defer();
/**
* the socketClient to ping
*/
public socketConnection: plugins.smartsocket.SocketConnection;
public authenticationRequests = [];
public subscribedChannels: UniverseChannel[] = [];
public authenticatedChannels: UniverseChannel[] = [];
public failedToJoinChannels: UniverseChannel[] = [];
/**
* terminates the connection
*/
public terminateConnection () {
this.socketConnection.socket.disconnect();
this.terminatedDeferred.resolve();
}
constructor(optionsArg: {
socketConnection: plugins.smartsocket.SocketConnection;
authenticationRequests
}) {
// TODO: check if this is correct
this.socketConnection.socket.disconnect();
}
}

View File

@ -0,0 +1,48 @@
import * as plugins from './smartuniverse.plugins';
import { UniverseConnection } from './smartuniverse.classes.universeconnection';
/**
* manages connections to a universe
*/
export class UniverseConnectionManager {
public connectionMap = new plugins.lik.Objectmap<UniverseConnection>();
public async addConnection(universeConnectionArg: UniverseConnection) {
let universeConnection = universeConnectionArg;
universeConnection = await this.deduplicateUniverseConnection(universeConnection);
universeConnection = await this.authenticateAuthenticationRequests(universeConnection);
}
/**
* deduplicates UniverseConnections
*/
public async deduplicateUniverseConnection (universeConnectionArg: UniverseConnection): Promise<UniverseConnection> {
let connectionToReturn: UniverseConnection;
this.connectionMap.forEach(async existingConnection => {
if (existingConnection.socketConnection = universeConnectionArg.socketConnection) {
connectionToReturn = await this.mergeUniverseConnections(existingConnection, universeConnectionArg);
}
});
if (!connectionToReturn) {
connectionToReturn = universeConnectionArg;
}
return connectionToReturn;
}
/**
* authenticate AuthenticationRequests
*/
public authenticateAuthenticationRequests(universeConnectionArg): Promise<UniverseConnection> {
// TODO: authenticate connections
return universeConnectionArg;
}
/**
* merges two UniverseConnections
*/
public mergeUniverseConnections (connectionArg1: UniverseConnection, connectionArg2: UniverseConnection) {
// TODO: merge connections
return connectionArg1;
}
}

View File

@ -14,7 +14,6 @@ import { IUniverseMessage } from './interfaces';
* acts as a container to save message states like authentication status * acts as a container to save message states like authentication status
*/ */
export class UniverseMessage implements interfaces.IUniverseMessage { export class UniverseMessage implements interfaces.IUniverseMessage {
public id: string; public id: string;
public timestamp: number; public timestamp: number;