feat: add plugin web stage

Signed-off-by: MiaoWoo <admin@yumc.pw>
This commit is contained in:
MiaoWoo 2020-07-06 16:10:33 +08:00
parent 3ea72f0aac
commit e55d48d713
4 changed files with 64 additions and 30 deletions

View File

@ -1,20 +1,29 @@
import { decorate, injectable } from "@ccms/container" import { plugin } from '@ccms/api'
import { decorate, injectable, getContainer } from "@ccms/container"
import { METADATA_KEY, PARAM_TYPE } from '../constants' import { METADATA_KEY, PARAM_TYPE } from '../constants'
import { interfaces } from "../interfaces" import { interfaces } from "../interfaces"
import { addControllerMetadata, addControllerAction, addActionParam } from "./utils" import { addControllerMetadata, addControllerAction, addActionParam, getControllerMetadata } from "./utils"
export const Controllers = (...controllers: any[]) => {
return (target: any, propertyKey: string) => {
for (const controller of controllers) {
addControllerMetadata(getControllerMetadata(controller), target)
}
}
}
export const Controller = (metadata?: string | interfaces.ControllerMetadata) => { export const Controller = (metadata?: string | interfaces.ControllerMetadata) => {
return (target: any) => { return (target: any) => {
if (!metadata) { metadata = target.name.toLowerCase().replace('controller', '') } if (!metadata) { metadata = target.name.toLowerCase().replace('controller', '') }
if (typeof metadata === "string") { metadata = { path: metadata } } if (typeof metadata === "string") { metadata = { path: metadata } }
metadata.target = target
metadata.name = metadata.name || target.name metadata.name = metadata.name || target.name
metadata.path = metadata.path ?? `/${metadata}` metadata.path = metadata.path ?? `/${metadata}`
metadata.path = metadata.path.startsWith('/') ? metadata.path : `/${metadata.path}` metadata.path = metadata.path.startsWith('/') ? metadata.path : `/${metadata.path}`
decorate(injectable(), target) decorate(injectable(), target)
Reflect.defineMetadata(METADATA_KEY.Controller, metadata, target) Reflect.defineMetadata(METADATA_KEY.Controller, metadata, target)
addControllerMetadata(metadata) addControllerMetadata(metadata)
return
} }
} }
@ -29,7 +38,6 @@ function action(method: interfaces.Method) {
metadata.executor = propertyKey metadata.executor = propertyKey
Reflect.defineMetadata(METADATA_KEY.Action, metadata, target[propertyKey]) Reflect.defineMetadata(METADATA_KEY.Action, metadata, target[propertyKey])
addControllerAction(target, propertyKey) addControllerAction(target, propertyKey)
return
} }
} }
} }
@ -50,7 +58,6 @@ function param(type: PARAM_TYPE) {
metadata.index = index metadata.index = index
metadata.paramtype = Reflect.getMetadata("design:paramtypes", target, propertyKey)[index] metadata.paramtype = Reflect.getMetadata("design:paramtypes", target, propertyKey)[index]
addActionParam(target, propertyKey, metadata) addActionParam(target, propertyKey, metadata)
return
} }
} }
} }
@ -72,7 +79,6 @@ function Middleware() {
metadata.executor = propertyKey metadata.executor = propertyKey
Reflect.defineMetadata(METADATA_KEY.Action, metadata, target[propertyKey]) Reflect.defineMetadata(METADATA_KEY.Action, metadata, target[propertyKey])
addControllerAction(target, propertyKey) addControllerAction(target, propertyKey)
return
} }
} }
} }

View File

