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"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user