Compare commits

...

101 Commits

Author SHA1 Message Date
9c1504ef02 1.0.64 2019-08-13 18:43:33 +02:00
e8f2e04d1c fix(core): update 2019-08-13 18:43:33 +02:00
e12aa7e961 1.0.63 2019-08-13 18:41:28 +02:00
857b7cd010 fix(core): update 2019-08-13 18:41:27 +02:00
e100dea160 1.0.62 2019-08-13 18:16:17 +02:00
e8e87fcdba fix(core): update 2019-08-13 18:16:16 +02:00
0d18b11721 1.0.61 2019-08-13 18:06:14 +02:00
eaaefddbe3 fix(core): update 2019-08-13 18:06:13 +02:00
8c6946ddb6 1.0.60 2019-08-13 15:55:01 +02:00
3a7ebcdd80 fix(core): update 2019-08-13 15:55:01 +02:00
ec2afbfd55 1.0.59 2019-08-13 15:48:21 +02:00
89feeca735 fix(core): update 2019-08-13 15:48:20 +02:00
c4261765ec 1.0.58 2019-08-13 13:04:49 +02:00
33fe6bcd41 fix(core): update 2019-08-13 13:04:49 +02:00
1baf1c318c 1.0.57 2019-08-12 17:23:11 +02:00
051aba3299 fix(core): update 2019-08-12 17:23:10 +02:00
7998d79b13 1.0.56 2019-08-12 15:12:32 +02:00
6838a8729a fix(core): update 2019-08-12 15:12:31 +02:00
67f4e33ca0 1.0.55 2019-08-12 15:10:40 +02:00
8a8277ae9f fix(core): update 2019-08-12 15:10:40 +02:00
ff9cb9132c 1.0.54 2019-08-12 14:59:38 +02:00
f4ce784a59 fix(core): update 2019-08-12 14:59:37 +02:00
b34be4dcba 1.0.53 2019-07-30 10:31:36 +02:00
6cc69efe2d fix(core): update 2019-07-30 10:31:35 +02:00
8c30f294bc 1.0.52 2019-06-11 03:06:18 +02:00
228eb791b7 fix(core): update 2019-06-11 03:06:17 +02:00
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
7693b52066 1.0.41 2019-04-22 23:11:52 +02:00
30a02ae48b fix(core): update 2019-04-22 23:11:51 +02:00
241182ed2e 1.0.40 2019-04-22 22:04:53 +02:00
3d82038ec3 fix(core): update 2019-04-22 22:04:52 +02:00
300d62ed12 1.0.39 2019-04-22 13:06:02 +02:00
a5e849aa17 fix(core): update 2019-04-22 13:06:01 +02:00
83807d7c5c 1.0.38 2019-04-22 09:58:36 +02:00
39d3a0f2f8 fix(core): update 2019-04-22 09:58:36 +02:00
904a48d414 1.0.37 2019-04-12 20:50:44 +02:00
e2acb28756 1.0.36 2019-04-11 18:59:46 +02:00
92e4379bd2 fix(core): update 2019-04-11 18:59:45 +02:00
e0ce732ee1 1.0.35 2019-04-11 18:57:24 +02:00
98be0f036c fix(core): update 2019-04-11 18:57:23 +02:00
54fca17142 1.0.34 2019-04-11 18:50:43 +02:00
1a7634e8db fix(core): update 2019-04-11 18:50:43 +02:00
8830b825ac 1.0.33 2019-04-11 18:32:36 +02:00
123324bf43 fix(core): update 2019-04-11 18:32:36 +02:00
4761ff31cf 1.0.32 2019-04-11 18:31:21 +02:00
430c3ea13a fix(core): update 2019-04-11 18:31:21 +02:00
6dd3782b0d 1.0.31 2019-04-11 18:16:14 +02:00
06aa721247 1.0.30 2019-04-11 17:52:02 +02:00
f728c56016 fix(core): update 2019-04-11 17:52:01 +02:00
f512acdfaa 1.0.29 2019-04-11 16:53:28 +02:00
331a098851 fix(core): update 2019-04-11 16:53:28 +02:00
a8053a6e20 1.0.28 2019-04-11 11:36:57 +02:00
a62f3eb750 fix(core): update 2019-04-11 11:36:57 +02:00
4b53044e8f 1.0.27 2019-04-11 11:15:09 +02:00
2db2c80130 fix(core): update 2019-04-11 11:15:09 +02:00
8b23eaf194 1.0.26 2019-04-11 11:11:46 +02:00
a03c54001f fix(core): update 2019-04-11 11:11:45 +02:00
7bc404ba21 1.0.25 2019-01-31 02:52:18 +01:00
ac2a1559b0 fix(core): update 2019-01-31 02:52:18 +01:00
57b37cb327 1.0.24 2019-01-30 11:01:49 +01:00
8b17e814cc fix(dependencies): update 2019-01-30 11:01:48 +01:00
f4b8cde347 fix(dependencies): update 2019-01-30 10:56:31 +01:00
3189d4d274 1.0.23 2018-05-30 16:34:07 +02:00
c2d134a6ea fix(websocket): switch to all websocket infrastructure 2018-05-30 16:34:06 +02:00
7552dc1e9f 1.0.22 2018-05-28 12:07:26 +02:00
fe91459510 fix(core): update to transparent universe 2018-05-28 12:07:25 +02:00
0e0ffb7634 1.0.21 2018-05-26 13:44:32 +02:00
37bb052774 fix(UniverseMessage / UniverseChannel): better authentication for of messages for channels 2018-05-26 13:44:32 +02:00
3c7683d40e 1.0.20 2018-05-24 17:08:29 +02:00
c19f27e873 fix(dependencies): update 2018-05-24 17:08:28 +02:00
c1a03fec0f 1.0.19 2018-05-24 16:55:25 +02:00
8b650c5ea7 fix(UniverseChannel): improve channel handling 2018-05-24 16:55:24 +02:00
4fc6e327ec 1.0.18 2018-05-24 00:14:57 +02:00
7991baf2bf fix(core): improve channel handling 2018-05-24 00:14:57 +02:00
d033780015 1.0.17 2018-05-23 23:50:46 +02:00
eae46e6461 fix(structure): format TypeScript 2018-05-23 23:50:45 +02:00
785acfaba4 1.0.16 2018-05-20 00:41:59 +02:00
5a4dceb75d fix(core): prepare for release 2018-05-20 00:41:59 +02:00
a17834a8f0 1.0.15 2018-05-07 18:50:07 +02:00
01765fa50f feat(message): refactor 2018-05-07 18:50:07 +02:00
547c159dc4 1.0.14 2018-04-29 17:30:03 +02:00
046d7d646d update to latest standards 2018-04-29 17:30:00 +02:00
32 changed files with 4269 additions and 2116 deletions

