diff --git a/packages/binding/src/activation.ts b/packages/binding/src/activation.ts index 7969c15..eda1ae3 100644 --- a/packages/binding/src/activation.ts +++ b/packages/binding/src/activation.ts @@ -15,7 +15,7 @@ let handler = { let origin = argumentsList[param.index]; let props = getVaildModelMetadata(param.type); for (const prop of props) { - if (!prop.handle(origin[prop.name])) { + if (!origin || !prop.handle(origin[prop.name])) { throw new VaildError(prop.message); } } diff --git a/packages/core/cc-server.http b/packages/core/cc-server.http new file mode 100644 index 0000000..5b6f3a9 --- /dev/null +++ b/packages/core/cc-server.http @@ -0,0 +1,21 @@ +@url=https://faas.n.yumc.pw + +### +GET {{url}}/example + +### +POST {{url}}/example + +{ + "username": 1 +} + +### +@websocket={{url}}/websocket + +### +POST {{websocket}} + +{ + "name":"Socket.IO" +} \ No newline at end of file diff --git a/packages/core/package.json b/packages/core/package.json index 2e655ff..306a332 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -25,6 +25,7 @@ "@cc-server/ioc": "^0.4.0", "@cc-server/ws": "^0.4.0", "body-parser": "^1.19.0", + "globby": "^9.2.0", "inversify": "^5.0.1", "inversify-express-utils": "^6.3.2", "prettyjson": "^1.2.1", diff --git a/packages/core/src/function/http.ts b/packages/core/src/function/http.ts index b1d4b64..1749fed 100644 --- a/packages/core/src/function/http.ts +++ b/packages/core/src/function/http.ts @@ -1,6 +1,7 @@ +import { TYPE, io } from '@cc-server/ws' +import { DBClient } from '@cc-server/db' import { inject, postConstruct } from '@cc-server/ioc'; import { Vaild, NotBlank, NotNull, controller, requestBody, httpGet, httpPost, requestParam } from '@cc-server/binding' -import { DBClient } from '@cc-server/db' import '@cc-server/db-mongo' //process.env.FAAS_MONGO_URL = 'mongodb://192.168.0.2:27017'; diff --git a/packages/core/src/function/websocket.ts b/packages/core/src/function/websocket.ts index 8a1b8ef..1d1ca3e 100644 --- a/packages/core/src/function/websocket.ts +++ b/packages/core/src/function/websocket.ts @@ -1,4 +1,6 @@ -import { namespace, listener, interfaces, io } from '@cc-server/ws' +import { controller, httpPost, requestBody } from '@cc-server/binding'; +import { namespace, listener, interfaces, io, TYPE } from '@cc-server/ws' +import { lazyInjectNamed } from '@cc-server/ioc' @namespace('/', (socket: io.Socket, next: (err?: any) => void) => { console.log(socket.nsp.name, socket.id, 'before connection'); @@ -31,3 +33,17 @@ export class Namespace extends interfaces.Namespace { return data; } } + +@controller('/websocket') +export class WebSocketController { + @lazyInjectNamed(TYPE.Namespace, Namespace.name) + private root: Namespace; + + @httpPost('/') + public async create( + @requestBody() model: Object + ): Promise { + this.root.nsp.send(JSON.stringify(model)); + return model; + } +} diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index babb142..9a075b2 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,38 +1,82 @@ import 'reflect-metadata'; -import * as http from 'http' +import * as fs from 'fs'; +import * as http from 'http'; +import * as globby from "globby"; import * as express from "express"; import * as prettyjson from "prettyjson"; 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' +import { buildWebSocket, io, getNamespaceInfo } from '@cc-server/ws' +import { buildProviderModule, Container, initContainer, getContainer } from '@cc-server/ioc'; +import { InversifyExpressServer, getRouteInfo, rebuildServer, controller, httpGet } from '@cc-server/binding'; -export { io, http, express } +export { http, express } + +@controller('/actuator') +class Actuator { + @httpGet('/mappings') + private mappings() { + return { + http: getRouteInfo(getContainer()), + websocket: getNamespaceInfo() + }; + } +} export class CcServerBoot { private _container: Container; private _server: http.Server; private _serverInstance: express.Application; private _serverInversify: InversifyExpressServer; - private _wsServer: io.Server; + private _serverWebsocket: io.Server; + private _beforeUse: express.RequestHandler[] = []; + private _afterUse: express.ErrorRequestHandler[] = []; constructor(container?: Container) { - this._container = container || new Container(); - this._serverInstance = express(); - this._server = http.createServer(this._serverInstance); - // start the server - this._serverInversify = new InversifyExpressServer(this._container, null, null, this._serverInstance); - this._serverInversify.setConfig((app) => { - app.use(bodyParser.urlencoded({ - extended: true - })); - app.use(bodyParser.json()); - app.use(bodyParser.raw()); - }); - this._wsServer = io(this._server, { + this._container = container || this.$createContainer(); + this._serverInstance = this.$createExpressServer(); + this._server = this.$createServer(); + this._serverInversify = this.$createInversifyServer(); + this._serverWebsocket = this.$createWebsocketServer(); + initContainer(this._container); + } + + protected $createContainer(): Container { + return new Container(); + } + + protected $createServer(): http.Server { + return http.createServer(this._serverInstance); + } + + protected $createExpressServer(): express.Application { + return express(); + } + + protected $createInversifyServer(): InversifyExpressServer { + return new InversifyExpressServer(this._container, null, null, this._serverInstance); + } + + protected $createWebsocketServer(): io.Server { + return io(this._server, { path: '/ws', serveClient: false, - }) + }); + } + + protected use(middleware: express.RequestHandler) { + this._beforeUse.push(middleware) + return this; + } + + protected error(middleware: express.ErrorRequestHandler) { + this._afterUse.push(middleware) + return this; + } + + protected $onMountingMiddlewares() { + this.use(bodyParser.urlencoded({ extended: true })) + .use(bodyParser.json()) + .use(bodyParser.raw()); } get server() { @@ -48,23 +92,34 @@ export class CcServerBoot { } get websocket() { - return this._wsServer; + return this._serverWebsocket; } - public setConfig(fn: interfaces.ConfigFunction) { - this._serverInversify.setConfig(fn) + public static(root: string = 'public') { + this.express.use(express.static(root)); + return this; } - public setErrorConfig(fn: interfaces.ConfigFunction) { - this._serverInversify.setErrorConfig(fn) + public scan(path: fs.PathLike) { + let files = fs.readdirSync(path); + for (const file of files) { + + } } public build() { + this.$onMountingMiddlewares(); + this._serverInversify.setConfig((app) => { + this._beforeUse.every(m => app.use(m)) + }) + this._serverInversify.setErrorConfig((app) => { + this._afterUse.every(m => app.use(m)) + }) this._container.load(buildProviderModule()); this._serverInstance = this._serverInversify.build(); rebuildServer(this._container); - buildWebSocket(this._container, this._wsServer); - return this._serverInstance; + buildWebSocket(this._container, this._serverWebsocket); + return this; } public start(port: number = 80) { diff --git a/packages/core/src/server.ts b/packages/core/src/server.ts index 027c918..13caeee 100644 --- a/packages/core/src/server.ts +++ b/packages/core/src/server.ts @@ -3,9 +3,4 @@ import { CcServerBoot, express } from './index' import './function/http'; import './function/websocket'; -let server = new CcServerBoot(); - -server.express.use(express.static('public')); - -server.build(); -server.start(); +new CcServerBoot().static('public').build().start(); diff --git a/packages/ioc/package.json b/packages/ioc/package.json index ac618a0..8668730 100644 --- a/packages/ioc/package.json +++ b/packages/ioc/package.json @@ -10,6 +10,7 @@ "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" }, diff --git a/packages/ioc/src/decorators.ts b/packages/ioc/src/decorators.ts new file mode 100644 index 0000000..0abac54 --- /dev/null +++ b/packages/ioc/src/decorators.ts @@ -0,0 +1,104 @@ +import { interfaces, Container } from "inversify"; + +let _container: Container; + +const CONTAINER = Symbol.for("@cc-server/ioc:Container"); +const INJECTION = Symbol.for("INJECTION"); + +function _proxyGetter( + proto: any, + key: string, + resolve: () => any, + doCache: boolean +) { + function getter() { + if (doCache && !Reflect.hasMetadata(INJECTION, this, key)) { + Reflect.defineMetadata(INJECTION, resolve(), this, key); + } + if (Reflect.hasMetadata(INJECTION, this, key)) { + return Reflect.getMetadata(INJECTION, this, key); + } else { + return resolve(); + } + } + + function setter(newVal: any) { + Reflect.defineMetadata(INJECTION, newVal, this, key); + } + + Object.defineProperty(proto, key, { + configurable: true, + enumerable: true, + get: getter, + set: setter + }); +} + +function initContainer(container: Container) { + Reflect.defineMetadata(CONTAINER, container, Reflect); + _container = container; +} + +function getContainer(): Container { + return _container || Reflect.getMetadata(CONTAINER, Reflect) +} + +function makePropertyInjectDecorator(doCache: boolean) { + return function(serviceIdentifier: interfaces.ServiceIdentifier) { + return function(proto: any, key: string): void { + let resolve = () => { + return getContainer().get(serviceIdentifier); + }; + _proxyGetter(proto, key, resolve, doCache); + }; + }; +} + +function makePropertyInjectNamedDecorator(doCache: boolean) { + return function(serviceIdentifier: interfaces.ServiceIdentifier, named: string) { + return function(proto: any, key: string): void { + let resolve = () => { + return getContainer().getNamed(serviceIdentifier, named); + }; + _proxyGetter(proto, key, resolve, doCache); + }; + }; +} + +function makePropertyInjectTaggedDecorator(doCache: boolean) { + return function(serviceIdentifier: interfaces.ServiceIdentifier, key: string, value: any) { + return function(proto: any, propertyName: string): void { + let resolve = () => { + return getContainer().getTagged(serviceIdentifier, key, value); + }; + _proxyGetter(proto, propertyName, resolve, doCache); + }; + }; +} + +function makePropertyMultiInjectDecorator(doCache: boolean) { + return function(serviceIdentifier: interfaces.ServiceIdentifier) { + return function(proto: any, key: string): void { + let resolve = () => { + return getContainer().getAll(serviceIdentifier); + }; + _proxyGetter(proto, key, resolve, doCache); + }; + }; +} + +let doCache = true; + +let lazyInject = makePropertyInjectDecorator(doCache) +let lazyInjectNamed = makePropertyInjectNamedDecorator(doCache) +let lazyInjectTagged = makePropertyInjectTaggedDecorator(doCache) +let lazyMultiInject = makePropertyMultiInjectDecorator(doCache) + +export { + initContainer, + getContainer, + lazyInject, + lazyInjectNamed, + lazyInjectTagged, + lazyMultiInject +}; diff --git a/packages/ioc/src/index.ts b/packages/ioc/src/index.ts index c5ad337..31bb6d2 100644 --- a/packages/ioc/src/index.ts +++ b/packages/ioc/src/index.ts @@ -1,17 +1,20 @@ import "reflect-metadata"; -import { Container, inject, interfaces, injectable, postConstruct } from 'inversify'; -import { autoProvide, provide, fluentProvide, buildProviderModule } from 'inversify-binding-decorators'; +import { interfaces } from 'inversify'; +import { fluentProvide } from 'inversify-binding-decorators'; const provideNamed = (identifier: interfaces.ServiceIdentifier, name: string) => { - return fluentProvide(identifier) - .whenTargetNamed(name) - .done(); + return fluentProvide(identifier).whenTargetNamed(name).done(); }; const provideSingleton = (identifier: interfaces.ServiceIdentifier) => { - return fluentProvide(identifier) - .inSingletonScope() - .done(); + return fluentProvide(identifier).inSingletonScope().done(); }; -export { autoProvide, provide, provideNamed, provideSingleton, Container, inject, injectable, postConstruct, buildProviderModule }; +export * from 'inversify' +export * from './decorators' +export * from 'inversify-binding-decorators' +export { + fluentProvide, + provideNamed, + provideSingleton +}; diff --git a/packages/ws/src/debug.ts b/packages/ws/src/debug.ts new file mode 100644 index 0000000..d771371 --- /dev/null +++ b/packages/ws/src/debug.ts @@ -0,0 +1,19 @@ +import { getNamespacesMetadata, getNamespaceMetadata, getNamespaceListenerMetadata } from './utils' + +export function getNamespaceInfo() { + let namespaces = getNamespacesMetadata(); + console.log(namespaces) + return namespaces.map(namespace => { + let listenerMetadata = getNamespaceListenerMetadata(namespace.target); + console.log(namespace, listenerMetadata) + return { + namespace: namespace.name, + listeners: listenerMetadata.map(listener => { + return { + event: listener.name, + method: listener.key, + } + }) + } + }) +} \ No newline at end of file diff --git a/packages/ws/src/decorators.ts b/packages/ws/src/decorators.ts index 3fae20f..f257cc3 100644 --- a/packages/ws/src/decorators.ts +++ b/packages/ws/src/decorators.ts @@ -1,7 +1,8 @@ import { inject, injectable, decorate } from "inversify"; import { interfaces } from './interfaces' -import { METADATA_KEY } from './constants' +import { METADATA_KEY, TYPE } from './constants' import { getNamespaceListenerMetadata, getNamespacesMetadata } from './utils' +import { provideNamed, fluentProvide } from "@cc-server/ioc/src"; /** * Socket.io Namespace @@ -16,6 +17,10 @@ export function namespace(name?: string, ...middleware: interfaces.Middleware[]) target: target }; decorate(injectable(), target); + //decorate(fluentProvide(TYPE.Namespace) + // .inSingletonScope() + // .whenTargetNamed(target.constructor.name) + // .done(), target); Reflect.defineMetadata(METADATA_KEY.namespace, currentMetadata, target); const previousMetadata: interfaces.NamespaceMetadata[] = getNamespacesMetadata(); Reflect.defineMetadata(METADATA_KEY.namespace, [currentMetadata, ...previousMetadata], Reflect); diff --git a/packages/ws/src/index.ts b/packages/ws/src/index.ts index cf52c6d..91ca3c0 100644 --- a/packages/ws/src/index.ts +++ b/packages/ws/src/index.ts @@ -3,5 +3,7 @@ import * as io from 'socket.io' export * from './builder' export * from './decorators' export * from './interfaces' +export * from './debug' +export { TYPE } from './constants' export { getSocketContext } from './utils' export { io }