'use strict' import * as Logger from 'log4js' import * as Knex from 'knex' import { FrontblockApiClient, FrontblockApiConf } from 'frontblock'; import { AdminBase } from 'frontblock-generic/Admin'; import { FrontblockApi } from 'frontblock-generic/Api'; import { Plugin } from 'frontblock-generic/Plugin'; import { socketioRPC } from 'frontblock-generic/RPC'; import { GitUpdater, RepoFolderStatus } from './GitUpdater'; Logger.configure({ appenders: { "admin": { type: 'stdout' }, //app: { type: 'file', filename: 'application.log' } }, categories: { default: { appenders: [ 'admin' ], level: 'debug' } } }) const logger = Logger.getLogger("admin") export type AdminConf = { httpPort: number} export class FrontblockAdmin extends AdminBase{ private pluginUpdaters:{[name in string]:GitUpdater} = {} constructor(runningPlugins: Plugin[] = []){ super(runningPlugins) } getDefaultConfig(): { apiConf: FrontblockApiConf; } & AdminConf & { dbConf:Knex.Config; } { return { httpPort: 8080, apiConf: { apiHost: "api.testnet.frontblock.me", apiKey: "", apiPort: 10001, tls: false }, dbConf: { client: 'sqlite3', connection: { filename: "./data/ApiClient.sqlite" }, useNullAsDefault: true } } } protected makeApiClient(conf: FrontblockApiConf): FrontblockApi { // @ts-ignore if(this.apiClient) this.apiClient.disconnect() this.apiClient = new FrontblockApiClient(conf) // @ts-ignore this.apiClient.connect() return this.apiClient } exportRPCs():socketioRPC[]{ return [ ...super.exportRPCs(), { name: 'installPlugin', func: async (name:string, force = false) => {return await this.installPlugin(name, force)}, type: 'call', visibility: 'private' },{ name: 'startPlugin', func: async (name:string) => {return await this.startPlugin(name)}, type: 'call', visibility: 'private' },{ name: 'updatePlugin', func: async (name) => {return await this.updatePlugin(name)}, type: 'call', visibility: 'private' },{ name: 'setPluginVersion', func: async (name, tag) => {return await this.setPluginVersion(name, tag)}, type: 'call', visibility: 'private' },{ name: 'getLoadedPluginNames', func: async () => {return await this.getPlugins().map(p => p.name)}, type: 'call', visibility: 'private' },{ name: 'selfUpdate', type:'call', func: async (force: boolean) => {return await this.selfUpdate(force)}, visibility: 'private' }, ] } private async selfUpdate(force:boolean = false){ const updater = new GitUpdater("./dist") let status = await updater.getStatus() if(force || !status.remote || !status.remote.includes("fb-dist/admin") || !status.exists || status.empty || !status.currentTag){ logger.warn("Cloning fb-dist/admin into ./dist ..."+(force?" USING FORCE!":"")) status = await updater.cloneRepo("https://gitea.frontblock.me/fb-dist/admin.git", force) this.destroy() const installer = require("./Installer").installAdmin installer(this.getPlugins()) } return status } async installPlugin(name: string, force:boolean = false):Promise{ logger.warn("Cloning fb-dist/"+name+".git into ./plugins/"+name+" ..."+(force?" USING FORCE!":"")) this.pluginUpdaters[name] = new GitUpdater("./plugins/"+name) const status = await this.pluginUpdaters[name].cloneRepo("https://gitea.frontblock.me/fb-dist/"+name.toLowerCase()+".git", force) return status } async startPlugin(name:string):Promise{ if(!this.pluginUpdaters[name]) return false const status = await this.pluginUpdaters[name].getStatus() if(!status.exists || status.empty || !status.tags || status.tags.length === 0){ if(status.currentTag && !status.latestTag){ //git glitches sometimes if you check immediately after clone logger.warn("re-fetching tag for "+name+"...") return await this.startPlugin(name) } logger.error("Bad repo status", name, status) return false } /* if(this.loadedPlugins[name]){ logger.error("Plugin", name, "is already started") return false } */ let str = "../plugins/"+name+"/Plugin" const pluginClass = await eval('require')(str) const pluginObj = new pluginClass.default() await pluginObj.start() this.addPlugin(pluginObj) return true } async updatePlugin(name:string):Promise{ if(!this.pluginUpdaters[name]) return false const status = await this.pluginUpdaters[name].getStatus() if(!status.exists || status.empty || !status.tags || status.tags.length === 0){ logger.error("Bad repo status", name, status) return false } if(status.currentTag == status.latestTag){ logger.warn(name, "already at latest tag") return false } /* if(this.loadedPlugins[name]){ logger.error("Plugin", name, "is running. Stop it first") return false } */ this.pluginUpdaters[name].checkoutTag(status.latestTag!) return true } async setPluginVersion(pluginName:string, tag:string):Promise{ const status = await this.pluginUpdaters[pluginName].getStatus() if(!status.exists || !status.tags || status.tags.length === 0 || !status.tags.includes(tag)){ logger.error("Bad repo status", pluginName, status) return status } return await this.pluginUpdaters[pluginName].checkoutTag(tag) } } process.on( 'SIGINT', function() { logger.info( "Gracefully shutting down from SIGINT (Ctrl-C)" ); // some other closing procedures go here process.exit( ); })