Major development, finished refactoring for new service-core version 3
This commit is contained in:
parent
f64acc95eb
commit
6a2cfcfb26
19 changed files with 484 additions and 357 deletions
|
@ -1,7 +1,46 @@
|
||||||
import { formatLog } from './loghelper.mjs'
|
import { formatLog } from './loghelper.mjs'
|
||||||
import { safeWrap } from '../util.mjs'
|
import { getStatus, safeWrap } from '../util.mjs'
|
||||||
|
|
||||||
export default function coremonitor(io, config, db, log, core) {
|
export default function coremonitor(io, ctx) {
|
||||||
|
|
||||||
|
ctx.core.applications.forEach(function(app) {
|
||||||
|
app.on('updating', safeWrap(ctx.log, `${app.name}.on('updating')`, function() {
|
||||||
|
io.emit('core.status', getStatus(ctx))
|
||||||
|
}))
|
||||||
|
app.on('running', safeWrap(ctx.log, `${app.name}.on('updating')`, function() {
|
||||||
|
io.emit('core.status', getStatus(ctx))
|
||||||
|
}))
|
||||||
|
|
||||||
|
app.on('updatelog', safeWrap(ctx.log, `${app.name}.on('updatelog')`, function(loglines) {
|
||||||
|
io.to(`app.${app.name}`).emit('app.updatelog', {
|
||||||
|
name: app.name,
|
||||||
|
log: loglines,
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
|
||||||
|
if (app.name !== ctx.app.name) {
|
||||||
|
app.ctx.log.on('newlog', safeWrap(ctx.log, `${app.name}.log.on('newlog')`, function(data) {
|
||||||
|
io.to('logger.' + app.name).emit('newlog', formatLog(data))
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
ctx.core.log.on('newlog', safeWrap(ctx.log, `core.log.on('newlog')`, function(data) {
|
||||||
|
io.to('logger.service-core').emit('newlog', formatLog(data))
|
||||||
|
}))
|
||||||
|
|
||||||
|
ctx.log.on('newlog', safeWrap(ctx.log, 'coremonitor.on.newlog', function(data) {
|
||||||
|
// Stop infinite regression
|
||||||
|
if (data?.err?.signal === 'newlogerror') return
|
||||||
|
try {
|
||||||
|
io.to('logger.' + ctx.app.name).emit('newlog', formatLog(data))
|
||||||
|
} catch (err) {
|
||||||
|
err.signal = 'newlogerror'
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
/*
|
||||||
log.on('newlog', safeWrap(log, 'coremonitor.on.newlog', function(data) {
|
log.on('newlog', safeWrap(log, 'coremonitor.on.newlog', function(data) {
|
||||||
io.to('logger').emit('newlog', formatLog(data))
|
io.to('logger').emit('newlog', formatLog(data))
|
||||||
}))
|
}))
|
||||||
|
@ -22,5 +61,5 @@ export default function coremonitor(io, config, db, log, core) {
|
||||||
name: 'manage',
|
name: 'manage',
|
||||||
logs: manage.logs,
|
logs: manage.logs,
|
||||||
})
|
})
|
||||||
}))
|
}))*/
|
||||||
}
|
}
|
|
@ -1,23 +1,8 @@
|
||||||
import defaults from '../defaults.mjs'
|
import defaults from '../defaults.mjs'
|
||||||
import { formatLog } from './loghelper.mjs'
|
import { formatLog } from './loghelper.mjs'
|
||||||
|
import { getStatus } from '../util.mjs'
|
||||||
|
|
||||||
/*
|
const stopSpam = {}
|
||||||
* Event: 'core.config'
|
|
||||||
*
|
|
||||||
* Get config
|
|
||||||
*/
|
|
||||||
export async function config(ctx, data, cb) {
|
|
||||||
let merge = {
|
|
||||||
applications: []
|
|
||||||
}
|
|
||||||
for (let app of ctx.core.applications) {
|
|
||||||
merge[app.name] = app.config
|
|
||||||
merge.applications.push(app.name)
|
|
||||||
}
|
|
||||||
let out = defaults(ctx.db.config, merge)
|
|
||||||
console.log(out)
|
|
||||||
cb(out)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Event: 'core.restart'
|
* Event: 'core.restart'
|
||||||
|
@ -25,7 +10,12 @@ export async function config(ctx, data, cb) {
|
||||||
* Restart server
|
* Restart server
|
||||||
*/
|
*/
|
||||||
export async function restart(ctx, data, cb) {
|
export async function restart(ctx, data, cb) {
|
||||||
|
if (ctx.db.config.allowRestart) {
|
||||||
ctx.core.restart()
|
ctx.core.restart()
|
||||||
|
} else {
|
||||||
|
ctx.log.fatal('Invalid core restart command')
|
||||||
|
ctx.log.event.error('Invalid core restart command')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -34,7 +24,17 @@ export async function restart(ctx, data, cb) {
|
||||||
* Returns last few log messages from log
|
* Returns last few log messages from log
|
||||||
*/
|
*/
|
||||||
export async function getlastlogs(ctx, data, cb) {
|
export async function getlastlogs(ctx, data, cb) {
|
||||||
cb(ctx.logroot.ringbuffer.records.map(formatLog))
|
if (data.name === 'service-core') {
|
||||||
|
return cb(ctx.core.log.ringbuffer.records.map(formatLog))
|
||||||
|
}
|
||||||
|
let app = ctx.core.applicationMap.get(data.name)
|
||||||
|
if (!app) {
|
||||||
|
ctx.log.warn('Invalid getlastlogs command for app ' + data.name)
|
||||||
|
ctx.log.event.warn('Invalid getlastlogs command for app ' + data.name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cb(app.ctx.log.ringbuffer.records.map(formatLog))
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -42,8 +42,8 @@ export async function getlastlogs(ctx, data, cb) {
|
||||||
*
|
*
|
||||||
* Start listening to new log lines
|
* Start listening to new log lines
|
||||||
*/
|
*/
|
||||||
export async function listenlogs(ctx) {
|
export async function listenlogs(ctx, data) {
|
||||||
ctx.socket.join('logger')
|
ctx.socket.join('logger.' + (data.name || 'service-core'))
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -51,8 +51,8 @@ export async function listenlogs(ctx) {
|
||||||
*
|
*
|
||||||
* Stop listening to new log lines
|
* Stop listening to new log lines
|
||||||
*/
|
*/
|
||||||
export async function unlistenlogs(ctx) {
|
export async function unlistenlogs(ctx, data) {
|
||||||
ctx.socket.leave('logger')
|
ctx.socket.leave('logger.' + (data.name || 'service-core'))
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -61,12 +61,27 @@ export async function unlistenlogs(ctx) {
|
||||||
* Update specific software
|
* Update specific software
|
||||||
*/
|
*/
|
||||||
export async function update(ctx, data, cb) {
|
export async function update(ctx, data, cb) {
|
||||||
if (data.name !== 'app' && data.name !== 'manage') {
|
let app = ctx.core.applicationMap.get(data.name)
|
||||||
|
if (!app) {
|
||||||
ctx.log.warn('Invalid update command for app ' + data.name)
|
ctx.log.warn('Invalid update command for app ' + data.name)
|
||||||
ctx.log.event.warn('Invalid update command for app ' + data.name)
|
ctx.log.event.warn('Invalid update command for app ' + data.name)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
await ctx.core.installLatestVersion(data.name)
|
|
||||||
|
let d = new Date()
|
||||||
|
if (stopSpam[app.name] && d - stopSpam[app.name] < 1000 * 60 * 5) {
|
||||||
|
ctx.log.warn('Update called too fast for app ' + data.name)
|
||||||
|
ctx.log.event.warn('Update called too fast for app ' + data.name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
stopSpam[app.name] = d
|
||||||
|
|
||||||
|
ctx.log.info('Checking for updates on app ' + data.name)
|
||||||
|
app.update().then(function(res) {
|
||||||
|
ctx.log.info(res, 'Update completed on app ' + data.name)
|
||||||
|
}, function(err) {
|
||||||
|
ctx.log.err(err, 'Error checking for updates on app ' + data.name)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -75,71 +90,52 @@ export async function update(ctx, data, cb) {
|
||||||
* Start specific software
|
* Start specific software
|
||||||
*/
|
*/
|
||||||
export async function start(ctx, data, cb) {
|
export async function start(ctx, data, cb) {
|
||||||
if (data.name !== 'app' && data.name !== 'manage') {
|
let app = ctx.core.applicationMap.get(data.name)
|
||||||
|
|
||||||
|
if (!app || (!app.config.scAllowStop && app.running)) {
|
||||||
ctx.log.warn('Invalid start command for app ' + data.name)
|
ctx.log.warn('Invalid start command for app ' + data.name)
|
||||||
ctx.log.event.warn('Invalid start command for app ' + data.name)
|
ctx.log.event.warn('Invalid start command for app ' + data.name)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
await ctx.core.tryStartProgram(data.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
let d = new Date()
|
||||||
* Event: 'core.updatestart'
|
if (app.running && stopSpam[app.name] && d - stopSpam[app.name] < 1000 * 60 * 5) {
|
||||||
*
|
ctx.log.warn('Update called too fast for app ' + data.name)
|
||||||
* Update and start specific software
|
ctx.log.event.warn('Update called too fast for app ' + data.name)
|
||||||
*/
|
|
||||||
export async function updatestart(ctx, data, cb) {
|
|
||||||
if (data.name !== 'app' && data.name !== 'manage') {
|
|
||||||
ctx.log.warn('Invalid updatestart command for app ' + data.name)
|
|
||||||
ctx.log.event.warn('Invalid updatestart command for app ' + data.name)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
await ctx.core.start(data.name)
|
stopSpam[app.name] = d
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
ctx.log.info('Checking for updates on app ' + data.name)
|
||||||
* Event: 'core.listencore'
|
ctx.core.runApplication(app).then(function(res) {
|
||||||
*
|
ctx.log.info('Successfully started ' + data.name + ' running ' + app.running)
|
||||||
* Start listening to new log lines
|
}, function(err) {
|
||||||
*/
|
ctx.log.err(err, 'Error starting app ' + data.name)
|
||||||
export async function listencore(ctx) {
|
})
|
||||||
ctx.socket.join('core')
|
|
||||||
ctx.socket.emit('core.db', ctx.db.data.core)
|
|
||||||
ctx.socket.emit('core.status', {})
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Event: 'core.unlistencore'
|
|
||||||
*
|
|
||||||
* Stop listening to new log lines
|
|
||||||
*/
|
|
||||||
export async function unlistencore(ctx) {
|
|
||||||
ctx.socket.leave('core')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Event: 'core.listentoapp'
|
* Event: 'core.listentoapp'
|
||||||
*
|
*
|
||||||
* Start listening to changes in core app
|
* Start listening to changes in core application name
|
||||||
*/
|
*/
|
||||||
export async function listentoapp(ctx) {
|
export async function listentoapp(ctx, data) {
|
||||||
ctx.socket.join('core.app')
|
if (!data.name) {
|
||||||
ctx.socket.emit('core.program.log', {
|
ctx.log.warn(`listento called with missing name`)
|
||||||
name: 'app',
|
return
|
||||||
logs: ctx.core.getProgramLogs('app')
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
let app = ctx.core.applicationMap.get(data.name)
|
||||||
* Event: 'core.listentomanage'
|
|
||||||
*
|
if (!app) {
|
||||||
* Start listening to changes in core manage
|
ctx.log.warn(`listento called on non-existing app ${data.name}`)
|
||||||
*/
|
return
|
||||||
export async function listentomanage(ctx) {
|
}
|
||||||
ctx.socket.join('core.manage')
|
ctx.socket.join('app.' + data.name)
|
||||||
ctx.socket.emit('core.program.log', {
|
let version = ctx.db.get(ctx.db.data.core[app.name].versions, ctx.db.data.core[app.name].latestInstalled)
|
||||||
name: 'manage',
|
ctx.socket.emit('app.updatelog', {
|
||||||
logs: ctx.core.getProgramLogs('manage')
|
name: data.name,
|
||||||
|
log: version?.log || ctx.db.data.core[app.name].updater
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,15 +144,18 @@ export async function listentomanage(ctx) {
|
||||||
*
|
*
|
||||||
* Stop listening to new log lines
|
* Stop listening to new log lines
|
||||||
*/
|
*/
|
||||||
export async function unlistentoapp(ctx) {
|
export async function unlistentoapp(ctx, data) {
|
||||||
ctx.socket.leave('core.app')
|
if (!data.name) {
|
||||||
|
ctx.log.warn(`unlistento called with missing name`)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
let app = ctx.core.applicationMap.get(data.name)
|
||||||
* Event: 'core.unlistentomanage'
|
|
||||||
*
|
if (!app) {
|
||||||
* Stop listening to new log lines
|
ctx.log.warn(`unlistento called on non-existing app ${data.name}`)
|
||||||
*/
|
return
|
||||||
export async function unlistentomanage(ctx) {
|
}
|
||||||
ctx.socket.leave('core.manage')
|
|
||||||
|
ctx.socket.leave('app.' + data.name)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,8 @@ export default function defaults(options, def) {
|
||||||
if (isObject(item)) return defaults(item)
|
if (isObject(item)) return defaults(item)
|
||||||
return item
|
return item
|
||||||
})
|
})
|
||||||
|
} else if (out[key] instanceof Date) {
|
||||||
|
out[key] = new Date(out[key])
|
||||||
} else if (out[key] && typeof out[key] === 'object') {
|
} else if (out[key] && typeof out[key] === 'object') {
|
||||||
out[key] = defaults(options[key], def && def[key])
|
out[key] = defaults(options[key], def && def[key])
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import * as core from './core/ioroutes.mjs'
|
import * as core from './core/ioroutes.mjs'
|
||||||
|
import { getConfig, getStatus } from './util.mjs'
|
||||||
|
|
||||||
function register(ctx, name, method) {
|
function register(ctx, name, method) {
|
||||||
if (typeof(method) === 'object') {
|
if (typeof(method) === 'object') {
|
||||||
|
@ -46,6 +47,9 @@ function onConnection(server, ctx, data) {
|
||||||
})
|
})
|
||||||
|
|
||||||
register(ioCtx, 'core', core)
|
register(ioCtx, 'core', core)
|
||||||
|
|
||||||
|
ioCtx.socket.emit('core.config', getConfig(ioCtx))
|
||||||
|
ioCtx.socket.emit('core.status', getStatus(ioCtx))
|
||||||
}
|
}
|
||||||
|
|
||||||
export default onConnection
|
export default onConnection
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import socket from 'socket.io-serveronly'
|
import socket from 'socket.io-serveronly'
|
||||||
import nStatic from 'node-static'
|
import nStatic from 'node-static'
|
||||||
// import coremonitor from './core/coremonitor.mjs'
|
import coremonitor from './core/coremonitor.mjs'
|
||||||
|
|
||||||
import onConnection from './routerio.mjs'
|
import onConnection from './routerio.mjs'
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ export function run(http, port, ctx) {
|
||||||
const io = new socket(server)
|
const io = new socket(server)
|
||||||
io.on('connection', onConnection.bind(this, io, ctx))
|
io.on('connection', onConnection.bind(this, io, ctx))
|
||||||
|
|
||||||
// coremonitor(io, config, db, log, core)
|
coremonitor(io, ctx)
|
||||||
|
|
||||||
return server.listenAsync(port)
|
return server.listenAsync(port)
|
||||||
.then(function() {
|
.then(function() {
|
||||||
|
|
40
api/util.mjs
40
api/util.mjs
|
@ -1,3 +1,5 @@
|
||||||
|
import defaults from './defaults.mjs'
|
||||||
|
|
||||||
export function safeWrap(log, name, fn) {
|
export function safeWrap(log, name, fn) {
|
||||||
return function(data, cb) {
|
return function(data, cb) {
|
||||||
try {
|
try {
|
||||||
|
@ -14,3 +16,41 @@ export function safeWrap(log, name, fn) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getConfig(ctx) {
|
||||||
|
let merge = {
|
||||||
|
applications: []
|
||||||
|
}
|
||||||
|
for (let app of ctx.core.applications) {
|
||||||
|
merge[app.name] = app.config
|
||||||
|
merge.applications.push(app.name)
|
||||||
|
}
|
||||||
|
return defaults(ctx.db.config, merge)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getStatus(ctx) {
|
||||||
|
let status = {}
|
||||||
|
for (let app of ctx.core.applications) {
|
||||||
|
if (app.provider.static) {
|
||||||
|
status[app.name] = {
|
||||||
|
active: 'static',
|
||||||
|
latestInstalled: 'static',
|
||||||
|
updated: '',
|
||||||
|
running: app.running,
|
||||||
|
updating: false,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let appDb = ctx.db.data.core[app.name]
|
||||||
|
let active = ctx.db.get(appDb.versions, appDb.active)
|
||||||
|
let installed = ctx.db.get(appDb.versions, appDb.latestInstalled)
|
||||||
|
status[app.name] = {
|
||||||
|
active: active,
|
||||||
|
latestInstalled: installed,
|
||||||
|
updated: appDb.updater,
|
||||||
|
running: app.running,
|
||||||
|
updating: app.updating,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return status
|
||||||
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ m.mount(document.getElementById('header'), Header)
|
||||||
m.route(document.getElementById('content'), '/', {
|
m.route(document.getElementById('content'), '/', {
|
||||||
'/': Status,
|
'/': Status,
|
||||||
'/log': Log,
|
'/log': Log,
|
||||||
|
'/log/:id': Log,
|
||||||
'/updater': Updater,
|
'/updater': Updater,
|
||||||
'/updater/:id': Updater,
|
'/updater/:id': Updater,
|
||||||
})
|
})
|
||||||
|
|
|
@ -3,7 +3,8 @@ const socket = require('../socket')
|
||||||
const Module = require('../module')
|
const Module = require('../module')
|
||||||
|
|
||||||
const Log = Module({
|
const Log = Module({
|
||||||
init: function() {
|
init: function(vnode) {
|
||||||
|
this.activeAppName = 'service-core'
|
||||||
this.connected = socket.connected
|
this.connected = socket.connected
|
||||||
this.loglines = []
|
this.loglines = []
|
||||||
this.logUpdated = false
|
this.logUpdated = false
|
||||||
|
@ -14,21 +15,39 @@ const Log = Module({
|
||||||
m.redraw()
|
m.redraw()
|
||||||
})
|
})
|
||||||
|
|
||||||
this._socketOn(() => this.loadData())
|
this.updateActiveApp(vnode.attrs.id)
|
||||||
|
this._socketOn(() => this.socketOpen())
|
||||||
|
},
|
||||||
|
|
||||||
|
onupdate: function(vnode) {
|
||||||
|
this.updateActiveApp(vnode.attrs.id)
|
||||||
},
|
},
|
||||||
|
|
||||||
remove: function() {
|
remove: function() {
|
||||||
socket.emit('core.unlistenlogs', {})
|
socket.emit('core.unlistenlogs', { name: this.activeAppName })
|
||||||
},
|
},
|
||||||
|
|
||||||
loadData: function() {
|
updateActiveApp(name) {
|
||||||
|
if (!name) {
|
||||||
|
name = 'service-core'
|
||||||
|
}
|
||||||
|
if (this.activeAppName === name) return
|
||||||
|
if (this.activeAppName !== name) {
|
||||||
|
socket.emit('core.unlistenlogs', { name: this.activeAppName })
|
||||||
|
}
|
||||||
|
this.activeAppName = name
|
||||||
|
this.socketOpen()
|
||||||
|
},
|
||||||
|
|
||||||
|
socketOpen: function() {
|
||||||
this.loglines = []
|
this.loglines = []
|
||||||
socket.emit('core.listenlogs', {})
|
socket.emit('core.listenlogs', { name: this.activeAppName })
|
||||||
socket.emit('core.getlastlogs', {}, (res) => {
|
socket.emit('core.getlastlogs', { name: this.activeAppName }, (res) => {
|
||||||
this.loglines = res.map(this.formatLine)
|
this.loglines = res.map(this.formatLine)
|
||||||
this.logUpdated = true
|
this.logUpdated = true
|
||||||
m.redraw()
|
m.redraw()
|
||||||
})
|
})
|
||||||
|
m.redraw()
|
||||||
},
|
},
|
||||||
|
|
||||||
formatLine: function(line) {
|
formatLine: function(line) {
|
||||||
|
@ -45,7 +64,18 @@ const Log = Module({
|
||||||
|
|
||||||
view: function() {
|
view: function() {
|
||||||
return [
|
return [
|
||||||
m('h1.header', 'Log'),
|
m('div.actions', [
|
||||||
|
m(m.route.Link, {
|
||||||
|
class: 'button ' + (this.activeAppName === 'service-core' ? 'active' : 'inactive'),
|
||||||
|
href: '/log',
|
||||||
|
}, 'service-core'),
|
||||||
|
this.core.apps.map((app) => {
|
||||||
|
return m(m.route.Link, {
|
||||||
|
class: 'button ' + (this.activeAppName === app.name ? 'active' : 'inactive'),
|
||||||
|
href: '/log/' + app.name,
|
||||||
|
}, app.name)
|
||||||
|
}),
|
||||||
|
]),
|
||||||
m('div#logger', {
|
m('div#logger', {
|
||||||
onupdate: (vnode) => {
|
onupdate: (vnode) => {
|
||||||
if (this.logUpdated) {
|
if (this.logUpdated) {
|
||||||
|
|
|
@ -1,12 +1,44 @@
|
||||||
|
const m = require('mithril')
|
||||||
const defaults = require('./defaults')
|
const defaults = require('./defaults')
|
||||||
const socket = require('./socket')
|
const socket = require('./socket')
|
||||||
|
|
||||||
|
const core = {
|
||||||
|
name: '...loading...',
|
||||||
|
title: '',
|
||||||
|
apps: [],
|
||||||
|
db: {},
|
||||||
|
status: {},
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.on('core.config', function(res) {
|
||||||
|
core.apps = []
|
||||||
|
let keys = Object.keys(res)
|
||||||
|
for (let key of keys) {
|
||||||
|
if (typeof(res[key]) !== 'object') {
|
||||||
|
core[key] = res[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let appName of res.applications) {
|
||||||
|
core.apps.push({
|
||||||
|
name: appName,
|
||||||
|
config: res[appName],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
m.redraw()
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.on('core.status', function(res) {
|
||||||
|
core.status = res
|
||||||
|
m.redraw()
|
||||||
|
})
|
||||||
|
|
||||||
module.exports = function Module(module) {
|
module.exports = function Module(module) {
|
||||||
return defaults(module, {
|
return defaults(module, {
|
||||||
init: function() {},
|
init: function() {},
|
||||||
|
|
||||||
oninit: function(vnode) {
|
oninit: function(vnode) {
|
||||||
this._listeners = []
|
this._listeners = []
|
||||||
|
this.core = core
|
||||||
this.init(vnode)
|
this.init(vnode)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -21,6 +53,11 @@ module.exports = function Module(module) {
|
||||||
},
|
},
|
||||||
|
|
||||||
on: function(name, cb) {
|
on: function(name, cb) {
|
||||||
|
for (let i = 0; i < this._listeners.length; i++) {
|
||||||
|
if (this._listeners[i][0] === name) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
this._listeners.push([name, cb])
|
this._listeners.push([name, cb])
|
||||||
socket.on(name, cb)
|
socket.on(name, cb)
|
||||||
},
|
},
|
||||||
|
@ -31,7 +68,7 @@ module.exports = function Module(module) {
|
||||||
this.remove()
|
this.remove()
|
||||||
if (!this._listeners) return
|
if (!this._listeners) return
|
||||||
for (let i = 0; i < this._listeners.length; i++) {
|
for (let i = 0; i < this._listeners.length; i++) {
|
||||||
socket.removeListener(this._listeners[0], this._listeners[1])
|
socket.off(this._listeners[i][0], this._listeners[i][1])
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,121 +1,67 @@
|
||||||
const m = require('mithril')
|
const m = require('mithril')
|
||||||
const socket = require('../socket')
|
const socket = require('../socket')
|
||||||
const Module = require('../module')
|
const Module = require('../module')
|
||||||
|
const util = require('../util')
|
||||||
|
|
||||||
const Status = Module({
|
const Status = Module({
|
||||||
init: function() {
|
init: function() {
|
||||||
this._name = '...loading...'
|
|
||||||
this._apps = []
|
|
||||||
|
|
||||||
this._socketOn(() => this.loadData())
|
|
||||||
},
|
|
||||||
|
|
||||||
loadData: function() {
|
|
||||||
socket.emit('core.config', {}, (res) => {
|
|
||||||
this._apps = []
|
|
||||||
console.log('config', res)
|
|
||||||
this._name = res.name
|
|
||||||
if (res.title) {
|
|
||||||
this._name += ' - ' + res.title
|
|
||||||
}
|
|
||||||
for (let appName of res.applications) {
|
|
||||||
this._apps.push({
|
|
||||||
name: appName,
|
|
||||||
config: res[appName],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
console.log(this._apps)
|
|
||||||
m.redraw()
|
|
||||||
})
|
|
||||||
|
|
||||||
this.on('core.db', (res) => {
|
|
||||||
console.log('db', res)
|
|
||||||
|
|
||||||
m.redraw()
|
|
||||||
})
|
|
||||||
|
|
||||||
this.on('core.status', (res) => {
|
|
||||||
console.log(res)
|
|
||||||
/*this._management.running = res.manage
|
|
||||||
this._management.updating = res.manageUpdating
|
|
||||||
this._management.starting = res.manageStarting
|
|
||||||
this._app.running = res.app
|
|
||||||
this._app.updating = res.appUpdating
|
|
||||||
this._app.starting = res.appStarting*/
|
|
||||||
|
|
||||||
m.redraw()
|
|
||||||
})
|
|
||||||
|
|
||||||
socket.emit('core.listencore', {})
|
|
||||||
},
|
},
|
||||||
|
|
||||||
remove: function() {
|
remove: function() {
|
||||||
socket.emit('core.unlistencore', {})
|
|
||||||
},
|
},
|
||||||
|
|
||||||
restartClicked: function() {
|
restartClicked: function() {
|
||||||
socket.emit('core.restart', {})
|
socket.emit('core.restart', {})
|
||||||
},
|
},
|
||||||
|
|
||||||
start: function(name) {
|
startSoftware: function(name) {
|
||||||
socket.emit('core.updatestart', {
|
socket.emit('core.start', {
|
||||||
name: name,
|
name: name,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
getStatus: function(active) {
|
|
||||||
if (active.updating) {
|
|
||||||
return '< Updating >'
|
|
||||||
} else {
|
|
||||||
return '< Starting >'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
view: function() {
|
view: function() {
|
||||||
let loopOver = [
|
let name = this.core.name
|
||||||
['Management service', '_management'],
|
if (this.core.title) {
|
||||||
['Application service', '_app'],
|
name += ' - ' + this.core.title
|
||||||
]
|
}
|
||||||
return m('div#status', [
|
return m('div#status', [
|
||||||
m('h1.header', this._name),
|
m('h1.header', name),
|
||||||
m('div.split', [
|
m('div.split', [
|
||||||
this._apps.map((app) => {
|
this.core.apps.map((app) => {
|
||||||
return m('div.item', [
|
let box = []
|
||||||
m('h4', app.name),
|
let appStatus = this.core.status[app.name] || {}
|
||||||
m('p', app.config.port
|
|
||||||
|
box.push(m('h4', [
|
||||||
|
app.name + ' (',
|
||||||
|
m('span', { class: appStatus.running ? 'running' : 'notrunning' }, appStatus.running ? 'Running ' + appStatus.running : 'Not running'),
|
||||||
|
')'
|
||||||
|
]))
|
||||||
|
box.push(m('p', app.config.port
|
||||||
? `Port: ${app.config.port}`
|
? `Port: ${app.config.port}`
|
||||||
: ''),
|
: ''))
|
||||||
m('p', app.config.repository
|
box.push(m('p', util.getRepoMessage(app)))
|
||||||
? `${app.config.repository}`
|
|
||||||
: '< no repository >'),
|
box.push(m('p', 'Running version: ' + util.getVersionSummary(appStatus.active, appStatus)))
|
||||||
m('p', app.config.active
|
box.push(m('p', 'Latest installed: ' + util.getVersionSummary(appStatus.latestInstalled)))
|
||||||
? `Running version: ${app.config.active}`
|
|
||||||
: '< no running version >'),
|
if (!this.core.status[app.name].running) {
|
||||||
m('p', app.config.latestInstalled
|
box.push(m('button', {
|
||||||
? `Latest installed: ${app.config.latestInstalled}`
|
onclick: () => this.startSoftware(app.name),
|
||||||
: '< no version installed >'),
|
}, 'Start'))
|
||||||
m('p', app.config.latestVersion
|
} else if (app.config.scAllowStop) {
|
||||||
? `Latest version: ${app.config.latestVersion}`
|
box.push(m('button.fatal', {
|
||||||
: '< no version found >'),
|
onclick: () => this.startSoftware(app.name),
|
||||||
app.config.running !== null && app.config.repository
|
}, 'Restart'))
|
||||||
? m('p',
|
}
|
||||||
{ class: app.config.running ? 'running' : 'notrunning' },
|
|
||||||
app.config.running ? 'Running' : 'Not Running'
|
return m('div.item', box)
|
||||||
)
|
|
||||||
: null,
|
|
||||||
!app.config.running && (app.config.updating || app.config.starting)
|
|
||||||
? m('div.status', this.getStatus(app.config))
|
|
||||||
: null,
|
|
||||||
m('button', {
|
|
||||||
hidden: app.config.running || app.config.updating || app.config.starting || !app.config.repository,
|
|
||||||
onclick: () => this.start(app.config.name),
|
|
||||||
}, 'Update/Start')
|
|
||||||
])
|
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
m('button', {
|
m('button.fatal', {
|
||||||
|
hidden: !this.core.allowRestart,
|
||||||
onclick: () => this.restartClicked(),
|
onclick: () => this.restartClicked(),
|
||||||
}, 'Restart service')
|
}, 'Restart ' + this.core.name)
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,142 +1,123 @@
|
||||||
const m = require('mithril')
|
const m = require('mithril')
|
||||||
const socket = require('../socket')
|
const socket = require('../socket')
|
||||||
const Module = require('../module')
|
const Module = require('../module')
|
||||||
|
const util = require('../util')
|
||||||
|
|
||||||
const Updater = Module({
|
const Updater = Module({
|
||||||
init: function(vnode) {
|
init: function(vnode) {
|
||||||
this.activeApp = vnode.attrs.id || null
|
this.activeApp = null
|
||||||
this.appRepository = null
|
this.activeAppName = null
|
||||||
this.manageRepository = null
|
|
||||||
this.db = null
|
|
||||||
this.app = {}
|
|
||||||
this.status = {}
|
|
||||||
this.logUpdated = false
|
this.logUpdated = false
|
||||||
this._socketOn(() => this.socketOpen())
|
this._socketOn(() => this.socketOpen())
|
||||||
this._active = null
|
|
||||||
|
|
||||||
if (this.activeApp && this.activeApp !== 'app'&& this.activeApp !== 'manage') {
|
this.on('app.updatelog', (res) => {
|
||||||
return m.route('/updater')
|
if (!this.activeApp || this.activeApp.name !== res.name) return
|
||||||
}
|
this.activeApp.log = res.log.replace(/\. /g, '.\n')
|
||||||
},
|
|
||||||
|
|
||||||
onupdate: function(vnode) {
|
|
||||||
if (this.activeApp === vnode.attrs.id) return
|
|
||||||
|
|
||||||
this.activeApp = vnode.attrs.id || null
|
|
||||||
if (this.activeApp && this.activeApp !== 'app'&& this.activeApp !== 'manage') {
|
|
||||||
return m.route.set('/updater')
|
|
||||||
}
|
|
||||||
if (this.activeApp && (this.appRepository || this.manageRepository)) {
|
|
||||||
this.loadAppData()
|
|
||||||
}
|
|
||||||
m.redraw()
|
|
||||||
},
|
|
||||||
|
|
||||||
socketOpen: function() {
|
|
||||||
socket.emit('core.config', {}, (res) => {
|
|
||||||
this.appRepository = res.appRepository
|
|
||||||
this.manageRepository = res.manageRepository
|
|
||||||
if (this.activeApp) {
|
|
||||||
this.loadAppData()
|
|
||||||
}
|
|
||||||
m.redraw()
|
|
||||||
})
|
|
||||||
|
|
||||||
socket.on('core.status', (res) => {
|
|
||||||
this.status = res
|
|
||||||
m.redraw()
|
|
||||||
})
|
|
||||||
|
|
||||||
this.on('core.db', (res) => {
|
|
||||||
this.db = res
|
|
||||||
this.updateActiveDb()
|
|
||||||
m.redraw()
|
|
||||||
})
|
|
||||||
|
|
||||||
this.on('core.program.log', (res) => {
|
|
||||||
this.app.logs = res.logs
|
|
||||||
this.logUpdated = true
|
this.logUpdated = true
|
||||||
m.redraw()
|
m.redraw()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (this.core.apps.length && vnode.attrs.id) {
|
||||||
socket.emit('core.listencore', {})
|
this.updateActiveApp(vnode.attrs.id)
|
||||||
},
|
} else if (this.core.apps.length) {
|
||||||
|
let apps = this.core.apps.filter(function(app) {
|
||||||
updateActiveDb() {
|
return Boolean(app.config.url)
|
||||||
if (this.db && this.activeApp) {
|
})
|
||||||
this.app = {
|
if (apps.length === 1) {
|
||||||
repository: this[this.activeApp + 'Repository'],
|
return m.route.set('/updater/' + apps[0].name)
|
||||||
active: this.db[this.activeApp + 'Active'],
|
|
||||||
latestInstalled: this.db[this.activeApp + 'LatestInstalled'],
|
|
||||||
latestVersion: this.db[this.activeApp + 'LatestVersion'],
|
|
||||||
logs: '',
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
this.app = {}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
loadAppData() {
|
onupdate: function(vnode) {
|
||||||
this.updateActiveDb()
|
if (this.core.apps.length) {
|
||||||
if (this.activeApp === 'app') {
|
if (!vnode.attrs.id) {
|
||||||
socket.emit('core.unlistentomanage', {})
|
let apps = this.core.apps.filter(function(app) {
|
||||||
socket.emit('core.listentoapp', {})
|
return Boolean(app.config.url)
|
||||||
} else {
|
})
|
||||||
socket.emit('core.unlistentoapp', {})
|
if (apps.length === 1) {
|
||||||
socket.emit('core.listentomanage', {})
|
return m.route.set('/updater/' + apps[0].name)
|
||||||
}
|
}
|
||||||
/* request to listen to app updates */
|
}
|
||||||
|
this.updateActiveApp(vnode.attrs.id)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
socketOpen: function() {
|
||||||
|
if (this.activeApp) {
|
||||||
|
socket.emit('core.listentoapp', { name: this.activeApp.name })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
updateActiveApp(orgName) {
|
||||||
|
let name = orgName || null
|
||||||
|
if (this.activeAppName === name && (!name || this.activeApp)) return
|
||||||
|
if (this.activeAppName !== name && this.activeAppName) {
|
||||||
|
socket.emit('core.unlistentoapp', { name: this.activeAppName })
|
||||||
|
}
|
||||||
|
|
||||||
|
this.activeAppName = name
|
||||||
|
this.activeApp = null
|
||||||
|
|
||||||
|
if (!name) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let app of this.core.apps) {
|
||||||
|
if (app.name === this.activeAppName) {
|
||||||
|
this.activeApp = app
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!this.activeApp) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
socket.emit('core.listentoapp', { name: this.activeApp.name })
|
||||||
},
|
},
|
||||||
|
|
||||||
remove: function() {
|
remove: function() {
|
||||||
socket.emit('core.unlistencore', {})
|
if (this.activeApp) {
|
||||||
socket.emit('core.unlistentoapp', {})
|
socket.emit('core.unlistentoapp', { name: this.activeApp.name })
|
||||||
socket.emit('core.unlistentomanage', {})
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
startUpdate: function() {
|
startUpdate: function() {
|
||||||
socket.emit('core.update', {
|
socket.emit('core.update', {
|
||||||
name: this.activeApp,
|
name: this.activeApp.name,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
startSoftware: function() {
|
startSoftware: function() {
|
||||||
socket.emit('core.start', {
|
socket.emit('core.start', {
|
||||||
name: this.activeApp,
|
name: this.activeApp.name,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
view: function() {
|
view: function() {
|
||||||
|
let apps = this.core.apps.filter(function(app) {
|
||||||
|
return Boolean(app.config.url)
|
||||||
|
})
|
||||||
return m('div#update', [
|
return m('div#update', [
|
||||||
m('div.actions', [
|
m('div.actions', [
|
||||||
m('h1.header', 'Updater'),
|
apps.map((app) => {
|
||||||
m('div.filler'),
|
return m(m.route.Link, {
|
||||||
m(m.route.Link, {
|
class: 'button ' + (this.activeAppName === app.name ? 'active' : 'inactive'),
|
||||||
hidden: !this.appRepository,
|
href: '/updater/' + app.name,
|
||||||
class: 'button ' + (this.activeApp === 'app' ? 'active' : 'inactive'),
|
}, app.name)
|
||||||
href: '/updater/app',
|
}),
|
||||||
}, 'Update App'),
|
|
||||||
m(m.route.Link, {
|
|
||||||
hidden: !this.manageRepository,
|
|
||||||
class: 'button ' + (this.activeApp === 'manage' ? 'active' : 'inactive'),
|
|
||||||
href: '/updater/manage',
|
|
||||||
}, 'Update Manager'),
|
|
||||||
]),
|
]),
|
||||||
this.activeApp && this.app ? [
|
this.activeApp ? (() => {
|
||||||
|
let appStatus = this.core.status[this.activeApp.name] || {}
|
||||||
|
console.log(appStatus)
|
||||||
|
|
||||||
|
return [
|
||||||
m('div.info', [
|
m('div.info', [
|
||||||
m('p', this.app.repository
|
m('p', util.getRepoMessage(this.activeApp)),
|
||||||
? `Repository: ${this.app.repository}`
|
m('p', 'Active version: ' + util.getVersionSummary(appStatus.active, appStatus)),
|
||||||
: '< no repository >'),
|
m('p', appStatus.updating
|
||||||
m('p', this.app.latestInstalled
|
? 'Updating...'
|
||||||
? `Latest installed: ${this.app.latestInstalled}`
|
: appStatus.running ? 'Running ' + appStatus.running : 'Not running'),
|
||||||
: '< no version installed >'),
|
m('p', 'Latest installed: ' + util.getVersionSummary(appStatus.latestInstalled, appStatus)),
|
||||||
m('p', this.app.active
|
|
||||||
? `Running version: ${this.app.active}`
|
|
||||||
: '< no running version >'),
|
|
||||||
m('p', this.app.latestVersion
|
|
||||||
? `Latest version: ${this.app.latestVersion}`
|
|
||||||
: '< no version found >'),
|
|
||||||
]),
|
]),
|
||||||
m('div.console', {
|
m('div.console', {
|
||||||
onupdate: (vnode) => {
|
onupdate: (vnode) => {
|
||||||
|
@ -146,26 +127,20 @@ const Updater = Module({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
m('pre', this.app.logs && this.app.logs || '')
|
m('pre', this.activeApp.log || '')
|
||||||
),
|
),
|
||||||
this.db
|
m('div.actions', {
|
||||||
? m('div.actions', {
|
hidden: appStatus.updating,
|
||||||
hidden: this.status[this.activeApp + 'Updating'],
|
|
||||||
}, [
|
}, [
|
||||||
m('button', {
|
m('button', {
|
||||||
onclick: () => this.startUpdate(),
|
|
||||||
}, 'Update & Install'),
|
|
||||||
m('button', {
|
|
||||||
hidden: this.status[this.activeApp] || !(this.db[this.activeApp + 'LastActive'] || this.db[this.activeApp + 'LatestInstalled']),
|
|
||||||
onclick: () => this.startSoftware(),
|
onclick: () => this.startSoftware(),
|
||||||
}, 'Start'),
|
}, appStatus.running ? 'Restart' : 'Start'),
|
||||||
m('button', {
|
m('button', {
|
||||||
hidden: !this.db[this.activeApp + 'LastActive']
|
onclick: () => this.startUpdate(),
|
||||||
|| this.db[this.activeApp + 'LastActive'] === this.db[this.activeApp + 'Active']
|
}, 'Check for updates'),
|
||||||
}, 'Use Last Version'),
|
]),
|
||||||
])
|
]
|
||||||
: null,
|
})() : null
|
||||||
] : null
|
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
42
client/util.js
Normal file
42
client/util.js
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
const m = require('mithril')
|
||||||
|
|
||||||
|
const regexRepoName = /([^\/]+\/[^\/]+)\/releases/
|
||||||
|
|
||||||
|
module.exports.getRepoMessage = function(app) {
|
||||||
|
let prefix = app.config.provider[0].toUpperCase() + app.config.provider.slice(1)
|
||||||
|
if (!app.config.url) {
|
||||||
|
return [prefix, null]
|
||||||
|
}
|
||||||
|
let url = app.config.url.replace('/api/v1/repos', '').replace('/releases', '')
|
||||||
|
let name = app.config.url
|
||||||
|
let temp = regexRepoName.exec(app.config.url)
|
||||||
|
if (temp) {
|
||||||
|
name = temp[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
prefix + ': ',
|
||||||
|
m('a', {
|
||||||
|
target: '_blank',
|
||||||
|
href: url,
|
||||||
|
}, name),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.getVersionSummary = function(version, status) {
|
||||||
|
if (!version) {
|
||||||
|
if (status && status.running) {
|
||||||
|
return '< unknown version >'
|
||||||
|
}
|
||||||
|
return '< no version >'
|
||||||
|
}
|
||||||
|
if (typeof(version) === 'string') {
|
||||||
|
return version
|
||||||
|
}
|
||||||
|
return `${version.version} (${
|
||||||
|
version.stable > 0
|
||||||
|
? 'Stable'
|
||||||
|
: version.stable === 0
|
||||||
|
? 'Not tested'
|
||||||
|
: 'Unstable'}) ${version.installed ? '' : '(Not installed)'}`
|
||||||
|
}
|
14
dev.mjs
14
dev.mjs
|
@ -7,15 +7,25 @@ const helloWorldConfig = {
|
||||||
"provider": "git",
|
"provider": "git",
|
||||||
"url": "https://git.nfp.is/api/v1/repos/thething/sc-helloworld/releases",
|
"url": "https://git.nfp.is/api/v1/repos/thething/sc-helloworld/releases",
|
||||||
"port": 8888,
|
"port": 8888,
|
||||||
|
"scAllowStop": true,
|
||||||
}
|
}
|
||||||
|
|
||||||
var core = new ServiceCore('sc-manager', import.meta.url)
|
var core = new ServiceCore('sc-manager', import.meta.url)
|
||||||
|
core.log = getLog('sc-manager', [{
|
||||||
|
"stream": process.stdout,
|
||||||
|
"level": "info"
|
||||||
|
}])
|
||||||
core.config['hello-world'] = helloWorldConfig
|
core.config['hello-world'] = helloWorldConfig
|
||||||
|
core.config.allowRestart = true
|
||||||
core.setConfig({
|
core.setConfig({
|
||||||
port: 4271,
|
port: 4271,
|
||||||
})
|
})
|
||||||
|
|
||||||
core.init(index).then(function() {
|
core.init(index).then(function() {
|
||||||
|
let coreLogger = getLog('service-core')
|
||||||
|
core.core.log = coreLogger
|
||||||
|
core.core.db.log = coreLogger
|
||||||
|
|
||||||
let provConstructor = Core.providers.get(core.db.config['hello-world'].provider)
|
let provConstructor = Core.providers.get(core.db.config['hello-world'].provider)
|
||||||
let provider = new provConstructor(core.db.config['hello-world'])
|
let provider = new provConstructor(core.db.config['hello-world'])
|
||||||
|
|
||||||
|
@ -33,9 +43,9 @@ core.init(index).then(function() {
|
||||||
|
|
||||||
return core.run()
|
return core.run()
|
||||||
}).then(function() {
|
}).then(function() {
|
||||||
return core.core.applicationMap.get('hello-world').update()
|
// return core.core.applicationMap.get('hello-world').update()
|
||||||
}).then(function() {
|
}).then(function() {
|
||||||
return core.core.runApplication(core.core.applicationMap.get('hello-world'))
|
// return core.core.runApplication(core.core.applicationMap.get('hello-world'))
|
||||||
}).then(function() {
|
}).then(function() {
|
||||||
let helloApp = core.core.applicationMap.get('hello-world')
|
let helloApp = core.core.applicationMap.get('hello-world')
|
||||||
helloApp.on('updated', core.core.runApplication.bind(core.core, helloApp))
|
helloApp.on('updated', core.core.runApplication.bind(core.core, helloApp))
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": "app",
|
"name": "sc-manager",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
|
@ -40,6 +40,6 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"asbundle": "^2.6.1",
|
"asbundle": "^2.6.1",
|
||||||
"mithril": "^2.0.4",
|
"mithril": "^2.0.4",
|
||||||
"service-core": "^3.0.0-beta.11"
|
"service-core": "^3.0.0-beta.13"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
BIN
public/favicon-16x16.png
Normal file
BIN
public/favicon-16x16.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 474 B |
BIN
public/favicon-32x32.png
Normal file
BIN
public/favicon-32x32.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 543 B |
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
|
@ -1,6 +1,8 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
|
||||||
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
|
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
|
||||||
<meta content="utf-8" http-equiv="encoding">
|
<meta content="utf-8" http-equiv="encoding">
|
||||||
<title>Service Core</title>
|
<title>Service Core</title>
|
||||||
|
|
|
@ -87,11 +87,33 @@ a.button {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button.fatal,
|
||||||
|
a.button.fatal {
|
||||||
|
background: hsl(0, 100%, 36%);
|
||||||
|
color: white;
|
||||||
|
border-color: hsl(0, 100%, 5%);
|
||||||
|
}
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
margin: 1rem 0;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions .button.inactive {
|
||||||
|
background: transparent;
|
||||||
|
color: #ffb843;
|
||||||
|
}
|
||||||
|
.actions .button.active {
|
||||||
|
}
|
||||||
|
|
||||||
/***************** Header ********************/
|
/***************** Header ********************/
|
||||||
#header {
|
#header {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -165,44 +187,38 @@ pre {
|
||||||
align-self: center;
|
align-self: center;
|
||||||
width: calc(100% - 4rem);
|
width: calc(100% - 4rem);
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
max-width: 700px;
|
max-width: 700px;
|
||||||
border: 1px solid #999;
|
border: 1px solid #999;
|
||||||
border-right: none;
|
|
||||||
}
|
}
|
||||||
#status .item {
|
#status .item {
|
||||||
flex: 2 1 50%;
|
flex: 2 1 50%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
border-right: 1px solid #999;
|
|
||||||
padding-bottom: 0.5rem;
|
padding-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
#status .item h4 {
|
#status .item h4 {
|
||||||
|
margin-top: -1px;
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
border-bottom: 1px solid #999;
|
border-bottom: 1px solid #999;
|
||||||
|
border-top: 1px solid #999;
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
#status .item p {
|
#status .item p {
|
||||||
padding: 0.25rem 1rem;
|
padding: 0.25rem 1rem;
|
||||||
}
|
}
|
||||||
#status .item p.running {
|
#status .item .running {
|
||||||
color: hsl(118, 84%, 46.3%);
|
color: hsl(118, 84%, 46.3%);
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
#status .item p.notrunning {
|
#status .item .notrunning {
|
||||||
color: hsl(354.7, 85.8%, 67.6%);
|
color: hsl(354.7, 85.8%, 67.6%);
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#status button {
|
#status button {
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
#status .status {
|
|
||||||
margin-top: 1rem;
|
|
||||||
align-self: center;
|
|
||||||
padding: 0.5rem;
|
|
||||||
color: #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************** Updater ********************/
|
/***************** Updater ********************/
|
||||||
#update {
|
#update {
|
||||||
|
@ -212,22 +228,6 @@ pre {
|
||||||
align-self: center;
|
align-self: center;
|
||||||
max-width: 700px;
|
max-width: 700px;
|
||||||
}
|
}
|
||||||
#update .actions {
|
|
||||||
margin: 1rem 0;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
#update .actions .filler {
|
|
||||||
flex-grow: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
#update .actions .button.inactive {
|
|
||||||
background: transparent;
|
|
||||||
color: #ffb843;
|
|
||||||
}
|
|
||||||
#update .actions .button.active {
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-device-width: 590px) {
|
@media only screen and (max-device-width: 590px) {
|
||||||
#update .actions .filler {
|
#update .actions .filler {
|
||||||
|
@ -273,7 +273,7 @@ pre {
|
||||||
|
|
||||||
@media only screen and (min-height: 650px) {
|
@media only screen and (min-height: 650px) {
|
||||||
#update .console {
|
#update .console {
|
||||||
height: calc(100vh - 300px);
|
height: calc(100vh - 280px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue