service-core/core/core.mjs

167 lines
5.2 KiB
JavaScript

import Application from './application.mjs'
import Util from './util.mjs'
import getLog from './log.mjs'
import { Low } from 'lowdb'
import StaticProvider from './providers/static.mjs'
import GitProvider from './providers/git.mjs'
export default class Core {
static providers = new Map()
static addProvider(name, provider) {
if (!name || typeof(name) !== 'string')
throw new Error('addProvider name must be a string')
if (typeof(provider) !== 'function')
throw new Error(`addProvider ${name} provider must be a class`)
let test = new provider({})
if (typeof(test.checkConfig) !== 'function')
throw new Error(`addProvider ${name} provider class missing checkConfig`)
if (typeof(test.getLatestVersion) !== 'function')
throw new Error(`addProvider ${name} provider class missing getLatestVersion`)
Core.providers.set(name, provider)
}
constructor(db, util, log) {
// some sanity checks
if (!log || typeof(log) !== 'object') throw new Error('log parameter was invalid')
if (typeof(log.event) !== 'object') throw new Error('log parameter was invalid')
if (typeof(log.info) !== 'function'
|| typeof(log.warn) !== 'function'
|| typeof(log.error) !== 'function'
|| typeof(log.event.info) !== 'function'
|| typeof(log.event.warn) !== 'function'
|| typeof(log.event.error) !== 'function') throw new Error('log parameter was invalid')
if (!util || !(util instanceof Util)) throw new Error('util not instance of Util')
if (!db || !(db instanceof Low)) throw new Error('db not instance of Low')
this.running = false
this.db = db
this.util = util
this.log = log
this.applications = []
this.applicationMap = new Map()
this._applicationFatalCrash = null
}
getApplication(name) {
return this.applicationMap.get(name)
}
async init() {
this.log.info(`Verifying config`)
this.util.verifyConfig(this.db.config)
let names = this.util.getAppNames(this.db.config)
this.log.info(`Found applications: ${names.join(', ')}.`)
for (let name of names) {
try {
let provConstructor = Core.providers.get(this.db.config[name].provider)
let provider = new provConstructor(this.db.config[name])
await provider.checkConfig(this.db.config[name])
let application = new Application({
db: this.db,
util: this.util,
log: getLog(name, this.db.config[name].log || null),
core: this,
}, provider, name)
this.applications.push(application)
this.applicationMap.set(name, application)
} catch (err) {
this.log.error(err, `Error creating application ${name} with provider ${this.db.config[name].provider}: ${err.message}`)
}
}
if (names.length && !this.applications.length) {
return Promise.reject(new Error('None of the application were successful in running'))
}
}
async run() {
if (this.running) return
this.running = true
this.log.info(`Running updater on ${this.applications.length} apps`)
await Promise.all(this.applications.map((app) => {
return app.update().catch(err => {
app.ctx.log.error(err, `Error updating: ${err.message}`)
})
}))
let found = false
for (let app of this.applications) {
app.startAutoupdater()
await this.runApplication(app).then(
() => {
found = true
},
err => {
app.ctx.log.error(err, `Error running: ${err.message}`)
}
)
app.on('updated', this.runApplication.bind(this, app))
}
if (!found) {
throw new Error('No stable application was found')
}
}
criticalError(application, version, errorCode) {
application.ctx.log.fatal(`Critical error ${errorCode} running ${version.version}`)
version.stable = -2
this.db.writeSync()
}
async runApplication(application) {
let name = application.name
let found = false
if (!this.db.data.core[name].versions.length) {
return Promise.reject(new Error(`No versions were found`))
}
for (let i = 0; i < this.db.data.core[name].versions.length; i++) {
let version = this.db.data.core[name].versions[i]
if (!version.installed || version.stable < -1) continue
if (version.stable < 0 && !application.fresh) continue
await application.closeServer()
this._applicationFatalCrash = this.criticalError.bind(this, application, version)
process.once('exit', this._applicationFatalCrash)
try {
application.ctx.log.info(`Attempting to run version ${version.version}`)
await application.runVersion(version.version)
found = true
version.stable = 1
await this.db.write()
break
} catch(err) {
if (application.fresh) {
version.stable = -2
} else {
version.stable = Math.min(version.stable, 0) - 1
}
await this.db.write()
application.ctx.log.error(err, `Error starting ${version.version}: ${err.message}`)
} finally {
process.off('exit', this._applicationFatalCrash)
}
}
if (!found) {
return Promise.reject(Error(`No stable versions were found`))
}
}
}
Core.addProvider('static', StaticProvider)
Core.addProvider('git', GitProvider)