feat: add websocket model
Signed-off-by: MiaoWoo <admin@yumc.pw>
This commit is contained in:
parent
c93c5c3fbb
commit
d9e3cad8a1
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"useWorkspaces": true,
|
"useWorkspaces": true,
|
||||||
"version": "0.3.3",
|
"version": "independent",
|
||||||
"packages": [
|
"packages": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
],
|
],
|
||||||
@ -14,4 +14,4 @@
|
|||||||
"registry": "https://repo.yumc.pw/repository/npm-hosted/"
|
"registry": "https://repo.yumc.pw/repository/npm-hosted/"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -14,7 +14,7 @@
|
|||||||
"url": "git+https://github.com/502647092/cc-server-parent.git"
|
"url": "git+https://github.com/502647092/cc-server-parent.git"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "npx ts-node src/index.ts",
|
"watch": "npx tsc --watch",
|
||||||
"build": "rimraf dist && npx tsc",
|
"build": "rimraf dist && npx tsc",
|
||||||
"test": "echo \"Error: run tests from root\" && exit 1"
|
"test": "echo \"Error: run tests from root\" && exit 1"
|
||||||
},
|
},
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
export * from './decorators'
|
export * from './decorators'
|
||||||
export * from './constants';
|
export * from './activation'
|
||||||
export * from './utils'
|
export * from 'inversify-express-utils'
|
||||||
export * from './activation'
|
|
||||||
|
@ -23,15 +23,17 @@
|
|||||||
"@cc-server/binding": "^0.3.3",
|
"@cc-server/binding": "^0.3.3",
|
||||||
"@cc-server/db-mongo": "^0.3.3",
|
"@cc-server/db-mongo": "^0.3.3",
|
||||||
"@cc-server/ioc": "^0.3.3",
|
"@cc-server/ioc": "^0.3.3",
|
||||||
|
"@cc-server/ws": "^0.3.3",
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"inversify": "^5.0.1",
|
"inversify": "^5.0.1",
|
||||||
"inversify-express-utils": "^6.3.2",
|
"inversify-express-utils": "^6.3.2",
|
||||||
"prettyjson": "^1.2.1",
|
"prettyjson": "^1.2.1",
|
||||||
"reflect-metadata": "^0.1.13"
|
"socket.io": "^2.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/body-parser": "^1.17.0",
|
"@types/body-parser": "^1.17.0",
|
||||||
"@types/express": "^4.17.0",
|
"@types/express": "^4.17.0",
|
||||||
|
"@types/socket.io": "^2.1.2",
|
||||||
"@types/prettyjson": "^0.0.29",
|
"@types/prettyjson": "^0.0.29",
|
||||||
"mocha": "^6.1.4",
|
"mocha": "^6.1.4",
|
||||||
"rimraf": "^2.6.3",
|
"rimraf": "^2.6.3",
|
||||||
@ -40,4 +42,4 @@
|
|||||||
"typescript": "^3.5.1"
|
"typescript": "^3.5.1"
|
||||||
},
|
},
|
||||||
"gitHead": "7d84393a3cb6be6be9ed51d71f12677d2d7d0728"
|
"gitHead": "7d84393a3cb6be6be9ed51d71f12677d2d7d0728"
|
||||||
}
|
}
|
55
packages/core/public/index.html
Normal file
55
packages/core/public/index.html
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/socket.io-client@2.2.0/dist/socket.io.js"> </script>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@3.12.2/dist/xterm.css" />
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@3.12.2/dist/addons/fullscreen/fullscreen.css">
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/xterm@3.12.2/dist/xterm.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/xterm@3.12.2/dist/addons/fit/fit.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/xterm@3.12.2/dist/addons/attach/attach.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/xterm@3.12.2/dist/addons/fullscreen/fullscreen.js"></script>
|
||||||
|
<style>
|
||||||
|
#terminal-container .terminal.xterm {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#terminal-container .xterm-viewport {
|
||||||
|
height: 100% !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="terminal" style="height: 100%;"></div>
|
||||||
|
<script type="text/javascript">
|
||||||
|
Terminal.applyAddon(fit);
|
||||||
|
Terminal.applyAddon(attach);
|
||||||
|
Terminal.applyAddon(fullscreen);
|
||||||
|
var term = new Terminal();
|
||||||
|
term.open(document.getElementById('terminal'));
|
||||||
|
term.toggleFullScreen();
|
||||||
|
term.fit();
|
||||||
|
var socket = io('',{
|
||||||
|
path: '/ws'
|
||||||
|
});
|
||||||
|
socket.on('connect', function(){
|
||||||
|
term.writeln('connect')
|
||||||
|
});
|
||||||
|
term.on('data', (data) => {
|
||||||
|
if (data =='\r') {
|
||||||
|
term.writeln(data)
|
||||||
|
}
|
||||||
|
socket.send(data);
|
||||||
|
});
|
||||||
|
socket.on('message', function(data){
|
||||||
|
term.write(data.toString());
|
||||||
|
});
|
||||||
|
socket.on('disconnect', function(){
|
||||||
|
term.reset();
|
||||||
|
term.writeln('disconnect')
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -1,8 +1,6 @@
|
|||||||
import {
|
import { inject, postConstruct } from '@cc-server/ioc';
|
||||||
controller, response, requestBody, httpGet, httpPost, queryParam, requestParam
|
import { Vaild, NotBlank, NotNull, controller, requestBody, httpGet, httpPost, requestParam } from '@cc-server/binding'
|
||||||
} from 'inversify-express-utils';
|
import { namespace, listener, interfaces, io, getSocketContext } from '@cc-server/ws'
|
||||||
import { inject, postConstruct } from 'inversify';
|
|
||||||
import { Vaild, NotBlank, NotNull } from '@cc-server/binding'
|
|
||||||
import { DBClient } from '@cc-server/db'
|
import { DBClient } from '@cc-server/db'
|
||||||
import '@cc-server/db-mongo'
|
import '@cc-server/db-mongo'
|
||||||
|
|
||||||
@ -21,9 +19,7 @@ class ExampleModel {
|
|||||||
email: string;
|
email: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Model = ExampleModel
|
@controller('/example')
|
||||||
|
|
||||||
@controller('')
|
|
||||||
export class Controller {
|
export class Controller {
|
||||||
@inject(DBClient)
|
@inject(DBClient)
|
||||||
private client: DBClient
|
private client: DBClient
|
||||||
@ -34,14 +30,14 @@ export class Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@httpGet('/')
|
@httpGet('/')
|
||||||
public async list(): Promise<Model[]> {
|
public async list(): Promise<ExampleModel[]> {
|
||||||
return this.client.find({});
|
return this.client.find({});
|
||||||
}
|
}
|
||||||
|
|
||||||
@httpGet('/:id')
|
@httpGet('/:id')
|
||||||
public async get(
|
public async get(
|
||||||
@requestParam('id') id: string
|
@requestParam('id') id: string
|
||||||
): Promise<Model> {
|
): Promise<ExampleModel> {
|
||||||
return this.client.findOneById(id);
|
return this.client.findOneById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,8 +52,40 @@ export class Controller {
|
|||||||
@httpPost('/:id')
|
@httpPost('/:id')
|
||||||
public async update(
|
public async update(
|
||||||
@requestParam('id') id: string,
|
@requestParam('id') id: string,
|
||||||
@requestBody() model: Model
|
@requestBody() model: ExampleModel
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
return this.client.updateById(id, model);
|
return this.client.updateById(id, model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@namespace('/', (socket: io.Socket, next: (err?: any) => void) => {
|
||||||
|
console.log(socket.nsp.name, socket.id, 'before connection');
|
||||||
|
next();
|
||||||
|
})
|
||||||
|
export class Namespace extends interfaces.Namespace {
|
||||||
|
private cache: { [key: string]: string } = {};
|
||||||
|
|
||||||
|
public async connection(socket: io.Socket) {
|
||||||
|
console.log(this.nsp.name, socket.id, 'connection');
|
||||||
|
return `Welcome to Websocket Chat Room Now: ${Date.now()} Your ID: ${socket.id}! \n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async disconnect(socket: io.Socket) {
|
||||||
|
console.log(this.nsp.name, socket.id, 'disconnect');
|
||||||
|
}
|
||||||
|
|
||||||
|
@listener('message', (socket: io.Socket, packet: io.Packet, next: (err?: any) => void) => {
|
||||||
|
console.log(socket.nsp.name, socket.id, 'listener middleware', [...packet]);
|
||||||
|
next();
|
||||||
|
})
|
||||||
|
public async message(socket: io.Socket, data: any) {
|
||||||
|
console.log(this.nsp.name, socket.id, 'message', data)
|
||||||
|
this.cache[socket.id] = (this.cache[socket.id] || '') + data;
|
||||||
|
if (data == '\r' && this.cache[socket.id] !== "") {
|
||||||
|
let result = this.broadcast(this.cache[socket.id] + '\n')
|
||||||
|
this.cache[socket.id] = '';
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,48 +1,76 @@
|
|||||||
import 'reflect-metadata';
|
import 'reflect-metadata';
|
||||||
|
import * as http from 'http'
|
||||||
import * as express from "express";
|
import * as express from "express";
|
||||||
import { InversifyExpressServer, interfaces, getRouteInfo } from 'inversify-express-utils';
|
|
||||||
import * as bodyParser from 'body-parser';
|
|
||||||
import { buildProviderModule } from '@cc-server/ioc';
|
|
||||||
import { rebuildServer } from '@cc-server/binding'
|
|
||||||
import * as prettyjson from "prettyjson";
|
import * as prettyjson from "prettyjson";
|
||||||
import { Container } from 'inversify';
|
import * as bodyParser from 'body-parser';
|
||||||
|
import { buildWebSocket, io } from '@cc-server/ws'
|
||||||
|
import { buildProviderModule, Container } from '@cc-server/ioc';
|
||||||
|
import { InversifyExpressServer, interfaces, getRouteInfo, rebuildServer } from '@cc-server/binding'
|
||||||
|
|
||||||
|
export { io, http, express }
|
||||||
|
|
||||||
export class CcServerBoot {
|
export class CcServerBoot {
|
||||||
private _container: Container;
|
private _container: Container;
|
||||||
private _server: InversifyExpressServer;
|
private _server: http.Server;
|
||||||
private _serverInstance: express.Application;
|
private _serverInstance: express.Application;
|
||||||
|
private _serverInversify: InversifyExpressServer;
|
||||||
|
private _wsServer: io.Server;
|
||||||
|
|
||||||
constructor(container?: Container) {
|
constructor(container?: Container) {
|
||||||
this._container = container || new Container();
|
this._container = container || new Container();
|
||||||
this._container.load(buildProviderModule());
|
this._serverInstance = express();
|
||||||
|
this._server = http.createServer(this._serverInstance);
|
||||||
// start the server
|
// start the server
|
||||||
this._server = new InversifyExpressServer(this._container);
|
this._serverInversify = new InversifyExpressServer(this._container, null, null, this._serverInstance);
|
||||||
this._server.setConfig((app) => {
|
this._serverInversify.setConfig((app) => {
|
||||||
app.use(bodyParser.urlencoded({
|
app.use(bodyParser.urlencoded({
|
||||||
extended: true
|
extended: true
|
||||||
}));
|
}));
|
||||||
app.use(bodyParser.json());
|
app.use(bodyParser.json());
|
||||||
app.use(bodyParser.raw());
|
app.use(bodyParser.raw());
|
||||||
});
|
});
|
||||||
|
this._wsServer = io(this._server, {
|
||||||
|
path: '/ws',
|
||||||
|
serveClient: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
get server() {
|
||||||
|
return this._server;
|
||||||
|
}
|
||||||
|
|
||||||
|
get express() {
|
||||||
|
return this._serverInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
get inversify() {
|
||||||
|
return this._serverInversify;
|
||||||
|
}
|
||||||
|
|
||||||
|
get websocket() {
|
||||||
|
return this._wsServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setConfig(fn: interfaces.ConfigFunction) {
|
public setConfig(fn: interfaces.ConfigFunction) {
|
||||||
this._server.setConfig(fn)
|
this._serverInversify.setConfig(fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
public setErrorConfig(fn: interfaces.ConfigFunction) {
|
public setErrorConfig(fn: interfaces.ConfigFunction) {
|
||||||
this._server.setErrorConfig(fn)
|
this._serverInversify.setErrorConfig(fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
public build() {
|
public build() {
|
||||||
this._serverInstance = this._server.build();
|
this._container.load(buildProviderModule());
|
||||||
|
this._serverInstance = this._serverInversify.build();
|
||||||
rebuildServer(this._container);
|
rebuildServer(this._container);
|
||||||
|
buildWebSocket(this._container, this._wsServer);
|
||||||
return this._serverInstance;
|
return this._serverInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public start(port: number = 80) {
|
public start(port: number = 80) {
|
||||||
const routeInfo = getRouteInfo(this._container);
|
const routeInfo = getRouteInfo(this._container);
|
||||||
console.log(prettyjson.render({ routes: routeInfo }));
|
console.log(prettyjson.render({ routes: routeInfo }));
|
||||||
this._serverInstance.listen(port);
|
this._server.listen(port);
|
||||||
console.log(`Server started on port ${port} :)`);
|
console.log(`Server started on port ${port} :)`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import { CcServerBoot } from './index'
|
import { CcServerBoot, express } from './index'
|
||||||
|
|
||||||
import './function/handle';
|
import './function/handle';
|
||||||
|
|
||||||
let server = new CcServerBoot();
|
let server = new CcServerBoot();
|
||||||
|
|
||||||
|
server.express.use(express.static('public'));
|
||||||
|
|
||||||
server.build();
|
server.build();
|
||||||
server.start();
|
server.start();
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
"registry": "https://repo.yumc.pw/repository/npm-hosted/"
|
"registry": "https://repo.yumc.pw/repository/npm-hosted/"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"watch": "npx tsc --watch",
|
||||||
"build": "rimraf dist && npx tsc",
|
"build": "rimraf dist && npx tsc",
|
||||||
"test": "echo \"Error: run tests from root\" && exit 1"
|
"test": "echo \"Error: run tests from root\" && exit 1"
|
||||||
},
|
},
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
"url": "git+https://github.com/502647092/cc-server-parent.git"
|
"url": "git+https://github.com/502647092/cc-server-parent.git"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"watch": "npx tsc --watch",
|
||||||
"build": "rimraf dist && npx tsc",
|
"build": "rimraf dist && npx tsc",
|
||||||
"test": "echo \"Error: run tests from root\" && exit 1"
|
"test": "echo \"Error: run tests from root\" && exit 1"
|
||||||
},
|
},
|
||||||
|
@ -20,8 +20,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"inversify": "^5.0.1",
|
"inversify": "^5.0.1",
|
||||||
"inversify-binding-decorators": "^4.0.0",
|
"inversify-binding-decorators": "^4.0.0",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13"
|
||||||
"ts-node-dev": "^1.0.0-pre.40"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"mocha": "^6.1.4",
|
"mocha": "^6.1.4",
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import "reflect-metadata";
|
import "reflect-metadata";
|
||||||
import { Container, inject, interfaces } from 'inversify';
|
import { Container, inject, interfaces, injectable, postConstruct } from 'inversify';
|
||||||
import { autoProvide, provide, fluentProvide, buildProviderModule } from 'inversify-binding-decorators';
|
import { autoProvide, provide, fluentProvide, buildProviderModule } from 'inversify-binding-decorators';
|
||||||
|
|
||||||
const provideNamed = (identifier: interfaces.ServiceIdentifier<any>, name: string) => {
|
const provideNamed = (identifier: interfaces.ServiceIdentifier<any>, name: string) => {
|
||||||
@ -14,4 +14,4 @@ const provideSingleton = (identifier: interfaces.ServiceIdentifier<any>) => {
|
|||||||
.done();
|
.done();
|
||||||
};
|
};
|
||||||
|
|
||||||
export { autoProvide, provide, provideNamed, provideSingleton, inject, buildProviderModule };
|
export { autoProvide, provide, provideNamed, provideSingleton, Container, inject, injectable, postConstruct, buildProviderModule };
|
||||||
|
4
packages/ws/.gitignore
vendored
Normal file
4
packages/ws/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/node_modules
|
||||||
|
/dist
|
||||||
|
/package-lock.json
|
||||||
|
/yarn.lock
|
22
packages/ws/.npmignore
Normal file
22
packages/ws/.npmignore
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
src
|
||||||
|
test
|
||||||
|
typings
|
||||||
|
bundled
|
||||||
|
build
|
||||||
|
coverage
|
||||||
|
docs
|
||||||
|
wiki
|
||||||
|
gulpfile.js
|
||||||
|
bower.json
|
||||||
|
karma.conf.js
|
||||||
|
tsconfig.json
|
||||||
|
typings.json
|
||||||
|
CONTRIBUTING.md
|
||||||
|
ISSUE_TEMPLATE.md
|
||||||
|
PULL_REQUEST_TEMPLATE.md
|
||||||
|
tslint.json
|
||||||
|
wallaby.js
|
||||||
|
.travis.yml
|
||||||
|
.gitignore
|
||||||
|
.vscode
|
||||||
|
type_definitions
|
11
packages/ws/README.md
Normal file
11
packages/ws/README.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# `cc-server-ws`
|
||||||
|
|
||||||
|
> TODO: description
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
const ccServerIoc = require('cc-server-ws');
|
||||||
|
|
||||||
|
// TODO: DEMONSTRATE API
|
||||||
|
```
|
33
packages/ws/package.json
Normal file
33
packages/ws/package.json
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"name": "@cc-server/ws",
|
||||||
|
"version": "0.3.3",
|
||||||
|
"description": "> TODO: description",
|
||||||
|
"author": "MiaoWoo <admin@yumc.pw>",
|
||||||
|
"homepage": "https://faas.yumc.pw",
|
||||||
|
"license": "ISC",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"publishConfig": {
|
||||||
|
"registry": "https://repo.yumc.pw/repository/npm-hosted/"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"watch": "npx tsc --watch",
|
||||||
|
"build": "rimraf dist && npx tsc",
|
||||||
|
"test": "echo \"Error: run tests from root\" && exit 1"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/502647092/cc-server-parent.git"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"inversify": "^5.0.1",
|
||||||
|
"reflect-metadata": "^0.1.13",
|
||||||
|
"socket.io": "^2.2.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/socket.io": "^2.1.2",
|
||||||
|
"mocha": "^6.1.4",
|
||||||
|
"rimraf": "^2.6.3",
|
||||||
|
"typescript": "^3.5.1"
|
||||||
|
},
|
||||||
|
"gitHead": "7d84393a3cb6be6be9ed51d71f12677d2d7d0728"
|
||||||
|
}
|
92
packages/ws/src/builder.ts
Normal file
92
packages/ws/src/builder.ts
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import "reflect-metadata";
|
||||||
|
import { Container } from 'inversify'
|
||||||
|
import { interfaces, BroadcastMessage } from './interfaces'
|
||||||
|
import { TYPE } from './constants'
|
||||||
|
import { getNamespaces, getNamespaceMetadata, getNamespaceListenerMetadata } from './utils'
|
||||||
|
import * as io from 'socket.io'
|
||||||
|
|
||||||
|
export function buildWebSocket(container: Container, server: io.Server) {
|
||||||
|
let constructors = getNamespaces();
|
||||||
|
if (!constructors.length) { return; }
|
||||||
|
|
||||||
|
registryNamespace(container, constructors);
|
||||||
|
|
||||||
|
// get all namespaces
|
||||||
|
let namespaces = container.getAll<interfaces.Namespace>(TYPE.Namespace)
|
||||||
|
for (const namespace of namespaces) {
|
||||||
|
let namespaceMetadata = getNamespaceMetadata(namespace);
|
||||||
|
let namespaceEventMetadata = getNamespaceListenerMetadata(namespace);
|
||||||
|
let ns = server.of(namespaceMetadata.name);
|
||||||
|
namespace.constructor.prototype.nsp = ns;
|
||||||
|
applyNamespaceMiddleware(namespaceMetadata, ns);
|
||||||
|
ns.on('connection', async (socket: io.Socket) => {
|
||||||
|
let namespaceInstance = container.getNamed<interfaces.Namespace>(TYPE.Namespace, namespace.constructor.name);
|
||||||
|
await applyEvent(namespaceInstance, socket);
|
||||||
|
await applyMiddlewares(namespaceEventMetadata, socket);
|
||||||
|
await applyListeners(namespaceEventMetadata, socket, namespaceInstance);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function registryNamespace(container: Container, constructors: any[]) {
|
||||||
|
constructors.forEach((constructor) => {
|
||||||
|
const name = constructor.name;
|
||||||
|
if (container.isBoundNamed(TYPE.Namespace, name)) {
|
||||||
|
throw new Error(`DUPLICATED_NAMESPACE(${name})`);
|
||||||
|
}
|
||||||
|
container.bind(TYPE.Namespace)
|
||||||
|
.to(constructor)
|
||||||
|
.whenTargetNamed(name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function applyNamespaceMiddleware(namespaceMetadata: interfaces.NamespaceMetadata, ns: io.Namespace) {
|
||||||
|
for (const middleware of namespaceMetadata.middleware) {
|
||||||
|
ns.use(middleware);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function flatten(arr: Array<any>) {
|
||||||
|
while (arr.some(item => Array.isArray(item))) {
|
||||||
|
arr = [].concat(...arr);
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyMiddlewares(namespaceEventMetadata: interfaces.ListenerMetadata[], socket: io.Socket) {
|
||||||
|
// socket.use((packet: io.Packet, next: (err?: any) => void) => {
|
||||||
|
// Reflect.defineMetadata(TYPE.SocketContext, socket, packet);
|
||||||
|
// next();
|
||||||
|
// })
|
||||||
|
let middlewares = [...new Set(flatten(namespaceEventMetadata.map((data) => data.middleware)))];
|
||||||
|
for (const middleware of middlewares) {
|
||||||
|
socket.use((packet: io.Packet, next: (err?: any) => void) => { middleware(socket, packet, next); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function applyEvent(namespaceInstance: interfaces.Namespace, socket: io.Socket) {
|
||||||
|
if (namespaceInstance.connection) {
|
||||||
|
let result = await namespaceInstance.connection(socket);
|
||||||
|
if (result != undefined) {
|
||||||
|
socket.send(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (namespaceInstance.disconnect) {
|
||||||
|
socket.on('disconnect', async () => await namespaceInstance.disconnect(socket));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyListeners(namespaceEventMetadata: interfaces.ListenerMetadata[], socket: io.Socket, namespaceInstance: interfaces.Namespace) {
|
||||||
|
for (const event of namespaceEventMetadata) {
|
||||||
|
socket.on(event.name, async data => {
|
||||||
|
let result = await namespaceInstance[event.key](socket, data);
|
||||||
|
if (result != undefined) {
|
||||||
|
if (result instanceof BroadcastMessage) {
|
||||||
|
socket.broadcast.emit(event.name, result.message);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
socket.emit(event.name, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
9
packages/ws/src/constants.ts
Normal file
9
packages/ws/src/constants.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export const TYPE = {
|
||||||
|
Namespace: Symbol.for('namespace'),
|
||||||
|
SocketContext: Symbol.for('context')
|
||||||
|
}
|
||||||
|
|
||||||
|
export const METADATA_KEY = {
|
||||||
|
namespace: "@cc-server/ws:namespace",
|
||||||
|
listener: "@cc-server/ws:listener"
|
||||||
|
};
|
40
packages/ws/src/decorators.ts
Normal file
40
packages/ws/src/decorators.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { inject, injectable, decorate } from "inversify";
|
||||||
|
import { interfaces } from './interfaces'
|
||||||
|
import { METADATA_KEY } from './constants'
|
||||||
|
import { getNamespaceListenerMetadata, getNamespacesMetadata } from './utils'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Socket.io Namespace
|
||||||
|
* @param name namespace name default is '/'
|
||||||
|
* @param middleware middleware array
|
||||||
|
*/
|
||||||
|
export function namespace(name?: string, ...middleware: interfaces.Middleware[]) {
|
||||||
|
return function(target: any) {
|
||||||
|
let currentMetadata: interfaces.NamespaceMetadata = {
|
||||||
|
name: name || '/',
|
||||||
|
middleware: middleware,
|
||||||
|
target: target
|
||||||
|
};
|
||||||
|
decorate(injectable(), target);
|
||||||
|
Reflect.defineMetadata(METADATA_KEY.namespace, currentMetadata, target);
|
||||||
|
const previousMetadata: interfaces.NamespaceMetadata[] = getNamespacesMetadata();
|
||||||
|
Reflect.defineMetadata(METADATA_KEY.namespace, [currentMetadata, ...previousMetadata], Reflect);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Socket.io listner
|
||||||
|
* @param name event name
|
||||||
|
*/
|
||||||
|
export function listener(name?: string, ...middleware: interfaces.ListenerMiddleware[]) {
|
||||||
|
return function(target: any, key: string, value: any) {
|
||||||
|
let currentMetadata: interfaces.ListenerMetadata = {
|
||||||
|
name: name || key,
|
||||||
|
middleware: middleware,
|
||||||
|
key: key,
|
||||||
|
target: target
|
||||||
|
};
|
||||||
|
const previousMetadata: interfaces.ListenerMetadata[] = getNamespaceListenerMetadata(target)
|
||||||
|
Reflect.defineMetadata(METADATA_KEY.listener, [currentMetadata, ...previousMetadata], target.constructor);
|
||||||
|
};
|
||||||
|
}
|
7
packages/ws/src/index.ts
Normal file
7
packages/ws/src/index.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import * as io from 'socket.io'
|
||||||
|
|
||||||
|
export * from './builder'
|
||||||
|
export * from './decorators'
|
||||||
|
export * from './interfaces'
|
||||||
|
export { getSocketContext } from './utils'
|
||||||
|
export { io }
|
64
packages/ws/src/interfaces.ts
Normal file
64
packages/ws/src/interfaces.ts
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import * as io from 'socket.io'
|
||||||
|
import { injectable } from 'inversify';
|
||||||
|
|
||||||
|
export class Message {
|
||||||
|
constructor(public message: any) { }
|
||||||
|
}
|
||||||
|
export class BroadcastMessage {
|
||||||
|
constructor(public message: any) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace interfaces {
|
||||||
|
@injectable()
|
||||||
|
export class Namespace {
|
||||||
|
/**
|
||||||
|
* @see io.Namespace
|
||||||
|
*/
|
||||||
|
public nsp?: io.Namespace;
|
||||||
|
/**
|
||||||
|
* The event fired when we get a new connection
|
||||||
|
* @param socket socket
|
||||||
|
* @return return data will send use socket.send(data)
|
||||||
|
*/
|
||||||
|
public connection?(socket: io.Socket): any;
|
||||||
|
/**
|
||||||
|
* The event fired when socket is close
|
||||||
|
* @param socket socket
|
||||||
|
*/
|
||||||
|
public disconnect?(socket: io.Socket): void;
|
||||||
|
/**
|
||||||
|
* broadcast message on this namespace
|
||||||
|
*/
|
||||||
|
public broadcast(message: any): BroadcastMessage {
|
||||||
|
return new BroadcastMessage(message);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Event Listener
|
||||||
|
* @param data event data
|
||||||
|
* @return return data will send use socket.emit(key, data)
|
||||||
|
*/
|
||||||
|
[key: string]: ((data: any, socket: io.Socket) => any) | any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Middleware {
|
||||||
|
(socket: io.Socket, next: (err?: any) => void): void;
|
||||||
|
}
|
||||||
|
export interface ListenerMiddleware {
|
||||||
|
(socket: io.Socket, packet: io.Packet, next: (err?: any) => void): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NamespaceMetadata {
|
||||||
|
name: string;
|
||||||
|
middleware?: Middleware[];
|
||||||
|
target: any;
|
||||||
|
}
|
||||||
|
export interface ListenerMetadata {
|
||||||
|
name: string;
|
||||||
|
key: string;
|
||||||
|
/**
|
||||||
|
* Socket Listener Middleware will share all event listener
|
||||||
|
*/
|
||||||
|
middleware?: ListenerMiddleware[];
|
||||||
|
target: any;
|
||||||
|
}
|
||||||
|
}
|
42
packages/ws/src/utils.ts
Normal file
42
packages/ws/src/utils.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { METADATA_KEY, TYPE } from './constants'
|
||||||
|
import { interfaces } from './interfaces'
|
||||||
|
|
||||||
|
function getNamespaces() {
|
||||||
|
return getNamespacesMetadata().map((target) => target.target);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNamespacesMetadata() {
|
||||||
|
let namespaceMetadata: interfaces.NamespaceMetadata[] = Reflect.getMetadata(
|
||||||
|
METADATA_KEY.namespace,
|
||||||
|
Reflect
|
||||||
|
) || [];
|
||||||
|
return namespaceMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNamespaceMetadata(target: any) {
|
||||||
|
let namespaceMetadata: interfaces.NamespaceMetadata = Reflect.getMetadata(
|
||||||
|
METADATA_KEY.namespace,
|
||||||
|
target.constructor
|
||||||
|
) || {};
|
||||||
|
return namespaceMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNamespaceListenerMetadata(target: any) {
|
||||||
|
let eventMetadata: interfaces.ListenerMetadata[] = Reflect.getMetadata(
|
||||||
|
METADATA_KEY.listener,
|
||||||
|
target.constructor
|
||||||
|
) || [];
|
||||||
|
return eventMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSocketContext(packet: any) {
|
||||||
|
return Reflect.getMetadata(TYPE.SocketContext, packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
getNamespaces,
|
||||||
|
getNamespaceMetadata,
|
||||||
|
getNamespacesMetadata,
|
||||||
|
getNamespaceListenerMetadata,
|
||||||
|
getSocketContext
|
||||||
|
}
|
7
packages/ws/tsconfig.json
Normal file
7
packages/ws/tsconfig.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": "src",
|
||||||
|
"outDir": "dist"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user