feat: backup plugins

Signed-off-by: MiaoWoo <admin@yumc.pw>
This commit is contained in:
2020-06-20 16:40:34 +08:00
parent bf3638dda0
commit 5f0c3bbdd4
5 changed files with 322 additions and 230 deletions

View File

@@ -2,58 +2,30 @@
/// <reference types="@ccms/types/dist/typings/tomcat/index" />
/// <reference types="@ccms/types/dist/typings/spring/index" />
import { constants, database, plugin } from "@ccms/api"
import { inject, ContainerInstance, Container, JSClass } from "@ccms/container"
import { constants, database, plugin, web } from "@ccms/api"
import { inject, ContainerInstance, Container } from "@ccms/container"
import { JSPlugin, interfaces, cmd } from "@ccms/plugin"
import { DataBase, DataBaseManager } from '@ccms/database'
import { Server, Context, RequestHandler } from '@ccms/web'
import * as fs from '@ccms/common/dist/fs'
import * as reflect from '@ccms/common/dist/reflect'
import * as querystring from 'querystring'
const WebProxyBeanName = 'webServerProxy'
const FilterProxyBeanName = 'webFilterProxy'
type RequestHandler = (ctx: Context) => any
interface InterceptorAdapter {
name: string
preHandle?(ctx: Context): void
postHandle?(ctx: Context): void
}
type RequestHeader = { [key: string]: string | string[] }
type RequestParams = { [key: string]: string | string[] }
interface Context {
request?: javax.servlet.http.HttpServletRequest
response?: javax.servlet.http.HttpServletResponse
header?: RequestHeader
url?: string
params?: RequestParams
body?: any
result?: any
}
@JSPlugin({ name: 'MiaoSpring', prefix: 'MSpring', version: '1.0.1', author: 'MiaoWoo', servers: [constants.ServerType.Bukkit], source: __filename })
@JSPlugin({ name: 'MiaoSpring', prefix: 'MSpring', version: '1.0.1', author: 'MiaoWoo', servers: [constants.ServerType.Spring], source: __filename })
export class MiaoSpring extends interfaces.Plugin {
@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
@inject(ContainerInstance)
private container: Container
@inject(plugin.PluginInstance)
private context: any
@inject(plugin.PluginManager)
private pluginManager: plugin.PluginManager
@inject(database.DataBaseManager)
private databaseManager: DataBaseManager
@inject(web.Server)
private webServer: Server
private ResponseEntity = org.springframework.http.ResponseEntity
private beanFactory: any
private mainDatabase: DataBase
private interceptors: InterceptorAdapter[] = []
private requestMapping: { [key: string]: RequestHandler } = {}
private mappings: Set<string>
@cmd()
mspring(sender: any) {
@@ -62,144 +34,24 @@ export class MiaoSpring extends interfaces.Plugin {
}
load() {
this.beanFactory = this.context.getAutowireCapableBeanFactory()
this.mainDatabase = this.databaseManager.getMainDatabase()
this.mappings = new Set()
}
enable() {
this.registryWebBean()
this.registryDefault()
this.registryPages()
this.registryDatabase()
}
registryWebBean() {
try { this.beanFactory.destroySingleton(FilterProxyBeanName); this.beanFactory.destroySingleton(WebProxyBeanName) } 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())
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()
}
}
}
}
let preHandleTime = Date.now() - startTime; startTime = Date.now()
ctx.result = this.execRequestHandle(ctx)
let execTime = Date.now() - startTime; startTime = Date.now()
for (const interceptor of this.interceptors) {
if (interceptor.postHandle) {
try {
ctx.result = interceptor.postHandle(ctx)
} catch (error) {
return {
status: 500,
msg: `Interceptor ${interceptor.name} postHandle error: ${error}`,
path: ctx.url,
error: console.stack(error, false),
timestamp: Date.now()
}
}
}
}
let postHandleTime = Date.now() - startTime
console.debug(`
===================== MiaoSpring =====================
Request URL : ${ctx.url}
preHandle Time : ${preHandleTime}ms
exec Time : ${execTime}ms
Response Body : ${JSON.stringify(Java.asJSONCompatible(ctx.result))}
postHandle Time : ${postHandleTime}ms
Handle Time : ${preHandleTime + execTime + postHandleTime}ms
======================================================`)
return ctx.result
}
private execRequestHandle(ctx: Context) {
if (!this.requestMapping[ctx.request.getRequestURI()]) {
return {
status: 404,
msg: "requestMapping Not Found!",
path: ctx.url,
timestamp: Date.now()
}
}
try {
return this.requestMapping[ctx.request.getRequestURI()](ctx)
} catch (error) {
return {
status: 500,
msg: '' + error,
path: ctx.url,
error: console.stack(error, false),
timestamp: Date.now()
}
}
}
registryMapping(path: string, handler: RequestHandler) {
this.requestMapping[path] = handler
this.mappings.add(path)
this.webServer.registryMapping(path, handler)
}
registryDefault() {
const foundMap = ["/node_modules/amis/sdk/sdk.js", 'https://houtai.baidu.com/v2/jssdk', "/node_modules/amis/sdk/sdk.css", 'https://houtai.baidu.com/v2/csssdk']
this.interceptors.push({
this.webServer.registryInterceptor({
name: 'RedirectHandle',
preHandle: (ctx: Context) => {
const index = foundMap.indexOf(ctx.request.getRequestURI())
@@ -208,7 +60,7 @@ Handle Time : ${preHandleTime + execTime + postHandleTime}ms
}
}
})
this.interceptors.push({
this.webServer.registryInterceptor({
name: 'StaticHandle',
preHandle: (ctx: Context) => {
let type = ctx.header['Accept'] || ''
@@ -225,27 +77,27 @@ Handle Time : ${preHandleTime + execTime + postHandleTime}ms
}
}
})
this.registryMapping('/api/eval', (ctx: Context) => {
this.webServer.registryMapping('/api/eval', (ctx: Context) => {
try {
return { status: 200, data: this.runCode(ctx.body + ''), msg: '代码执行成功!' }
} catch (error) {
return { status: 500, data: console.stack(error, false), msg: '代码执行异常!' }
}
})
this.registryMapping('/api/plugin/list', () => {
this.webServer.registryMapping('/api/plugin/list', () => {
return { status: 200, data: [...this.pluginManager.getPlugins().values()].map((plugin) => plugin.description), msg: '插件列表获取成功!' }
})
this.registryMapping('/api/plugin/update', (ctx: Context) => {
this.webServer.registryMapping('/api/plugin/update', (ctx: Context) => {
if (!ctx.params.name) { return { status: 400, msg: '插件名称不得为空!' } }
})
}
private registryPages() {
this.registryMapping('/api/page/list', () => {
this.webServer.registryMapping('/api/page/list', () => {
return { status: 0, data: { rows: this.mainDatabase.query('SELECT `id`, `type`, `name`, `content` FROM `pages` WHERE `deleted` = 0') } }
})
this.registryMapping('/api/page/get', (ctx: Context) => {
this.webServer.registryMapping('/api/page/get', (ctx: Context) => {
let name = decodeURIComponent(`${ctx.params.name}`)
let varable = undefined
if (!name) { return { status: 400, msg: '名称不能为空!' } }
@@ -265,20 +117,20 @@ Handle Time : ${preHandleTime + execTime + postHandleTime}ms
}
return { status: 0, data: JSON.parse(content) }
})
this.registryMapping('/api/page/add', (ctx: Context) => {
this.webServer.registryMapping('/api/page/add', (ctx: Context) => {
let body = ctx.body
if (typeof body.content !== "string") { body.content = JSON.stringify(body.content) }
this.mainDatabase.update("INSERT INTO `pages`(`type`, `name`, `content`) VALUES (?, ?, ?)", body.type || 1, body.name, body.content)
return { status: 0, msg: `${body.name} 新增成功!` }
})
this.registryMapping('/api/page/update', (ctx: Context) => {
this.webServer.registryMapping('/api/page/update', (ctx: Context) => {
if (!ctx.params.id) { return { status: 400, msg: 'ID 不能为空!' } }
const body = ctx.body
if (typeof body.content !== "string") { body.content = JSON.stringify(body.content) }
this.mainDatabase.update("UPDATE `pages` SET `name` = ?, `content` = ? WHERE id = ?", body.name, body.content, ctx.params.id)
return { status: 0, msg: `${body.name} 更新成功!` }
})
this.registryMapping('/api/page/delete', (ctx: Context) => {
this.webServer.registryMapping('/api/page/delete', (ctx: Context) => {
if (!ctx.params.name) { return { status: 400, msg: '页面 名称 不能为空!' } }
this.mainDatabase.update("UPDATE `pages` SET `name` = CONCAT(name, '_deleted'), deleted = 1 WHERE name = ?", ctx.params.name)
return { status: 0, msg: `${ctx.params.name} 删除成功!` }
@@ -288,17 +140,17 @@ Handle Time : ${preHandleTime + execTime + postHandleTime}ms
private configTable = "config"
private registryDatabase() {
this.registryMapping('/api/config/list', (ctx: Context) => {
this.webServer.registryMapping('/api/config/list', (ctx: Context) => {
return { status: 0, data: this.mainDatabase.query('SELECT id, name, label, url, driver, username, password FROM `' + this.configTable + '` WHERE deleted = 0 AND type = ?', ctx.params.type || 0) }
})
this.registryMapping('/api/config/get', (ctx: Context) => {
this.webServer.registryMapping('/api/config/get', (ctx: Context) => {
let name = ctx.params.name
if (!name) { return { status: 400, msg: '名称不能为空!' } }
let result = this.mainDatabase.query('SELECT id, name, label, url, driver, username, password FROM `' + this.configTable + '` WHERE `name` = ?', name)
if (!result.length) { return { status: 404, msg: `配置 ${name} 不存在!` } }
return { status: 0, data: result[0] }
})
this.registryMapping('/api/config/add', (ctx: Context) => {
this.webServer.registryMapping('/api/config/add', (ctx: Context) => {
let body = ctx.body
if (!body.name) { return { status: 400, msg: '名称不能为空!' } }
this.mainDatabase.update(
@@ -307,7 +159,7 @@ Handle Time : ${preHandleTime + execTime + postHandleTime}ms
)
return { status: 0, msg: `配置 ${body.name} 新增成功!` }
})
this.registryMapping('/api/config/update', (ctx: Context) => {
this.webServer.registryMapping('/api/config/update', (ctx: Context) => {
if (!ctx.params.id) { return { status: 400, msg: 'ID 不能为空!' } }
let body = ctx.body
this.mainDatabase.update(
@@ -327,7 +179,6 @@ Handle Time : ${preHandleTime + execTime + postHandleTime}ms
'pluginManager'
]
let params = [
this.beanFactory,
this.mainDatabase,
reflect,
this.container,
@@ -341,7 +192,6 @@ return eval(${JSON.stringify(code)});`)
}
disable() {
Object.keys(this.requestMapping).forEach((r) => delete this.requestMapping[r])
this.beanFactory.destroySingleton(WebProxyBeanName)
Object.keys(this.mappings).forEach((r) => this.webServer.unregistryMapping(r))
}
}