Make service-core librariable

master
Jonatan Nilsson 2020-09-08 08:11:42 +00:00
parent a7c5cee7da
commit e0413bbaa6
11 changed files with 117 additions and 66 deletions

View File

@ -4,8 +4,6 @@
"description": "NodeJS Test Service",
"port": 4270,
"managePort": 4269,
"hasManage": true,
"appRepository": "thething/sc-helloworld",
"manageRepository": null,
"useDev": true
"manageRepository": null
}

View File

@ -1,13 +1,13 @@
import fs from 'fs'
import { EventEmitter } from 'events'
import { request } from './client.mjs'
import { getPathFromRoot, getUrlFromRoot, runCommand } from './util.mjs'
const fsp = fs.promises
export default class Core extends EventEmitter{
constructor(config, db, log, closeCb) {
constructor(util, config, db, log, closeCb) {
super()
this._util = util
this._config = config
this._db = db
this._log = log
@ -107,39 +107,39 @@ export default class Core extends EventEmitter{
}
async installVersion(name, active, version) {
if (fs.existsSync(getPathFromRoot(`./${name}/` + version.name))) {
await runCommand('rmdir', ['/S', '/Q', `"${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)}"`])
}
try {
await fsp.mkdir(getPathFromRoot(`./${name}/` + version.name))
await fsp.mkdir(this._util.getPathFromRoot(`./${name}/` + version.name))
} catch(err) {
if (err.code !== 'EEXIST') {
throw err
}
}
// await fsp.mkdir(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`)
let filePath = getPathFromRoot(`./${name}/` + version.name + '/' + version.name + '.zip')
let filePath = this._util.getPathFromRoot(`./${name}/` + version.name + '/' + version.name + '.zip')
await request(version.url, filePath)
this.logActive(name, active, `[Core] Downloading finished, starting extraction\n`)
await runCommand(
await this._util.runCommand(
'"C:\\Program Files\\7-Zip\\7z.exe"',
['x', `"${filePath}"`],
getPathFromRoot(`./${name}/` + version.name + '/'),
this._util.getPathFromRoot(`./${name}/` + version.name + '/'),
this.logActive.bind(this, name, active)
)
if (!fs.existsSync(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`)
throw new Error(`Missing index.mjs in ${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`)
await runCommand(
await this._util.runCommand(
'npm.cmd',
['install', '--production', '--no-optional', '--no-package-lock', '--no-audit'],
getPathFromRoot(`./${name}/` + version.name + '/'),
this._util.getPathFromRoot(`./${name}/` + version.name + '/'),
this.logActive.bind(this, name, active)
)
@ -188,7 +188,7 @@ export default class Core extends EventEmitter{
async tryStartProgram(name, active, version) {
if (!version) return false
this.logActive(name, active, `[${name}] Attempting to start ${version}\n`)
let indexPath = getUrlFromRoot(`./${name}/` + version + '/index.mjs')
let indexPath = this._util.getUrlFromRoot(`./${name}/` + version + '/index.mjs')
let module
try {

View File

@ -131,9 +131,9 @@ const lodashId = {
}
}
const adapter = new FileAsync('db.json')
export default function GetDB(util, log) {
const adapter = new FileAsync(util.getPathFromRoot('./db.json'))
export default function GetDB(log) {
return lowdb(adapter)
.then(function(db) {
db._.mixin(lodashId)

View File

@ -1,6 +1,5 @@
import nodewindows from 'node-windows'
import bunyan from 'bunyan-lite'
import lowdb from './db.mjs'
export default function getLog(name) {
let settings

View File

@ -2,41 +2,47 @@ import path from 'path'
import { spawn } from 'child_process'
import { fileURLToPath, pathToFileURL } from 'url'
export function getPathFromRoot(add) {
const __dirname = path.dirname(fileURLToPath(import.meta.url));
return path.join(__dirname,'../', add)
}
export default class Util {
constructor(root_import_meta_url) {
this._root_import_meta_url = root_import_meta_url
}
export function getUrlFromRoot(add) {
return path.join(import.meta.url,'../../', add)
}
getPathFromRoot(add) {
const __dirname = path.dirname(fileURLToPath(this._root_import_meta_url));
return path.join(__dirname,'./', add)
}
export function runCommand(command, options = [], folder = null, stream = function() {}) {
return new Promise(function(res, rej) {
stream(`[Command] ${folder ? folder : ''}${command} ${options.join(' ')}\n`)
let processor = spawn(command, options, {
shell: true,
cwd: folder,
getUrlFromRoot(add) {
return path.join(this._root_import_meta_url,'../', add)
}
runCommand(command, options = [], folder = null, stream = function() {}) {
return new Promise(function(res, rej) {
stream(`[Command] ${folder ? folder : ''}${command} ${options.join(' ')}\n`)
let processor = spawn(command, options, {
shell: true,
cwd: folder,
})
let timeOuter = setTimeout(function() {
processor.stdin.write('n\n')
}, 250)
processor.stdout.on('data', function(data) {
stream(data.toString())
})
processor.stderr.on('data', function(data) {
stream(data.toString())
})
processor.on('error', function(err) {
clearInterval(timeOuter)
rej(err)
})
processor.on('exit', function (code) {
clearInterval(timeOuter)
if (code !== 0) {
return rej(new Error('Program returned error code: ' + code))
}
res(code)
})
})
let timeOuter = setTimeout(function() {
processor.stdin.write('n\n')
}, 250)
processor.stdout.on('data', function(data) {
stream(data.toString())
})
processor.stderr.on('data', function(data) {
stream(data.toString())
})
processor.on('error', function(err) {
clearInterval(timeOuter)
rej(err)
})
processor.on('exit', function (code) {
clearInterval(timeOuter)
if (code !== 0) {
return rej(new Error('Program returned error code: ' + code))
}
res(code)
})
})
}
}

View File

@ -1 +0,0 @@
@ECHO off

View File

@ -1,5 +0,0 @@
export function start(config, db, log, core) {
return import('./api/server.mjs').then(function(module) {
return module.run(config, db, log, core)
})
}

47
lib.mjs Normal file
View File

@ -0,0 +1,47 @@
import Util from './core/util.mjs'
import { readFileSync } from 'fs'
import { getLog } from './core/log.mjs'
import lowdb from './core/db.mjs'
export default class ServiceCore {
constructor(name, root_import_meta_url) {
if (!root_import_meta_url) {
throw new Error('ServiceCore must be called with the full string from "import.meta.url" from a file residing in the root directory')
}
this._root_import_meta_url = root_import_meta_url
this.util = new Util(this._root_import_meta_url)
this.log = getLog(name)
this.db = null
this.config = null
this.core = null
}
close(err) {
if (err) {
log.fatal(err, 'App recorded a fatal error')
process.exit(4)
}
log.warn('App asked to be restarted')
process.exit(0)
}
async init() {
try {
this.config = JSON.parse(readFileSync(this.util.getPathFromRoot('./config.json')))
} catch (err) {
throw new Error('Unable to read config.json from root directory: ' + err)
}
try {
this.db = await lowdb(this.util, this.log)
} catch (err) {
throw new Error('Unable to read initialise lowdb: ' + err)
}
this.core = new Core(this.util, this.config, this.db, this.log, (err) => this.close(err))
}
async startModule(module) {
return module.start(config, db, log, core)
}
}

View File

@ -2,7 +2,7 @@
"name": "service-core",
"version": "1.0.0",
"description": "Core boiler plate code to install node server as windows service",
"main": "index.js",
"main": "lib.js",
"scripts": {
"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"

View File

@ -1,7 +1,8 @@
import { readFileSync } from 'fs'
import getLog from './log.mjs'
import lowdb from './db.mjs'
import getLog from './core/log.mjs'
import lowdb from './core/db.mjs'
import Core from './core/core.mjs'
import Util from './core/util.mjs'
let config
@ -32,8 +33,10 @@ const close = function(err) {
})
}
lowdb(log).then(function(db) {
let core = new Core(config, db, log, close)
const util = new Util(import.meta.url)
lowdb(util, log).then(function(db) {
let core = new Core(util, config, db, log, close)
if (config.useDev) {
return import('./dev/index.mjs').then(function(module) {

View File

@ -1,8 +1,12 @@
import path from 'path'
import { readFileSync } from 'fs'
import { getPathFromRoot } from '../core/util.mjs'
import nodewindows from 'node-windows'
function getPathFromRoot(add) {
const __dirname = path.dirname(fileURLToPath(import.meta.url));
return path.join(__dirname,'../', add)
}
let config = JSON.parse(readFileSync(getPathFromRoot('./config.json')))
const Service = nodewindows.Service