@ -1,11 +1,13 @@
import { interfaces } from '../interfaces' import { interfaces } from '../interfaces'
import { METADATA_KEY } from '../constants' import { METADATA_KEY } from '../constants'
export function getControllerMetadatas(): interfaces.ControllerMetadata[] { export function getControllerMetadatas(target: any = Reflect): Map<string, interfaces.ControllerMetadata> {
return Reflect.getMetadata(METADATA_KEY.Controller, Reflect) || [] return Reflect.getMetadata(METADATA_KEY.Controller, target) || new Map<string, interfaces.ControllerMetadata>()
} }
export function addControllerMetadata(metadata: interfaces.ControllerMetadata) { export function addControllerMetadata(metadata: interfaces.ControllerMetadata, target: any = Reflect) {
Reflect.defineMetadata(METADATA_KEY.Controller, [metadata, ...getControllerMetadatas()], Reflect) let metadatas = getControllerMetadatas(target)
metadatas.set(metadata.name, metadata)
Reflect.defineMetadata(METADATA_KEY.Controller, metadatas, target)
} }
export function getControllerActions(target: any): string[] { export function getControllerActions(target: any): string[] {
return Reflect.getMetadata(METADATA_KEY.Action, target.constructor) || [] return Reflect.getMetadata(METADATA_KEY.Action, target.constructor) || []

View File

@ -19,7 +19,7 @@ export namespace interfaces {
/** /**
* *
*/ */
target?: string target?: any
} }
export interface ControllerMetadata extends WebMetadata { export interface ControllerMetadata extends WebMetadata {

View File

@ -1,11 +1,11 @@
import * as querystring from 'querystring' import * as querystring from 'querystring'
import { web } from '@ccms/api' import { web, plugin } from '@ccms/api'
import { provideSingleton, JSClass, postConstruct, Container, ContainerInstance, inject } from '@ccms/container' import { provideSingleton, JSClass, postConstruct, Container, ContainerInstance, inject } from '@ccms/container'
import { WebProxyBeanName, FilterProxyBeanName, METADATA_KEY, PARAM_TYPE } from './constants' import { WebProxyBeanName, FilterProxyBeanName, METADATA_KEY, PARAM_TYPE } from './constants'
import { Context, InterceptorAdapter, RequestHandler, interfaces } from './interfaces' import { Context, InterceptorAdapter, RequestHandler, interfaces } from './interfaces'
import { getControllerActions, getActionMetadata, getControllerMetadata, getActionParams } from './decorators' import { getControllerActions, getActionMetadata, getControllerMetadata, getActionParams, getControllerMetadatas } from './decorators'
const HttpServletRequestWrapper = Java.type('javax.servlet.http.HttpServletRequestWrapper') const HttpServletRequestWrapper = Java.type('javax.servlet.http.HttpServletRequestWrapper')
const HttpServletResponseWrapper = Java.type('javax.servlet.http.HttpServletResponseWrapper') const HttpServletResponseWrapper = Java.type('javax.servlet.http.HttpServletResponseWrapper')
@ -25,6 +25,7 @@ export class Server {
private StreamUtils = org.springframework.util.StreamUtils private StreamUtils = org.springframework.util.StreamUtils
private ResponseEntity = org.springframework.http.ResponseEntity private ResponseEntity = org.springframework.http.ResponseEntity
private pluginControllers: Map<string, any>
private interceptors: Map<string, InterceptorAdapter> private interceptors: Map<string, InterceptorAdapter>
private methodMappings: Map<string, Map<string, RequestHandler>> private methodMappings: Map<string, Map<string, RequestHandler>>
@ -36,6 +37,8 @@ export class Server {
this.interceptors = new Map() this.interceptors = new Map()
this.methodMappings = new Map() this.methodMappings = new Map()
this.start() this.start()
process.on('plugin.after.enable', (plugin: plugin.Plugin) => this.registryPlugin(plugin))
process.on('plugin.after.disable', (plugin: plugin.Plugin) => this.unregistryPlugin(plugin))
} }
start() { start() {
@ -52,23 +55,34 @@ export class Server {
} }
} }
registryPlugin(plugin: plugin.Plugin) {
let controllers = getControllerMetadatas(plugin).values()
for (const controller of controllers) {
console.debug(`Plugin ${plugin.description.name} Registry Controller ${controller.name}.`)
this.registryController(controller.target)
}
}
unregistryPlugin(plugin: plugin.Plugin) {
let controllers = getControllerMetadatas(plugin).values()
for (const controller of controllers) {
console.debug(`Plugin ${plugin.description.name} Unregistry Controller ${controller.name}.`)
this.unregistryController(controller.target)
}
}
registryController(target: any) { registryController(target: any) {
if (!target) { throw new Error('Controller can\'t be undefiend!') } if (!target) { throw new Error('Controller can\'t be undefiend!') }
let controllerMetadata = getControllerMetadata(target) let controllerMetadata = getControllerMetadata(target)
if (!controllerMetadata) { throw new Error(`Controller ${target.name} must have @Controller decorator!`) } if (!controllerMetadata) { throw new Error(`Controller ${target.name} must have @Controller decorator!`) }
try { target = this.bindController(target)
this.container.rebind(METADATA_KEY.Controller).to(target).inSingletonScope().whenTargetNamed(target.name)
} catch{
this.container.bind(METADATA_KEY.Controller).to(target).inSingletonScope().whenTargetNamed(target.name)
}
target = this.container.getNamed(METADATA_KEY.Controller, target.name)
let actions = getControllerActions(target) let actions = getControllerActions(target)
for (const action of actions) { for (const action of actions) {
let actionMetadata = getActionMetadata(target, action) let actionMetadata = getActionMetadata(target, action)
let path = `${controllerMetadata.path || ''}${actionMetadata.path || ''}` let path = `${controllerMetadata.path || ''}${actionMetadata.path || ''}`
if (!path) throw new Error(`Controller ${controllerMetadata.name} Action ${actionMetadata.name} path is empty!`) if (!path) throw new Error(`Controller ${controllerMetadata.name} Action ${actionMetadata.name} path is empty!`)
if (!this.methodMappings.has(path)) { this.methodMappings.set(path, new Map()) } if (!this.methodMappings.has(path)) { this.methodMappings.set(path, new Map()) }
console.debug(`Controller ${controllerMetadata.name} Registry ${path} to ${actionMetadata.executor || '<anonymous>'} Action function.`) console.debug(`Controller ${controllerMetadata.name} Registry ${path} Action to ${actionMetadata.executor || '<anonymous>'} function.`)
this.methodMappings.get(path).set(actionMetadata.method || 'ALL', (ctx: Context) => { this.methodMappings.get(path).set(actionMetadata.method || 'ALL', (ctx: Context) => {
let args = [] let args = []
let params = getActionParams(target, action) let params = getActionParams(target, action)
@ -97,6 +111,15 @@ export class Server {
} }
} }
private bindController(target: any) {
try {
this.container.rebind(METADATA_KEY.Controller).to(target).inSingletonScope().whenTargetNamed(target.name)
} catch{
this.container.bind(METADATA_KEY.Controller).to(target).inSingletonScope().whenTargetNamed(target.name)
}
return this.container.getNamed(METADATA_KEY.Controller, target.name)
}
unregistryController(target: any) { unregistryController(target: any) {
if (!target) { throw new Error('Controller can\'t be undefiend!') } if (!target) { throw new Error('Controller can\'t be undefiend!') }
let controllerMetadata = getControllerMetadata(target) let controllerMetadata = getControllerMetadata(target)
@ -112,6 +135,7 @@ export class Server {
let path = `${controllerMetadata.path || ''}${actionMetadata.path || ''}` let path = `${controllerMetadata.path || ''}${actionMetadata.path || ''}`
if (!this.methodMappings.has(path)) { continue } if (!this.methodMappings.has(path)) { continue }
this.methodMappings.get(path).delete(actionMetadata.method) this.methodMappings.get(path).delete(actionMetadata.method)
console.debug(`Controller ${controllerMetadata.name} Unregistry ${path} Action.`)
} }
} }
@ -130,7 +154,9 @@ export class Server {
this.interceptors.set(interceptor.name, interceptor) this.interceptors.set(interceptor.name, interceptor)
} }
unregistryInterceptor(interceptor: InterceptorAdapter) { unregistryInterceptor(interceptor: string | InterceptorAdapter) {
if (typeof interceptor === "string") { interceptor = { name: interceptor } }
console.debug(`Unregistry ${interceptor.name} Interceptor.`)
this.interceptors.delete(interceptor.name) this.interceptors.delete(interceptor.name)
} }
@ -170,12 +196,12 @@ export class Server {
// return wrapper // return wrapper
// } // }
private notFound(method: string, path: string) { private notFound(ctx: Context) {
return { return {
status: 404, status: 404,
msg: "handlerMapping Not Found!", msg: "handlerMapping Not Found!",
method, method: ctx.request.getMethod(),
path, path: ctx.request.getRequestURI(),
timestamp: Date.now() timestamp: Date.now()
} }
} }
@ -184,12 +210,7 @@ export class Server {
try { this.beanFactory.destroySingleton(WebProxyBeanName) } catch (ex) { } try { this.beanFactory.destroySingleton(WebProxyBeanName) } catch (ex) { }
var WebServerProxyNashorn = Java.extend(this.WebServerProxy, { var WebServerProxyNashorn = Java.extend(this.WebServerProxy, {
process: (req: javax.servlet.http.HttpServletRequest, resp: javax.servlet.http.HttpServletResponse) => { process: (req: javax.servlet.http.HttpServletRequest, resp: javax.servlet.http.HttpServletResponse) => {
let path = req.getRequestURI() let ctx: Context = { request: req, response: resp, params: {}, body: {} }
if (!this.methodMappings.has(path)) return this.notFound(req.getMethod(), path)
let mappings = this.methodMappings.get(req.getRequestURI())
let handler = mappings.get(req.getMethod()) || mappings.get("ALL")
if (!handler) return this.notFound(req.getMethod(), path)
let ctx: Context = { request: req, response: resp, params: {}, body: {}, handler }
ctx.url = req.getRequestURI() ctx.url = req.getRequestURI()
// @ts-ignore // @ts-ignore
ctx.headers = { __noSuchProperty__: (name: string) => req.getHeader(name) } ctx.headers = { __noSuchProperty__: (name: string) => req.getHeader(name) }
@ -250,6 +271,10 @@ export class Server {
} }
} }
} }
let path = ctx.request.getRequestURI()
if (!this.methodMappings.has(path)) return this.notFound(ctx)
let mappings = this.methodMappings.get(ctx.request.getRequestURI())
ctx.handler = mappings.get(ctx.request.getMethod()) || mappings.get("ALL")
ctx.result = this.execRequestHandle(ctx) ctx.result = this.execRequestHandle(ctx)
for (const [_, interceptor] of this.interceptors) { for (const [_, interceptor] of this.interceptors) {
if (interceptor.postHandle) { if (interceptor.postHandle) {
@ -283,6 +308,7 @@ Handle Time : ${Date.now() - startTime}ms
} }
private execRequestHandle(ctx: Context) { private execRequestHandle(ctx: Context) {
if (!ctx.handler) return this.notFound(ctx)
try { try {
return ctx.handler(ctx) return ctx.handler(ctx)
} catch (error) { } catch (error) {