import { Plugin, socketioRPC } from "frontblock-generic/Plugin"; import { GitUpdater, RepoFolderStatus } from "./GitUpdater"; import { FrontblockCherryPicker } from "git-cherrypicker"; import * as Logger from 'log4js' Logger.configure({ appenders: { "admin/updatemanager": { type: 'stdout' }, //app: { type: 'file', filename: 'application.log' } }, categories: { default: { appenders: [ 'admin/updatemanager' ], level: 'debug' } } }) const logger = Logger.getLogger("admin/updatemanager") export class UpdateManager implements Plugin{ name: string = "UpdateManager" private loadedPlugins:{[name in string]:Plugin} = {} private dashboardUpdater:GitUpdater private adminUpdater:GitUpdater private pluginUpdaters:{[name in string]:GitUpdater} = {} getDefaultConfig(): void { return } exportRPCs(): socketioRPC[] { return [{ name: 'installPlugin', rpc: async (name:string) => {return await this.installPlugin(name)}, type: 'call', visibility: 'private' },{ name: 'startPlugin', rpc: async (name:string) => {return await this.startPlugin(name)}, type: 'call', visibility: 'private' },{ name: 'updatePlugin', rpc: async (name) => {return await this.updatePlugin(name)}, type: 'call', visibility: 'private' },{ name: 'setPluginVersion', rpc: async (name, tag) => {return await this.setPluginVersion(name, tag)}, type: 'call', visibility: 'private' },{ name: 'updateDashboard', rpc: async () => {return await this.updateDashboard()}, type: 'call', visibility: 'private' },{ name: 'getLoadedPluginNames', rpc: async () => {return await this.getLoadedPluginNames()}, type: 'call', visibility: 'private' }] } async updateAdmin(force:boolean = false){ this.adminUpdater = new GitUpdater("./dist") let status = await this.adminUpdater.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 this.adminUpdater.cloneRepo("https://gitea.frontblock.me/fb-dist/admin.git", force) } return status } async updateFrontblockLib(){ logger.warn("Picking FrontblockLib...") await (new FrontblockCherryPicker("fb-dist/admin", "master", "FrontblockLib.js")).write("./static/FrontblockLib.js") } async updateDashboard(force:boolean = false):Promise{ this.dashboardUpdater = new GitUpdater("./static") let status = await this.dashboardUpdater.getStatus() if(force || !status.exists || status.empty || !status.currentTag){ logger.warn("Cloning fb-dist/dashboard into ./static ..."+(force?" USING FORCE!":"")) status = await this.dashboardUpdater.cloneRepo("https://gitea.frontblock.me/fb-dist/dashboard.git", force) } 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){ 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 import(str) const pluginObj = new pluginClass.default() await pluginObj.start() this.loadedPlugins[name] = 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) } getLoadedPlugins():Plugin[]{ return Object.values(this.loadedPlugins) } getLoadedPluginNames():string[]{ return Object.keys(this.loadedPlugins) } start(): void | Promise { throw new Error("Method not implemented (on purpose)"); } stop(): void | Promise { throw new Error("Method not implemented (on purpose)"); } }