service-core/core/util.mjs

201 lines
6.5 KiB
JavaScript

import os from 'os'
import path from 'path'
import fs from 'fs/promises'
import { spawn, execSync } from 'child_process'
import { fileURLToPath, pathToFileURL } from 'url'
export default class Util {
constructor(root_import_meta_url) {
this._root_import_meta_url = root_import_meta_url
}
getPathFromRoot(add) {
const __dirname = path.dirname(fileURLToPath(this._root_import_meta_url));
return path.join(__dirname,'./', add)
}
getUrlFromRoot(add) {
return path.join(this._root_import_meta_url,'../', add)
}
getExtension(filename) {
let extension = path.extname(filename)
if (filename.indexOf('.tar.') > 0) {
return '.tar' + extension
}
return extension
}
getAppNames(config) {
const validLevels = [
'fatal',
'error',
'warn',
'info',
'debug',
'trace',
]
let out = []
let keys = Object.keys(config)
for (let key of keys) {
if (typeof(config[key]) !== 'object' || config[key] == null)
continue
if (typeof(config[key].port) !== 'number' || !config[key].port)
continue
if (typeof(config[key].provider) !== 'string' || !config[key].provider)
continue
if (config[key].https != null && typeof(config[key].https) !== 'boolean')
continue
if (config[key].updateEvery != null && (typeof(config[key].updateEvery) !== 'number' || config[key].updateEvery < 0))
continue
if (config[key].startWaitUntilFail != null && (typeof(config[key].startWaitUntilFail) !== 'number' || config[key].startWaitUntilFail < 10))
continue
if (config[key].heartbeatTimeout != null && (typeof(config[key].heartbeatTimeout) !== 'number' || config[key].heartbeatTimeout < 10))
continue
if (config[key].heartbeatAttempts != null && (typeof(config[key].heartbeatAttempts) !== 'number' || config[key].heartbeatAttempts < 1))
continue
if (config[key].heartbeatAttemptsWait != null && (typeof(config[key].heartbeatAttemptsWait) !== 'number' || config[key].heartbeatAttemptsWait < 10))
continue
if (config[key].heartbeatPath != null && (typeof(config[key].heartbeatPath) !== 'string' || config[key].heartbeatPath[0] !== '/'))
continue
if (config[key].log != null) {
if (!Array.isArray(config[key].log))
continue
let valid = true
for (let stream of config[key].log) {
if (!stream || typeof(stream) !== 'object' || Array.isArray(stream)) {
valid = false
break
}
if (typeof(stream.level) !== 'string' || !stream.level || !validLevels.includes(stream.level)) {
valid = false
break
}
if ((typeof(stream.path) !== 'string' || !stream.path) && stream.stream !== 'process.stdout') {
valid = false
break
}
}
if (!valid)
continue
}
out.push(key)
}
return out
}
get7zipExecutable() {
const util = new Util(import.meta.url)
if (process.platform === 'win32') {
return util.getPathFromRoot('../bin/7za.exe')
}
return util.getPathFromRoot('../bin/7zas')
}
verifyConfig(config) {
if (!config.name) throw new Error('name is missing in config.json')
if (this.getAppNames(config).length === 0) throw new Error('no application was found in config')
if (config.debugPort != null && (typeof(config.debugPort) !== 'number' || !config.debugPort)) throw new Error('debugPort in config not a valid number')
}
extractFile(file, stream = function() {}) {
return this.runCommand(this.get7zipExecutable(), ['x', file], path.dirname(file), stream)
.then(() => {
if (!file.indexOf('.tar.')) return
let tarFile = file.slice(0, file.lastIndexOf('.'))
return fs.stat(tarFile)
.catch(function() { return null })
.then((stat) => {
if (!stat) return
return this.extractFile(tarFile)
.then(function() { return fs.rm(tarFile, { force: true }) })
})
})
}
runCommand(command, options = [], folder = null, stream = function() {}) {
return new Promise(function(res, rej) {
let fullcommand = path.join(folder ? folder : '', command)
if (command.indexOf('/') >= 0 || command.indexOf('\\') >= 0) {
fullcommand = command
}
stream(`[Command] ${fullcommand} ${options.join(' ')}\n`)
let processor = spawn(command, options, {
shell: true,
cwd: folder,
})
let timeOuter = setInterval(function() {
try {
processor.stdin.write('n\n')
} catch {}
}, 250)
processor.stdout.on('data', function(data) {
stream(data.toString().replace(/\r\n/g, '\n'))
})
processor.stderr.on('data', function(data) {
stream(data.toString().replace(/\r\n/g, '\n'))
})
processor.stdin.on('error', function() {
clearInterval(timeOuter)
})
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)
})
})
}
runCommandBackground(command, options = [], folder = null, stream = function() {}) {
let fullcommand = path.join(folder ? folder : '', command)
if (command.indexOf('/') >= 0 || command.indexOf('\\') >= 0) {
fullcommand = command
}
stream(`[Command] ${fullcommand} ${options.join(' ')}\n`)
let processor = spawn(command, options, {
shell: true,
cwd: folder,
})
let timeOuter = setInterval(function() {
try {
processor.stdin.write('n\n')
} catch {}
}, 250)
processor.stdout.on('data', function(data) {
stream(data.toString().replace(/\r\n/g, '\n'))
})
processor.stderr.on('data', function(data) {
stream(data.toString().replace(/\r\n/g, '\n'))
})
processor.stdin.on('error', function() {
clearInterval(timeOuter)
})
processor.on('error', function(err) {
clearInterval(timeOuter)
})
processor.on('exit', function (code) {
clearInterval(timeOuter)
})
processor._kill = processor.kill
processor.kill = function() {
if(os.platform() === 'win32'){
try {
execSync('taskkill /pid ' + processor.pid + ' /T /F')
} catch {}
}else{
processor.kill();
}
}
return processor
}
}