From 0c38130296d1e10563dc861fa896be1cc2583fdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=BCbleitner?= Date: Tue, 27 Aug 2019 15:09:29 +0200 Subject: [PATCH] moved stuff --- package-lock.json | 100 ++++++++++- package.json | 1 + src/backend/Extension.ts | 350 ++++++++++++++++++++++++++++++++++++ src/backend/UpdateManger.ts | 347 ----------------------------------- 4 files changed, 444 insertions(+), 354 deletions(-) create mode 100644 src/backend/Extension.ts diff --git a/package-lock.json b/package-lock.json index ad95d86..a517e7c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -733,6 +733,21 @@ "qs": "6.7.0", "raw-body": "2.4.0", "type-is": "~1.6.17" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } } }, "brace-expansion": { @@ -1362,11 +1377,11 @@ "dev": true }, "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "decamelize": { @@ -1675,6 +1690,14 @@ "to-regex": "^3.0.1" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", @@ -1690,6 +1713,11 @@ "requires": { "is-extendable": "^0.1.0" } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" } } }, @@ -1736,6 +1764,21 @@ "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } } }, "extend": { @@ -1875,6 +1918,21 @@ "parseurl": "~1.3.3", "statuses": "~1.5.0", "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } } }, "find-cache-dir": { @@ -3864,9 +3922,9 @@ } }, "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "mysql": { "version": "2.17.1", @@ -4984,6 +5042,21 @@ "statuses": "~1.5.0" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, "ms": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", @@ -5118,6 +5191,14 @@ "use": "^3.1.0" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", @@ -5134,6 +5215,11 @@ "is-extendable": "^0.1.0" } }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", diff --git a/package.json b/package.json index 52135d1..ba7069f 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "bsert": "0.0.10", "bsock": "^0.1.9", "child-process-promise": "^2.2.1", + "debug": "^4.1.1", "express": "^4.16.4", "frontblock": "^0.14.0", "frontblock-generic": "^0.30.1", diff --git a/src/backend/Extension.ts b/src/backend/Extension.ts new file mode 100644 index 0000000..51fa10e --- /dev/null +++ b/src/backend/Extension.ts @@ -0,0 +1,350 @@ + +import * as Logger from 'log4js' +import * as path from "path"; +import { promises as fs } from "fs" +import * as git from "simple-git/promise" +import * as trash from "trash" + + +Logger.configure({ + appenders: + { + "admin/extension": { type: 'stdout' }, + //app: { type: 'file', filename: 'application.log' } + }, + categories: + { + default: { appenders: [ 'admin/extension' ], level: 'debug' } + } +}) +const exec = require('child-process-promise').exec; +const logger = Logger.getLogger("admin/updatemanager") + +type SharedStatus = { + name: string + installed?: boolean + outdated?: boolean +} + +abstract class Extension{ + constructor( + protected name: string + ){} + + /** + * the generic Status object + */ + protected async status():Promise{ + const installed = await this.isInstalled() + const outdated = await this.isOutdated() + return { name: this.name, installed: installed, outdated: outdated } + } + + /** + * true => extension was installed + * false => extension was not install + * undefined => signals error + */ + abstract async install():Promise + + /** + * reverts the install procedure + */ + abstract async uninstall():Promise + + /** + * true => an update was performed + * false => no update was performed + * undefined => signals error + */ + abstract async update(force?: boolean): Promise + + /** + * true => update would install a newer version + * false => nothing would be updated if update would be performed + * undefined => signals error + */ + abstract async isOutdated():Promise + + /** + * promises a boolean + * true => install would do nothing would it be called + * false => install would try to install the newest extension + * undefined => signals error + */ + abstract async isInstalled():Promise +} + +export type FSStatus = SharedStatus & { + prefix: string + empty?: boolean +} + +export abstract class FSExtension extends Extension{ + constructor( + name: string, + protected readonly prefix: string){ + super(name) + } + + public async status(): Promise{ + const shdStatus = await super.status() + const empty = await this.isEmpty() + return {...shdStatus, prefix: this.prefix, empty: empty} + } + + public async install():Promise{ + const installed = await this.isInstalled() + if (!installed) { + logger.info('Installing extension to', path.resolve(this.prefix, this.name)) + await fs.mkdir(path.resolve(this.prefix, this.name), {recursive: true}) + return true + } + logger.warn('Extension already installed at', path.resolve(this.prefix, this.name)) + return false + } + + public async uninstall(): Promise{ + logger.info('Uninstalling extension from', path.resolve(this.prefix, this.name)) + return await trash(path.resolve(this.prefix, this.name)) + } + + public async update(force?: boolean): Promise{ + if (force) await this.uninstall() + return await this.install() + } + + public async isEmpty(): Promise{ + if (!this.isInstalled()) return true + try { + const files = await fs.readdir(path.resolve(this.prefix, this.name)) + return files.length === 0 + } catch (error) { + logger.warn('TODO') + return + } + } + + public async isInstalled(): Promise{ + try { + await fs.access(path.resolve(this.prefix, this.name)) + return true + } catch (error) { + return false + } + } +} + +type NPMStatus = FSStatus & { + latest?: string, + current?: string, + author?: string +} + +export class NPMExtension extends FSExtension{ + + constructor( + pkgName: string, + prefix: string){ + super(pkgName, prefix) + } + + public async status(): Promise { + + const fsStatus = await super.status() + const latest = await this.getLatestVersion() + const author = await this.getAuthor() + const current = await this.getCurrentVersion() + return { + ...fsStatus, + latest: latest, + author: author, + current: current + } + } + + public async install(): Promise { + super.install() + logger.info("npm install", this.name, 'to', path.resolve(this.prefix, this.name)) + const { error, stdout, stderr } = await exec('npm install --prefix '+ this.prefix + ' ' + this.name) + if (error !== null) { + logger.warn('npm install', this.name, 'from prefix', this.prefix, 'stderr:', stderr) + return false + } else { + logger.info('npm install', this.name, 'from prefix', this.prefix, 'stdout:', stdout) + return true + } + } + + public async uninstall(force?: boolean): Promise { + super.uninstall() + logger.info("npm uninstall", this.name, 'from', path.resolve(this.prefix, this.name)) + const { error, stdout, stderr } = await exec('npm uninstall --prefix '+ this.prefix + ' ' + this.name) + if (error) { + logger.warn('npm uninstall', this.name, 'from prefix', this.prefix, 'stderr:', stderr) + } else { + logger.info('npm uninstall', this.name, 'from prefix', this.prefix, 'stdout:', stdout) + } + } + + public async update(force?: boolean): Promise { + super.update(force) + logger.info("npm update", path.resolve(this.prefix, this.name)) + const { error, stdout, stderr } = await exec('npm update --prefix '+ this.prefix + ' ' + this.name) + if (error) { + logger.warn('npm update', this.name, 'from prefix', this.prefix, 'stderr:', stderr) + return false + } else { + logger.info('npm update', this.name, 'from prefix', this.prefix, 'stdout:', stdout) + return true + } + } + + public async isOutdated(): Promise { + try { + const { + error, + stdout, + stderr } = await exec('npm outdated --prefix ' + this.prefix + " --json" + ' ' + this.name) + if (error) throw new Error(stderr) + if (Object.keys(JSON.parse(stdout)).length > 0) return true + else return false + } catch (error) { + logger.warn('npm outdated', this.name, 'from prefix', this.prefix, 'stderr:', error) + return + } + } + + private async getAuthor(): Promise{ + const { + error, + stdout, + stderr } = await exec('npm author ls --prefix ' + this.prefix + ' ' + this.name) + + if(error){ + logger.warn(stderr) + return + } else { + logger.info(stdout) + return stdout + } + } + + private async getLatestVersion(): Promise{ + const { + error, + stdout, + stderr } = await exec('npm view --prefix ' + this.prefix + ' ' + this.name + " version") + + if(error){ + logger.warn(stderr) + return + } else { + logger.info(stdout) + return stdout + } + } + + private async getCurrentVersion(): Promise{ + return //TODO + } +} + +export type GitStatus = FSStatus & { + branch?: string + remote?: string + latest?: string +} + +export class GitExtension extends FSExtension{ + protected repo: git.SimpleGit + constructor( + repoName:string, + localPrefix:string, + protected gitCloneURL: string, + protected credentials?: [string, string] + ){ + super(repoName, localPrefix) + } + + public async status(): Promise{ + const fsStatus = await super.status() + + return { + ...fsStatus, + branch: 'wat', + remote: this.gitCloneURL, + latest: 'kek' + } + } + + public async install(): Promise { + if (super.install()){ + if (this.credentials){ + + } else { + + } + } else { + + } + } + + + public async update(): Promise { + return true // do git pull remote origin && git checkout + } + + public async isOutdated(): Promise { + return true + } +} + +export type PluginStatus = GitStatus & { + hasFrontend: boolean, + hasBackend: boolean, + isFrontendLoaded: boolean, + isBackendLoaded: boolean +} + +export class PluginExtension extends GitExtension { + protected frontendLoaded: boolean + protected backendLoaded: boolean + + constructor( + pluginName: string, + gitCloneURL: string, + localPrefix: string = './plugins', + credentials?: [string, string], + protected hasBackend: boolean = true, + protected hasFrontend: boolean = true){ + super(pluginName,localPrefix,gitCloneURL,credentials) + } + + public async load(): Promise{ + if (this.hasFrontend) await this.loadFrontend() + if (this.hasBackend) await this.loadBackend() + } + + public async loadFrontend(): Promise{ + return //TODO + } + + public async loadBackend(): Promise{ + return //TODO + } + + public async unload(): Promise{ + if (this.hasFrontend) await this.unloadFrontend() + if (this.hasBackend) await this.unloadBackend() + } + + public async unloadFrontend(): Promise{ + return //TODO + } + + public async unloadBackend(): Promise{ + return //TODO + } + +} \ No newline at end of file diff --git a/src/backend/UpdateManger.ts b/src/backend/UpdateManger.ts index 839eadb..6243ee7 100644 --- a/src/backend/UpdateManger.ts +++ b/src/backend/UpdateManger.ts @@ -28,353 +28,6 @@ Logger.configure({ }) const logger = Logger.getLogger("admin/updatemanager") -/* -cool snibbet - -const SharedStatus = - (privateStatus: Status) => - (name: string) => - (installed: boolean) => - (outdated: boolean) => - { - return { - name: name, - installed: installed, - outdated: outdated, - ...privateStatus - } - } -*/ - -type SharedStatus = { - name: string - installed?: boolean - outdated?: boolean -} - -abstract class Extension{ - constructor( - protected name: string - ){} - - /** - * the generic Status object - */ - protected async status():Promise{ - const installed = await this.isInstalled() - const outdated = await this.isOutdated() - return { name: this.name, installed: installed, outdated: outdated } - } - - /** - * true => extension was installed - * false => extension was not install - * undefined => signals error - */ - abstract async install():Promise - - /** - * reverts the install procedure - */ - abstract async uninstall():Promise - - /** - * true => an update was performed - * false => no update was performed - * undefined => signals error - */ - abstract async update(force?: boolean): Promise - - /** - * true => update would install a newer version - * false => nothing would be updated if update would be performed - * undefined => signals error - */ - abstract async isOutdated():Promise - - /** - * promises a boolean - * true => install would do nothing would it be called - * false => install would try to install the newest extension - * undefined => signals error - */ - abstract async isInstalled():Promise -} - -export type FSStatus = SharedStatus & { - prefix: string - empty?: boolean -} - -export abstract class FSExtension extends Extension{ - constructor( - name: string, - protected readonly prefix: string){ - super(name) - } - - public async status(): Promise{ - const shdStatus = await super.status() - const empty = await this.isEmpty() - return {...shdStatus, prefix: this.prefix, empty: empty} - } - - public async install():Promise{ - const installed = await this.isInstalled() - if (!installed) { - logger.info('Installing extension to', path.resolve(this.prefix, this.name)) - await fs.mkdir(path.resolve(this.prefix, this.name), {recursive: true}) - return true - } - logger.warn('Extension already installed at', path.resolve(this.prefix, this.name)) - return false - } - - public async uninstall(): Promise{ - logger.info('Uninstalling extension from', path.resolve(this.prefix, this.name)) - return await trash(path.resolve(this.prefix, this.name)) - } - - public async update(force?: boolean): Promise{ - if (force) await this.uninstall() - return await this.install() - } - - public async isEmpty(): Promise{ - if (!this.isInstalled()) return true - try { - const files = await fs.readdir(path.resolve(this.prefix, this.name)) - return files.length === 0 - } catch (error) { - logger.warn('TODO') - return - } - } - - public async isInstalled(): Promise{ - try { - await fs.access(path.resolve(this.prefix, this.name)) - return true - } catch (error) { - return false - } - } -} - -type NPMStatus = FSStatus & { - latest?: string, - current?: string, - author?: string -} - -export class NPMExtension extends FSExtension{ - - constructor( - pkgName: string, - prefix: string){ - super(pkgName, prefix) - } - - public async status(): Promise { - - const fsStatus = await super.status() - const latest = await this.getLatestVersion() - const author = await this.getAuthor() - const current = await this.getCurrentVersion() - return { - ...fsStatus, - latest: latest, - author: author, - current: current - } - } - - public async install(): Promise { - super.install() - logger.info("npm install", this.name, 'to', path.resolve(this.prefix, this.name)) - const { error, stdout, stderr } = await exec('npm install --prefix '+ this.prefix + ' ' + this.name) - if (error !== null) { - logger.warn('npm install', this.name, 'from prefix', this.prefix, 'stderr:', stderr) - return false - } else { - logger.info('npm install', this.name, 'from prefix', this.prefix, 'stdout:', stdout) - return true - } - } - - public async uninstall(force?: boolean): Promise { - super.uninstall() - logger.info("npm uninstall", this.name, 'from', path.resolve(this.prefix, this.name)) - const { error, stdout, stderr } = await exec('npm uninstall --prefix '+ this.prefix + ' ' + this.name) - if (error) { - logger.warn('npm uninstall', this.name, 'from prefix', this.prefix, 'stderr:', stderr) - } else { - logger.info('npm uninstall', this.name, 'from prefix', this.prefix, 'stdout:', stdout) - } - } - - public async update(force?: boolean): Promise { - super.update(force) - logger.info("npm update", path.resolve(this.prefix, this.name)) - const { error, stdout, stderr } = await exec('npm update --prefix '+ this.prefix + ' ' + this.name) - if (error) { - logger.warn('npm update', this.name, 'from prefix', this.prefix, 'stderr:', stderr) - return false - } else { - logger.info('npm update', this.name, 'from prefix', this.prefix, 'stdout:', stdout) - return true - } - } - - public async isOutdated(): Promise { - try { - const { - error, - stdout, - stderr } = await exec('npm outdated --prefix ' + this.prefix + " --json" + ' ' + this.name) - if (error) throw new Error(stderr) - if (Object.keys(JSON.parse(stdout)).length > 0) return true - else return false - } catch (error) { - logger.warn('npm outdated', this.name, 'from prefix', this.prefix, 'stderr:', error) - return - } - } - - private async getAuthor(): Promise{ - const { - error, - stdout, - stderr } = await exec('npm author ls --prefix ' + this.prefix + ' ' + this.name) - - if(error){ - logger.warn(stderr) - return - } else { - logger.info(stdout) - return stdout - } - } - - private async getLatestVersion(): Promise{ - const { - error, - stdout, - stderr } = await exec('npm view --prefix ' + this.prefix + ' ' + this.name + " version") - - if(error){ - logger.warn(stderr) - return - } else { - logger.info(stdout) - return stdout - } - } - - private async getCurrentVersion(): Promise{ - return //TODO - } -} - -export type GitStatus = FSStatus & { - branch?: string - remote?: string - latest?: string -} - -export class GitExtension extends FSExtension{ - protected repo: git.SimpleGit - constructor( - repoName:string, - localPrefix:string, - protected gitCloneURL: string, - protected credentials?: [string, string] - ){ - super(repoName, localPrefix) - } - - public async status(): Promise{ - const fsStatus = await super.status() - - return { - ...fsStatus, - branch: 'wat', - remote: this.gitCloneURL, - latest: 'kek' - } - } - - public async install(): Promise { - if (super.install()){ - if (this.credentials){ - - } else { - - } - } else { - - } - } - - - public async update(): Promise { - return true // do git pull remote origin && git checkout - } - - public async isOutdated(): Promise { - return true - } -} - -export type PluginStatus = GitStatus & { - hasFrontend: boolean, - hasBackend: boolean, - isFrontendLoaded: boolean, - isBackendLoaded: boolean -} - -export class PluginExtension extends GitExtension { - protected frontendLoaded: boolean - protected backendLoaded: boolean - - constructor( - pluginName: string, - gitCloneURL: string, - localPrefix: string = './plugins', - credentials?: [string, string], - protected hasBackend: boolean = true, - protected hasFrontend: boolean = true){ - super(pluginName,localPrefix,gitCloneURL,credentials) - } - - public async load(): Promise{ - if (this.hasFrontend) await this.loadFrontend() - if (this.hasBackend) await this.loadBackend() - } - - public async loadFrontend(): Promise{ - return //TODO - } - - public async loadBackend(): Promise{ - return //TODO - } - - public async unload(): Promise{ - if (this.hasFrontend) await this.unloadFrontend() - if (this.hasBackend) await this.unloadBackend() - } - - public async unloadFrontend(): Promise{ - return //TODO - } - - public async unloadBackend(): Promise{ - return //TODO - } - -} - export class UpdateManager extends Plugin{ private loadedPlugins:{[name in string]:Plugin} = {}