Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

Admin.ts 7.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. 'use strict'
  2. import { getLogger, Logger, } from "log4js";
  3. import { promises as fs, mkdirSync } from "fs"
  4. import { RPCServer } from 'rpclibrary'
  5. import * as Path from 'path'
  6. import * as Knex from 'knex';
  7. import * as http from 'http';
  8. import * as express from 'express';
  9. import { existsSync } from "fs";
  10. import { GuildManager } from '../Components/Guild/GuildManager';
  11. import { ItemManager } from '../Components/Item/ItemManager';
  12. import { RaidManager } from '../Components/Raid/RaidManager';
  13. import { CharacterManager } from '../Components/Character/CharacterManager';
  14. import { UserManager } from '../Components/User/UserManager';
  15. import { RootComponent } from '../Injector/ServiceDecorator';
  16. import { TableDefinitionExporter } from '../Types/Interfaces';
  17. import { AdminConf, TableDefiniton } from '../Types/Types';
  18. import { RPCConfigLoader } from '../Components/RPCConfigLoader';
  19. import { FrontworkComponent } from '../Types/FrontworkComponent';
  20. import { IAdmin } from './Interface';
  21. import { Injector } from '../Injector/Injector';
  22. import { PubSub } from '../Components/PubSub/PubSub';
  23. @RootComponent({
  24. injectable: IAdmin,
  25. injects: [
  26. GuildManager,
  27. ItemManager,
  28. RaidManager,
  29. CharacterManager,
  30. UserManager,
  31. PubSub
  32. ]
  33. })
  34. export class FrontworkAdmin
  35. implements TableDefinitionExporter, IAdmin {
  36. knex: Knex
  37. config: RPCConfigLoader<AdminConf>
  38. rpcServer: RPCServer
  39. private express : express.Application
  40. private httpServer
  41. constructor(private frontworkComponents: FrontworkComponent[] = []) {
  42. this.config = new RPCConfigLoader<AdminConf>({
  43. name: "FrontworkAdminConf",
  44. getDefaultConfig: () => {
  45. return {
  46. httpPort: 8080,
  47. eventBusConf: {},
  48. dbConf: {
  49. client: 'sqlite3',
  50. connection: {
  51. filename: Path.resolve(__dirname, '../../../..', "data/frontworkAdmin.sqlite"),
  52. },
  53. migrations: {
  54. directory: Path.resolve(__dirname, '../../../..', "migrations"),
  55. extension: 'ts'
  56. },
  57. useNullAsDefault: true,
  58. }
  59. }
  60. }
  61. }, './config', this.configChangeHandler)
  62. }
  63. async start() {
  64. let port: number = this.config.getConfig().httpPort
  65. await this.makeKnex()
  66. const app = await this.makeExpress()
  67. const httpServer = new http.Server(app)
  68. await this.startWebsocket(httpServer)
  69. httpServer.listen(port)
  70. await this.attachAngularSSR(app)
  71. getLogger('Admin#startWebsocket').debug("Webserver up on", port)
  72. await Promise.all(this.frontworkComponents.map(c => c.initialize ? c.initialize() : undefined))
  73. getLogger('Admin#start').debug(this.frontworkComponents.length + " components initialized")
  74. }
  75. stop() {
  76. Promise.all([
  77. ...this.frontworkComponents.map(c => c.stop ? c.stop() : undefined),
  78. ])
  79. .catch(e => getLogger('Admin#stop').warn(e))
  80. .finally(() => {
  81. this.rpcServer.close()
  82. process.exit(0)
  83. })
  84. }
  85. protected configChangeHandler = (conf: AdminConf, key?: string) => {
  86. if (key === 'dbConf') {
  87. this.makeKnex()
  88. }
  89. }
  90. getConfigKey(key: string) {
  91. return this.config.getConfigKey(key)
  92. }
  93. setConfigKey(key: string, value: any) {
  94. return this.config.setConfigKey(key, value)
  95. }
  96. getTableDefinitions(): TableDefiniton[] {
  97. return [
  98. ...this.frontworkComponents
  99. ]
  100. .filter(exp => exp.getTableDefinitions != null)
  101. .flatMap(exp => exp.getTableDefinitions())
  102. }
  103. private startWebsocket(httpServer: http.Server) {
  104. this.rpcServer = new RPCServer([
  105. ...this.frontworkComponents,
  106. {
  107. name: "debug",
  108. RPCs: () => [{
  109. name: 'dumpDb',
  110. call: async (table) => await this.knex(table).select('*')
  111. }]
  112. }
  113. ], {
  114. errorHandler: (sock, err, rpc, args) => {
  115. console.log("RPC", rpc)
  116. console.log("ERR", err)
  117. console.log("ARGS", args)
  118. }
  119. })
  120. .attach(httpServer)
  121. }
  122. private async makeExpress() {
  123. if (this.httpServer != null || this.express != null) {
  124. getLogger('Admin#startWebserver').warn("Webserver is already running")
  125. return
  126. }
  127. this.express = express()
  128. const distFolder = "../../../../dist"
  129. this.express.get('*.*', (req, res) => {
  130. //console.log('*.*', req.path);
  131. try{
  132. const filepath = Path.join(__dirname, distFolder, 'browser', decodeURIComponent(req.path))
  133. if(!existsSync(filepath)){
  134. throw new Error("Bad file access: "+filepath)
  135. }
  136. res.sendFile(filepath)
  137. res.status(200)
  138. }catch(e){
  139. getLogger('Admin#startWebserver#serveFile').error(String(e))
  140. res.send("404 NOT FOUND")
  141. res.status(404)
  142. }
  143. })
  144. return this.express
  145. }
  146. attachAngularSSR = async (express : express.Application) => {
  147. const distFolder = "../../../../dist"
  148. const ngExpressServer = Path.join(__dirname, distFolder, 'server.js')
  149. let port: number = this.config.getConfig().httpPort
  150. try {
  151. const req = require(distFolder + "/server.js")
  152. await req.attachExpress(express, './dist', getLogger('angularSSR#'))
  153. getLogger('Admin#startWebserver').debug('Frontend from ' + ngExpressServer + " loaded")
  154. } catch (e) {
  155. getLogger('Admin#startWebserver').error(e)
  156. getLogger('Admin#startWebserver').warn("No angular SSR module was provided in " + ngExpressServer)
  157. getLogger('Admin#startWebserver').warn("This is not fatal, but your page will not render on *" + port)
  158. }
  159. }
  160. private stopWebserver() {
  161. if (this.httpServer == null || this.express == null) {
  162. getLogger('Admin#stopWebserver').warn("Webserver is not running")
  163. return
  164. }
  165. this.httpServer.close()
  166. this.httpServer = null
  167. this.express = null
  168. getLogger('Admin#stopWebserver').info("Webserver stopped")
  169. }
  170. async makeKnex(): Promise<Knex> {
  171. const conf: Knex.Config = this.config.getConfigKey("dbConf")
  172. getLogger('Admin#makeKnex').debug("Making new knex:", conf)
  173. if (conf.client === 'sqlite3') {
  174. mkdirSync(Path.dirname((<any>conf.connection).filename), { recursive: true })
  175. }
  176. this.knex = Knex(conf)
  177. if (conf.client === 'sqlite3') {
  178. await this.knex.raw('PRAGMA foreign_keys = ON');
  179. }
  180. await Promise.all(
  181. this.getTableDefinitions()
  182. //make unique by name
  183. .filter((other, index, self) => index === self.findIndex(
  184. (self) => self.name === other.name)
  185. )
  186. //create table if not exists
  187. .map(async (def) => {
  188. const hasTable = await this.knex.schema.hasTable(def.name)
  189. if (!hasTable)
  190. return await this.knex.schema.createTable(def.name, def.tableBuilder)
  191. })
  192. )
  193. await this.knex.migrate.latest().catch(e => {
  194. getLogger('Admin#makeKnex').error(e)
  195. process.exit(-1)
  196. })
  197. return this.knex
  198. }
  199. }
  200. process.on('SIGINT', function () {
  201. getLogger('process#SIGINT').info("Shutting down from SIGINT (Ctrl-C)");
  202. Injector.resolve<FrontworkAdmin>(FrontworkAdmin).stop()
  203. })