moved stuff
This commit is contained in:
@@ -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<Status extends SharedStatus>{
|
||||
constructor(
|
||||
protected name: string
|
||||
){}
|
||||
|
||||
/**
|
||||
* the generic Status object
|
||||
*/
|
||||
protected async status():Promise<Status>{
|
||||
const installed = await this.isInstalled()
|
||||
const outdated = await this.isOutdated()
|
||||
return <Status>{ name: this.name, installed: installed, outdated: outdated }
|
||||
}
|
||||
|
||||
/**
|
||||
* true => extension was installed
|
||||
* false => extension was not install
|
||||
* undefined => signals error
|
||||
*/
|
||||
abstract async install():Promise<boolean | undefined>
|
||||
|
||||
/**
|
||||
* reverts the install procedure
|
||||
*/
|
||||
abstract async uninstall():Promise<void>
|
||||
|
||||
/**
|
||||
* true => an update was performed
|
||||
* false => no update was performed
|
||||
* undefined => signals error
|
||||
*/
|
||||
abstract async update(force?: boolean): Promise<boolean | undefined>
|
||||
|
||||
/**
|
||||
* true => update would install a newer version
|
||||
* false => nothing would be updated if update would be performed
|
||||
* undefined => signals error
|
||||
*/
|
||||
abstract async isOutdated():Promise<boolean | undefined>
|
||||
|
||||
/**
|
||||
* 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<boolean | undefined>
|
||||
}
|
||||
|
||||
export type FSStatus = SharedStatus & {
|
||||
prefix: string
|
||||
empty?: boolean
|
||||
}
|
||||
|
||||
export abstract class FSExtension<Status extends FSStatus> extends Extension<Status>{
|
||||
constructor(
|
||||
name: string,
|
||||
protected readonly prefix: string){
|
||||
super(name)
|
||||
}
|
||||
|
||||
public async status(): Promise<Status>{
|
||||
const shdStatus = await super.status()
|
||||
const empty = await this.isEmpty()
|
||||
return <Status>{...shdStatus, prefix: this.prefix, empty: empty}
|
||||
}
|
||||
|
||||
public async install():Promise<boolean>{
|
||||
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<void>{
|
||||
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<boolean | undefined>{
|
||||
if (force) await this.uninstall()
|
||||
return await this.install()
|
||||
}
|
||||
|
||||
public async isEmpty(): Promise<boolean | undefined>{
|
||||
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<boolean>{
|
||||
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<NPMStatus>{
|
||||
|
||||
constructor(
|
||||
pkgName: string,
|
||||
prefix: string){
|
||||
super(pkgName, prefix)
|
||||
}
|
||||
|
||||
public async status(): Promise<NPMStatus> {
|
||||
|
||||
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<boolean> {
|
||||
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<void> {
|
||||
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<boolean> {
|
||||
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<boolean | undefined> {
|
||||
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<string | undefined>{
|
||||
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<string | undefined>{
|
||||
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<string | undefined>{
|
||||
return //TODO
|
||||
}
|
||||
}
|
||||
|
||||
export type GitStatus = FSStatus & {
|
||||
branch?: string
|
||||
remote?: string
|
||||
latest?: string
|
||||
}
|
||||
|
||||
export class GitExtension<Status extends GitStatus> extends FSExtension<Status>{
|
||||
protected repo: git.SimpleGit
|
||||
constructor(
|
||||
repoName:string,
|
||||
localPrefix:string,
|
||||
protected gitCloneURL: string,
|
||||
protected credentials?: [string, string]
|
||||
){
|
||||
super(repoName, localPrefix)
|
||||
}
|
||||
|
||||
public async status(): Promise<Status>{
|
||||
const fsStatus = await super.status()
|
||||
|
||||
return {
|
||||
...fsStatus,
|
||||
branch: 'wat',
|
||||
remote: this.gitCloneURL,
|
||||
latest: 'kek'
|
||||
}
|
||||
}
|
||||
|
||||
public async install(): Promise<boolean> {
|
||||
if (super.install()){
|
||||
if (this.credentials){
|
||||
|
||||
} else {
|
||||
|
||||
}
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public async update(): Promise<boolean> {
|
||||
return true // do git pull remote origin && git checkout
|
||||
}
|
||||
|
||||
public async isOutdated(): Promise<boolean> {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
export type PluginStatus = GitStatus & {
|
||||
hasFrontend: boolean,
|
||||
hasBackend: boolean,
|
||||
isFrontendLoaded: boolean,
|
||||
isBackendLoaded: boolean
|
||||
}
|
||||
|
||||
export class PluginExtension<Status extends GitStatus> extends GitExtension<Status> {
|
||||
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<void>{
|
||||
if (this.hasFrontend) await this.loadFrontend()
|
||||
if (this.hasBackend) await this.loadBackend()
|
||||
}
|
||||
|
||||
public async loadFrontend(): Promise<void>{
|
||||
return //TODO
|
||||
}
|
||||
|
||||
public async loadBackend(): Promise<void>{
|
||||
return //TODO
|
||||
}
|
||||
|
||||
public async unload(): Promise<void>{
|
||||
if (this.hasFrontend) await this.unloadFrontend()
|
||||
if (this.hasBackend) await this.unloadBackend()
|
||||
}
|
||||
|
||||
public async unloadFrontend(): Promise<void>{
|
||||
return //TODO
|
||||
}
|
||||
|
||||
public async unloadBackend(): Promise<void>{
|
||||
return //TODO
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,353 +28,6 @@ Logger.configure({
|
||||
})
|
||||
const logger = Logger.getLogger("admin/updatemanager")
|
||||
|
||||
/*
|
||||
cool snibbet
|
||||
|
||||
const SharedStatus = <Status>
|
||||
(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<Status extends SharedStatus>{
|
||||
constructor(
|
||||
protected name: string
|
||||
){}
|
||||
|
||||
/**
|
||||
* the generic Status object
|
||||
*/
|
||||
protected async status():Promise<Status>{
|
||||
const installed = await this.isInstalled()
|
||||
const outdated = await this.isOutdated()
|
||||
return <Status>{ name: this.name, installed: installed, outdated: outdated }
|
||||
}
|
||||
|
||||
/**
|
||||
* true => extension was installed
|
||||
* false => extension was not install
|
||||
* undefined => signals error
|
||||
*/
|
||||
abstract async install():Promise<boolean | undefined>
|
||||
|
||||
/**
|
||||
* reverts the install procedure
|
||||
*/
|
||||
abstract async uninstall():Promise<void>
|
||||
|
||||
/**
|
||||
* true => an update was performed
|
||||
* false => no update was performed
|
||||
* undefined => signals error
|
||||
*/
|
||||
abstract async update(force?: boolean): Promise<boolean | undefined>
|
||||
|
||||
/**
|
||||
* true => update would install a newer version
|
||||
* false => nothing would be updated if update would be performed
|
||||
* undefined => signals error
|
||||
*/
|
||||
abstract async isOutdated():Promise<boolean | undefined>
|
||||
|
||||
/**
|
||||
* 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<boolean | undefined>
|
||||
}
|
||||
|
||||
export type FSStatus = SharedStatus & {
|
||||
prefix: string
|
||||
empty?: boolean
|
||||
}
|
||||
|
||||
export abstract class FSExtension<Status extends FSStatus> extends Extension<Status>{
|
||||
constructor(
|
||||
name: string,
|
||||
protected readonly prefix: string){
|
||||
super(name)
|
||||
}
|
||||
|
||||
public async status(): Promise<Status>{
|
||||
const shdStatus = await super.status()
|
||||
const empty = await this.isEmpty()
|
||||
return <Status>{...shdStatus, prefix: this.prefix, empty: empty}
|
||||
}
|
||||
|
||||
public async install():Promise<boolean>{
|
||||
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<void>{
|
||||
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<boolean | undefined>{
|
||||
if (force) await this.uninstall()
|
||||
return await this.install()
|
||||
}
|
||||
|
||||
public async isEmpty(): Promise<boolean | undefined>{
|
||||
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<boolean>{
|
||||
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<NPMStatus>{
|
||||
|
||||
constructor(
|
||||
pkgName: string,
|
||||
prefix: string){
|
||||
super(pkgName, prefix)
|
||||
}
|
||||
|
||||
public async status(): Promise<NPMStatus> {
|
||||
|
||||
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<boolean> {
|
||||
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<void> {
|
||||
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<boolean> {
|
||||
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<boolean | undefined> {
|
||||
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<string | undefined>{
|
||||
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<string | undefined>{
|
||||
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<string | undefined>{
|
||||
return //TODO
|
||||
}
|
||||
}
|
||||
|
||||
export type GitStatus = FSStatus & {
|
||||
branch?: string
|
||||
remote?: string
|
||||
latest?: string
|
||||
}
|
||||
|
||||
export class GitExtension<Status extends GitStatus> extends FSExtension<Status>{
|
||||
protected repo: git.SimpleGit
|
||||
constructor(
|
||||
repoName:string,
|
||||
localPrefix:string,
|
||||
protected gitCloneURL: string,
|
||||
protected credentials?: [string, string]
|
||||
){
|
||||
super(repoName, localPrefix)
|
||||
}
|
||||
|
||||
public async status(): Promise<Status>{
|
||||
const fsStatus = await super.status()
|
||||
|
||||
return {
|
||||
...fsStatus,
|
||||
branch: 'wat',
|
||||
remote: this.gitCloneURL,
|
||||
latest: 'kek'
|
||||
}
|
||||
}
|
||||
|
||||
public async install(): Promise<boolean> {
|
||||
if (super.install()){
|
||||
if (this.credentials){
|
||||
|
||||
} else {
|
||||
|
||||
}
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public async update(): Promise<boolean> {
|
||||
return true // do git pull remote origin && git checkout
|
||||
}
|
||||
|
||||
public async isOutdated(): Promise<boolean> {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
export type PluginStatus = GitStatus & {
|
||||
hasFrontend: boolean,
|
||||
hasBackend: boolean,
|
||||
isFrontendLoaded: boolean,
|
||||
isBackendLoaded: boolean
|
||||
}
|
||||
|
||||
export class PluginExtension<Status extends GitStatus> extends GitExtension<Status> {
|
||||
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<void>{
|
||||
if (this.hasFrontend) await this.loadFrontend()
|
||||
if (this.hasBackend) await this.loadBackend()
|
||||
}
|
||||
|
||||
public async loadFrontend(): Promise<void>{
|
||||
return //TODO
|
||||
}
|
||||
|
||||
public async loadBackend(): Promise<void>{
|
||||
return //TODO
|
||||
}
|
||||
|
||||
public async unload(): Promise<void>{
|
||||
if (this.hasFrontend) await this.unloadFrontend()
|
||||
if (this.hasBackend) await this.unloadBackend()
|
||||
}
|
||||
|
||||
public async unloadFrontend(): Promise<void>{
|
||||
return //TODO
|
||||
}
|
||||
|
||||
public async unloadBackend(): Promise<void>{
|
||||
return //TODO
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class UpdateManager extends Plugin{
|
||||
|
||||
private loadedPlugins:{[name in string]:Plugin} = {}
|
||||
|
||||
Reference in New Issue
Block a user