Files
fb-admin/src/backend/UpdateManger.ts
T
2019-08-27 15:09:29 +02:00

191 lines
6.6 KiB
TypeScript

import { Plugin } from "frontblock-generic/Plugin";
import { socketioRPC } from "frontblock-generic/RPC";
import { GitUpdater, RepoFolderStatus } from "./GitUpdater";
import { FrontblockCherryPicker } from "git-cherrypicker";
import * as Logger from 'log4js'
import * as path from "path";
import * as rmrf from "rimraf"
import { FrontblockAdmin } from "./FrontblockAdmin";
import { TableDefiniton } from "frontblock-generic/Admin";
import { promises as fs, mkdirSync as mkdir } from "fs"
import { version } from "punycode";
import * as git from "simple-git/promise"
import * as trash from "trash"
import { type } from "os";
var exec = require('child-process-promise').exec;
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 extends Plugin{
private loadedPlugins:{[name in string]:Plugin} = {}
private dashboardUpdater:GitUpdater
private adminUpdater:GitUpdater
private pluginUpdaters:{[name in string]:GitUpdater} = {}
constructor(admin:FrontblockAdmin){
super(admin, "UpdateManager")
}
getDefaultConfig(): {} {
return {}
}
exportRPCs(): socketioRPC[] {
return [{
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: 'updateDashboard',
func: async () => {return await this.updateDashboard()},
type: 'call',
visibility: 'private'
},{
name: 'getLoadedPluginNames',
func: async () => {return await this.getLoadedPluginNames()},
type: 'call',
visibility: 'private'
}]
}
getTableDefinitions(): TableDefiniton[] {
return []
}
async updatePlatformDependencies(){
logger.info("installing npm stuff")
/*
const res = await exec('npm install --prefix ./plugins knex sqlite3')
logger.warn(res.stderr)
logger.info(res.stdout)
*/
await Promise.all([
new NPMExtension("knex@0.19.2", "./plugins").install(),
new NPMExtension("sqlite3@4.1.0", "./plugins").install()
])
}
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<RepoFolderStatus>{
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 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.admin.addPlugin(pluginObj)
this.loadedPlugins[name] = 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)
}
getLoadedPlugins():Plugin<any>[]{
return Object.values(this.loadedPlugins)
}
getLoadedPluginNames():string[]{
return Object.keys(this.loadedPlugins)
}
}