'use strict' import * as Logger from 'log4js'; import fetch = require('node-fetch'); import fs = require('fs'); import path = require('path') import unzip = require('unzip') Logger.configure({ appenders: { "plugin-manager": { type: 'stdout' }, //app: { type: 'file', filename: 'application.log' } }, categories: { default: { appenders: [ 'plugin-manager' ], level: 'debug' } } }) const logger = Logger.getLogger("plugin-manager") export class PluginManager{ private installDir: string private cacheDir: string private loadedPlugins: string[] constructor(installDir: string = './plugins'){ this.installDir = installDir this.cacheDir = path.join(installDir, '.cache') this.initialize() } private initialize() { logger.info('Initializing manager') try { fs.access(this.cacheDir, async err => { if (err && err.code === 'ENOENT') { await fs.promises.mkdir(this.cacheDir, {recursive: true}) logger.info('Manager installed to ' + this.installDir) } else { logger.info('Manager already installed at ' + this.installDir) } }) } catch (error) { logger.error(error) } } public async getAvailablePlugins(): Promise { return [ 'apiclient', 'paymentmanager', 'wallet', 'htmlsupplier' ] } public async getInstalledPlugins(): Promise { try { const installed = await fs.promises.readdir(this.installDir) return installed.filter( entry => entry[0] != '.') } catch (error) { logger.error(error) return } } public async getLoadedPlugins(): Promise { return this.loadedPlugins } private async downloadFile(url: URL, dest: string) { try { fs.access(path.dirname(dest), err => { if (err && err.code === 'ENOENT') { fs.mkdirSync(path.dirname(dest), {recursive: true}) } }) const file = fs.createWriteStream(dest) file.on('error', err => {throw new Error(err)}) const response = await fetch(url) if (!response.ok) throw new Error(response.statusText) response.body.pipe(file) } catch (error) { logger.error(error) await fs.promises.unlink(dest) } } private async downloadPlugin(pluginName: string) { try { const response = await fetch('https://gitea.frontblock.me/api/v1/repos/fb-vendor/' + pluginName + '/releases') if (!response.ok) throw new Error(response.statusText) const releases = await response.json() if (!releases || !releases.length) throw new Error('Empty response') const latest = releases[0] const version = latest.tag_name const archiveURL = latest.assets.filter(asset => asset.name === pluginName + '.zip')[0].browser_download_url const checksumURL = latest.assets.filter(asset => asset.name === 'md5sum.txt')[0].browser_download_url if (!version || ! archiveURL || !checksumURL) throw new Error('Malformed response') await this.downloadFile(archiveURL, path.join(this.cacheDir, pluginName, version, 'plugin.zip')) await this.downloadFile(checksumURL, path.join(this.cacheDir, pluginName, version, 'md5sum.txt')) } catch (error) { logger.error(error) } } public async installPlugin(pluginName: string) { try { await this.downloadPlugin(pluginName) } catch (error) { logger.debug(error) } } public uninstallPlugin(pluginName: string) { } public async loadPlugin() { } public async unloadPlugin() { } }