feat: add web package
Signed-off-by: MiaoWoo <admin@yumc.pw>
This commit is contained in:
parent
8a07e85fda
commit
2c1eea5bc1
1
packages/web/.npmignore
Symbolic link
1
packages/web/.npmignore
Symbolic link
@ -0,0 +1 @@
|
||||
../../.npmignore
|
29
packages/web/package.json
Normal file
29
packages/web/package.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "@ccms/web",
|
||||
"version": "0.7.0",
|
||||
"description": "MiaoScript web package",
|
||||
"keywords": [
|
||||
"miaoscript",
|
||||
"minecraft",
|
||||
"bukkit",
|
||||
"sponge"
|
||||
],
|
||||
"author": "MiaoWoo <admin@yumc.pw>",
|
||||
"homepage": "https://github.com/circlecloud/ms.git",
|
||||
"license": "ISC",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
"clean": "rimraf dist",
|
||||
"watch": "tsc --watch",
|
||||
"build": "yarn clean && tsc",
|
||||
"test": "echo \"Error: run tests from root\" && exit 1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rimraf": "^3.0.2",
|
||||
"typescript": "^3.9.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ccms/container": "^0.7.0"
|
||||
}
|
||||
}
|
2
packages/web/src/constants.ts
Normal file
2
packages/web/src/constants.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export const WebProxyBeanName = 'webServerProxy'
|
||||
export const FilterProxyBeanName = 'webFilterProxy'
|
30
packages/web/src/decorators/index.ts
Normal file
30
packages/web/src/decorators/index.ts
Normal file
@ -0,0 +1,30 @@
|
||||
export const Controller = () => {
|
||||
return <TFunction extends Function>(target: TFunction): ClassDecorator => {
|
||||
return
|
||||
}
|
||||
}
|
||||
export const Header = () => {
|
||||
return <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>): MethodDecorator => {
|
||||
return
|
||||
}
|
||||
}
|
||||
export const Post = () => {
|
||||
return <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>): MethodDecorator => {
|
||||
return
|
||||
}
|
||||
}
|
||||
export const Get = () => {
|
||||
return <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>): MethodDecorator => {
|
||||
return
|
||||
}
|
||||
}
|
||||
export const Param = () => {
|
||||
return (target: Object, propertyKey: string | symbol, parameterIndex: number): ParameterDecorator => {
|
||||
return
|
||||
}
|
||||
}
|
||||
export const RequestBody = () => {
|
||||
return (target: Object, propertyKey: string | symbol, parameterIndex: number): ParameterDecorator => {
|
||||
return
|
||||
}
|
||||
}
|
8
packages/web/src/index.ts
Normal file
8
packages/web/src/index.ts
Normal file
@ -0,0 +1,8 @@
|
||||
/// <reference types="@ccms/types/dist/typings/jdk/index" />
|
||||
/// <reference types="@ccms/types/dist/typings/tomcat/index" />
|
||||
/// <reference types="@ccms/types/dist/typings/spring/index" />
|
||||
/// <reference types="@ccms/types/dist/typings/spring/beans/index" />
|
||||
|
||||
export * from './server'
|
||||
export * from './decorators'
|
||||
export * from './interfaces'
|
19
packages/web/src/interfaces/context.ts
Normal file
19
packages/web/src/interfaces/context.ts
Normal file
@ -0,0 +1,19 @@
|
||||
export type RequestHandler = (ctx: Context) => any
|
||||
export interface InterceptorAdapter {
|
||||
name: string
|
||||
preHandle?(ctx: Context): void
|
||||
postHandle?(ctx: Context): void
|
||||
}
|
||||
|
||||
export type RequestHeader = { [key: string]: string | string[] }
|
||||
export type RequestParams = { [key: string]: string | string[] }
|
||||
|
||||
export interface Context {
|
||||
request?: javax.servlet.http.HttpServletRequest
|
||||
response?: javax.servlet.http.HttpServletResponse
|
||||
header?: RequestHeader
|
||||
url?: string
|
||||
params?: RequestParams
|
||||
body?: any
|
||||
result?: any
|
||||
}
|
2
packages/web/src/interfaces/index.ts
Normal file
2
packages/web/src/interfaces/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './context'
|
||||
export * from './metadata'
|
14
packages/web/src/interfaces/metadata.ts
Normal file
14
packages/web/src/interfaces/metadata.ts
Normal file
@ -0,0 +1,14 @@
|
||||
export interface BaseMetadata {
|
||||
/**
|
||||
* 名称 为空则为对象名称
|
||||
*/
|
||||
name?: string
|
||||
/**
|
||||
* 支持的服务器列表 为空则代表所有
|
||||
*/
|
||||
servers?: string[]
|
||||
}
|
||||
|
||||
export interface ControllerMetadata extends BaseMetadata {
|
||||
|
||||
}
|
187
packages/web/src/server.ts
Normal file
187
packages/web/src/server.ts
Normal file
@ -0,0 +1,187 @@
|
||||
import * as querystring from 'querystring'
|
||||
|
||||
import { web } from '@ccms/api'
|
||||
import { provideSingleton, JSClass, postConstruct } from '@ccms/container'
|
||||
|
||||
import { WebProxyBeanName, FilterProxyBeanName } from './constants'
|
||||
import { Context, InterceptorAdapter, RequestHandler } from './interfaces'
|
||||
|
||||
@provideSingleton(web.Server)
|
||||
export class Server {
|
||||
@JSClass('pw.yumc.MiaoScript.web.WebServerProxy')
|
||||
private WebServerProxy: any
|
||||
@JSClass('pw.yumc.MiaoScript.web.WebFilterProxy')
|
||||
private WebFilterProxy: any
|
||||
|
||||
private StreamUtils = org.springframework.util.StreamUtils
|
||||
private ResponseEntity = org.springframework.http.ResponseEntity
|
||||
|
||||
private interceptors: Map<string, InterceptorAdapter>
|
||||
private handlerMapping: Map<string, RequestHandler>
|
||||
|
||||
private beanFactory: org.springframework.beans.factory.support.DefaultListableBeanFactory
|
||||
|
||||
@postConstruct()
|
||||
initialization() {
|
||||
this.beanFactory = base.getInstance().getAutowireCapableBeanFactory()
|
||||
this.interceptors = new Map()
|
||||
this.handlerMapping = new Map()
|
||||
this.start()
|
||||
}
|
||||
|
||||
start() {
|
||||
this.registryFilterProxy()
|
||||
this.registryWebProxy()
|
||||
}
|
||||
|
||||
stop() {
|
||||
try {
|
||||
this.beanFactory.destroySingleton(FilterProxyBeanName)
|
||||
this.beanFactory.destroySingleton(WebProxyBeanName)
|
||||
} catch (error) {
|
||||
console.ex(error)
|
||||
}
|
||||
}
|
||||
|
||||
registryMapping(path: string, handler: RequestHandler) {
|
||||
console.debug(`Registry Mapping ${path} to handle ${handler.name || '<anonymous>'} function.`)
|
||||
this.handlerMapping.set(path, handler)
|
||||
}
|
||||
|
||||
unregistryMapping(path: string) {
|
||||
this.handlerMapping.delete(path)
|
||||
}
|
||||
|
||||
registryInterceptor(interceptor: InterceptorAdapter) {
|
||||
console.debug(`Registry ${interceptor.name} Interceptor.`)
|
||||
this.interceptors.set(interceptor.name, interceptor)
|
||||
}
|
||||
|
||||
unregistryInterceptor(interceptor: InterceptorAdapter) {
|
||||
this.interceptors.delete(interceptor.name)
|
||||
}
|
||||
|
||||
private registryFilterProxy() {
|
||||
try { this.beanFactory.destroySingleton(FilterProxyBeanName) } catch (ex) { }
|
||||
var WebFilterProxyNashorn = Java.extend(this.WebFilterProxy, {
|
||||
doFilter: (servletRequest: javax.servlet.http.HttpServletRequest, servletResponse: javax.servlet.http.HttpServletResponse, filterChain: javax.servlet.FilterChain) => {
|
||||
console.log('WebFilterProxyNashorn', 'doFilter', servletRequest, servletResponse)
|
||||
filterChain.doFilter(servletRequest, servletResponse)
|
||||
}
|
||||
})
|
||||
this.beanFactory.registerSingleton(FilterProxyBeanName, new WebFilterProxyNashorn())
|
||||
}
|
||||
|
||||
private registryWebProxy() {
|
||||
try { this.beanFactory.destroySingleton(WebProxyBeanName) } catch (ex) { }
|
||||
var WebServerProxyNashorn = Java.extend(this.WebServerProxy, {
|
||||
process: (req: javax.servlet.http.HttpServletRequest, resp: javax.servlet.http.HttpServletResponse) => {
|
||||
let ctx: Context = { request: req, response: resp }
|
||||
ctx.url = req.getRequestURI()
|
||||
// @ts-ignore
|
||||
ctx.header = { __noSuchProperty__: (name: string) => req.getHeader(name) + '' }
|
||||
if (req.getQueryString()) {
|
||||
ctx.url += `?${req.getQueryString()}`
|
||||
ctx.params = querystring.parse(req.getQueryString())
|
||||
}
|
||||
if (req.getMethod() == "POST") {
|
||||
ctx.body = this.StreamUtils.copyToString(req.getInputStream(), java.nio.charset.StandardCharsets.UTF_8)
|
||||
if ((ctx.header['Content-Type'] || '').includes('application/json')) {
|
||||
try {
|
||||
ctx.body = JSON.parse(ctx.body)
|
||||
} catch (error) {
|
||||
return {
|
||||
status: 500,
|
||||
msg: `parse json body error: ${error}`,
|
||||
path: ctx.url,
|
||||
error: console.stack(error, false),
|
||||
timestamp: Date.now()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let result = this.process(ctx)
|
||||
result?.status && resp.setStatus(result.status)
|
||||
return result
|
||||
}
|
||||
})
|
||||
this.beanFactory.registerSingleton(WebProxyBeanName, new WebServerProxyNashorn())
|
||||
}
|
||||
|
||||
|
||||
private process(ctx: Context) {
|
||||
let startTime = Date.now()
|
||||
for (const [_, interceptor] of this.interceptors) {
|
||||
if (interceptor.preHandle) {
|
||||
try {
|
||||
let startTime = Date.now()
|
||||
ctx.result = interceptor.preHandle(ctx)
|
||||
let preHandleTime = Date.now() - startTime
|
||||
if (preHandleTime > 20) {
|
||||
console.debug(`[WARN] Interceptor ${interceptor.name} preHandle cost time ${preHandleTime}ms!`)
|
||||
}
|
||||
if (ctx.result) { return ctx.result }
|
||||
} catch (error) {
|
||||
console.ex(error)
|
||||
return {
|
||||
status: 500,
|
||||
msg: `Interceptor ${interceptor.name} preHandle error: ${error}`,
|
||||
path: ctx.url,
|
||||
error: console.stack(error, false),
|
||||
timestamp: Date.now()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx.result = this.execRequestHandle(ctx)
|
||||
for (const [_, interceptor] of this.interceptors) {
|
||||
if (interceptor.postHandle) {
|
||||
try {
|
||||
let startTime = Date.now()
|
||||
ctx.result = interceptor.postHandle(ctx)
|
||||
let preHandleTime = Date.now() - startTime
|
||||
if (preHandleTime > 20) {
|
||||
console.debug(`[WARN] Interceptor ${interceptor.name} preHandle cost time ${preHandleTime}ms!`)
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
status: 500,
|
||||
msg: `Interceptor ${interceptor.name} postHandle error: ${error}`,
|
||||
path: ctx.url,
|
||||
error: console.stack(error, false),
|
||||
timestamp: Date.now()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
console.debug(`
|
||||
===================== MiaoSpring =====================
|
||||
Request URL : ${ctx.url}
|
||||
Response Body : ${JSON.stringify(Java.asJSONCompatible(ctx.result))}
|
||||
Handle Time : ${Date.now() - startTime}ms
|
||||
======================================================`)
|
||||
return ctx.result
|
||||
}
|
||||
|
||||
private execRequestHandle(ctx: Context) {
|
||||
if (!this.handlerMapping.has(ctx.request.getRequestURI())) {
|
||||
return {
|
||||
status: 404,
|
||||
msg: "handlerMapping Not Found!",
|
||||
path: ctx.url,
|
||||
timestamp: Date.now()
|
||||
}
|
||||
}
|
||||
try {
|
||||
return this.handlerMapping.get(ctx.request.getRequestURI())(ctx)
|
||||
} catch (error) {
|
||||
return {
|
||||
status: 500,
|
||||
msg: '' + error,
|
||||
path: ctx.url,
|
||||
error: console.stack(error, false),
|
||||
timestamp: Date.now()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
7
packages/web/tsconfig.json
Normal file
7
packages/web/tsconfig.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "src",
|
||||
"outDir": "dist"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user