You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Admin.ts 4.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. 'use strict'
  2. import { getLogger } from 'frontblock-generic/Types';
  3. import { promises as fs, mkdirSync } from "fs"
  4. import { RPCServer } from 'rpclibrary/js/src/Backend'
  5. import { AdminConf, TableDefiniton } from './Types';
  6. import { RPCConfigLoader } from './RPCConfigLoader';
  7. import * as Path from 'path'
  8. import Knex = require('knex');
  9. import http = require('http');
  10. import express = require('express');
  11. import { TableDefinitionExporter } from './Interfaces';
  12. import { FrontworkEventBus } from './Eventbus';
  13. import { Plugin } from './Plugin';
  14. const logger = getLogger("admin", 'debug')
  15. export class FrontworkAdmin
  16. implements TableDefinitionExporter {
  17. private express
  18. private httpServer
  19. private eventBus:FrontworkEventBus = new FrontworkEventBus(this)
  20. private plugins: Plugin[] = []
  21. config: RPCConfigLoader<AdminConf>
  22. knex:Knex
  23. constructor(){
  24. this.eventBus = new FrontworkEventBus(this)
  25. }
  26. async start(){
  27. this.initConfig()
  28. await this.makeKnex()
  29. this.startWebsocket()
  30. this.startWebserver()
  31. }
  32. protected configChangeHandler = (conf:AdminConf, key?:string) => {
  33. if(key === 'dbConf'){
  34. this.makeKnex()
  35. }
  36. }
  37. getConfigKey(key:string){
  38. return this.config.getConfigKey(key)
  39. }
  40. setConfigKey(key:string, value:any){
  41. return this.config.setConfigKey(key, value)
  42. }
  43. private initConfig(){
  44. this.config = new RPCConfigLoader<AdminConf>({
  45. name: "FrontworkAdminConf",
  46. getDefaultConfig: () => {
  47. return {
  48. httpPort: 8080,
  49. eventBusConf: {},
  50. dbConf: {
  51. client: 'sqlite3',
  52. connection: {
  53. filename: Path.join(__dirname, "data/frontworkAdmin.sqlite")
  54. },
  55. useNullAsDefault: true
  56. }
  57. }
  58. }
  59. }, './config', this.configChangeHandler)
  60. }
  61. private startWebsocket(){
  62. new RPCServer(20000, [
  63. this.config,
  64. ...this.plugins
  65. ])
  66. }
  67. private startWebserver(){
  68. if(this.httpServer != null || this.express != null){
  69. logger.warn("Webserver is already running")
  70. return
  71. }
  72. let port:number = this.config.getConfig().httpPort
  73. this.express = express()
  74. this.express.use('/', express.static('dist/static'))
  75. /**
  76. * get the compiled FrontendPlugins.js
  77. */
  78. this.express.get('/plugins/:id'+".js", async (request, response) => {
  79. const pth = Path.resolve("plugins/"+request.params.id, "FrontendPlugin.js");
  80. const file = await fs.readFile(pth)
  81. const frontend = file.toString()
  82. response.status(200)
  83. response.set('Content-Type', 'application/javascript')
  84. response.send(frontend)
  85. })
  86. /**
  87. * serve the index.html from the static folder
  88. */
  89. this.express.get("/", (request, response) => {
  90. response.status(200)
  91. response.sendFile('index.html');
  92. })
  93. /**
  94. * redirect all the other traffic to the single page app
  95. */
  96. this.express.get("*", (request, response) => {
  97. response.status(301)
  98. response.redirect('/')
  99. })
  100. this.httpServer = new http.Server(this.express)
  101. this.httpServer.listen(port, () => {
  102. logger.info('Admin panel listening for HTTP on *'+port)
  103. })
  104. }
  105. private stopWebserver(){
  106. if(this.httpServer == null || this.express == null){
  107. logger.warn("Webserver is not running")
  108. return
  109. }
  110. this.httpServer.close()
  111. this.httpServer = null
  112. this.express = null
  113. logger.info("Webserver stopped")
  114. }
  115. protected async makeKnex():Promise<Knex>{
  116. const conf:Knex.Config = this.config.getConfigKey("dbConf")
  117. logger.debug("Making new knex:", conf)
  118. if(conf.client === 'sqlite3'){
  119. mkdirSync(Path.dirname((<any>conf.connection).filename), {recursive: true})
  120. }
  121. this.knex = Knex(conf)
  122. await Promise.all(this.getTableDefinitions().map(async (def)=>{
  123. const hasTable = await this.knex.schema.hasTable(def.name)
  124. if(!hasTable){
  125. await this.knex.schema.createTable(def.name, def.tableBuilder)
  126. }
  127. }))
  128. return this.knex
  129. }
  130. getTableDefinitions(): TableDefiniton[]{
  131. return [
  132. this.eventBus,
  133. ...this.plugins
  134. ].flatMap(exporter => exporter.getTableDefinitions())
  135. }
  136. }
  137. process.on( 'SIGINT', function() {
  138. logger.info("Shutting down from SIGINT (Ctrl-C)" );
  139. // some other closing procedures go here
  140. process.exit(0);
  141. })