Much dev later
This commit is contained in:
parent
0b50d50f3b
commit
0c9870717d
6 changed files with 167 additions and 2694 deletions
167
core/core.mjs
167
core/core.mjs
|
@ -1,7 +1,7 @@
|
||||||
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 { getPathFromRoot, runCommand } from './util.mjs'
|
import { getPathFromRoot, getUrlFromRoot, runCommand } from './util.mjs'
|
||||||
|
|
||||||
const fsp = fs.promises
|
const fsp = fs.promises
|
||||||
|
|
||||||
|
@ -16,10 +16,12 @@ export default class Core extends EventEmitter{
|
||||||
this._manageRunning = false
|
this._manageRunning = false
|
||||||
this._appUpdating = {
|
this._appUpdating = {
|
||||||
status: false,
|
status: false,
|
||||||
|
starting: false,
|
||||||
logs: '',
|
logs: '',
|
||||||
}
|
}
|
||||||
this._manageUpdating = {
|
this._manageUpdating = {
|
||||||
status: false,
|
status: false,
|
||||||
|
starting: false,
|
||||||
logs: '',
|
logs: '',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,7 +41,7 @@ export default class Core extends EventEmitter{
|
||||||
|
|
||||||
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, `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`)
|
||||||
|
@ -56,7 +58,7 @@ export default class Core extends EventEmitter{
|
||||||
let item = items[x]
|
let item = items[x]
|
||||||
for (let i = 0; i < item.assets.length; i++) {
|
for (let i = 0; i < item.assets.length; i++) {
|
||||||
if (item.assets[i].name.endsWith('-sc.zip')) {
|
if (item.assets[i].name.endsWith('-sc.zip')) {
|
||||||
this.logActive(name, active, `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()
|
||||||
|
@ -105,48 +107,39 @@ export default class Core extends EventEmitter{
|
||||||
}
|
}
|
||||||
|
|
||||||
async installVersion(name, active, version) {
|
async installVersion(name, active, version) {
|
||||||
if (fs.existsSync(getPathFromRoot('./app/' + version.name))) {
|
if (fs.existsSync(getPathFromRoot(`./${name}/` + version.name))) {
|
||||||
await runCommand('rmdir', ['/S', '/Q', `"${getPathFromRoot('./app/' + version.name)}"`])
|
await runCommand('rmdir', ['/S', '/Q', `"${getPathFromRoot(`./${name}/` + version.name)}"`])
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await fsp.mkdir(getPathFromRoot('./app/' + version.name))
|
await fsp.mkdir(getPathFromRoot(`./${name}/` + version.name))
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
if (err.code !== 'EEXIST') {
|
if (err.code !== 'EEXIST') {
|
||||||
throw err
|
throw err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// await fsp.mkdir(getPathFromRoot('./app/' + version.name + '/node_modules'))
|
// await fsp.mkdir(getPathFromRoot(`./${name}/` + version.name + '/node_modules'))
|
||||||
this.logActive(name, active, `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 = getPathFromRoot('./app/' + version.name + '/' + version.name + '.zip')
|
let filePath = getPathFromRoot(`./${name}/` + version.name + '/' + version.name + '.zip')
|
||||||
await request(version.url, filePath)
|
await request(version.url, filePath)
|
||||||
this.logActive(name, active, `Downloading finished, starting extraction\n`)
|
this.logActive(name, active, `[Core] Downloading finished, starting extraction\n`)
|
||||||
await runCommand(
|
await runCommand(
|
||||||
'"C:\\Program Files\\7-Zip\\7z.exe"',
|
'"C:\\Program Files\\7-Zip\\7z.exe"',
|
||||||
['x', `"${filePath}"`],
|
['x', `"${filePath}"`],
|
||||||
getPathFromRoot('./app/' + version.name + '/'),
|
getPathFromRoot(`./${name}/` + version.name + '/'),
|
||||||
this.logActive.bind(this, name, active)
|
this.logActive.bind(this, name, active)
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!fs.existsSync(getPathFromRoot('./app/' + version.name + '/index.mjs'))) {
|
if (!fs.existsSync(getPathFromRoot(`./${name}/` + version.name + '/index.mjs'))) {
|
||||||
this.logActive(name, active, `\nERROR: 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 ${getPathFromRoot('./app/' + version.name + '/index.mjs')}`)
|
throw new Error(`Missing index.mjs in ${getPathFromRoot(`./${name}/` + version.name + '/index.mjs')}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logActive(name, active, `\nStarting npm install\n`)
|
this.logActive(name, active, `\n[Core] Starting npm install\n`)
|
||||||
|
|
||||||
await runCommand(
|
await runCommand(
|
||||||
'npm.cmd',
|
'npm.cmd',
|
||||||
['install', '--production', '--no-optional', '--no-package-lock', '--no-audit'],
|
['install', '--production', '--no-optional', '--no-package-lock', '--no-audit'],
|
||||||
getPathFromRoot('./app/' + version.name + '/'),
|
getPathFromRoot(`./${name}/` + version.name + '/'),
|
||||||
this.logActive.bind(this, name, active)
|
|
||||||
)
|
|
||||||
|
|
||||||
this.logActive(name, active, `\nInstalled:\n`)
|
|
||||||
|
|
||||||
await runCommand(
|
|
||||||
'npm.cmd',
|
|
||||||
['list'],
|
|
||||||
getPathFromRoot('./app/' + version.name + '/'),
|
|
||||||
this.logActive.bind(this, name, active)
|
this.logActive.bind(this, name, active)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -154,10 +147,103 @@ export default class Core extends EventEmitter{
|
||||||
.write()
|
.write()
|
||||||
this.emit('dbupdated', {})
|
this.emit('dbupdated', {})
|
||||||
|
|
||||||
this.logActive(name, active, `\nSuccessfully installed ${version.name}\n`)
|
this.logActive(name, active, `\n[Core] Successfully installed ${version.name}\n`)
|
||||||
|
}
|
||||||
|
|
||||||
|
getActive(name) {
|
||||||
|
if (name === 'app') {
|
||||||
|
return this._appUpdating
|
||||||
|
} else if (name === 'manage') {
|
||||||
|
return this._manageUpdating
|
||||||
|
} else {
|
||||||
|
throw new Error('Invalid name: ' + name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async startProgram(name) {
|
async startProgram(name) {
|
||||||
|
let active = this.getActive(name)
|
||||||
|
|
||||||
|
if ((name === 'app' && this._appRunning)
|
||||||
|
|| (name === 'manage' && this._manageRunning)
|
||||||
|
|| active.starting) {
|
||||||
|
this._log.event.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)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
active.starting = true
|
||||||
|
|
||||||
|
let core = this._db.get('core').value()
|
||||||
|
let version = core[name + 'LatestInstalled']
|
||||||
|
if (await this.tryStartProgram(name, active, version)) return
|
||||||
|
version = core[name + 'LastActive']
|
||||||
|
if (await this.tryStartProgram(name, active,version)) return
|
||||||
|
|
||||||
|
this._log.error('Unable to start ' + name)
|
||||||
|
this._log.event.error('Unable to start ' + name)
|
||||||
|
|
||||||
|
active.starting = false
|
||||||
|
}
|
||||||
|
|
||||||
|
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 module
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.logActive(name, active, `[${name}] Loading ${indexPath}\n`)
|
||||||
|
module = await import(indexPath)
|
||||||
|
} catch (err) {
|
||||||
|
this.logActive(name, active, `[${name}] Error importing module\n`, true)
|
||||||
|
this.logActive(name, active, `[${name}] ${err.stack}\n`, true)
|
||||||
|
this._log.error(err, `Failed to load ${indexPath}`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
let checkTimeout = null
|
||||||
|
try {
|
||||||
|
await new Promise((res, rej) => {
|
||||||
|
try {
|
||||||
|
let checkTimeout = setTimeout(function() {
|
||||||
|
rej(new Error('Program took longer than 60 seconds to resolve promise'))
|
||||||
|
}, 60 * 1000)
|
||||||
|
|
||||||
|
this.logActive(name, active, `[${name}] Starting module\n`)
|
||||||
|
let out = module.start(this._config, this._db, this._log, this)
|
||||||
|
if (out.then) {
|
||||||
|
return out.then(res, rej)
|
||||||
|
} else {
|
||||||
|
res()
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
rej(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
clearTimeout(checkTimeout)
|
||||||
|
this.logActive(name, active, `[${name}] Error starting\n`, true)
|
||||||
|
this.logActive(name, active, `[${name}] ${err.stack}\n`, true)
|
||||||
|
this._log.error(err, `Failed to start ${name}`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
clearTimeout(checkTimeout)
|
||||||
|
|
||||||
|
this.logActive(name, active, `[${name}] Successfully started version ${version}\n`)
|
||||||
|
await this._db.set(`core.${name}Active`, version)
|
||||||
|
.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') {
|
||||||
|
this._appRunning = true
|
||||||
|
} else {
|
||||||
|
this._manageRunning = true
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logActive(name, active, `[${name}] Module is running successfully\n`)
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateProgram(name) {
|
async updateProgram(name) {
|
||||||
|
@ -172,35 +258,40 @@ export default class Core extends EventEmitter{
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let active = null
|
let active = this.getActive(name)
|
||||||
if (name === 'app') {
|
|
||||||
active = this._appUpdating
|
|
||||||
} else {
|
|
||||||
active = this._manageUpdating
|
|
||||||
}
|
|
||||||
active.status = true
|
active.status = true
|
||||||
active.logs = ''
|
active.logs = ''
|
||||||
|
|
||||||
this.emit('statusupdated', {})
|
this.emit('statusupdated', {})
|
||||||
this.logActive(name, active, 'Checking for updates...\n')
|
this.logActive(name, active, `[Core] Time: ${new Date().toISOString().replace('T', ' ').split('.')[0]}\n`)
|
||||||
|
this.logActive(name, active, '[Core] Checking for updates...\n')
|
||||||
|
|
||||||
let version = null
|
let version = null
|
||||||
|
let installed = 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()
|
||||||
if (!core[name + 'Current'] || (core[name + 'Current'] !== version.name && core[name + 'CurrentVersion'] !== version)) {
|
let fromDb = this._db.get(`core_${name}History`).getById(version.name).value()
|
||||||
|
console.log(fromDb)
|
||||||
|
if (!fromDb || !fromDb.installed) {
|
||||||
let oldVersion = core[name + 'Current'] || '<none>'
|
let oldVersion = core[name + 'Current'] || '<none>'
|
||||||
this.logActive(name, active, `Updating from ${oldVersion} to ${version.name}\n`)
|
this.logActive(name, active, `[Core] Updating from ${oldVersion} to ${version.name}\n`)
|
||||||
await this.installVersion(name, active, version)
|
await this.installVersion(name, active, version)
|
||||||
|
this.logActive(name, active, `[Core] Finished: ${new Date().toISOString().replace('T', ' ').split('.')[0]}\n`)
|
||||||
|
installed = new Date()
|
||||||
|
} else {
|
||||||
|
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`)
|
||||||
}
|
}
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
this.logActive(name, active, '\n', true)
|
this.logActive(name, active, '\n', true)
|
||||||
this.logActive(name, active, `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.status = false
|
||||||
if (version) {
|
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,
|
||||||
|
@ -208,6 +299,8 @@ export default class Core extends EventEmitter{
|
||||||
url: version.url,
|
url: version.url,
|
||||||
description: version.description,
|
description: version.description,
|
||||||
logs: active.logs,
|
logs: active.logs,
|
||||||
|
stable: 0,
|
||||||
|
installed: installed,
|
||||||
}).write()
|
}).write()
|
||||||
}
|
}
|
||||||
this.emit('statusupdated', {})
|
this.emit('statusupdated', {})
|
||||||
|
|
|
@ -1,14 +1,19 @@
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { spawn } from 'child_process'
|
import { spawn } from 'child_process'
|
||||||
import { fileURLToPath } from 'url'
|
import { fileURLToPath, pathToFileURL } from 'url'
|
||||||
|
|
||||||
export function getPathFromRoot(add) {
|
export function getPathFromRoot(add) {
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
return path.join(__dirname,'../', add)
|
return path.join(__dirname,'../', add)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getUrlFromRoot(add) {
|
||||||
|
return path.join(import.meta.url,'../../', add)
|
||||||
|
}
|
||||||
|
|
||||||
export function runCommand(command, options = [], folder = null, stream = function() {}) {
|
export function runCommand(command, options = [], folder = null, stream = function() {}) {
|
||||||
return new Promise(function(res, rej) {
|
return new Promise(function(res, rej) {
|
||||||
|
stream(`[Command] ${folder ? folder : ''}${command} ${options.join(' ')}\n`)
|
||||||
let processor = spawn(command, options, {
|
let processor = spawn(command, options, {
|
||||||
shell: true,
|
shell: true,
|
||||||
cwd: folder,
|
cwd: folder,
|
||||||
|
@ -18,11 +23,9 @@ export function runCommand(command, options = [], folder = null, stream = functi
|
||||||
}, 250)
|
}, 250)
|
||||||
processor.stdout.on('data', function(data) {
|
processor.stdout.on('data', function(data) {
|
||||||
stream(data.toString())
|
stream(data.toString())
|
||||||
processor.stdin.write('n\n')
|
|
||||||
})
|
})
|
||||||
processor.stderr.on('data', function(data) {
|
processor.stderr.on('data', function(data) {
|
||||||
stream(data.toString())
|
stream(data.toString())
|
||||||
processor.stdin.write('n\n')
|
|
||||||
})
|
})
|
||||||
processor.on('error', function(err) {
|
processor.on('error', function(err) {
|
||||||
clearInterval(timeOuter)
|
clearInterval(timeOuter)
|
||||||
|
@ -30,6 +33,9 @@ export function runCommand(command, options = [], folder = null, stream = functi
|
||||||
})
|
})
|
||||||
processor.on('exit', function (code) {
|
processor.on('exit', function (code) {
|
||||||
clearInterval(timeOuter)
|
clearInterval(timeOuter)
|
||||||
|
if (code !== 0) {
|
||||||
|
return rej(new Error('Program returned error code: ' + code))
|
||||||
|
}
|
||||||
res(code)
|
res(code)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
2
db.mjs
2
db.mjs
|
@ -151,7 +151,7 @@ export default function GetDB(log) {
|
||||||
},
|
},
|
||||||
core_appHistory: [],
|
core_appHistory: [],
|
||||||
core_manageHistory: [],
|
core_manageHistory: [],
|
||||||
version: 1,
|
core_version: 1,
|
||||||
})
|
})
|
||||||
.write()
|
.write()
|
||||||
.then(
|
.then(
|
||||||
|
|
|
@ -61,6 +61,22 @@ export async function update(ctx, data, cb) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Event: 'core.start'
|
||||||
|
*
|
||||||
|
* Start specific software
|
||||||
|
*/
|
||||||
|
export async function start(ctx, data, cb) {
|
||||||
|
if (data.name === 'app') {
|
||||||
|
await ctx.core.startProgram('app')
|
||||||
|
} else if (data.name === 'manage') {
|
||||||
|
await ctx.core.startProgram('manage')
|
||||||
|
} else {
|
||||||
|
ctx.log.warn('Invalid start command for app ' + data.name)
|
||||||
|
ctx.log.event.warn('Invalid start command for app ' + data.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Event: 'core.listencore'
|
* Event: 'core.listencore'
|
||||||
*
|
*
|
||||||
|
|
|
@ -59,6 +59,7 @@ const Updater = Module({
|
||||||
m.redraw()
|
m.redraw()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
socket.emit('core.listencore', {})
|
socket.emit('core.listencore', {})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -96,6 +97,12 @@ const Updater = Module({
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
startSoftware: function() {
|
||||||
|
socket.emit('core.start', {
|
||||||
|
name: this.activeApp,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
view: function() {
|
view: function() {
|
||||||
return m('div#update', [
|
return m('div#update', [
|
||||||
m('div.actions', [
|
m('div.actions', [
|
||||||
|
@ -147,6 +154,10 @@ const Updater = Module({
|
||||||
m('button', {
|
m('button', {
|
||||||
onclick: () => this.startUpdate(),
|
onclick: () => this.startUpdate(),
|
||||||
}, 'Update & Install'),
|
}, 'Update & Install'),
|
||||||
|
m('button', {
|
||||||
|
hidden: this.status[this.activeApp] || !(this.db[this.activeApp + 'LastActive'] || this.db[this.activeApp + 'LatestInstalled']),
|
||||||
|
onclick: () => this.startSoftware(),
|
||||||
|
}, 'Start'),
|
||||||
m('button', {
|
m('button', {
|
||||||
hidden: !this.db[this.activeApp + 'LastActive']
|
hidden: !this.db[this.activeApp + 'LastActive']
|
||||||
|| this.db[this.activeApp + 'LastActive'] === this.db[this.activeApp + 'Active']
|
|| this.db[this.activeApp + 'LastActive'] === this.db[this.activeApp + 'Active']
|
||||||
|
|
2653
dev/public/main.js
2653
dev/public/main.js
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue