'use strict' import { getLogger } from 'frontblock-generic/Types'; import { promises as fs, mkdirSync } from "fs" import { RPCServer } from 'rpclibrary/js/src/Backend' import { AdminConf, TableDefiniton } from './Types'; import { RPCConfigLoader } from './RPCConfigLoader'; import * as Path from 'path' import Knex = require('knex'); import http = require('http'); import express = require('express'); import { TableDefinitionExporter } from './Interfaces'; import { FrontworkEventBus } from './Eventbus'; import { Plugin } from './Plugin'; const logger = getLogger("admin", 'debug') export class FrontworkAdmin implements TableDefinitionExporter { private express private httpServer private eventBus:FrontworkEventBus = new FrontworkEventBus(this) private plugins: Plugin[] = [] config: RPCConfigLoader knex:Knex constructor(){ this.eventBus = new FrontworkEventBus(this) } async start(){ this.initConfig() await this.makeKnex() this.startWebsocket() this.startWebserver() } protected configChangeHandler = (conf:AdminConf, key?:string) => { if(key === 'dbConf'){ this.makeKnex() } } getConfigKey(key:string){ return this.config.getConfigKey(key) } setConfigKey(key:string, value:any){ return this.config.setConfigKey(key, value) } private initConfig(){ this.config = new RPCConfigLoader({ name: "FrontworkAdminConf", getDefaultConfig: () => { return { httpPort: 8080, eventBusConf: {}, dbConf: { client: 'sqlite3', connection: { filename: Path.join(__dirname, "data/frontworkAdmin.sqlite") }, useNullAsDefault: true } } } }, './config', this.configChangeHandler) } private startWebsocket(){ new RPCServer(20000, [ this.config, ...this.plugins ]) } private startWebserver(){ if(this.httpServer != null || this.express != null){ logger.warn("Webserver is already running") return } let port:number = this.config.getConfig().httpPort this.express = express() this.express.use('/', express.static('dist/static')) /** * get the compiled FrontendPlugins.js */ this.express.get('/plugins/:id'+".js", async (request, response) => { const pth = Path.resolve("plugins/"+request.params.id, "FrontendPlugin.js"); const file = await fs.readFile(pth) const frontend = file.toString() response.status(200) response.set('Content-Type', 'application/javascript') response.send(frontend) }) /** * serve the index.html from the static folder */ this.express.get("/", (request, response) => { response.status(200) response.sendFile('index.html'); }) /** * redirect all the other traffic to the single page app */ this.express.get("*", (request, response) => { response.status(301) response.redirect('/') }) this.httpServer = new http.Server(this.express) this.httpServer.listen(port, () => { logger.info('Admin panel listening for HTTP on *'+port) }) } private stopWebserver(){ if(this.httpServer == null || this.express == null){ logger.warn("Webserver is not running") return } this.httpServer.close() this.httpServer = null this.express = null logger.info("Webserver stopped") } protected async makeKnex():Promise{ const conf:Knex.Config = this.config.getConfigKey("dbConf") logger.debug("Making new knex:", conf) if(conf.client === 'sqlite3'){ mkdirSync(Path.dirname((conf.connection).filename), {recursive: true}) } this.knex = Knex(conf) await Promise.all(this.getTableDefinitions().map(async (def)=>{ const hasTable = await this.knex.schema.hasTable(def.name) if(!hasTable){ await this.knex.schema.createTable(def.name, def.tableBuilder) } })) return this.knex } getTableDefinitions(): TableDefiniton[]{ return [ this.eventBus, ...this.plugins ].flatMap(exporter => exporter.getTableDefinitions()) } } process.on( 'SIGINT', function() { logger.info("Shutting down from SIGINT (Ctrl-C)" ); // some other closing procedures go here process.exit(0); })