Implemented basic service core support
This commit is contained in:
parent
9f0dddc5ad
commit
4b274445ca
7 changed files with 211 additions and 95 deletions
|
@ -3,7 +3,8 @@
|
||||||
"serviceName": "Service-Core Node",
|
"serviceName": "Service-Core Node",
|
||||||
"description": "NodeJS Test Service",
|
"description": "NodeJS Test Service",
|
||||||
"port": 4270,
|
"port": 4270,
|
||||||
"managePort": 4269,
|
"managePort": 4271,
|
||||||
|
"devPort": 4269,
|
||||||
"appRepository": "thething/sc-helloworld",
|
"appRepository": "thething/sc-helloworld",
|
||||||
"manageRepository": null
|
"manageRepository": null
|
||||||
}
|
}
|
186
core/core.mjs
186
core/core.mjs
|
@ -1,26 +1,28 @@
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import { EventEmitter } from 'events'
|
import { EventEmitter } from 'events'
|
||||||
import { request } from './client.mjs'
|
import { request } from './client.mjs'
|
||||||
|
import HttpServer from './http.mjs'
|
||||||
|
|
||||||
const fsp = fs.promises
|
const fsp = fs.promises
|
||||||
|
|
||||||
export default class Core extends EventEmitter{
|
export default class Core extends EventEmitter{
|
||||||
constructor(util, config, db, log, closeCb) {
|
constructor(util, config, db, log, closeCb) {
|
||||||
super()
|
super()
|
||||||
this._util = util
|
this.http = new HttpServer()
|
||||||
this._config = config
|
this.util = util
|
||||||
this._db = db
|
this.config = config
|
||||||
this._log = log
|
this.db = db
|
||||||
|
this.log = log
|
||||||
this._close = closeCb
|
this._close = closeCb
|
||||||
this._appRunning = false
|
this.appRunning = false
|
||||||
this._manageRunning = false
|
this.manageRunning = false
|
||||||
this._appUpdating = {
|
this._appUpdating = {
|
||||||
status: false,
|
updating: false,
|
||||||
starting: false,
|
starting: false,
|
||||||
logs: '',
|
logs: '',
|
||||||
}
|
}
|
||||||
this._manageUpdating = {
|
this._manageUpdating = {
|
||||||
status: false,
|
updating: false,
|
||||||
starting: false,
|
starting: false,
|
||||||
logs: '',
|
logs: '',
|
||||||
}
|
}
|
||||||
|
@ -32,19 +34,21 @@ export default class Core extends EventEmitter{
|
||||||
|
|
||||||
status() {
|
status() {
|
||||||
return {
|
return {
|
||||||
app: this._appRunning,
|
app: this.appRunning,
|
||||||
manage: this._manageRunning,
|
manage: this.manageRunning,
|
||||||
appUpdating: this._appUpdating.status,
|
appUpdating: this._appUpdating.updating,
|
||||||
manageUpdating: this._manageUpdating.status,
|
manageUpdating: this._manageUpdating.updating,
|
||||||
|
appStarting: this._appUpdating.starting,
|
||||||
|
manageStarting: this._manageUpdating.starting,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getLatestVersion(active, name) {
|
async getLatestVersion(active, name) {
|
||||||
// Example: 'https://api.github.com/repos/thething/sc-helloworld/releases'
|
// Example: 'https://api.github.com/repos/thething/sc-helloworld/releases'
|
||||||
this.logActive(name, active, `[Core] Fetching release info from: https://api.github.com/repos/${this._config[name + 'Repository']}/releases\n`)
|
this.logActive(name, active, `[Core] Fetching release info from: https://api.github.com/repos/${this.config[name + 'Repository']}/releases\n`)
|
||||||
|
|
||||||
|
|
||||||
let result = await request(`https://api.github.com/repos/${this._config[name + 'Repository']}/releases`)
|
let result = await request(`https://api.github.com/repos/${this.config[name + 'Repository']}/releases`)
|
||||||
|
|
||||||
let items = result.body.filter(function(item) {
|
let items = result.body.filter(function(item) {
|
||||||
if (!item.assets.length) return false
|
if (!item.assets.length) return false
|
||||||
|
@ -60,7 +64,7 @@ export default class Core extends EventEmitter{
|
||||||
if (item.assets[i].name.endsWith('-sc.zip')) {
|
if (item.assets[i].name.endsWith('-sc.zip')) {
|
||||||
this.logActive(name, active, `[Core] Found version ${item.name} with file ${item.assets[i].name}\n`)
|
this.logActive(name, active, `[Core] Found version ${item.name} with file ${item.assets[i].name}\n`)
|
||||||
|
|
||||||
await this._db.set(`core.${name}LatestVersion`, item.name)
|
await this.db.set(`core.${name}LatestVersion`, item.name)
|
||||||
.write()
|
.write()
|
||||||
this.emit('dbupdated', {})
|
this.emit('dbupdated', {})
|
||||||
|
|
||||||
|
@ -80,7 +84,7 @@ export default class Core extends EventEmitter{
|
||||||
|
|
||||||
logActive(name, active, logline, doNotPrint = false) {
|
logActive(name, active, logline, doNotPrint = false) {
|
||||||
if (!doNotPrint) {
|
if (!doNotPrint) {
|
||||||
this._log.info(`Log ${name}: ` + logline.replace(/\n/g, ''))
|
this.log.info(`Log ${name}: ` + logline.replace(/\n/g, ''))
|
||||||
}
|
}
|
||||||
active.logs += logline
|
active.logs += logline
|
||||||
this.emit(name + 'log', active)
|
this.emit(name + 'log', active)
|
||||||
|
@ -93,57 +97,57 @@ export default class Core extends EventEmitter{
|
||||||
return this._manageUpdating.logs
|
return this._manageUpdating.logs
|
||||||
}
|
}
|
||||||
|
|
||||||
let latestInstalled = this._db.get('core.' + name + 'LatestInstalled').value()
|
let latestInstalled = this.db.get('core.' + name + 'LatestInstalled').value()
|
||||||
let latestVersion = this._db.get('core.' + name + 'LatestVersion').value()
|
let latestVersion = this.db.get('core.' + name + 'LatestVersion').value()
|
||||||
if (latestVersion) {
|
if (latestVersion) {
|
||||||
let value = this._db.get(`core_${name}History`).getById(latestVersion).value()
|
let value = this.db.get(`core_${name}History`).getById(latestVersion).value()
|
||||||
if (value) return value.logs
|
if (value) return value.logs
|
||||||
}
|
}
|
||||||
if (latestInstalled) {
|
if (latestInstalled) {
|
||||||
let value = this._db.get(`core_${name}History`).getById(latestInstalled).value()
|
let value = this.db.get(`core_${name}History`).getById(latestInstalled).value()
|
||||||
if (value) return value.logs
|
if (value) return value.logs
|
||||||
}
|
}
|
||||||
return '< no logs found >'
|
return '< no logs found >'
|
||||||
}
|
}
|
||||||
|
|
||||||
async installVersion(name, active, version) {
|
async installVersion(name, active, version) {
|
||||||
if (fs.existsSync(this._util.getPathFromRoot(`./${name}/` + version.name))) {
|
if (fs.existsSync(this.util.getPathFromRoot(`./${name}/` + version.name))) {
|
||||||
await this._util.runCommand('rmdir', ['/S', '/Q', `"${this._util.getPathFromRoot(`./${name}/` + version.name)}"`])
|
await this.util.runCommand('rmdir', ['/S', '/Q', `"${this.util.getPathFromRoot(`./${name}/` + version.name)}"`])
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await fsp.mkdir(this._util.getPathFromRoot(`./${name}/` + version.name))
|
await fsp.mkdir(this.util.getPathFromRoot(`./${name}/` + version.name))
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
if (err.code !== 'EEXIST') {
|
if (err.code !== 'EEXIST') {
|
||||||
throw err
|
throw err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// await fsp.mkdir(this._util.getPathFromRoot(`./${name}/` + version.name + '/node_modules'))
|
// await fsp.mkdir(this.util.getPathFromRoot(`./${name}/` + version.name + '/node_modules'))
|
||||||
this.logActive(name, active, `[Core] Downloading ${version.name} (${version.url}) to ${version.name + '/' + version.name + '.zip'}\n`)
|
this.logActive(name, active, `[Core] Downloading ${version.name} (${version.url}) to ${version.name + '/' + version.name + '.zip'}\n`)
|
||||||
let filePath = this._util.getPathFromRoot(`./${name}/` + version.name + '/' + version.name + '.zip')
|
let filePath = this.util.getPathFromRoot(`./${name}/` + version.name + '/' + version.name + '.zip')
|
||||||
await request(version.url, filePath)
|
await request(version.url, filePath)
|
||||||
this.logActive(name, active, `[Core] Downloading finished, starting extraction\n`)
|
this.logActive(name, active, `[Core] Downloading finished, starting extraction\n`)
|
||||||
await this._util.runCommand(
|
await this.util.runCommand(
|
||||||
'"C:\\Program Files\\7-Zip\\7z.exe"',
|
'"C:\\Program Files\\7-Zip\\7z.exe"',
|
||||||
['x', `"${filePath}"`],
|
['x', `"${filePath}"`],
|
||||||
this._util.getPathFromRoot(`./${name}/` + version.name + '/'),
|
this.util.getPathFromRoot(`./${name}/` + version.name + '/'),
|
||||||
this.logActive.bind(this, name, active)
|
this.logActive.bind(this, name, active)
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!fs.existsSync(this._util.getPathFromRoot(`./${name}/` + version.name + '/index.mjs'))) {
|
if (!fs.existsSync(this.util.getPathFromRoot(`./${name}/` + version.name + '/index.mjs'))) {
|
||||||
this.logActive(name, active, `\n[Core] ERROR: Missing index.mjs in the folder, exiting\n`)
|
this.logActive(name, active, `\n[Core] ERROR: Missing index.mjs in the folder, exiting\n`)
|
||||||
throw new Error(`Missing index.mjs in ${this._util.getPathFromRoot(`./${name}/` + version.name + '/index.mjs')}`)
|
throw new Error(`Missing index.mjs in ${this.util.getPathFromRoot(`./${name}/` + version.name + '/index.mjs')}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logActive(name, active, `\n[Core] Starting npm install\n`)
|
this.logActive(name, active, `\n[Core] Starting npm install\n`)
|
||||||
|
|
||||||
await this._util.runCommand(
|
await this.util.runCommand(
|
||||||
'npm.cmd',
|
'npm.cmd',
|
||||||
['install', '--production', '--no-optional', '--no-package-lock', '--no-audit'],
|
['install', '--production', '--no-optional', '--no-package-lock', '--no-audit'],
|
||||||
this._util.getPathFromRoot(`./${name}/` + version.name + '/'),
|
this.util.getPathFromRoot(`./${name}/` + version.name + '/'),
|
||||||
this.logActive.bind(this, name, active)
|
this.logActive.bind(this, name, active)
|
||||||
)
|
)
|
||||||
|
|
||||||
await this._db.set(`core.${name}LatestInstalled`, version.name)
|
await this.db.set(`core.${name}LatestInstalled`, version.name)
|
||||||
.write()
|
.write()
|
||||||
this.emit('dbupdated', {})
|
this.emit('dbupdated', {})
|
||||||
|
|
||||||
|
@ -160,35 +164,67 @@ export default class Core extends EventEmitter{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async startProgram(name) {
|
async startModule(module, port) {
|
||||||
|
let out = await module.start(this.config, this.db, this.log, this, this.http, port)
|
||||||
|
if (out && out.then) {
|
||||||
|
await out
|
||||||
|
}
|
||||||
|
if (!this.http.getCurrentServer()) {
|
||||||
|
this.log.warn('Module did not call http.createServer')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async tryStartProgram(name) {
|
||||||
let active = this.getActive(name)
|
let active = this.getActive(name)
|
||||||
|
|
||||||
if ((name === 'app' && this._appRunning)
|
if ((name === 'app' && this.appRunning)
|
||||||
|| (name === 'manage' && this._manageRunning)
|
|| (name === 'manage' && this.manageRunning)
|
||||||
|| active.starting) {
|
|| active.starting) {
|
||||||
this._log.event.warn('Attempting to start ' + name + ' which is already running')
|
this.log.event.warn('Attempting to start ' + name + ' which is already running')
|
||||||
this._log.warn('Attempting to start ' + name + ' which is already running')
|
this.log.warn('Attempting to start ' + name + ' which is already running')
|
||||||
this.logActive(name, active, `[${name}] Attempting to start it but it is already running\n`, true)
|
this.logActive(name, active, `[${name}] Attempting to start it but it is already running\n`, true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
active.starting = true
|
active.starting = true
|
||||||
|
|
||||||
let core = this._db.get('core').value()
|
let history = this.db.get(`core_${name}History`)
|
||||||
let version = core[name + 'LatestInstalled']
|
.filter('installed')
|
||||||
if (await this.tryStartProgram(name, active, version)) return
|
.orderBy('installed', 'desc')
|
||||||
version = core[name + 'LastActive']
|
.value()
|
||||||
if (await this.tryStartProgram(name, active,version)) return
|
|
||||||
|
|
||||||
this._log.error('Unable to start ' + name)
|
for (let i = 0; i < history.length; i++) {
|
||||||
this._log.event.error('Unable to start ' + name)
|
if (history[i].stable < 0) continue
|
||||||
|
|
||||||
|
await this.db.set(`core.${name}Active`, history[i].name)
|
||||||
|
.write()
|
||||||
|
this.emit('dbupdated', {})
|
||||||
|
|
||||||
|
let running = await this.tryStartProgramVersion(name, active, history[i].name)
|
||||||
|
if (running) {
|
||||||
|
history[i].stable = 1
|
||||||
|
} else {
|
||||||
|
history[i].stable = -1
|
||||||
|
await this.db.set(`core.${name}Active`, null)
|
||||||
|
.write()
|
||||||
|
this.emit('dbupdated', {})
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.db.get(`core_${name}History`).updateById(history[i].id, history[i].stable).write()
|
||||||
|
if (history[i].stable > 0) break
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.db.get(`core.${name}Active`).value()) {
|
||||||
|
this.log.error('Unable to start ' + name)
|
||||||
|
this.log.event.error('Unable to start ' + name)
|
||||||
|
}
|
||||||
|
|
||||||
active.starting = false
|
active.starting = false
|
||||||
}
|
}
|
||||||
|
|
||||||
async tryStartProgram(name, active, version) {
|
async tryStartProgramVersion(name, active, version) {
|
||||||
if (!version) return false
|
if (!version) return false
|
||||||
this.logActive(name, active, `[${name}] Attempting to start ${version}\n`)
|
this.logActive(name, active, `[${name}] Attempting to start ${version}\n`)
|
||||||
let indexPath = this._util.getUrlFromRoot(`./${name}/` + version + '/index.mjs')
|
let indexPath = this.util.getUrlFromRoot(`./${name}/` + version + '/index.mjs')
|
||||||
let module
|
let module
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -197,49 +233,50 @@ export default class Core extends EventEmitter{
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.logActive(name, active, `[${name}] Error importing module\n`, true)
|
this.logActive(name, active, `[${name}] Error importing module\n`, true)
|
||||||
this.logActive(name, active, `[${name}] ${err.stack}\n`, true)
|
this.logActive(name, active, `[${name}] ${err.stack}\n`, true)
|
||||||
this._log.error(err, `Failed to load ${indexPath}`)
|
this.log.error(err, `Failed to load ${indexPath}`)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
let checkTimeout = null
|
let checkTimeout = null
|
||||||
try {
|
try {
|
||||||
await new Promise((res, rej) => {
|
await new Promise((res, rej) => {
|
||||||
try {
|
|
||||||
let checkTimeout = setTimeout(function() {
|
let checkTimeout = setTimeout(function() {
|
||||||
rej(new Error('Program took longer than 60 seconds to resolve promise'))
|
rej(new Error('Program took longer than 60 seconds to resolve promise'))
|
||||||
}, 60 * 1000)
|
}, 60 * 1000)
|
||||||
|
|
||||||
this.logActive(name, active, `[${name}] Starting module\n`)
|
this.logActive(name, active, `[${name}] Starting module\n`)
|
||||||
let out = module.start(this._config, this._db, this._log, this)
|
|
||||||
if (out.then) {
|
try {
|
||||||
return out.then(res, rej)
|
this.http.setContext(name)
|
||||||
} else {
|
this.startModule(module, name === 'app' ? this.config.port : this.config.managePort)
|
||||||
res()
|
.then(res, rej)
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
rej(err)
|
rej(err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
clearTimeout(checkTimeout)
|
clearTimeout(checkTimeout)
|
||||||
|
await this.http.closeServer(name)
|
||||||
|
|
||||||
this.logActive(name, active, `[${name}] Error starting\n`, true)
|
this.logActive(name, active, `[${name}] Error starting\n`, true)
|
||||||
this.logActive(name, active, `[${name}] ${err.stack}\n`, true)
|
this.logActive(name, active, `[${name}] ${err.stack}\n`, true)
|
||||||
this._log.error(err, `Failed to start ${name}`)
|
this.log.error(err, `Failed to start ${name}`)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
clearTimeout(checkTimeout)
|
clearTimeout(checkTimeout)
|
||||||
|
|
||||||
this.logActive(name, active, `[${name}] Successfully started version ${version}\n`)
|
this.logActive(name, active, `[${name}] Successfully started version ${version}\n`)
|
||||||
await this._db.set(`core.${name}Active`, version)
|
await this.db.set(`core.${name}Active`, version)
|
||||||
.write()
|
.write()
|
||||||
|
|
||||||
let port = name === 'app' ? this._config.port : this._config.managePort
|
let port = name === 'app' ? this.config.port : this.config.managePort
|
||||||
this.logActive(name, active, `[${name}] Checking if listening to port ${port}\n`)
|
this.logActive(name, active, `[${name}] Checking if listening to port ${port}\n`)
|
||||||
|
|
||||||
if (name === 'app') {
|
if (name === 'app') {
|
||||||
this._appRunning = true
|
this.appRunning = true
|
||||||
} else {
|
} else {
|
||||||
this._manageRunning = true
|
this.manageRunning = true
|
||||||
}
|
}
|
||||||
|
this.emit('statusupdated', {})
|
||||||
|
|
||||||
this.logActive(name, active, `[${name}] Module is running successfully\n`)
|
this.logActive(name, active, `[${name}] Module is running successfully\n`)
|
||||||
|
|
||||||
|
@ -247,20 +284,19 @@ export default class Core extends EventEmitter{
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateProgram(name) {
|
async updateProgram(name) {
|
||||||
if (!this._config[name + 'Repository']) {
|
if (!this.config[name + 'Repository']) {
|
||||||
if (name === 'app') {
|
if (name === 'app') {
|
||||||
this._log.error(name + 'Repository was missing from config')
|
this.log.error(name + 'Repository was missing from config')
|
||||||
this._log.event.error(name + 'Repository was missing from config')
|
this.log.event.error(name + 'Repository was missing from config')
|
||||||
} else {
|
} else {
|
||||||
this._log.warn(name + 'Repository was missing from config')
|
this.log.warn(name + 'Repository was missing from config')
|
||||||
this._log.event.warn(name + 'Repository was missing from config')
|
this.log.event.warn(name + 'Repository was missing from config')
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let active = this.getActive(name)
|
let active = this.getActive(name)
|
||||||
active.status = true
|
active.updating = true
|
||||||
active.logs = ''
|
|
||||||
|
|
||||||
this.emit('statusupdated', {})
|
this.emit('statusupdated', {})
|
||||||
this.logActive(name, active, `[Core] Time: ${new Date().toISOString().replace('T', ' ').split('.')[0]}\n`)
|
this.logActive(name, active, `[Core] Time: ${new Date().toISOString().replace('T', ' ').split('.')[0]}\n`)
|
||||||
|
@ -271,9 +307,8 @@ export default class Core extends EventEmitter{
|
||||||
let found = false
|
let found = false
|
||||||
try {
|
try {
|
||||||
version = await this.getLatestVersion(active, name)
|
version = await this.getLatestVersion(active, name)
|
||||||
let core = this._db.get('core').value()
|
let core = this.db.get('core').value()
|
||||||
let fromDb = this._db.get(`core_${name}History`).getById(version.name).value()
|
let fromDb = this.db.get(`core_${name}History`).getById(version.name).value()
|
||||||
console.log(fromDb)
|
|
||||||
if (!fromDb || !fromDb.installed) {
|
if (!fromDb || !fromDb.installed) {
|
||||||
let oldVersion = core[name + 'Current'] || '<none>'
|
let oldVersion = core[name + 'Current'] || '<none>'
|
||||||
this.logActive(name, active, `[Core] Updating from ${oldVersion} to ${version.name}\n`)
|
this.logActive(name, active, `[Core] Updating from ${oldVersion} to ${version.name}\n`)
|
||||||
|
@ -282,17 +317,17 @@ export default class Core extends EventEmitter{
|
||||||
installed = new Date()
|
installed = new Date()
|
||||||
} else {
|
} else {
|
||||||
found = true
|
found = true
|
||||||
this.logActive(name, active, `[Core] Version ${version.name} already installed\n\n[Core] Logs from previous install:\n----------------------------------\n\n${fromDb.logs}\n----------------------------------\n[Core] Old logs finished`)
|
this.logActive(name, active, `[Core] Version ${version.name} already installed\n`)
|
||||||
}
|
}
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
this.logActive(name, active, '\n', true)
|
this.logActive(name, active, '\n', true)
|
||||||
this.logActive(name, active, `[Error] Exception occured while updating ${name}\n`, true)
|
this.logActive(name, active, `[Error] Exception occured while updating ${name}\n`, true)
|
||||||
this.logActive(name, active, err.stack, true)
|
this.logActive(name, active, err.stack, true)
|
||||||
this._log.error(err, 'Error while updating ' + name)
|
this.log.error(err, 'Error while updating ' + name)
|
||||||
}
|
}
|
||||||
active.status = false
|
active.updating = false
|
||||||
if (version && !found) {
|
if (version && !found) {
|
||||||
await this._db.get(`core_${name}History`).upsert({
|
await this.db.get(`core_${name}History`).upsert({
|
||||||
id: version.name,
|
id: version.name,
|
||||||
name: version.name,
|
name: version.name,
|
||||||
filename: version.filename,
|
filename: version.filename,
|
||||||
|
@ -308,8 +343,9 @@ export default class Core extends EventEmitter{
|
||||||
|
|
||||||
async start(name) {
|
async start(name) {
|
||||||
await this.updateProgram(name)
|
await this.updateProgram(name)
|
||||||
if (core[name + 'CurrentVersion']) {
|
var version = this.db.get('core.' + name + 'LatestVersion').value()
|
||||||
await this.startProgram(name)
|
if (version) {
|
||||||
|
await this.tryStartProgram(name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -141,11 +141,9 @@ export default function GetDB(util, log) {
|
||||||
db.defaults({
|
db.defaults({
|
||||||
core: {
|
core: {
|
||||||
"appActive": null, // Current active running
|
"appActive": null, // Current active running
|
||||||
"appLastActive": null, // Last active stable running
|
|
||||||
"appLatestInstalled": null, // Latest installed version
|
"appLatestInstalled": null, // Latest installed version
|
||||||
"appLatestVersion": null, // Newest version available
|
"appLatestVersion": null, // Newest version available
|
||||||
"manageActive": null,
|
"manageActive": null,
|
||||||
"manageLastActive": null,
|
|
||||||
"manageLatestInstalled": null,
|
"manageLatestInstalled": null,
|
||||||
"manageLatestVersion": null
|
"manageLatestVersion": null
|
||||||
},
|
},
|
||||||
|
|
66
core/http.mjs
Normal file
66
core/http.mjs
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
import http from 'http'
|
||||||
|
|
||||||
|
export default class HttpServer {
|
||||||
|
constructor() {
|
||||||
|
this.active = {
|
||||||
|
app: false,
|
||||||
|
manage: false,
|
||||||
|
dev: false,
|
||||||
|
}
|
||||||
|
this.sockets = {
|
||||||
|
app: new Set(),
|
||||||
|
manage: new Set(),
|
||||||
|
dev: new Set(),
|
||||||
|
}
|
||||||
|
this._context = 'dev'
|
||||||
|
}
|
||||||
|
|
||||||
|
setContext(name) {
|
||||||
|
if (name !== 'app' && name !== 'manage' && name !== 'dev') {
|
||||||
|
throw new Error('Cannot call setContext with values other than app or manage')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createServer(opts, listener) {
|
||||||
|
return this._createServer(this._context, opts, listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
_createServer(name, opts, listener) {
|
||||||
|
let server = http.createServer(opts, listener)
|
||||||
|
|
||||||
|
server.on('connection', (socket) => {
|
||||||
|
this.sockets[name].add(socket)
|
||||||
|
|
||||||
|
socket.once('close', () => {
|
||||||
|
this.sockets[name].delete(socket)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
this.active[name] = server
|
||||||
|
return server
|
||||||
|
}
|
||||||
|
|
||||||
|
getServer(name) {
|
||||||
|
return this.active[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
closeServer(name) {
|
||||||
|
if (!this.active[name]) return
|
||||||
|
|
||||||
|
return new Promise((res, rej) => {
|
||||||
|
this.sockets[name].forEach(function(socket) {
|
||||||
|
socket.destroy()
|
||||||
|
})
|
||||||
|
this.sockets[name].clear()
|
||||||
|
|
||||||
|
this.active[name].close(function(err) {
|
||||||
|
if (err) return rej(err)
|
||||||
|
res()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentServer() {
|
||||||
|
return this.active[this._context]
|
||||||
|
}
|
||||||
|
}
|
17
lib.mjs
17
lib.mjs
|
@ -1,7 +1,8 @@
|
||||||
import Util from './core/util.mjs'
|
import Util from './core/util.mjs'
|
||||||
import { readFileSync } from 'fs'
|
import { readFileSync } from 'fs'
|
||||||
import { getLog } from './core/log.mjs'
|
import getLog from './core/log.mjs'
|
||||||
import lowdb from './core/db.mjs'
|
import lowdb from './core/db.mjs'
|
||||||
|
import Core from './core/core.mjs'
|
||||||
|
|
||||||
export default class ServiceCore {
|
export default class ServiceCore {
|
||||||
constructor(name, root_import_meta_url) {
|
constructor(name, root_import_meta_url) {
|
||||||
|
@ -18,14 +19,14 @@ export default class ServiceCore {
|
||||||
|
|
||||||
close(err) {
|
close(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
log.fatal(err, 'App recorded a fatal error')
|
this.log.fatal(err, 'App recorded a fatal error')
|
||||||
process.exit(4)
|
process.exit(4)
|
||||||
}
|
}
|
||||||
log.warn('App asked to be restarted')
|
this.log.warn('App asked to be restarted')
|
||||||
process.exit(0)
|
process.exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
async init() {
|
async init(module = null) {
|
||||||
try {
|
try {
|
||||||
this.config = JSON.parse(readFileSync(this.util.getPathFromRoot('./config.json')))
|
this.config = JSON.parse(readFileSync(this.util.getPathFromRoot('./config.json')))
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -39,9 +40,13 @@ export default class ServiceCore {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.core = new Core(this.util, this.config, this.db, this.log, (err) => this.close(err))
|
this.core = new Core(this.util, this.config, this.db, this.log, (err) => this.close(err))
|
||||||
|
|
||||||
|
if (module) {
|
||||||
|
return this.startModule(module)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async startModule(module) {
|
startModule(module) {
|
||||||
return module.start(config, db, log, core)
|
return this.core.startModule(module, this.config.devPort)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"name": "service-core",
|
"name": "service-core",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "Core boiler plate code to install node server as windows service",
|
"description": "Core boiler plate code to install node server as windows service",
|
||||||
"main": "lib.js",
|
"main": "lib.mjs",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "nodemon --watch dev/api --watch core --watch runner.mjs --watch db.mjs --watch log.mjs runner.mjs | bunyan",
|
"dev": "nodemon --watch dev/api --watch core --watch runner.mjs --watch db.mjs --watch log.mjs runner.mjs | bunyan",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
|
22
runner.mjs
22
runner.mjs
|
@ -35,13 +35,23 @@ const close = function(err) {
|
||||||
|
|
||||||
const util = new Util(import.meta.url)
|
const util = new Util(import.meta.url)
|
||||||
|
|
||||||
lowdb(util, log).then(function(db) {
|
lowdb(util, log).then(async function(db) {
|
||||||
let core = new Core(util, config, db, log, close)
|
let core = new Core(util, config, db, log, close)
|
||||||
|
let errors = 0
|
||||||
if (config.useDev) {
|
try {
|
||||||
return import('./dev/index.mjs').then(function(module) {
|
await core.start('app')
|
||||||
return module.start(config, db, log, core)
|
} catch (err) {
|
||||||
})
|
log.error(err, 'Unable to start app')
|
||||||
|
errors++
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await core.start('manage')
|
||||||
|
} catch (err) {
|
||||||
|
log.error(err, 'Unable to start manage')
|
||||||
|
errors++
|
||||||
|
}
|
||||||
|
if (errors === 2 || (!core.appRunning && !core.manageRunning)) {
|
||||||
|
throw new Error('Neither manage or app were started, exiting.')
|
||||||
}
|
}
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
log.fatal(err, 'Critical error opening database')
|
log.fatal(err, 'Critical error opening database')
|
||||||
|
|
Loading…
Reference in a new issue