import { EventEmitter } from 'events' import fs from 'fs/promises' export default class Application extends EventEmitter { constructor(util, db, provider, name, opts = {}) { super() this.util = util this.db = db this.config = db.config[name] || { } this.provider = provider this.name = name this.updating = false Object.assign(this, { setInterval: opts.setInterval || setInterval, fs: opts.fs || fs, }) this.db.addApplication(name) } startAutoupdater() { if (this.provider.static) return let timer = this.setInterval(() => { this.update().then( () => { this.db.data.core[this.name].updater += 'Automatic update finished successfully. ' }, (err) => { this.db.data.core[this.name].updater += 'Error while running automatic update: ' + err.message + '. ' } ) }, (this.config.updateEvery || 180) * 60 * 1000) timer.unref() } updateLog(message) { this.db.data.core[this.name].updater += message this.db.log.info(message) return message } async update() { if (this.updating) return if (this.provider.static) { this.updateLog('Provider in question is static and so no update required, nothing to do.') return } this.updating = true this.db.data.core[this.name].updater = '' let cleanup = true let folder = '' let log = '' let latest = null try { log += this.updateLog(`Checking for latest version at ${new Date().toISOString().replace('T', ' ').split('.')[0]}. `) + '\n' latest = await this.provider.getLatestVersion() log += this.updateLog(`Found ${latest.version}. `) + '\n' if (this.db.data.core[this.name].latestInstalled === latest.version) { this.updateLog('Already up to date, nothing to do. ') this.updating = false return } latest.id = latest.version var found = this.db.get(this.db.data.core[this.name].versions, latest.id) if (found) { Object.keys(latest).forEach(function(key) { found[key] = latest[key] }) latest = found log = latest.log + log } else { this.db.upsert(this.db.data.core[this.name].versions, latest) } if (latest.failtodownload && latest.failtodownload > 3) { this.updateLog('Version failed to download too many times, skipping this version. ') this.updating = false return } if (latest.failtoinstall && latest.failtoinstall > 3) { this.updateLog('Version failed to install too many times, skipping this version. ') this.updating = false return } let target = this.util.getPathFromRoot(`./${this.name}/${latest.version}/file${this.util.getExtension(latest.filename)}`) folder = this.util.getPathFromRoot(`./${this.name}/${latest.version}`) await this.fs.mkdir(folder, { recursive: true }) log += this.updateLog(`Downloading ${latest.link} to ${target}. `) + '\n' await this.provider.downloadVersion(latest, target) .catch(function(err) { latest.failtodownload = (latest.failtodownload || 0) + 1 return Promise.reject(err) }) log += '\n' + this.updateLog(`Extracting ${target}. `) + '\n' await this.util.extractFile(target, function(msg) { log += msg }).catch(function(err) { latest.failtodownload = (latest.failtodownload || 0) + 1 return Promise.reject(err) }) await this.fs.rm(target, { force: true }).catch(function() {}) if (!log.endsWith('\n')) { log += '\n' } if (!log.endsWith('\n\n')) { log += '\n' } await this.fs.stat(this.util.getPathFromRoot(`./${this.name}/${latest.version}/index.mjs`)) .catch((err) => { latest.failtodownload = (latest.failtodownload || 0) + 1 log += this.updateLog('Version did not include or was missing index.mjs. ') + '\n' return Promise.reject(err) }) cleanup = false let packageStat = await this.fs.stat(this.util.getPathFromRoot(`./${this.name}/${latest.version}/package.json`)) .catch(function() { return null }) if (packageStat) { log += this.updateLog(`running npm install --production. `) + '\n' await this.util.runCommand( 'npm.cmd', ['install', '--production', '--no-optional', '--no-package-lock', '--no-audit', '--loglevel=notice'], folder, function(msg) { log += msg } ).catch(function(err) { latest.failtoinstall = (latest.failtoinstall || 0) + 1 return Promise.reject(err) }) if (!log.endsWith('\n')) { log += '\n' } if (!log.endsWith('\n\n')) { log += '\n' } } else { log += this.updateLog('Release did not contain package.json, skipping npm install. ') + '\n' } } catch (err) { this.updating = false log += this.updateLog(`Error: ${err.message}. `) + '\n' if (folder && cleanup) { await this.fs.rm(folder, { force: true, recursive: true }).catch((err) => { this.updateLog(`Error while cleaning up: ${err.message}. `) }) } if (latest) { latest.log = log } return Promise.reject(err) } log += this.updateLog(`Finished updating ${this.name} to version ${latest.version}.`) + '\n' this.db.data.core[this.name].latestInstalled = latest.version latest.log = log this.updating = false } }