diff --git a/package-lock.json b/package-lock.json index 0555714..856f1c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1981,9 +1981,9 @@ } }, "frontblock-generic": { - "version": "0.29.0", - "resolved": "https://registry.npmjs.org/frontblock-generic/-/frontblock-generic-0.29.0.tgz", - "integrity": "sha512-L+g2hVBgTlqhPXWUMK0T6RtxZUKBI0Ts1SQ4ltspwToPiAWPkqBiAFx8ZK1qwr4kQ/lVeNl6GNX2An5wUkULzw==", + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/frontblock-generic/-/frontblock-generic-0.30.1.tgz", + "integrity": "sha512-r+4/Zq9WGkD6Ma9F2AQp2ZP546jgXLde7aB050HHwF9t0O4ewy+ohfgyvEEH3dpIoQBbUWuD9uaAtahpNUYcqA==", "requires": { "@types/lowdb": "^1.0.9", "@types/node": "^12.7.1", diff --git a/package.json b/package.json index 5490ff0..309e192 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "child-process-promise": "^2.2.1", "express": "^4.16.4", "frontblock": "^0.14.0", - "frontblock-generic": "^0.29.0", + "frontblock-generic": "^0.30.1", "git-cherrypicker": "0.0.3", "git-describe": "^4.0.4", "http": "0.0.0", diff --git a/src/backend/FrontblockAdmin.ts b/src/backend/FrontblockAdmin.ts index 0a84b74..96e26f4 100644 --- a/src/backend/FrontblockAdmin.ts +++ b/src/backend/FrontblockAdmin.ts @@ -4,7 +4,7 @@ import * as Logger from 'log4js' import * as Knex from 'knex' import { FrontblockApiClient } from 'frontblock'; import { socketioRPC, rpcHooker, ExtendedRpcInfo } from 'frontblock-generic/RPC'; -import { ApiClientBase } from 'frontblock-generic/Admin'; +import { ApiClientBase, AdminBase } from 'frontblock-generic/Admin'; import express = require('express'); import http = require('http'); import bsock = require('bsock'); @@ -12,6 +12,7 @@ import { promises as fs } from "fs" import * as path from "path" import { UpdateManager } from './UpdateManger'; import { FrontblockApiConf, FrontblockApi } from 'frontblock-generic/Api'; +import { Plugin } from 'frontblock-generic/Plugin'; var exec = require('child-process-promise').exec; Logger.configure({ @@ -38,7 +39,7 @@ export type AdminConf = { httpPort: number} * * The list of available plugins is published via the frontblock API and downloaded via gitea-releases */ -export class FrontblockAdmin extends ApiClientBase{ +export class FrontblockAdmin extends AdminBase{ private express private httpServer @@ -46,9 +47,8 @@ export class FrontblockAdmin extends ApiClientBase{ private wsServer = http.createServer() private updateManager:UpdateManager = new UpdateManager(this) - constructor(){ - super("FrontblockAdmin") - + constructor(runningPlugins: Plugin[] = []){ + super(runningPlugins) this.initialize() } @@ -72,10 +72,17 @@ export class FrontblockAdmin extends ApiClientBase{ } protected makeApiClient(conf: FrontblockApiConf): FrontblockApi { - return new FrontblockApiClient(conf) + // @ts-ignore + if(this.apiClient) this.apiClient.disconnect() + this.apiClient = new FrontblockApiClient(conf) + + // @ts-ignore + this.apiClient.connect() + return this.apiClient } private async initialize(){ + this.addPlugin(this.updateManager) await this.updateManager.updateAdmin() await this.updateManager.updatePlatformDependencies() await this.updateManager.updateDashboard() @@ -85,27 +92,6 @@ export class FrontblockAdmin extends ApiClientBase{ this.startWebserver() } - private initApis(socket){ - - const adminRPCs:socketioRPC[] = [{ - name: 'info', - type: 'call', - visibility: 'private', - func: async () => rpcInfos - }] - - const rpcInfos:ExtendedRpcInfo[] = [ - ...rpcHooker(socket, "Admin", adminRPCs, false), - ...rpcHooker(socket, "ApiClient", this.exportRPCs()), - ...rpcHooker(socket, "UpdateManager", this.updateManager.exportRPCs()), - ...this.updateManager.getLoadedPlugins().flatMap(plugin => rpcHooker(socket, plugin.name, plugin.exportRPCs())) - ] - - socket.on('close', () => { - logger.info("Client disconnected") - }) - } - private startWebserver(){ if(this.httpServer != null || this.express != null){ logger.warn("Webserver is already running") diff --git a/src/backend/UpdateManger.ts b/src/backend/UpdateManger.ts index 0ca673e..32870e4 100644 --- a/src/backend/UpdateManger.ts +++ b/src/backend/UpdateManger.ts @@ -3,8 +3,11 @@ import { socketioRPC } from "frontblock-generic/RPC"; import { GitUpdater, RepoFolderStatus } from "./GitUpdater"; import { FrontblockCherryPicker } from "git-cherrypicker"; import * as Logger from 'log4js' -import { mkdirSync } from "fs"; +import * as path from "path"; import { FrontblockAdmin } from "./FrontblockAdmin"; +import { TableDefiniton } from "frontblock-generic/Admin"; +import { promises as fs, mkdirSync } from "fs" + var exec = require('child-process-promise').exec; Logger.configure({ @@ -20,10 +23,106 @@ Logger.configure({ }) const logger = Logger.getLogger("admin/updatemanager") +abstract class Extension{ + constructor( + public name: string, + public version: string, + ){} -export class UpdateManager extends Plugin{ - name: string = "UpdateManager" - private loadedPlugins:{[name in string]:Plugin} = {} + abstract install():Promise + abstract uninstall():Promise + abstract update():Promise + abstract isOutdated():Promise +} + +export class NPMExtension extends Extension{ + constructor( + name, + version = "latest", + public localPrefix, + ){ + super(name, version) + } + + public async install(): Promise { + logger.info("npm install", this.name, 'to', path.resolve(this.localPrefix, this.name)) + await fs.mkdir(this.localPrefix, {recursive: true}) + const { error, stdout, stderr } = await exec('npm install --prefix '+ this.localPrefix + ' ' + this.name+"@"+this.version) + if (error !== null) { + logger.warn('npm install', this.name, 'from prefix', this.localPrefix, 'stderr:', stderr) + return false + } else { + logger.info('npm install', this.name, 'from prefix', this.localPrefix, 'stdout:', stdout) + return true + } + } + + public async uninstall(): Promise { + logger.info("npm uninstall", this.name, 'from', path.resolve(this.localPrefix, this.name)) + const { error, stdout, stderr } = await exec('npm uninstall --prefix '+ this.localPrefix + ' ' + this.name) + if (error !== null) { + logger.warn('npm uninstall', this.name, 'from prefix', this.localPrefix, 'stderr:', stderr) + return false + } else { + logger.info('npm uninstall', this.name, 'from prefix', this.localPrefix, 'stdout:', stdout) + return true + } + } + + public async update(): Promise { + logger.info("npm update", path.resolve(this.localPrefix, this.name)) + const { error, stdout, stderr } = await exec('npm update --prefix '+ this.localPrefix + ' ' + this.name) + if (error !== null) { + logger.warn('npm update', this.name, 'from prefix', this.localPrefix, 'stderr:', stderr) + return false + } else { + logger.info('npm update', this.name, 'from prefix', this.localPrefix, 'stdout:', stdout) + return true + } + } + + public async isOutdated(): Promise { + logger.info("npm outdated", path.resolve(this.localPrefix, this.name)) + const { error, stdout, stderr } = await exec('npm list --prefix '+ this.localPrefix + ' --depth=0 | grep ' + this.name) + if (error !== null) { + logger.warn('npm outdated', this.name, 'from prefix', this.localPrefix, 'stderr:', stderr) + return false + } else { + logger.info('npm outdated', this.name, 'from prefix', this.localPrefix, 'stdout:', stdout) + return true + } + } +} + +export class GitExtension extends Extension{ + constructor( + name:string, + version = "latest", + public localPrefix:string, + ){ + super(name, version) + } + + public async install(): Promise { + + } + + public async uninstall(): Promise { + + } + + public async update(): Promise { + + } + + public async isOutdated(): Promise { + + } +} + +export class UpdateManager extends Plugin{ + + private loadedPlugins:{[name in string]:Plugin} = {} private dashboardUpdater:GitUpdater private adminUpdater:GitUpdater private pluginUpdaters:{[name in string]:GitUpdater} = {} @@ -32,8 +131,8 @@ export class UpdateManager extends Plugin{ super(admin, "UpdateManager") } - getDefaultConfig(): void { - return + getDefaultConfig(): {} { + return {} } exportRPCs(): socketioRPC[] { @@ -70,13 +169,23 @@ export class UpdateManager extends Plugin{ }] } + 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", "./plugins", "0.19.2").install(), + new NPMExtension("sqlite3", "./plugins", "4.1.0").install() + ]) } async updateAdmin(force:boolean = false){ @@ -104,11 +213,6 @@ export class UpdateManager extends Plugin{ 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) - return await this.pluginUpdaters[name].cloneRepo("https://gitea.frontblock.me/fb-dist/"+name.toLowerCase()+".git", force) - } async startPlugin(name:string):Promise{ if(!this.pluginUpdaters[name]) return false @@ -133,6 +237,7 @@ export class UpdateManager extends 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 }