Files
fb-admin/src/backend/Admin.ts
T

188 lines
6.5 KiB
TypeScript

'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<AdminConf>{
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<RepoFolderStatus>{
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<boolean>{
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<boolean>{
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<RepoFolderStatus>{
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( );
})