20
.gitignore vendored
View File

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

View File

@ -1,95 +1,119 @@
# gitzone standard
image: hosttoday/ht-docker-node:npmci
# gitzone ci_default
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
cache:
paths:
- .yarn/
- .npmci_cache/
key: "$CI_BUILD_STAGE"
stages:
- security
- test
- release
- trigger
- pages
- metadata
# ====================
# security stage
# ====================
mirror:
stage: security
script:
- npmci git mirror
tags:
- docker
- notpriv
snyk:
stage: security
script:
- npmci command yarn global add snyk
- npmci command yarn install --ignore-scripts
- npmci npm prepare
- npmci command npm install -g snyk
- npmci command npm install --ignore-scripts
- npmci command snyk test
tags:
- docker
- notpriv
testLEGACY:
stage: test
script:
- npmci node install legacy
- npmci npm install
- npmci npm test
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- docker
allow_failure: true
# ====================
# test stage
# ====================
testLTS:
stage: test
script:
- npmci npm prepare
- npmci node install lts
- npmci npm install
- npmci npm test
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- docker
testSTABLE:
- notpriv
testBuild:
stage: test
script:
- npmci node install stable
- npmci npm prepare
- npmci node install lts
- npmci npm install
- npmci npm test
- npmci command npm run build
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- docker
- notpriv
release:
stage: release
script:
- npmci npm prepare
- npmci node install lts
- npmci npm publish
only:
- tags
tags:
- docker
- notpriv
# ====================
# metadata stage
# ====================
codequality:
stage: metadata
allow_failure: true
script:
- npmci command npm install -g tslint typescript
- npmci npm install
- npmci command "tslint -c tslint.json ./ts/**/*.ts"
tags:
- docker
- priv
trigger:
stage: trigger
stage: metadata
script:
- npmci trigger
only:
- tags
tags:
- docker
- notpriv
pages:
image: hosttoday/ht-docker-node:npmci
stage: pages
image: hosttoday/ht-docker-dbase:npmci
services:
- docker:18-dind
stage: metadata
script:
- npmci command yarn global add npmpage
- npmci command npmpage
- npmci command npm install -g @gitzone/tsdoc
- npmci npm prepare
- npmci npm install
- npmci command tsdoc
tags:
- docker
- notpriv
only:
- tags
artifacts:
expire_in: 1 week
paths:
- public
allow_failure: true

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: {}

29
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,29 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "current file",
"type": "node",
"request": "launch",
"args": [
"${relativeFile}"
],
"runtimeArgs": ["-r", "@gitzone/tsrun"],
"cwd": "${workspaceRoot}",
"protocol": "inspector",
"internalConsoleOptions": "openOnSessionStart"
},
{
"name": "test.ts",
"type": "node",
"request": "launch",
"args": [
"test/test.ts"
],
"runtimeArgs": ["-r", "@gitzone/tsrun"],
"cwd": "${workspaceRoot}",
"protocol": "inspector",
"internalConsoleOptions": "openOnSessionStart"
}
]
}

19
license Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2018 Lossless GmbH (hello@lossless.com)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,9 +1,17 @@
{
"gitzone": {
"module": {
"githost": "gitlab.com",
"gitscope": "pushrocks",
"gitrepo": "smartuniverse",
"shortDescription": "messaging service for micro services",
"npmPackagename": "@pushrocks/smartuniverse",
"license": "MIT",
"projectDomain": "push.rocks"
}
},
"npmci": {
"npmGlobalTools": [
"@gitzone/npmts",
"ts-node"
],
"npmGlobalTools": [],
"npmAccessLevel": "public"
}
}
}

3126
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,30 +1,52 @@
{
"name": "@pushrocks/smartuniverse",
"version": "1.0.13",
"version": "1.0.64",
"private": false,
"description": "messaging service for your micro services",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"author": "Lossless GmbH",
"license": "MIT",
"scripts": {
"test": "ts-node test/test.ts",
"build": "(npmts)",
"test": "(tstest test/)",
"testManual": "(tsrun test/test.ts)",
"build": "(tsbuild)",
"format": "(gitzone format)"
},
"devDependencies": {
"@types/node": "^9.6.0",
"tapbundle": "^2.0.0"
"@gitzone/tsbuild": "^2.1.11",
"@gitzone/tstest": "^1.0.24",
"@pushrocks/tapbundle": "^3.0.11",
"@types/node": "^12.7.1",
"tslint": "^5.18.0",
"tslint-config-prettier": "^1.18.0"
},
"peerDependencies": {
"rxjs": "*"
},
"dependencies": {
"lik": "^2.0.5",
"rxjs": "^5.5.8",
"smartcli": "^2.0.12",
"smartexpress": "^1.0.21",
"smartfile": "^4.2.28",
"smartq": "^1.1.8",
"smartrequest": "^1.0.8",
"smartrx": "^1.0.5",
"smartsocket": "^1.1.19",
"smarttime": "^2.0.0"
}
"@pushrocks/lik": "^3.0.10",
"@pushrocks/smartdelay": "^2.0.3",
"@pushrocks/smartexpress": "^3.0.38",
"@pushrocks/smartfile": "^7.0.4",
"@pushrocks/smarthash": "^2.0.6",
"@pushrocks/smartlog": "^2.0.19",
"@pushrocks/smartpromise": "^3.0.2",
"@pushrocks/smartrequest": "^1.1.16",
"@pushrocks/smartrx": "^2.0.3",
"@pushrocks/smartsocket": "^1.1.44",
"@pushrocks/smarttime": "^3.0.12",
"@pushrocks/smartunique": "^3.0.1"
},
"files": [
"ts/*",
"ts_web/*",
"dist/*",
"dist_web/*",
"dist_ts_web/*",
"assets/*",
"cli.js",
"npmextra.json",
"readme.md"
]
}

View File

