/// /// import { constants, database, plugin, web } from "@ccms/api" import { inject, ContainerInstance, Container, JSClass, postConstruct } from "@ccms/container" import { JSPlugin, interfaces, cmd } from "@ccms/plugin" import { DataBase, DataBaseManager } from '@ccms/database' import { Server, Context, RequestHandler, Controllers, Controller, Get, Post, Param, Body } from '@ccms/web' import * as fs from '@ccms/common/dist/fs' import * as reflect from '@ccms/common/dist/reflect' @Controller() class PluginController { @inject(plugin.PluginManager) private pluginManager: plugin.PluginManager @inject(database.DataBaseManager) private databaseManager: DataBaseManager private mainDB: DataBase @postConstruct() initialize() { this.mainDB = this.databaseManager.getMainDatabase() } @Get() list(@Param('install') install: boolean) { if (install) { return { status: 200, data: [...this.pluginManager.getPlugins().values()].map((plugin) => plugin.description), msg: '插件列表获取成功!' } } else { return { status: 200, data: this.mainDB.query("SELECT name FROM plugins WHERE deleted = 0") } } } @Post() deploy(@Body() info: Plugin) { let plugin = this.mainDB.query("SELECT name FROM plugins WHERE name = ?", info.name) if (plugin.length == 0) { this.mainDB.update("INSERT INTO `plugins`(`name`, `source`) VALUES (?, ?)", info.name, info.source) return { status: 200, msg: `插件 ${info.name} 新增成功!` } } else { this.mainDB.update("UPDATE `plugins` SET `source` = ? WHERE id = ?", info.source, plugin[0].id) return { status: 200, msg: `插件 ${info.name} 更新成功!` } } } } @JSPlugin({ author: 'MiaoWoo', servers: [constants.ServerType.Spring], source: __filename }) export class MiaoSpring extends interfaces.Plugin { @inject(ContainerInstance) private container: Container @inject(plugin.PluginManager) private pluginManager: plugin.PluginManager @inject(database.DataBaseManager) private databaseManager: DataBaseManager @inject(web.Server) private webServer: Server @JSClass('org.springframework.http.HttpStatus') private HttpStatus: org.springframework.http.HttpStatus private ResponseEntity = org.springframework.http.ResponseEntity private mainDatabase: DataBase private mappings: Set @cmd() mspring(sender: any) { this.pluginManager.reload(this) this.logger.sender(sender, "§a插件重载完成!") } load() { this.mainDatabase = this.databaseManager.getMainDatabase() this.mappings = new Set() } @Controllers(PluginController) enable() { this.registryDefault() this.registryPages() this.registryDatabase() } registryMapping(path: string, handler: RequestHandler) { 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.webServer.registryInterceptor({ name: 'RedirectHandle', preHandle: (ctx: Context) => { const index = foundMap.indexOf(ctx.request.getRequestURI()) if (index != -1) { return this.ResponseEntity.status(org.springframework.http.HttpStatus.FOUND).header('Location', foundMap[index + 1]).build() } } }) this.webServer.registryInterceptor({ name: 'StaticHandle', preHandle: (ctx: Context) => { let type = ctx.headers['Accept'] || '' if (type == '*/*' || type.includes('text/css') || type.includes('text/html')) { let filePath = fs.concat(root, 'wwwroot', (ctx.request.getRequestURI() == '/' ? 'index.html' : ctx.request.getRequestURI())) let fileType = 'text/html;charset=utf-8' if (!fs.exists(filePath)) { filePath = filePath + '.html' } if (fs.exists(filePath)) { if (filePath.endsWith('.js')) { fileType = 'application/javascript' } if (filePath.endsWith('.css')) { fileType = 'text/css' } return this.ResponseEntity.ok().header('Content-Type', fileType).body(base.read(filePath)) } } } }) this.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: '代码执行异常!' } } }) } private registryPages() { this.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) => { let name = decodeURIComponent(`${ctx.params.name}`) let varable = undefined if (!name) { return { status: 400, msg: '名称不能为空!' } } if (name.includes('|')) { const temp = `${name}`.split('|') name = temp[0] varable = temp[1].replace(/'/ig, '"') } let result = this.mainDatabase.query('SELECT content FROM `pages` WHERE name = ?', name) if (!result.length) { return { status: 404, msg: `'页面配置 ${name} 不存在!` } } let content = result[0].content if (varable) { let varObj = JSON.parse(varable) for (const key in varObj) { content = content.replace(new RegExp(`{{${key}}}`, 'ig'), varObj[key]) } } return { status: 0, data: JSON.parse(content) } }) this.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) => { 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) => { 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} 删除成功!` } }) } private configTable = "config" private registryDatabase() { this.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) => { 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) => { let body = ctx.body if (!body.name) { return { status: 400, msg: '名称不能为空!' } } this.mainDatabase.update( 'INSERT INTO `' + this.configTable + '`(`type`, `name`, `label`, `url`, `driver`, `username`, `password`) VALUES (?, ?, ?, ?, ?, ?, ?)', ctx.params.type || 0, body.name, body.label || '', body.url, body.driver || '', body.username || '', body.password || '' ) return { status: 0, msg: `配置 ${body.name} 新增成功!` } }) this.registryMapping('/api/config/update', (ctx: Context) => { if (!ctx.params.id) { return { status: 400, msg: 'ID 不能为空!' } } let body = ctx.body this.mainDatabase.update( 'UPDATE `' + this.configTable + '` SET `name` = ?, `label` = ?, `url` = ?, `driver` = ?, `username` = ?, `password` = ? WHERE `id` = ?', body.name, body.label || '', body.url, body.driver || '', body.username || '', body.password || '', ctx.params.id ) return { status: 0, msg: `配置 ${body.name} 更新成功!` } }) } private runCode(code: string) { let paramNames = [ 'beanFactory', 'mainDatabase', 'reflect', 'container', 'pluginManager' ] let params = [ base.getInstance().getAutowireCapableBeanFactory(), this.mainDatabase, reflect, this.container, this.pluginManager ] let tfunc = new Function( ...paramNames, `var api = require('@ccms/api'); return eval(${JSON.stringify(code)});`) return tfunc.apply(this, params) } disable() { Object.keys(this.mappings).forEach((r) => this.webServer.unregistryMapping(r)) this.webServer.unregistryInterceptor({ name: 'RedirectHandle' }) this.webServer.unregistryInterceptor({ name: 'StaticHandle' }) } } class Plugin { id: number name: string source: string }