Fixed last few issues and bugs
This commit is contained in:
parent
aca7e0d535
commit
8952c93f2c
8 changed files with 101 additions and 20 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -107,6 +107,7 @@ dist
|
||||||
db.json
|
db.json
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
|
||||||
|
daemon
|
||||||
app/*
|
app/*
|
||||||
manage/*
|
manage/*
|
||||||
dev/public/main.js
|
dev/public/main.js
|
||||||
|
|
Binary file not shown.
|
@ -1,10 +1,14 @@
|
||||||
import http from 'http'
|
import http from 'http'
|
||||||
import https from 'https'
|
import https from 'https'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import { URL } from 'url'
|
import url from 'url'
|
||||||
|
|
||||||
export function request(path, filePath = null, redirects = 0) {
|
export function request(path, filePath = null, redirects, returnText = false) {
|
||||||
let parsed = new URL(path)
|
let newRedirects = redirects + 1
|
||||||
|
if (!path || !path.startsWith('http')) {
|
||||||
|
return Promise.reject(new Error('URL was empty or missing http in front'))
|
||||||
|
}
|
||||||
|
let parsed = new url.URL(path)
|
||||||
|
|
||||||
let h
|
let h
|
||||||
if (parsed.protocol === 'https:') {
|
if (parsed.protocol === 'https:') {
|
||||||
|
@ -25,6 +29,7 @@ export function request(path, filePath = null, redirects = 0) {
|
||||||
'User-Agent': 'TheThing/service-core',
|
'User-Agent': 'TheThing/service-core',
|
||||||
Accept: 'application/vnd.github.v3+json'
|
Accept: 'application/vnd.github.v3+json'
|
||||||
},
|
},
|
||||||
|
timeout: returnText ? 5000 : 60000,
|
||||||
hostname: parsed.hostname
|
hostname: parsed.hostname
|
||||||
}, function(res) {
|
}, function(res) {
|
||||||
let output = ''
|
let output = ''
|
||||||
|
@ -38,12 +43,19 @@ export function request(path, filePath = null, redirects = 0) {
|
||||||
}
|
}
|
||||||
res.on('end', function() {
|
res.on('end', function() {
|
||||||
if (res.statusCode >= 300 && res.statusCode < 400) {
|
if (res.statusCode >= 300 && res.statusCode < 400) {
|
||||||
if (redirects > 5) {
|
if (newRedirects > 5) {
|
||||||
return reject(new Error(`Too many redirects (last one was ${res.headers.location})`))
|
return reject(new Error(`Too many redirects (last one was ${res.headers.location})`))
|
||||||
}
|
}
|
||||||
return resolve(request(res.headers.location, filePath, redirects + 1))
|
if (!res.headers.location) {
|
||||||
|
return reject(new Error('Redirect returned no path in location header'))
|
||||||
|
}
|
||||||
|
if (res.headers.location.startsWith('http')) {
|
||||||
|
return resolve(request(res.headers.location, filePath, newRedirects, returnText))
|
||||||
|
} else {
|
||||||
|
return resolve(request(url.resolve(path, res.headers.location), filePath, newRedirects, returnText))
|
||||||
|
}
|
||||||
} else if (res.statusCode >= 400) {
|
} else if (res.statusCode >= 400) {
|
||||||
return reject(new Error(`HTTP Error ${statusCode}: ${output}`))
|
return reject(new Error(`HTTP Error ${res.statusCode}: ${output}`))
|
||||||
}
|
}
|
||||||
resolve({
|
resolve({
|
||||||
statusCode: res.statusCode,
|
statusCode: res.statusCode,
|
||||||
|
@ -54,10 +66,13 @@ export function request(path, filePath = null, redirects = 0) {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
req.on('error', reject)
|
req.on('error', reject)
|
||||||
|
req.on('timeout', function(err) {
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
req.end()
|
req.end()
|
||||||
}).then(function(res) {
|
}).then(function(res) {
|
||||||
if (!filePath) {
|
if (!filePath && !returnText) {
|
||||||
try {
|
try {
|
||||||
res.body = JSON.parse(res.body)
|
res.body = JSON.parse(res.body)
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
|
|
|
@ -8,20 +8,24 @@ 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()
|
||||||
|
process.stdin.resume()
|
||||||
this.http = new HttpServer()
|
this.http = new HttpServer()
|
||||||
this.util = util
|
this.util = util
|
||||||
this.config = config
|
this.config = config
|
||||||
this.db = db
|
this.db = db
|
||||||
this.log = log
|
this.log = log
|
||||||
this._close = closeCb
|
this._close = closeCb
|
||||||
|
this._activeCrashHandler = null
|
||||||
this.appRunning = false
|
this.appRunning = false
|
||||||
this.manageRunning = false
|
this.manageRunning = false
|
||||||
this._appUpdating = {
|
this._appUpdating = {
|
||||||
|
fresh: true,
|
||||||
updating: false,
|
updating: false,
|
||||||
starting: false,
|
starting: false,
|
||||||
logs: '',
|
logs: '',
|
||||||
}
|
}
|
||||||
this._manageUpdating = {
|
this._manageUpdating = {
|
||||||
|
fresh: true,
|
||||||
updating: false,
|
updating: false,
|
||||||
starting: false,
|
starting: false,
|
||||||
logs: '',
|
logs: '',
|
||||||
|
@ -114,6 +118,9 @@ export default class Core extends EventEmitter{
|
||||||
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)}"`])
|
||||||
}
|
}
|
||||||
|
if (!fs.existsSync(this.util.getPathFromRoot(`./${name}/`))) {
|
||||||
|
await fsp.mkdir(this.util.getPathFromRoot(`./${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) {
|
||||||
|
@ -195,7 +202,8 @@ export default class Core extends EventEmitter{
|
||||||
this.logActive(name, active, `[${name}] Finding available version of ${name}\n`)
|
this.logActive(name, active, `[${name}] Finding available version of ${name}\n`)
|
||||||
|
|
||||||
for (let i = 0; i < history.length; i++) {
|
for (let i = 0; i < history.length; i++) {
|
||||||
if (history[i].stable < 0) {
|
if ((history[i].stable === -1 && !active.fresh)
|
||||||
|
|| (history[i].stable < -1)) {
|
||||||
this.logActive(name, active, `[${name}] Skipping version ${history[i].name} due to marked as unstable\n`)
|
this.logActive(name, active, `[${name}] Skipping version ${history[i].name} due to marked as unstable\n`)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -208,11 +216,16 @@ export default class Core extends EventEmitter{
|
||||||
if (running) {
|
if (running) {
|
||||||
history[i].stable = 1
|
history[i].stable = 1
|
||||||
} else {
|
} else {
|
||||||
history[i].stable = -1
|
if (active.fresh || history[i].stable === -1) {
|
||||||
|
history[i].stable = -2
|
||||||
|
} else {
|
||||||
|
history[i].stable = -1
|
||||||
|
}
|
||||||
await this.db.set(`core.${name}Active`, null)
|
await this.db.set(`core.${name}Active`, null)
|
||||||
.write()
|
.write()
|
||||||
this.emit('dbupdated', {})
|
this.emit('dbupdated', {})
|
||||||
}
|
}
|
||||||
|
active.fresh = false
|
||||||
|
|
||||||
await this.db.get(`core_${name}History`).updateById(history[i].id, history[i].stable).write()
|
await this.db.get(`core_${name}History`).updateById(history[i].id, history[i].stable).write()
|
||||||
if (history[i].stable > 0) break
|
if (history[i].stable > 0) break
|
||||||
|
@ -227,6 +240,17 @@ export default class Core extends EventEmitter{
|
||||||
active.starting = false
|
active.starting = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
programCrashed(name, version, active, oldStable) {
|
||||||
|
let newStable = -2
|
||||||
|
console.log('EXITING:', oldStable, active)
|
||||||
|
if (oldStable === 0 && !active.fresh) {
|
||||||
|
newStable = -1
|
||||||
|
}
|
||||||
|
let temp = this.db.get(`core_${name}History`).getById(version).set('stable', newStable )
|
||||||
|
temp.value() // Trigger update on __wrapped__
|
||||||
|
fs.writeFileSync(this.db.adapterFilePath, JSON.stringify(temp.__wrapped__, null, 2))
|
||||||
|
}
|
||||||
|
|
||||||
async tryStartProgramVersion(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`)
|
||||||
|
@ -242,10 +266,15 @@ export default class Core extends EventEmitter{
|
||||||
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
|
||||||
|
let oldStable = this.db.get(`core_${name}History`).getById(version).value().stable
|
||||||
|
this._activeCrashHandler = this.programCrashed.bind(this, name, version, active, oldStable)
|
||||||
|
process.once('exit', this._activeCrashHandler)
|
||||||
try {
|
try {
|
||||||
|
let port = name === 'app' ? this.config.port : this.config.managePort
|
||||||
await new Promise((res, rej) => {
|
await new Promise((res, rej) => {
|
||||||
let checkTimeout = setTimeout(function() {
|
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)
|
||||||
|
|
||||||
|
@ -253,14 +282,20 @@ export default class Core extends EventEmitter{
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.http.setContext(name)
|
this.http.setContext(name)
|
||||||
this.startModule(module, name === 'app' ? this.config.port : this.config.managePort)
|
this.startModule(module, port)
|
||||||
.then(res, rej)
|
.then(res, rej)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
rej(err)
|
rej(err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
clearTimeout(checkTimeout)
|
||||||
|
|
||||||
|
this.logActive(name, active, `[${name}] Testing out module port ${version}\n`)
|
||||||
|
await this.checkProgramRunning(name, active, port)
|
||||||
|
process.off('exit', this._activeCrashHandler)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
clearTimeout(checkTimeout)
|
clearTimeout(checkTimeout)
|
||||||
|
process.off('exit', this._activeCrashHandler)
|
||||||
await this.http.closeServer(name)
|
await this.http.closeServer(name)
|
||||||
|
|
||||||
this.logActive(name, active, `[${name}] Error starting\n`, true)
|
this.logActive(name, active, `[${name}] Error starting\n`, true)
|
||||||
|
@ -268,15 +303,12 @@ export default class Core extends EventEmitter{
|
||||||
this.log.error(err, `Failed to start ${name}`)
|
this.log.error(err, `Failed to start ${name}`)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
clearTimeout(checkTimeout)
|
this._activeCrashHandler = null
|
||||||
|
|
||||||
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
|
|
||||||
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 {
|
||||||
|
@ -289,6 +321,26 @@ export default class Core extends EventEmitter{
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async checkProgramRunning(name, active, port) {
|
||||||
|
let start = new Date()
|
||||||
|
let error = null
|
||||||
|
let success = false
|
||||||
|
|
||||||
|
while (new Date() - start < 10 * 1000) {
|
||||||
|
try {
|
||||||
|
let check = await request(`http://localhost:${port}`, null, 0, true)
|
||||||
|
success = true
|
||||||
|
break
|
||||||
|
} catch(err) {
|
||||||
|
this.logActive(name, active, `[${name}:${port}] ${err.message}, retrying in 3 seconds\n`)
|
||||||
|
error = err
|
||||||
|
await new Promise(function(res) { setTimeout(res, 3000)})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (success) return true
|
||||||
|
throw error || new Error('Checking server failed')
|
||||||
|
}
|
||||||
|
|
||||||
async updateProgram(name) {
|
async updateProgram(name) {
|
||||||
if (!this.config[name + 'Repository']) {
|
if (!this.config[name + 'Repository']) {
|
||||||
if (name === 'app') {
|
if (name === 'app') {
|
||||||
|
@ -302,6 +354,11 @@ export default class Core extends EventEmitter{
|
||||||
}
|
}
|
||||||
|
|
||||||
let active = this.getActive(name)
|
let active = this.getActive(name)
|
||||||
|
let oldLogs = active.logs || ''
|
||||||
|
if (oldLogs) {
|
||||||
|
oldLogs += '\n'
|
||||||
|
}
|
||||||
|
active.logs = ''
|
||||||
active.updating = true
|
active.updating = true
|
||||||
|
|
||||||
this.emit('statusupdated', {})
|
this.emit('statusupdated', {})
|
||||||
|
@ -329,7 +386,7 @@ export default class Core extends EventEmitter{
|
||||||
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('Error while updating ' + name, err)
|
||||||
}
|
}
|
||||||
active.updating = false
|
active.updating = false
|
||||||
if (version && !found) {
|
if (version && !found) {
|
||||||
|
@ -341,9 +398,11 @@ export default class Core extends EventEmitter{
|
||||||
description: version.description,
|
description: version.description,
|
||||||
logs: active.logs,
|
logs: active.logs,
|
||||||
stable: 0,
|
stable: 0,
|
||||||
installed: installed,
|
installed: installed && installed.toISOString(),
|
||||||
}).write()
|
}).write()
|
||||||
}
|
}
|
||||||
|
active.logs = oldLogs + active.logs
|
||||||
|
this.emit(name + 'log', active)
|
||||||
this.emit('statusupdated', {})
|
this.emit('statusupdated', {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -137,6 +137,7 @@ export default function GetDB(util, log) {
|
||||||
return lowdb(adapter)
|
return lowdb(adapter)
|
||||||
.then(function(db) {
|
.then(function(db) {
|
||||||
db._.mixin(lodashId)
|
db._.mixin(lodashId)
|
||||||
|
db.adapterFilePath = util.getPathFromRoot('./db.json')
|
||||||
|
|
||||||
db.defaults({
|
db.defaults({
|
||||||
core: {
|
core: {
|
||||||
|
|
|
@ -19,6 +19,7 @@ export default class HttpServer {
|
||||||
if (name !== 'app' && name !== 'manage' && name !== 'dev') {
|
if (name !== 'app' && name !== 'manage' && name !== 'dev') {
|
||||||
throw new Error('Cannot call setContext with values other than app or manage')
|
throw new Error('Cannot call setContext with values other than app or manage')
|
||||||
}
|
}
|
||||||
|
this._context = name
|
||||||
}
|
}
|
||||||
|
|
||||||
createServer(opts, listener) {
|
createServer(opts, listener) {
|
||||||
|
@ -45,7 +46,7 @@ export default class HttpServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
closeServer(name) {
|
closeServer(name) {
|
||||||
if (!this.active[name]) return
|
if (!this.active[name]) return console.log('no active found with name', name, this.active)
|
||||||
|
|
||||||
return new Promise((res, rej) => {
|
return new Promise((res, rej) => {
|
||||||
this.sockets[name].forEach(function(socket) {
|
this.sockets[name].forEach(function(socket) {
|
||||||
|
@ -55,7 +56,9 @@ export default class HttpServer {
|
||||||
|
|
||||||
this.active[name].close(function(err) {
|
this.active[name].close(function(err) {
|
||||||
if (err) return rej(err)
|
if (err) return rej(err)
|
||||||
res()
|
|
||||||
|
// Waiting 1 second for it to close down
|
||||||
|
setTimeout(res, 1000)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
node service\install.mjs
|
node service\install.mjs
|
||||||
|
PAUSE
|
|
@ -1,5 +1,6 @@
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { readFileSync } from 'fs'
|
import { readFileSync } from 'fs'
|
||||||
|
import { fileURLToPath } from 'url'
|
||||||
import nodewindows from 'node-windows'
|
import nodewindows from 'node-windows'
|
||||||
|
|
||||||
function getPathFromRoot(add) {
|
function getPathFromRoot(add) {
|
||||||
|
|
Loading…
Reference in a new issue