@ -1,33 +1,55 @@
# smartuniverse
# @pushrocks/smartuniverse
messaging service for micro services
messaging service for your micro services
## Availabililty
[![npm](https://pushrocks.gitlab.io/assets/repo-button-npm.svg)](https://www.npmjs.com/package/@pushrocks/smartuniverse)
[![git](https://pushrocks.gitlab.io/assets/repo-button-git.svg)](https://GitLab.com/pushrocks/smartuniverse)
[![git](https://pushrocks.gitlab.io/assets/repo-button-mirror.svg)](https://github.com/pushrocks/smartuniverse)
[![docs](https://pushrocks.gitlab.io/assets/repo-button-docs.svg)](https://pushrocks.gitlab.io/smartuniverse/)
## Availabililty and Links
* [npmjs.org (npm package)](https://www.npmjs.com/package/@pushrocks/smartuniverse)
* [gitlab.com (source)](https://gitlab.com/pushrocks/smartuniverse)
* [github.com (source mirror)](https://github.com/pushrocks/smartuniverse)
* [docs (typedoc)](https://pushrocks.gitlab.io/smartuniverse/)
## Status for 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)
[![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)
[![npm downloads per month](https://img.shields.io/npm/dm/@pushrocks/smartuniverse.svg)](https://www.npmjs.com/package/@pushrocks/smartuniverse)
[![bitHound Dependencies](https://www.bithound.io/github/pushrocks/smartuniverse/badges/dependencies.svg)](https://www.bithound.io/github/pushrocks/smartuniverse/master/dependencies/npm)
[![bitHound Code](https://www.bithound.io/github/pushrocks/smartuniverse/badges/code.svg)](https://www.bithound.io/github/pushrocks/smartuniverse)
[![Known Vulnerabilities](https://snyk.io/test/npm/@pushrocks/smartuniverse/badge.svg)](https://snyk.io/test/npm/@pushrocks/smartuniverse)
[![TypeScript](https://img.shields.io/badge/TypeScript-2.x-blue.svg)](https://nodejs.org/dist/latest-v6.x/docs/api/)
[![node](https://img.shields.io/badge/node->=%206.x.x-blue.svg)](https://nodejs.org/dist/latest-v6.x/docs/api/)
[![JavaScript Style Guide](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com/)
[![TypeScript](https://img.shields.io/badge/TypeScript->=%203.x-blue.svg)](https://nodejs.org/dist/latest-v10.x/docs/api/)
[![node](https://img.shields.io/badge/node->=%2010.x.x-blue.svg)](https://nodejs.org/dist/latest-v10.x/docs/api/)
[![JavaScript Style Guide](https://img.shields.io/badge/code%20style-prettier-ff69b4.svg)](https://prettier.io/)
## Usage
Use TypeScript for best in class instellisense.
For further information read the linked docs at the top of this README.
### What is smartuniverse all about?
> licensed | **©** [Lossless GmbH](https://lossless.gmbh)
> | By using this npm module you agree to our [privacy policy](https://lossless.gmbH/privacy.html)
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**.
[![repo-footer](https://pushrocks.gitlab.io/assets/repo-footer.svg)](https://push.rocks)
### Server side
every universe has a server that manages messages.
Think Kafka, but without Kafka.
```typescript
import * as smartuniverse from '@pushrocks/smartuniverse';
const myUniverse = new smartuniverse.Universe({
messageExpiryInMilliseconds: 60000 // the standard time in milliseconds until a message expires
});
// create as many channels as you like
myUniverse.addChannel('awesomeChannel', 'awesomeChannelPass');
myUniverse.addChannel('awesomeChannel2', 'jhkjhfsdf87eerkjslkfja9');
myUniverse.start(8765); // start the server and provide the port on which to listen on
```
### Client side
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.
> MIT licensed | **©** [Lossless GmbH](https://lossless.gmbh)
| 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)

View File

@ -1,12 +1,22 @@
// tslint:disable-next-line:no-implicit-dependencies
import { expect, tap } from 'tapbundle';
import { expect, tap } from '@pushrocks/tapbundle';
import * as smartuniverse from '../ts/index';
import { Observable } from 'rxjs';
let testUniverse: smartuniverse.Universe;
let testUniverseClient: smartuniverse.UniverseClient;
let testMessageObservable: Observable<smartuniverse.UniverseMessage>;
let testClientUniverse: smartuniverse.ClientUniverse;
let testClientUniverse2: smartuniverse.ClientUniverse;
let testClientChannel: smartuniverse.ClientUniverseChannel;
const testServerData = {
serverAddress: 'http://localhost:8765'
};
const testChannelData = {
channelName: 'awesomeTestChannel',
channelPass: 'awesomeChannelPass'
};
tap.test('first test', async () => {
testUniverse = new smartuniverse.Universe({
@ -15,31 +25,56 @@ tap.test('first test', async () => {
});
tap.test('add a message to the SmartUniverse', async () => {
await testUniverse.initServer(8765);
await testUniverse.start(8765);
});
// testing message handling
tap.test('create smartuniverse client', async () => {
testUniverseClient = new smartuniverse.UniverseClient({
serverAddress: 'http://localhost:8765'
testClientUniverse = new smartuniverse.ClientUniverse({
serverAddress: testServerData.serverAddress
});
expect(testUniverseClient).to.be.instanceof(smartuniverse.UniverseClient);
expect(testClientUniverse).to.be.instanceof(smartuniverse.ClientUniverse);
});
tap.test('should add a channel to the universe', async () => {
testUniverse.addChannel(testChannelData.channelName, testChannelData.channelPass);
});
tap.test('should add the same channel to the client universe in the same way', async () => {
testClientUniverse.addChannel(testChannelData.channelName, testChannelData.channelPass);
});
tap.test('should start the ClientUniverse', async () => {
await testClientUniverse.start();
})
tap.test('should get a observable correctly', async () => {
testClientChannel = testClientUniverse.getChannel(testChannelData.channelName);
expect(testClientChannel).to.be.instanceof(smartuniverse.ClientUniverseChannel);
});
tap.test('should send a message correctly', async () => {
await testUniverseClient.sendMessage('greeting', {
anyBool: true
await (testClientUniverse.getChannel(testChannelData.channelName)).sendMessage({
messageText: 'hello'
});
});
tap.test('should get a observable correctly', async () => {
testMessageObservable = testUniverseClient.getMessageObservable();
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 disconnect the client correctly', async () => {
testUniverseClient.close();
testClientUniverse.stop();
});
tap.test('should end the server correctly', async tools => {

View File

@ -1,3 +1,12 @@
// Client classes
export * from './smartuniverse.classes.clientuniverse';
export * from './smartuniverse.classes.clientuniversechannel';
export * from './smartuniverse.classes.clientuniversemessage';
// Server classes
export * from './smartuniverse.classes.universe';
export * from './smartuniverse.classes.universeclient';
export * from './smartuniverse.classes.universecache';
export * from './smartuniverse.classes.universechannel';
export * from './smartuniverse.classes.universemessage';
export * from './interfaces';

View File

@ -0,0 +1,15 @@
export interface IServerGetMessagesRequestBody {
channel: string;
topic?: string;
youngerThan: number;
}
/**
* the interface for a standard request
*/
export interface IServerPutMessageRequestBody {
channel: string;
passphrase: string;
message: string;
payload: any;
}

4
ts/interfaces/index.ts Normal file
View File

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

View File

@ -0,0 +1,17 @@
export type IServerCallActions =
| 'channelSubscription'
| 'processMessage'
| 'channelUnsubscribe'
| 'terminateConnection';
/**
* the interface for a subscription
*/
export interface IServerCallSubscribeActionPayload {
name: string;
passphrase: string;
}
export interface IServerUnsubscribeActionPayload {
name: string;
}

View File

@ -0,0 +1 @@
export interface IUniverseChannel {}

View File

@ -0,0 +1,18 @@
export interface IMessageCreator {
messageText: string;
payload?: string | number | any;
payloadStringType?: 'Buffer' | 'string' | 'object';
}
/**
*
*/
export interface IUniverseMessage extends IMessageCreator {
id: string;
/**
* time of creation
*/
timestamp: number;
passphrase: string;
targetChannelName: string;
}

View File

@ -0,0 +1,130 @@
import * as plugins from './smartuniverse.plugins';
import { Objectmap } from '@pushrocks/lik';
import { Observable } from 'rxjs';
import { Smartsocket, SmartsocketClient } from '@pushrocks/smartsocket';
import * as url from 'url';
import * as interfaces from './interfaces';
import { ClientUniverseChannel, ClientUniverseMessage } from './';
import { ClientUniverseCache } from './smartuniverse.classes.clientuniversecache';
export interface IClientOptions {
serverAddress: string;
}
/**
* this class is for client side only!!!
* allows connecting to a universe server
*/
export class ClientUniverse {
public options;
public smartsocketClient: plugins.smartsocket.SmartsocketClient;
public observableIntake: plugins.smartrx.ObservableIntake<ClientUniverseMessage>;
public clientUniverseCache = new ClientUniverseCache();
constructor(optionsArg: IClientOptions) {
this.options = optionsArg;
}
/**
* adds a channel to the channelcache
* TODO: verify channel before adding it to the channel cache
*/
public addChannel(channelNameArg: string, passphraseArg: string) {
const existingChannel = this.getChannel(channelNameArg);
if (existingChannel) {
throw new Error('channel exists');
}
// lets create the channel
const clientUniverseChannel = ClientUniverseChannel.createClientUniverseChannel(this, channelNameArg, passphraseArg);
return clientUniverseChannel;
}
/**
* gets a channel from the channelcache
* @param channelName
* @param passphraseArg
*/
public getChannel(channelName: string): ClientUniverseChannel {
const clientUniverseChannel = this.clientUniverseCache.channelMap.find(channel => {
return channel.name === channelName;
});
return clientUniverseChannel;
}
/**
* remove a a achannel
* @param messageArg
*/
public removeChannel(channelNameArg, notifyServer = true) {
const clientUniverseChannel = this.clientUniverseCache.channelMap.findOneAndRemove(channelItemArg => {
return channelItemArg.name === channelNameArg;
});
}
public async start() {
await this.checkConnection();
}
public stop() {
this.smartsocketClient.disconnect();
}
/**
* checks the connection towards a universe server
* since password validation is done through other means, a connection should always be possible
*/
public async checkConnection(): Promise<void> {
if (!this.smartsocketClient && !this.observableIntake) {
const parsedURL = url.parse(this.options.serverAddress);
const socketConfig: plugins.smartsocket.ISmartsocketClientOptions = {
alias: process.env.SOCKET_ALIAS || 'someclient',
password: 'UniverseClient',
port: parseInt(parsedURL.port, 10),
role: 'UniverseClient',
url: parsedURL.protocol + '//' + parsedURL.hostname
};
this.smartsocketClient = new SmartsocketClient(socketConfig);
this.observableIntake = new plugins.smartrx.ObservableIntake();
// lets define some basic actions
/**
* should handle a forced unsubscription by the server
*/
const socketFunctionUnsubscribe = new plugins.smartsocket.SocketFunction({
funcName: 'unsubscribe',
allowedRoles: [],
funcDef: async (data: interfaces.IServerUnsubscribeActionPayload) => {
throw new Error('TODO');
}
});
/**
* handles message reception
*/
const socketFunctionProcessMessage = new plugins.smartsocket.SocketFunction({
funcName: 'processMessage',
allowedRoles: [],
funcDef: async (messageDescriptorArg: interfaces.IUniverseMessage) => {
plugins.smartlog.defaultLogger.log('info', 'Got message from server');
this.observableIntake.push(ClientUniverseMessage.createMessageFromMessageDescriptor(messageDescriptorArg));
}
});
// add functions
this.smartsocketClient.addSocketFunction(socketFunctionUnsubscribe);
this.smartsocketClient.addSocketFunction(socketFunctionProcessMessage);
await this.smartsocketClient.connect();
plugins.smartlog.defaultLogger.log('info', 'universe client connected successfully');
await this.clientUniverseCache.channelMap.forEach(async clientUniverseChannelArg => {
await clientUniverseChannelArg.subscribe();
});
}
}
}

View File

@ -0,0 +1,11 @@
import * as plugins from './smartuniverse.plugins';
import { ClientUniverseChannel } from './smartuniverse.classes.clientuniversechannel';
/**
* a cache for clients
* keeps track of which messages have already been received
* good for deduplication in mesh environments
*/
export class ClientUniverseCache {
public channelMap = new plugins.lik.Objectmap<ClientUniverseChannel>();
}

View File

@ -0,0 +1,77 @@
import * as plugins from './smartuniverse.plugins';
import * as interfaces from './interfaces';
import { ClientUniverse } from './';
export class ClientUniverseChannel implements interfaces.IUniverseChannel {
// ======
// STATIC
// ======
/**
* creates a channel and adds it to the cache of clientUniverseArg
* @param clientUniverseArg
* @param channelNameArg
* @param passphraseArg
*/
public static createClientUniverseChannel(
clientUniverseArg: ClientUniverse,
channelNameArg: string,
passphraseArg: string
): ClientUniverseChannel {
const clientChannel = new ClientUniverseChannel(
clientUniverseArg,
channelNameArg,
passphraseArg
);
clientUniverseArg.clientUniverseCache.channelMap.add(clientChannel);
return clientChannel;
}
// ========
// INSTANCE
// ========
// properties
public name: string;
public passphrase: string;
// refs
public clientUniverseRef: ClientUniverse;
constructor(clientUniverseArg: ClientUniverse, nameArg: string, passphraseArg: string) {
this.clientUniverseRef = clientUniverseArg;
this.name = nameArg;
this.passphrase = passphraseArg;
}
/**
* subscribes to a channel
* tells the universe about this instances interest into a channel
*/
public async subscribe() {
const serverCallActionName: interfaces.IServerCallActions = 'channelSubscription';
const serverCallActionPayload: interfaces.IServerCallSubscribeActionPayload = {
name: this.name,
passphrase: this.passphrase
};
await this.clientUniverseRef.smartsocketClient.serverCall(serverCallActionName, serverCallActionPayload);
}
/**
* sends a message towards the server
* @param messageArg
*/
public async sendMessage(messageArg: interfaces.IMessageCreator) {
await this.clientUniverseRef.checkConnection();
const universeMessageToSend: interfaces.IUniverseMessage = {
id: plugins.smartunique.shortId(),
timestamp: Date.now(),
passphrase: this.passphrase,
targetChannelName: this.name,
messageText: messageArg.messageText,
payload: messageArg.payload,
payloadStringType: messageArg.payloadStringType
};
await this.clientUniverseRef.smartsocketClient.serverCall('processMessage', universeMessageToSend);
}
}

View File

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

View File

@ -1,22 +0,0 @@
import * as plugins from './smartuniverse.plugins';
import { Objectmap } from 'lik';
import { UniverseChannel } from './smartuniverse.classes.universechannel';
export class UniverseManager {
public channelStore = new Objectmap<UniverseChannel>();
/**
* register a new member
*/
public async registerMember() {}
/**
* register a new channel within the universe
* @param channelName the name of the channel
* @param authSecret the secret against which to verify members of the channel
*/
public async registerChannel(channelName: string, authSecret: string) {
}
}

View File

@ -1,40 +1,52 @@
import * as plugins from './smartuniverse.plugins';
import { Handler, Route, Server } from 'smartexpress';
import { UniverseManager } from './smartuniverse.classes.manager';
import { UniverseChannel } from './smartuniverse.classes.universechannel';
import { UniverseMessage } from './smartuniverse.classes.universemessage';
import { UniverseStore } from './smartuniverse.classes.universestore';
import { Handler, Route, Server } from '@pushrocks/smartexpress';
import { UniverseCache, UniverseChannel, UniverseMessage } from './';
import * as paths from './smartuniverse.paths';
import * as interfaces from './interfaces';
import { UniverseConnection } from './smartuniverse.classes.universeconnection';
export interface ISmartUniverseConstructorOptions {
messageExpiryInMilliseconds: number;
}
export interface IServerGetMessagesRequestBody {
channel: string;
topic?: string;
youngerThan: number;
}
export interface IServerPutMessageRequestBody {
message: string;
payload: any;
}
/**
* main class that setups a Universe
*/
export class Universe {
// subinstances
public universeStore: UniverseStore;
public universeManager: UniverseManager;
public universeCache: UniverseCache;
// options
private options: ISmartUniverseConstructorOptions;
// Store version handling
/**
* the smartexpress server used
*/
private smartexpressServer: plugins.smartexpress.Server;
/**
* the smartsocket used
*/
private smartsocket: plugins.smartsocket.Smartsocket;
constructor(optionsArg: ISmartUniverseConstructorOptions) {
this.options = optionsArg;
this.universeCache = new UniverseCache(this, this.options.messageExpiryInMilliseconds);
}
/**
* 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;
private get universeVersion() {
/**
* get the currently running version of smartuniverse
*/
public getUniverseVersion() {
if (this.universeVersionStore) {
return this.universeVersionStore;
} else {
@ -44,65 +56,106 @@ export class Universe {
}
}
private smartexpressServer: plugins.smartexpress.Server;
private smartsocket: plugins.smartsocket.Smartsocket;
constructor(optionsArg: ISmartUniverseConstructorOptions) {
this.options = optionsArg;
this.universeStore = new UniverseStore(this.options.messageExpiryInMilliseconds);
this.universeManager = new UniverseManager();
/**
* adds a channel to the Universe
*/
public addChannel(nameArg: string, passphraseArg: string) {
const newChannel = UniverseChannel.createChannel(this, nameArg, passphraseArg);
}
/**
* initiates a server
*/
public async initServer(portArg: number | string) {
public async start(portArg: number) {
// lets create the base smartexpress server
this.smartexpressServer = new plugins.smartexpress.Server({
cors: true,
defaultAnswer: `smartuniverse server ${this.universeVersion}`,
defaultAnswer: async () => {
return `smartuniverse server ${this.getUniverseVersion()}`;
},
forceSsl: false,
port: portArg
});
// message handling
// adds messages
const addMessageHandler = new Handler('PUT', request => {
const requestBody = request.body;
this.universeStore.addMessage(requestBody.message, requestBody.payload);
console.log(requestBody);
return true;
});
// gets messages
const readMessageHandler = new Handler('GET', request => {
const done = plugins.smartq.defer<UniverseMessage[]>();
const requestBody = request.body;
const messageObservable = this.universeStore.readMessagesYoungerThan(requestBody.since);
messageObservable.toArray().subscribe(universeMessageArrayArg => {
done.resolve(universeMessageArrayArg);
});
return done.promise;
});
// create new Route for messages
const messageRoute = new Route(this.smartexpressServer, 'message');
messageRoute.addHandler(addMessageHandler);
messageRoute.addHandler(readMessageHandler);
const leaderElectionRoute = new Route(this.smartexpressServer, 'leadelection');
// TODO: implement Handlers for leader election
// add websocket upgrade
this.smartsocket = new plugins.smartsocket.Smartsocket({
port: 12345 // fix this within smartsocket
this.smartsocket = new plugins.smartsocket.Smartsocket({});
// add a role for the clients
const ClientRole = new plugins.smartsocket.SocketRole({
name: 'UniverseClient',
passwordHash: plugins.smarthash.sha256FromStringSync('UniverseClient') // authentication happens on another level
});
this.smartsocket.setExternalServer('express', this.smartexpressServer as any); // should work with express as well
this.smartsocket.start();
// add the role to smartsocket
this.smartsocket.addSocketRoles([ClientRole]);
const socketFunctionSubscription = new plugins.smartsocket.SocketFunction({
allowedRoles: [ClientRole], // there is only one client role, Authentication happens on another level
funcName: 'channelSubscription',
funcDef: async (
dataArg: interfaces.IServerCallSubscribeActionPayload,
socketConnectionArg
) => {
// run in "this context" of this class
await (async () => {
const universeConnection = new UniverseConnection({
socketConnection: socketConnectionArg,
authenticationRequests: [dataArg]
});
await UniverseConnection.addConnectionToCache(this, universeConnection);
return {
'subscription status': 'success'
};
})();
}
});
const socketFunctionProcessMessage = new plugins.smartsocket.SocketFunction({
allowedRoles: [ClientRole], // there is only one client role, Authentication happens on another level
funcName: 'processMessage',
funcDef: async (dataArg: interfaces.IUniverseMessage, socketConnectionArg) => {
// run in "this" context of this class
await (async () => {
const universeConnection = UniverseConnection.findUniverseConnectionBySocketConnection(
this.universeCache,
socketConnectionArg
);
if (universeConnection) {
plugins.smartlog.defaultLogger.log('ok', 'found UniverseConnection for socket for incoming message');
} else {
plugins.smartlog.defaultLogger.log('warn', 'found no Authorized channel for incoming message');
return {
error: 'You need to authenticate for a channel'
};
}
const unauthenticatedMessage = UniverseMessage.createMessageFromPayload(socketConnectionArg, dataArg);
const foundChannel = await UniverseChannel.authorizeAMessageForAChannel(
this.universeCache,
unauthenticatedMessage
);
if (foundChannel && unauthenticatedMessage.authenticated) {
const authenticatedMessage = unauthenticatedMessage;
await this.universeCache.addMessage(authenticatedMessage);
}
})();
}
});
// add socket functions
this.smartsocket.addSocketFunction(socketFunctionSubscription);
this.smartsocket.addSocketFunction(socketFunctionProcessMessage);
// add smartsocket to the running smartexpress app
this.smartsocket.setExternalServer('smartexpress', this.smartexpressServer as any);
// start everything
await this.smartexpressServer.start();
await this.smartsocket.start();
plugins.smartlog.defaultLogger.log('success', 'started universe');
}
/**
* stop everything
*/
public async stopServer() {
await this.smartsocket.stop();
await this.smartexpressServer.stop();

View File

@ -0,0 +1,80 @@
import * as plugins from './smartuniverse.plugins';
import { UniverseChannel } from './smartuniverse.classes.universechannel';
import { UniverseMessage } from './smartuniverse.classes.universemessage';
import { Objectmap } from '@pushrocks/lik';
import { Observable, from } from 'rxjs';
import { filter } from 'rxjs/operators';
import { rxjs } from '@pushrocks/smartrx';
import { UniverseConnection } from './smartuniverse.classes.universeconnection';
import { Universe } from './smartuniverse.classes.universe';
/**
* universe store handles the creation, storage and retrieval of messages.
*/
export class UniverseCache {
// ========
// INSTANCE
// ========
public standardMessageExpiry: number;
public destructionTime: number = 60000;
/**
* stores messages for this instance
*/
public messageMap = new Objectmap<UniverseMessage>();
/**
* stores the channels that are available within the universe
*/
public channelMap = new Objectmap<UniverseChannel>();
/**
* stores all connections
*/
public connectionMap = new plugins.lik.Objectmap<UniverseConnection>();
/**
* allows messages to be processed in a blacklist mode for further analysis
*/
public blackListChannel: UniverseChannel;
public universeRef: Universe;
constructor(universeArg: Universe, standardMessageExpiryArg: number) {
this.universeRef = universeArg;
this.standardMessageExpiry = standardMessageExpiryArg;
this.blackListChannel = new UniverseChannel(this.universeRef, 'blacklist', 'nada');
}
/**
* add a message to the UniverseCache
* @param messageArg
* @param attachedPayloadArg
*/
public async addMessage(messageArg: UniverseMessage) {
messageArg.setUniverseCache(this);
UniverseChannel.authorizeAMessageForAChannel(this, messageArg);
this.messageMap.add(messageArg);
messageArg.universeChannelList.forEach(universeChannel => {
universeChannel.pushToClients(messageArg);
});
}
/**
* Read a message from the UniverseCache
*/
public readMessagesYoungerThan(
unixTimeArg?: number,
channelName?: string
): Observable<UniverseMessage> {
const messageObservable = from(this.messageMap.getArray()).pipe(
filter(messageArg => {
return messageArg.smartTimestamp.isYoungerThanMilliSeconds(this.destructionTime);
})
);
return messageObservable;
}
}

View File

@ -1,32 +1,137 @@
import * as plugins from './smartuniverse.plugins';
import * as interfaces from './interfaces';
import { Objectmap } from 'lik';
import { UniverseCache } from './smartuniverse.classes.universecache';
import { UniverseMessage } from './smartuniverse.classes.universemessage';
import { UniverseConnection } from './smartuniverse.classes.universeconnection';
import { Universe } from './smartuniverse.classes.universe';
/**
* enables messages to stay within a certain scope.
*/
export class UniverseChannel {
/**
* stores the channels that are available within the universe
*/
public static channelStore = new Objectmap();
// ======
// STATIC
// ======
/**
* creates new channels
* @param channelArg the name of the topic
* @param secretArg the secret thats used for a certain topic.
* @param passphraseArg the secret thats used for a certain topic.
*/
public static createChannel = (channelArg: string, secretArg: string) => {
public static createChannel(
universeArg: Universe,
channelNameArg: string,
passphraseArg: string
) {
const newChannel = new UniverseChannel(universeArg, channelNameArg, passphraseArg);
universeArg.universeCache.channelMap.add(newChannel);
return newChannel;
}
credentials: {
user: string;
password: string;
};
/**
* returns boolean wether certain channel exists
*/
public static async doesChannelExists(universeCacheArg: UniverseCache, channelNameArg: string) {
const channel = universeCacheArg.channelMap.find(channelArg => {
return channelArg.name === channelNameArg;
});
if (channel) {
return true;
} else {
return false;
}
}
/**
* authenticates a client on the server side
* a static message authorization function that takes the UniverseCache
* (where messages and channels are stored and their lifetime is managed)
* and the universemessage to find a fitting channel for the message
* @param universeCacheArg
* @param universeMessageArg
*/
async authenticateClient() {}
public static authorizeAMessageForAChannel(
universeCacheArg: UniverseCache,
universeMessageArg: UniverseMessage
): UniverseChannel {
const foundChannel = universeCacheArg.channelMap.find(universeChannel => {
const result = universeChannel.authenticate(universeMessageArg);
return result;
});
if (foundChannel) {
universeMessageArg.authenticated = true;
universeMessageArg.universeChannelList.add(foundChannel);
plugins.smartlog.defaultLogger.log('ok', 'message authorized');
return foundChannel;
} else {
universeMessageArg.authenticated = false;
universeMessageArg.universeChannelList.add(universeCacheArg.blackListChannel);
plugins.smartlog.defaultLogger.log('warn', 'message not valid');
return null;
}
}
public static getUniverseChannelByName(universeRef: Universe, universeChannelName: string) {
return universeRef.universeCache.channelMap.find(channelArg => {
return channelArg.name === universeChannelName;
});
}
// ========
// INSTANCE
// ========
/**
* the name of the channel
*/
public name: string;
public universeRef: Universe;
/**
* the passphrase for the channel
*/
public passphrase: string;
constructor(universeArg: Universe, channelNameArg: string, passphraseArg: string) {
this.universeRef = universeArg;
this.name = channelNameArg;
this.passphrase = passphraseArg;
}
/**
* authenticates a client on the server side by matching
* # the messages channelName against the unverseChannel's name
* # the messages password against the universeChannel's password
*/
public authenticate(universeMessageArg: UniverseMessage): boolean {
return (
this.name === universeMessageArg.targetChannelName &&
this.passphrase === universeMessageArg.passphrase
);
}
/**
* pushes a message to clients
* @param messageArg
*/
public async pushToClients(messageArg: UniverseMessage) {
const universeConnectionsWithChannelAccess: UniverseConnection[] = [];
this.universeRef.universeCache.connectionMap.forEach(async socketConnection => {
if (socketConnection.authenticatedChannels.includes(this)) {
universeConnectionsWithChannelAccess.push(socketConnection);
}
});
for (const universeConnection of universeConnectionsWithChannelAccess) {
const smartsocket = universeConnection.socketConnection.smartsocketRef as plugins.smartsocket.Smartsocket;
const universeMessageToSend: interfaces.IUniverseMessage = {
id: messageArg.id,
timestamp: messageArg.timestamp,
passphrase: messageArg.passphrase,
targetChannelName: this.name,
messageText: messageArg.messageText,
payload: messageArg.payload,
payloadStringType: messageArg.payloadStringType
};
smartsocket.clientCall('processMessage', universeMessageToSend, universeConnection.socketConnection);
}
}
}

View File

@ -1,60 +0,0 @@
import * as plugins from './smartuniverse.plugins';
import { Observable } from 'rxjs';
import { Smartsocket, SmartsocketClient } from 'smartsocket';
import * as url from 'url';
import {
IServerGetMessagesRequestBody,
IServerPutMessageRequestBody
} from './smartuniverse.classes.universe';
import { UniverseMessage } from './smartuniverse.classes.universemessage';
export interface IClientOptions {
serverAddress: string;
}
/**
* this class is for client side only!!!
* allows connecting to a universe server
*/
export class UniverseClient {
public options;
private socketClient: plugins.smartsocket.SmartsocketClient;
private observableIntake: plugins.smartrx.ObservableIntake<UniverseMessage>;
constructor(optionsArg: IClientOptions) {
this.options = optionsArg;
}
public async sendMessage(messageArg, payloadArg) {
const requestBody = {
message: messageArg,
payload: payloadArg
};
// TODO: User websocket connection if available
await plugins.smartrequest.post(this.options.serverAddress, {
requestBody
});
}
public getMessageObservable() {
if (!this.socketClient && !this.observableIntake) {
const parsedURL = url.parse(this.options.serverAddress);
this.socketClient = new SmartsocketClient({
alias: process.env.SOCKET_ALIAS || 'someclient',
password: 'UniverseClient',
port: parseInt(parsedURL.port, 10),
role: 'UniverseClient',
url: parsedURL.hostname
});
this.observableIntake = new plugins.smartrx.ObservableIntake();
this.socketClient.connect();
}
return this.observableIntake.observable;
}
public close() {
this.socketClient.disconnect();
}
}

View File

@ -0,0 +1,118 @@
import * as plugins from './smartuniverse.plugins';
import * as interfaces from './interfaces';
import { UniverseChannel } from './smartuniverse.classes.universechannel';
import { UniverseCache } from './smartuniverse.classes.universecache';
import { Universe } from './smartuniverse.classes.universe';
/**
* represents a connection to the universe
*/
export class UniverseConnection {
/**
*
* @param universeConnectionArg
*/
public static async addConnectionToCache(
universeRef: Universe,
universeConnectionArg: UniverseConnection
) {
let universeConnection = universeConnectionArg;
universeConnection = await UniverseConnection.deduplicateUniverseConnection(
universeRef.universeCache,
universeConnection
);
universeConnection = await UniverseConnection.authenticateAuthenticationRequests(
universeRef,
universeConnection
);
universeRef.universeCache.connectionMap.add(universeConnection);
}
/**
* deduplicates UniverseConnections
*/
public static async deduplicateUniverseConnection(
universeCache: UniverseCache,
universeConnectionArg: UniverseConnection
): Promise<UniverseConnection> {
let connectionToReturn: UniverseConnection;
universeCache.connectionMap.forEach(async existingConnection => {
if (existingConnection.socketConnection === universeConnectionArg.socketConnection) {
connectionToReturn = await this.mergeUniverseConnections(
existingConnection,
universeConnectionArg
);
}
});
if (!connectionToReturn) {
connectionToReturn = universeConnectionArg;
}
return connectionToReturn;
}
/**
* authenticate AuthenticationRequests
*/
public static async authenticateAuthenticationRequests(
universeRef: Universe,
universeConnectionArg: UniverseConnection
): Promise<UniverseConnection> {
for (const authenticationRequest of universeConnectionArg.authenticationRequests) {
const universeChannelToAuthenticateAgainst = UniverseChannel.getUniverseChannelByName(universeRef, authenticationRequest.name);
if (universeChannelToAuthenticateAgainst.passphrase === authenticationRequest.passphrase) {
universeConnectionArg.authenticatedChannels.push(universeChannelToAuthenticateAgainst);
}
}
return universeConnectionArg;
}
/**
* merges two UniverseConnections
*/
public static mergeUniverseConnections(
connectionArg1: UniverseConnection,
connectionArg2: UniverseConnection
) {
return connectionArg1;
}
/**
* finds a UniverseConnection by providing a socket connection
*/
public static findUniverseConnectionBySocketConnection(
universeCache: UniverseCache,
socketConnectionArg: plugins.smartsocket.SocketConnection
): UniverseConnection {
const universeConnection = universeCache.connectionMap.find(universeConnectionArg => {
return universeConnectionArg.socketConnection === socketConnectionArg;
});
return universeConnection;
}
public terminatedDeferred = plugins.smartpromise.defer();
/**
* the socketClient to ping
*/
public socketConnection: plugins.smartsocket.SocketConnection;
public authenticationRequests: interfaces.IServerCallSubscribeActionPayload[] = [];
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: interfaces.IServerCallSubscribeActionPayload[];
}) {
this.authenticationRequests = optionsArg.authenticationRequests;
this.socketConnection = optionsArg.socketConnection;
}
}

View File

@ -1,49 +1,107 @@
import * as plugins from './smartuniverse.plugins';
import * as interfaces from './interfaces';
import { Timer, TimeStamp } from 'smarttime';
import { UniverseStore } from './smartuniverse.classes.universestore';
import { Objectmap } from '@pushrocks/lik';
import { Timer, TimeStamp } from '@pushrocks/smarttime';
import { Universe } from './smartuniverse.classes.universe';
import { UniverseChannel } from './smartuniverse.classes.universechannel';
import { UniverseCache } from './smartuniverse.classes.universecache';
import { SocketConnection } from '@pushrocks/smartsocket';
/**
* represents a message within a universe
* acts as a container to save message states like authentication status
*/
export class UniverseMessage {
export class UniverseMessage implements interfaces.IUniverseMessage {
public static createMessageFromPayload(socketConnectionArg: SocketConnection, dataArg: interfaces.IUniverseMessage) {
const universeMessageInstance = new UniverseMessage(dataArg);
universeMessageInstance.socketConnection = socketConnectionArg;
return universeMessageInstance;
}
public id: string;
public timestamp: number;
public smartTimestamp: TimeStamp;
public messageText: string;
public passphrase: string;
public payload: any;
public payloadStringType;
public targetChannelName: string;
public socketConnection: SocketConnection;
/**
* public and unique id
* numeric ascending
* adheres to time in milliseconds
* avoids duplications though
* the UniverseCache the message is attached to
*/
public universeCache: UniverseCache;
/**
* enables unprotected grouping of messages for efficiency purposes.
*/
public universeChannelList = new Objectmap<UniverseChannel>();
/**
* wether the message is authenticated
*/
public authenticated: boolean = false;
/**
* a destruction timer for this message
*/
public id: number;
public universeStore: UniverseStore;
public timestamp: TimeStamp; // when has this message been created
public topic: string; // enables unprotected grouping of messages for efficiency purposes.
public message: string; // the actual message
public attachedPayload: any; // any attached payloads. Can be of binary format.
public destructionTimer: Timer; // a timer to take care of message destruction
/**
* the constructor to create a universe message
* @param parentUniverseStore
* @param messageArg
* @param attachedPayloadArg
* @param selfdestructAfterArg
* @param messageArg
* @param attachedPayloadArg
*/
constructor(
parentUniverseStore: UniverseStore,
messageArg: string,
attachedPayloadArg: any,
selfdestructAfterArg: number
) {
this.universeStore = parentUniverseStore;
this.timestamp = new TimeStamp();
this.message = messageArg;
this.attachedPayload = attachedPayloadArg;
this.destructionTimer = new Timer(selfdestructAfterArg);
this.destructionTimer.start();
constructor(messageDescriptor: interfaces.IUniverseMessage) {
this.smartTimestamp = new TimeStamp(this.timestamp);
this.messageText = messageDescriptor.messageText;
this.targetChannelName = messageDescriptor.targetChannelName;
this.passphrase = messageDescriptor.passphrase;
this.payload = messageDescriptor.payload;
// prevent memory issues
this.fallBackDestruction();
}
// set up self destruction by removing this from the parent messageStore
this.destructionTimer.completed.then(async () => {
this.universeStore.messageStore.remove(this);
public setUniverseCache(universeCacheArg: UniverseCache) {
this.universeCache = universeCacheArg;
}
public setTargetChannel() {
}
public setDestructionTimer(selfdestructAfterArg: number) {
if (selfdestructAfterArg) {
this.destructionTimer = new Timer(selfdestructAfterArg);
this.destructionTimer.start();
// set up self destruction by removing this from the parent messageCache
this.destructionTimer.completed.then(async () => {
this.universeCache.messageMap.remove(this);
});
} else {
this.fallBackDestruction();
}
}
/**
* handles bad messages for further analysis
*/
public handleAsBadMessage() {
plugins.smartlog.defaultLogger.log('warn', 'received a bad message');
}
/**
* prevents memory leaks if channels have no default
*/
private fallBackDestruction() {
plugins.smartdelay.delayFor(1000).then(() => {
if (!this.destructionTimer) {
this.setDestructionTimer(6000);
}
});
}
}

View File

@ -1,45 +0,0 @@
import * as plugins from './smartuniverse.plugins';
import { UniverseMessage } from './smartuniverse.classes.universemessage';
import { Objectmap } from 'lik';
import { Observable } from 'rxjs';
import { rxjs } from 'smartrx';
/**
* universe store handles the creation, storage and retrieval of messages.
*/
export class UniverseStore {
public standardMessageExpiry: number;
public destructionTime: number = 60000;
public messageStore = new Objectmap<UniverseMessage>();
private lastId: number = 0; // stores the last id
constructor(standardMessageExpiryArg: number) {
this.standardMessageExpiry = standardMessageExpiryArg;
}
/**
* add a message to the UniverseStore
* @param messageArg
* @param attachedPayloadArg
*/
public addMessage(messageArg, attachedPayloadArg) {
this.messageStore.add(
new UniverseMessage(this, messageArg, attachedPayloadArg, this.destructionTime)
);
}
/**
* Read a message from the UniverseStore
*/
public readMessagesYoungerThan(unixTimeArg?: number): Observable<UniverseMessage> {
const messageObservable = rxjs.Observable.from(this.messageStore.getArray()).filter(
messageArg => {
return messageArg.timestamp.isYoungerThanMilliSeconds(this.destructionTime);
}
);
return messageObservable;
}
}

View File

@ -1,14 +0,0 @@
import * as plugins from './smartuniverse.plugins';
import { Universe } from './index';
process.env.CLI = 'true';
const universeCli = new plugins.smartcli.Smartcli();
universeCli.standardTask().then(async argvArg => {
const standardUniverse = new Universe({
messageExpiryInMilliseconds: 60000
});
await standardUniverse.initServer(8765);
});

View File

@ -1,23 +1,33 @@
import * as lik from 'lik';
// node native
import * as path from 'path';
import * as smartcli from 'smartcli';
import * as smartexpress from 'smartexpress';
import * as smartfile from 'smartfile';
import * as smartq from 'smartq';
import * as smartrequest from 'smartrequest';
import * as smartrx from 'smartrx';
import * as smartsocket from 'smartsocket';
import * as smarttime from 'smarttime';
export { path };
// pushrocks scope
import * as lik from '@pushrocks/lik';
import * as smarthash from '@pushrocks/smarthash';
import * as smartdelay from '@pushrocks/smartdelay';
import * as smartexpress from '@pushrocks/smartexpress';
import * as smartfile from '@pushrocks/smartfile';
import * as smartlog from '@pushrocks/smartlog';
import * as smartpromise from '@pushrocks/smartpromise';
import * as smartrequest from '@pushrocks/smartrequest';
import * as smartrx from '@pushrocks/smartrx';
import * as smartsocket from '@pushrocks/smartsocket';
import * as smarttime from '@pushrocks/smarttime';
import * as smartunique from '@pushrocks/smartunique';
export {
lik,
path,
smartcli,
smarthash,
smartdelay,
smartexpress,
smartfile,
smartq,
smartlog,
smartpromise,
smartrx,
smartrequest,
smartsocket,
smarttime
smarttime,
smartunique
};

View File

@ -1,12 +1,17 @@
{
"extends": [
"tslint:latest",
"tslint-config-prettier"
],
"extends": ["tslint:latest", "tslint-config-prettier"],
"rules": {
"semicolon": [
true,
"always"
]
}
"semicolon": [true, "always"],
"no-console": false,
"ordered-imports": false,
"object-literal-sort-keys": false,
"member-ordering": {
"options":{
"order": [
"static-method"
]
}
}
},
"defaultSeverity": "warning"
}

1761
yarn.lock

File diff suppressed because it is too large Load Diff