Big cleanup
This commit is contained in:
parent
e0413bbaa6
commit
9f0dddc5ad
19 changed files with 0 additions and 1292 deletions
|
@ -1,20 +0,0 @@
|
||||||
import { formatLog } from './loghelper.mjs'
|
|
||||||
import { safeWrap } from '../util.mjs'
|
|
||||||
|
|
||||||
export default function coremonitor(io, config, db, log, core) {
|
|
||||||
log.on('newlog', safeWrap(log, 'coremonitor.on.newlog', function(data) {
|
|
||||||
io.to('logger').emit('newlog', formatLog(data))
|
|
||||||
}))
|
|
||||||
core.on('dbupdated', safeWrap(log, 'coremonitor.on.dbupdated', function() {
|
|
||||||
io.to('core').emit('core.db', db.get('core').value())
|
|
||||||
}))
|
|
||||||
core.on('statusupdated', safeWrap(log, 'coremonitor.on.statusupdated', function() {
|
|
||||||
io.to('core').emit('core.status', core.status())
|
|
||||||
}))
|
|
||||||
core.on('applog', safeWrap(log, 'coremonitor.on.applog', function(app) {
|
|
||||||
io.to('core.app').emit('core.program.log', {
|
|
||||||
name: 'app',
|
|
||||||
logs: app.logs,
|
|
||||||
})
|
|
||||||
}))
|
|
||||||
}
|
|
|
@ -1,120 +0,0 @@
|
||||||
import { formatLog } from './loghelper.mjs'
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Event: 'core.config'
|
|
||||||
*
|
|
||||||
* Get config
|
|
||||||
*/
|
|
||||||
export async function config(ctx, data, cb) {
|
|
||||||
cb(ctx.config)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Event: 'core.restart'
|
|
||||||
*
|
|
||||||
* Restart server
|
|
||||||
*/
|
|
||||||
export async function restart(ctx, data, cb) {
|
|
||||||
ctx.core.restart()
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Event: 'core.getlastlogs'
|
|
||||||
*
|
|
||||||
* Returns last few log messages from log
|
|
||||||
*/
|
|
||||||
export async function getlastlogs(ctx, data, cb) {
|
|
||||||
cb(ctx.logroot.ringbuffer.records.map(formatLog))
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Event: 'core.listenlogs'
|
|
||||||
*
|
|
||||||
* Start listening to new log lines
|
|
||||||
*/
|
|
||||||
export async function listenlogs(ctx) {
|
|
||||||
ctx.socket.join('logger')
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Event: 'core.unlistenlogs'
|
|
||||||
*
|
|
||||||
* Stop listening to new log lines
|
|
||||||
*/
|
|
||||||
export async function unlistenlogs(ctx) {
|
|
||||||
ctx.socket.leave('logger')
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Event: 'core.update'
|
|
||||||
*
|
|
||||||
* Update specific software
|
|
||||||
*/
|
|
||||||
export async function update(ctx, data, cb) {
|
|
||||||
if (data.name === 'app') {
|
|
||||||
await ctx.core.updateProgram('app')
|
|
||||||
} else if (data.name === 'manage') {
|
|
||||||
await ctx.core.updateProgram('manage')
|
|
||||||
} else {
|
|
||||||
ctx.log.warn('Invalid update command for app ' + data.name)
|
|
||||||
ctx.log.event.warn('Invalid update command for app ' + data.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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'
|
|
||||||
*
|
|
||||||
* Start listening to new log lines
|
|
||||||
*/
|
|
||||||
export async function listencore(ctx) {
|
|
||||||
ctx.socket.join('core')
|
|
||||||
ctx.socket.emit('core.db', ctx.db.get('core').value())
|
|
||||||
ctx.socket.emit('core.status', ctx.core.status())
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Event: 'core.unlistencore'
|
|
||||||
*
|
|
||||||
* Stop listening to new log lines
|
|
||||||
*/
|
|
||||||
export async function unlistencore(ctx) {
|
|
||||||
ctx.socket.leave('core')
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Event: 'core.listentoapp'
|
|
||||||
*
|
|
||||||
* Start listening to changes in core app
|
|
||||||
*/
|
|
||||||
export async function listentoapp(ctx) {
|
|
||||||
ctx.socket.join('core.app')
|
|
||||||
ctx.socket.emit('core.program.log', {
|
|
||||||
name: 'app',
|
|
||||||
logs: ctx.core.getProgramLogs('app')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Event: 'core.unlistentoapp'
|
|
||||||
*
|
|
||||||
* Stop listening to new log lines
|
|
||||||
*/
|
|
||||||
export async function unlistentoapp(ctx) {
|
|
||||||
ctx.socket.leave('core.app')
|
|
||||||
}
|
|
|
@ -1,188 +0,0 @@
|
||||||
import _ from 'lodash'
|
|
||||||
import { format } from 'util'
|
|
||||||
|
|
||||||
// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
|
|
||||||
// Suggested colors (some are unreadable in common cases):
|
|
||||||
// - Good: cyan, yellow (limited use), bold, green, magenta, red
|
|
||||||
// - Bad: blue (not visible on cmd.exe), grey (same color as background on
|
|
||||||
// Solarized Dark theme from <https://github.com/altercation/solarized>, see
|
|
||||||
// issue #160)
|
|
||||||
var colors = {
|
|
||||||
'bold' : [1, 22],
|
|
||||||
'italic' : [3, 23],
|
|
||||||
'underline' : [4, 24],
|
|
||||||
'inverse' : [7, 27],
|
|
||||||
'white' : [37, 39],
|
|
||||||
'grey' : [90, 39],
|
|
||||||
'black' : [30, 39],
|
|
||||||
'blue' : [34, 39],
|
|
||||||
'cyan' : [36, 39],
|
|
||||||
'green' : [32, 39],
|
|
||||||
'magenta' : [35, 39],
|
|
||||||
'red' : [31, 39],
|
|
||||||
'yellow' : [33, 39]
|
|
||||||
};
|
|
||||||
|
|
||||||
// Levels
|
|
||||||
var TRACE = 10;
|
|
||||||
var DEBUG = 20;
|
|
||||||
var INFO = 30;
|
|
||||||
var WARN = 40;
|
|
||||||
var ERROR = 50;
|
|
||||||
var FATAL = 60;
|
|
||||||
|
|
||||||
var levelFromName = {
|
|
||||||
'trace': TRACE,
|
|
||||||
'debug': DEBUG,
|
|
||||||
'info': INFO,
|
|
||||||
'warn': WARN,
|
|
||||||
'error': ERROR,
|
|
||||||
'fatal': FATAL
|
|
||||||
};
|
|
||||||
|
|
||||||
var upperPaddedNameFromLevel = {};
|
|
||||||
Object.keys(levelFromName).forEach(function (name) {
|
|
||||||
var lvl = levelFromName[name];
|
|
||||||
upperPaddedNameFromLevel[lvl] = (
|
|
||||||
name.length === 4 ? ' ' : '') + name.toUpperCase();
|
|
||||||
});
|
|
||||||
|
|
||||||
function stylize(str, color) {
|
|
||||||
if (!str)
|
|
||||||
return '';
|
|
||||||
var codes = colors[color];
|
|
||||||
if (codes) {
|
|
||||||
return '\\033[' + codes[0] + 'm' + str +
|
|
||||||
'\\033[' + codes[1] + 'm';
|
|
||||||
} else {
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function indent(s) {
|
|
||||||
return ' ' + s.split(/\r?\n/).join('\n ');
|
|
||||||
}
|
|
||||||
|
|
||||||
export function formatLog(data) {
|
|
||||||
let rec = _.cloneDeep(data)
|
|
||||||
|
|
||||||
delete rec.v;
|
|
||||||
|
|
||||||
// Time.
|
|
||||||
var time = '[' + rec.time.toISOString().replace('T', ' ').replace('Z', '') + ']'
|
|
||||||
time = stylize(time, 'none')
|
|
||||||
|
|
||||||
delete rec.time;
|
|
||||||
|
|
||||||
var nameStr = rec.name;
|
|
||||||
delete rec.name;
|
|
||||||
|
|
||||||
if (rec.component) {
|
|
||||||
nameStr += '/' + rec.component;
|
|
||||||
}
|
|
||||||
delete rec.component;
|
|
||||||
|
|
||||||
nameStr += '/' + rec.pid;
|
|
||||||
delete rec.pid;
|
|
||||||
|
|
||||||
var level = (upperPaddedNameFromLevel[rec.level] || 'LVL' + rec.level);
|
|
||||||
var colorFromLevel = {
|
|
||||||
10: 'white', // TRACE
|
|
||||||
20: 'yellow', // DEBUG
|
|
||||||
30: 'cyan', // INFO
|
|
||||||
40: 'magenta', // WARN
|
|
||||||
50: 'red', // ERROR
|
|
||||||
60: 'inverse', // FATAL
|
|
||||||
};
|
|
||||||
level = stylize(level, colorFromLevel[rec.level]);
|
|
||||||
delete rec.level;
|
|
||||||
|
|
||||||
var src = '';
|
|
||||||
if (rec.src && rec.src.file) {
|
|
||||||
var s = rec.src;
|
|
||||||
if (s.func) {
|
|
||||||
src = format(' (%s:%d in %s)', s.file, s.line, s.func);
|
|
||||||
} else {
|
|
||||||
src = format(' (%s:%d)', s.file, s.line);
|
|
||||||
}
|
|
||||||
src = stylize(src, 'green');
|
|
||||||
}
|
|
||||||
delete rec.src;
|
|
||||||
|
|
||||||
var hostname = rec.hostname;
|
|
||||||
delete rec.hostname;
|
|
||||||
|
|
||||||
var extras = [];
|
|
||||||
var details = [];
|
|
||||||
|
|
||||||
if (rec.req_id) {
|
|
||||||
extras.push('req_id=' + rec.req_id);
|
|
||||||
}
|
|
||||||
delete rec.req_id;
|
|
||||||
|
|
||||||
var onelineMsg;
|
|
||||||
if (rec.msg.indexOf('\n') !== -1) {
|
|
||||||
onelineMsg = '';
|
|
||||||
details.push(indent(stylize(rec.msg, 'cyan')));
|
|
||||||
} else {
|
|
||||||
onelineMsg = ' ' + stylize(rec.msg, 'cyan');
|
|
||||||
}
|
|
||||||
delete rec.msg;
|
|
||||||
|
|
||||||
if (rec.err && rec.err.stack) {
|
|
||||||
var err = rec.err
|
|
||||||
if (typeof (err.stack) !== 'string') {
|
|
||||||
details.push(indent(err.stack.toString()));
|
|
||||||
} else {
|
|
||||||
details.push(indent(err.stack));
|
|
||||||
}
|
|
||||||
delete err.message;
|
|
||||||
delete err.name;
|
|
||||||
delete err.stack;
|
|
||||||
// E.g. for extra 'foo' field on 'err', add 'err.foo' at
|
|
||||||
// top-level. This *does* have the potential to stomp on a
|
|
||||||
// literal 'err.foo' key.
|
|
||||||
Object.keys(err).forEach(function (k) {
|
|
||||||
rec['err.' + k] = err[k];
|
|
||||||
})
|
|
||||||
delete rec.err;
|
|
||||||
}
|
|
||||||
|
|
||||||
var leftover = Object.keys(rec);
|
|
||||||
for (var i = 0; i < leftover.length; i++) {
|
|
||||||
var key = leftover[i];
|
|
||||||
var value = rec[key];
|
|
||||||
var stringified = false;
|
|
||||||
if (typeof (value) !== 'string') {
|
|
||||||
value = JSON.stringify(value, null, 2);
|
|
||||||
if (typeof (value) !== 'string') {
|
|
||||||
value = 'null'
|
|
||||||
}
|
|
||||||
stringified = true;
|
|
||||||
}
|
|
||||||
if (value.indexOf('\n') !== -1 || value.length > 50) {
|
|
||||||
details.push(indent(key + ': ' + value));
|
|
||||||
} else if (!stringified && (value.indexOf(' ') != -1 ||
|
|
||||||
value.length === 0))
|
|
||||||
{
|
|
||||||
extras.push(key + '=' + JSON.stringify(value));
|
|
||||||
} else {
|
|
||||||
extras.push(key + '=' + value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extras = stylize(
|
|
||||||
(extras.length ? ' (' + extras.join(', ') + ')' : ''), 'none');
|
|
||||||
details = stylize(
|
|
||||||
(details.length ? details.join('\n --\n') + '\n' : ''), 'none');
|
|
||||||
|
|
||||||
return format('%s %s: %s on %s%s:%s%s\n%s',
|
|
||||||
time,
|
|
||||||
level,
|
|
||||||
nameStr,
|
|
||||||
hostname || '<no-hostname>',
|
|
||||||
src,
|
|
||||||
onelineMsg,
|
|
||||||
extras,
|
|
||||||
details)
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
import * as core from './core/ioroutes.mjs'
|
|
||||||
|
|
||||||
function register(ctx, name, method) {
|
|
||||||
if (typeof(method) === 'object') {
|
|
||||||
Object.keys(method).forEach(key => {
|
|
||||||
register(ctx, [name, key].join('.'), method[key])
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.socket.on(name, async function(data, cb) {
|
|
||||||
ctx.log.debug('SocketIO: ' + name)
|
|
||||||
|
|
||||||
try {
|
|
||||||
await method(ctx, data, cb)
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
ctx.log.error(error, `Error processing ${name}`)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function onConnection(server, config, db, log, coreService, data) {
|
|
||||||
const io = server
|
|
||||||
const socket = data
|
|
||||||
|
|
||||||
const child = log.child({
|
|
||||||
id: socket.id,
|
|
||||||
})
|
|
||||||
|
|
||||||
child.info('Got new socket connection')
|
|
||||||
|
|
||||||
let ctx = {
|
|
||||||
config,
|
|
||||||
io,
|
|
||||||
socket,
|
|
||||||
log: child,
|
|
||||||
db,
|
|
||||||
core: coreService,
|
|
||||||
logroot: log,
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.socket.on('disconnect', function() {
|
|
||||||
child.info('Closed connection')
|
|
||||||
})
|
|
||||||
|
|
||||||
register(ctx, 'core', core)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default onConnection
|
|
|
@ -1,82 +0,0 @@
|
||||||
import http from 'http'
|
|
||||||
import path from 'path'
|
|
||||||
import { fileURLToPath } from 'url'
|
|
||||||
import socket from 'socket.io-serveronly'
|
|
||||||
import nStatic from 'node-static'
|
|
||||||
import coremonitor from './core/coremonitor.mjs'
|
|
||||||
|
|
||||||
import onConnection from './routerio.mjs'
|
|
||||||
|
|
||||||
export function run(config, db, log, core) {
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
||||||
const staticRoot = path.join(__dirname,'../public')
|
|
||||||
|
|
||||||
const fileServer = new nStatic.Server(staticRoot)
|
|
||||||
const server = http.createServer(function (req, res) {
|
|
||||||
const child = log.child({})
|
|
||||||
|
|
||||||
const d1 = new Date().getTime()
|
|
||||||
|
|
||||||
let finishedRequest = false
|
|
||||||
var done = function () {
|
|
||||||
if (finishedRequest) return
|
|
||||||
finishedRequest = true
|
|
||||||
if (req.url === '/main.css.map') return
|
|
||||||
var requestTime = new Date().getTime() - d1
|
|
||||||
|
|
||||||
let level = 'debug'
|
|
||||||
if (res.statusCode >= 400) {
|
|
||||||
level = 'warn'
|
|
||||||
}
|
|
||||||
if (res.statusCode >= 500) {
|
|
||||||
level = 'error'
|
|
||||||
}
|
|
||||||
|
|
||||||
let status = ''
|
|
||||||
if (res.statusCode >= 400) {
|
|
||||||
status = res.statusCode + ' '
|
|
||||||
}
|
|
||||||
|
|
||||||
child[level]({
|
|
||||||
duration: requestTime,
|
|
||||||
status: res.statusCode,
|
|
||||||
}, `<-- ${status}${req.method} ${req.url}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
res.addListener('finish', done);
|
|
||||||
res.addListener('close', done);
|
|
||||||
|
|
||||||
req.addListener('end', function () {
|
|
||||||
if (req.url === '/') {
|
|
||||||
res.writeHead(302, { Location: '/index.html' })
|
|
||||||
return res.end()
|
|
||||||
}
|
|
||||||
|
|
||||||
fileServer.serve(req, res, function (err) {
|
|
||||||
if (err) {
|
|
||||||
if (err.status !== 404) {
|
|
||||||
log.error(err, req.url);
|
|
||||||
}
|
|
||||||
|
|
||||||
res.writeHead(err.status, err.headers);
|
|
||||||
res.end(err.message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}).resume()
|
|
||||||
})
|
|
||||||
|
|
||||||
const io = new socket(server)
|
|
||||||
io.on('connection', onConnection.bind(this, io, config, db, log, core))
|
|
||||||
|
|
||||||
coremonitor(io, config, db, log, core)
|
|
||||||
|
|
||||||
server.listen(config.managePort, '0.0.0.0', function(err) {
|
|
||||||
if (err) {
|
|
||||||
log.fatal(err)
|
|
||||||
log.event.error('Error starting server: ' + err.message)
|
|
||||||
return process.exit(2)
|
|
||||||
}
|
|
||||||
log.event.info(`Server is listening on ${config.managePort} serving files on ${staticRoot}`)
|
|
||||||
log.info(`Server is listening on ${config.managePort} serving files on ${staticRoot}`)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
export function safeWrap(log, name, fn) {
|
|
||||||
return function(data, cb) {
|
|
||||||
try {
|
|
||||||
let out = fn(data, cb)
|
|
||||||
if (out && out.then) {
|
|
||||||
out.then(function() {}, function(err) {
|
|
||||||
log.error(err, 'Unknown error in ' + name)
|
|
||||||
log.event.error('Unknown error occured in ' + name + ': ' + err.message)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
log.error(err, 'Unknown error in ' + name)
|
|
||||||
log.event.error('Unknown error occured in ' + name + ': ' + err.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because one or more lines are too long
|
@ -1,34 +0,0 @@
|
||||||
|
|
||||||
// taken from isobject npm library
|
|
||||||
function isObject(val) {
|
|
||||||
return val != null && typeof val === 'object' && Array.isArray(val) === false
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = function defaults(options, def) {
|
|
||||||
let out = { }
|
|
||||||
|
|
||||||
if (options) {
|
|
||||||
Object.keys(options || {}).forEach(key => {
|
|
||||||
out[key] = options[key]
|
|
||||||
|
|
||||||
if (Array.isArray(out[key])) {
|
|
||||||
out[key] = out[key].map(item => {
|
|
||||||
if (isObject(item)) return defaults(item)
|
|
||||||
return item
|
|
||||||
})
|
|
||||||
} else if (out[key] && typeof out[key] === 'object') {
|
|
||||||
out[key] = defaults(options[key], def && def[key])
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (def) {
|
|
||||||
Object.keys(def).forEach(function(key) {
|
|
||||||
if (typeof out[key] === 'undefined') {
|
|
||||||
out[key] = def[key]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return out
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
const m = require('mithril')
|
|
||||||
const socket = require('./socket')
|
|
||||||
|
|
||||||
const Header = {
|
|
||||||
oninit: function() {
|
|
||||||
this.connected = socket.connected
|
|
||||||
|
|
||||||
socket.on('connect', () => {
|
|
||||||
this.connected = true
|
|
||||||
m.redraw()
|
|
||||||
})
|
|
||||||
socket.on('disconnect', () => {
|
|
||||||
this.connected = false
|
|
||||||
m.redraw()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
view: function() {
|
|
||||||
let path = m.route.get() || ''
|
|
||||||
|
|
||||||
return [
|
|
||||||
m('div.seperator'),
|
|
||||||
m(m.route.Link, {
|
|
||||||
href: '/',
|
|
||||||
class: path === '/' || path === '' ? 'active' : '',
|
|
||||||
}, 'Status'),
|
|
||||||
m('div.seperator'),
|
|
||||||
m(m.route.Link, {
|
|
||||||
href: '/log',
|
|
||||||
class: path === '/log' ? 'active' : '',
|
|
||||||
}, 'Log'),
|
|
||||||
m('div.seperator'),
|
|
||||||
m(m.route.Link, {
|
|
||||||
href: '/updater',
|
|
||||||
class: path.startsWith('/updater') ? 'active' : '',
|
|
||||||
}, 'Updater'),
|
|
||||||
m('div.seperator'),
|
|
||||||
!this.connected && m('div.disconnected', `
|
|
||||||
Lost connection with server, Attempting to reconnect
|
|
||||||
`) || null,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Header
|
|
|
@ -1,31 +0,0 @@
|
||||||
/**
|
|
||||||
* @license
|
|
||||||
* stream-manager <https://filadelfia.is>
|
|
||||||
* Copyright 2015 Jonatan Nilsson <http://jonatan.nilsson.is/>
|
|
||||||
*
|
|
||||||
* Available under WTFPL License (http://www.wtfpl.net/txt/copying/)
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
//Add debug components to window. Allows us to play with controls
|
|
||||||
//in the console.
|
|
||||||
window.components = {}
|
|
||||||
|
|
||||||
require('./socket')
|
|
||||||
|
|
||||||
const m = require('mithril')
|
|
||||||
const Header = require('./header')
|
|
||||||
|
|
||||||
const Status = require('./status/status')
|
|
||||||
const Log = require('./log/log')
|
|
||||||
const Updater = require('./updater/updater')
|
|
||||||
|
|
||||||
m.mount(document.getElementById('header'), Header)
|
|
||||||
|
|
||||||
m.route(document.getElementById('content'), '/', {
|
|
||||||
'/': Status,
|
|
||||||
'/log': Log,
|
|
||||||
'/updater': Updater,
|
|
||||||
'/updater/:id': Updater,
|
|
||||||
})
|
|
|
@ -1,56 +0,0 @@
|
||||||
const m = require('mithril')
|
|
||||||
const socket = require('../socket')
|
|
||||||
const Module = require('../module')
|
|
||||||
|
|
||||||
const Log = Module({
|
|
||||||
init: function() {
|
|
||||||
this.connected = socket.connected
|
|
||||||
this.loglines = []
|
|
||||||
|
|
||||||
this.on('newlog', data => {
|
|
||||||
this.loglines.push(this.formatLine(data))
|
|
||||||
m.redraw()
|
|
||||||
})
|
|
||||||
|
|
||||||
this._socketOn(() => this.loadData())
|
|
||||||
},
|
|
||||||
|
|
||||||
remove: function() {
|
|
||||||
socket.emit('core.unlistenlogs', {})
|
|
||||||
},
|
|
||||||
|
|
||||||
loadData: function() {
|
|
||||||
this.loglines = []
|
|
||||||
socket.emit('core.listenlogs', {})
|
|
||||||
socket.emit('core.getlastlogs', {}, (res) => {
|
|
||||||
this.loglines = res.map(this.formatLine)
|
|
||||||
m.redraw()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
formatLine: function(line) {
|
|
||||||
return m.trust(line.replace(/\\033\[37m/g, '<span class="white">')
|
|
||||||
.replace(/\\033\[33m/g, '<span class="yellow">')
|
|
||||||
.replace(/\\033\[36m/g, '<span class="cyan">')
|
|
||||||
.replace(/\\033\[35m/g, '<span class="magenta">')
|
|
||||||
.replace(/\\033\[31m/g, '<span class="red">')
|
|
||||||
.replace(/\\033\[7m/g, '<span class="inverse">')
|
|
||||||
.replace(/\\033\[32m/g, '<span class="green">')
|
|
||||||
.replace(/\\033\[27m/g, '</span>')
|
|
||||||
.replace(/\\033\[39m/g, '</span>'))
|
|
||||||
},
|
|
||||||
|
|
||||||
view: function() {
|
|
||||||
return [
|
|
||||||
m('h1.header', 'Log'),
|
|
||||||
m('div#logger', [
|
|
||||||
this.loglines.map((line, i) => {
|
|
||||||
return m('div', { key: i }, line)
|
|
||||||
}),
|
|
||||||
m('div.padder'),
|
|
||||||
]),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
module.exports = Log
|
|
|
@ -1,38 +0,0 @@
|
||||||
const defaults = require('./defaults')
|
|
||||||
const socket = require('./socket')
|
|
||||||
|
|
||||||
module.exports = function Module(module) {
|
|
||||||
return defaults(module, {
|
|
||||||
init: function() {},
|
|
||||||
|
|
||||||
oninit: function(vnode) {
|
|
||||||
this._listeners = []
|
|
||||||
this.init(vnode)
|
|
||||||
},
|
|
||||||
|
|
||||||
_listeners: null,
|
|
||||||
|
|
||||||
_socketOn: function(cb) {
|
|
||||||
socket.on('connect', () => cb())
|
|
||||||
|
|
||||||
if (socket.connected) {
|
|
||||||
cb()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
on: function(name, cb) {
|
|
||||||
this._listeners.push([name, cb])
|
|
||||||
socket.on(name, cb)
|
|
||||||
},
|
|
||||||
|
|
||||||
remove: function() {},
|
|
||||||
|
|
||||||
onremove: function() {
|
|
||||||
this.remove()
|
|
||||||
if (!this._listeners) return
|
|
||||||
for (let i = 0; i < this._listeners.length; i++) {
|
|
||||||
socket.removeListener(this._listeners[0], this._listeners[1])
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
var io = require('./client')
|
|
||||||
var socket = io()
|
|
||||||
|
|
||||||
module.exports = socket
|
|
|
@ -1,119 +0,0 @@
|
||||||
const m = require('mithril')
|
|
||||||
const socket = require('../socket')
|
|
||||||
const Module = require('../module')
|
|
||||||
|
|
||||||
const Status = Module({
|
|
||||||
init: function() {
|
|
||||||
this._name = '...loading...'
|
|
||||||
this._management = {
|
|
||||||
port: null,
|
|
||||||
repository: null,
|
|
||||||
active: null,
|
|
||||||
latestInstalled: null,
|
|
||||||
lastActive: null,
|
|
||||||
latestVersion: null,
|
|
||||||
running: null,
|
|
||||||
}
|
|
||||||
this._app = {
|
|
||||||
port: null,
|
|
||||||
repository: null,
|
|
||||||
active: null,
|
|
||||||
latestInstalled: null,
|
|
||||||
lastActive: null,
|
|
||||||
latestVersion: null,
|
|
||||||
running: null,
|
|
||||||
}
|
|
||||||
|
|
||||||
this._socketOn(() => this.loadData())
|
|
||||||
},
|
|
||||||
|
|
||||||
loadData: function() {
|
|
||||||
socket.emit('core.config', {}, (res) => {
|
|
||||||
this._name = res.name + ' - ' + res.serviceName
|
|
||||||
this._app.port = res.port
|
|
||||||
this._app.repository = res.appRepository
|
|
||||||
this._management.port = res.managePort
|
|
||||||
this._management.repository = res.manageRepository
|
|
||||||
m.redraw()
|
|
||||||
})
|
|
||||||
|
|
||||||
this.on('core.db', (res) => {
|
|
||||||
this._management.active = res.manageActive
|
|
||||||
this._management.latestInstalled = res.manageLatestInstalled
|
|
||||||
this._management.lastActive = res.manageLastActive
|
|
||||||
this._management.latestVersion = res.manageLatestVersion
|
|
||||||
this._app.active = res.appActive
|
|
||||||
this._app.latestInstalled = res.appLatestInstalled
|
|
||||||
this._app.lastActive = res.appLastActive
|
|
||||||
this._app.latestVersion = res.appLatestVersion
|
|
||||||
|
|
||||||
m.redraw()
|
|
||||||
})
|
|
||||||
|
|
||||||
this.on('core.status', (res) => {
|
|
||||||
this._management.running = res.manage
|
|
||||||
this._app.running = res.app
|
|
||||||
|
|
||||||
m.redraw()
|
|
||||||
})
|
|
||||||
|
|
||||||
socket.emit('core.listencore', {})
|
|
||||||
},
|
|
||||||
|
|
||||||
remove: function() {
|
|
||||||
socket.emit('core.unlistencore', {})
|
|
||||||
},
|
|
||||||
|
|
||||||
restartClicked: function() {
|
|
||||||
socket.emit('core.restart', {})
|
|
||||||
},
|
|
||||||
|
|
||||||
view: function() {
|
|
||||||
let loopOver = [
|
|
||||||
['Management service', '_management'],
|
|
||||||
['Application service', '_app'],
|
|
||||||
]
|
|
||||||
return m('div#status', [
|
|
||||||
m('h1.header', this._name),
|
|
||||||
m('div.split', [
|
|
||||||
loopOver.map((group) => {
|
|
||||||
return m('div.item', [
|
|
||||||
m('h4', group[0]),
|
|
||||||
m('p', this[group[1]].port
|
|
||||||
? `Port: ${this[group[1]].port}`
|
|
||||||
: ''),
|
|
||||||
m('p', this[group[1]].repository
|
|
||||||
? `${this[group[1]].repository}`
|
|
||||||
: '< no repository >'),
|
|
||||||
m('p', this[group[1]].active
|
|
||||||
? `Running version: ${this[group[1]].active}`
|
|
||||||
: '< no running version >'),
|
|
||||||
m('p', this[group[1]].latestInstalled
|
|
||||||
? `Latest installed: ${this[group[1]].latestInstalled}`
|
|
||||||
: '< no version installed >'),
|
|
||||||
m('p', this[group[1]].lastActive
|
|
||||||
? `Last stable version: ${this[group[1]].lastActive}`
|
|
||||||
: '< no last stable version >'),
|
|
||||||
m('p', this[group[1]].latestVersion
|
|
||||||
? `Latest version: ${this[group[1]].latestVersion}`
|
|
||||||
: '< no version found >'),
|
|
||||||
this[group[1]].running !== null
|
|
||||||
? m('p',
|
|
||||||
{ class: group[1].running ? 'running' : 'notrunning' },
|
|
||||||
group[1].running ? 'Running' : 'Not Running'
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
m('button', {
|
|
||||||
|
|
||||||
}, 'Update/Start')
|
|
||||||
])
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
m('button', {
|
|
||||||
onclick: () => this.restartClicked(),
|
|
||||||
}, 'Restart service')
|
|
||||||
])
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
module.exports = Status
|
|
|
@ -1,172 +0,0 @@
|
||||||
const m = require('mithril')
|
|
||||||
const socket = require('../socket')
|
|
||||||
const Module = require('../module')
|
|
||||||
|
|
||||||
const Updater = Module({
|
|
||||||
init: function(vnode) {
|
|
||||||
this.activeApp = vnode.attrs.id || null
|
|
||||||
this.appRepository = null
|
|
||||||
this.manageRepository = null
|
|
||||||
this.db = null
|
|
||||||
this.app = {}
|
|
||||||
this.status = {}
|
|
||||||
this.logUpdated = false
|
|
||||||
this._socketOn(() => this.socketOpen())
|
|
||||||
this._active = null
|
|
||||||
|
|
||||||
if (this.activeApp && this.activeApp !== 'app'&& this.activeApp !== 'manage') {
|
|
||||||
return m.route('/updater')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
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
|
|
||||||
m.redraw()
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
socket.emit('core.listencore', {})
|
|
||||||
},
|
|
||||||
|
|
||||||
updateActiveDb() {
|
|
||||||
if (this.db && this.activeApp) {
|
|
||||||
this.app = {
|
|
||||||
repository: this[this.activeApp + 'Repository'],
|
|
||||||
active: this.db[this.activeApp + 'Active'],
|
|
||||||
lastActive: this.db[this.activeApp + 'LastActive'],
|
|
||||||
latestInstalled: this.db[this.activeApp + 'LatestInstalled'],
|
|
||||||
latestVersion: this.db[this.activeApp + 'LatestVersion'],
|
|
||||||
logs: '',
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.app = {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
loadAppData() {
|
|
||||||
this.updateActiveDb()
|
|
||||||
if (this.activeApp === 'app') {
|
|
||||||
socket.emit('core.listentoapp', {})
|
|
||||||
}
|
|
||||||
/* request to listen to app updates */
|
|
||||||
},
|
|
||||||
|
|
||||||
remove: function() {
|
|
||||||
socket.emit('core.unlistencore', {})
|
|
||||||
socket.emit('core.unlistentoapp', {})
|
|
||||||
},
|
|
||||||
|
|
||||||
startUpdate: function() {
|
|
||||||
socket.emit('core.update', {
|
|
||||||
name: this.activeApp,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
startSoftware: function() {
|
|
||||||
socket.emit('core.start', {
|
|
||||||
name: this.activeApp,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
view: function() {
|
|
||||||
return m('div#update', [
|
|
||||||
m('div.actions', [
|
|
||||||
m('h1.header', 'Updater'),
|
|
||||||
m('div.filler'),
|
|
||||||
m(m.route.Link, {
|
|
||||||
hidden: !this.appRepository,
|
|
||||||
class: 'button' + (this.activeApp === 'app' ? ' active' : ''),
|
|
||||||
href: '/updater/app',
|
|
||||||
}, 'Update App'),
|
|
||||||
m(m.route.Link, {
|
|
||||||
hidden: this.manageRepository,
|
|
||||||
class: 'button' + (this.activeApp === 'manage' ? ' active' : ''),
|
|
||||||
href: '/updater/manage',
|
|
||||||
}, 'Update Manager'),
|
|
||||||
]),
|
|
||||||
this.activeApp && this.app ? [
|
|
||||||
m('h4', this.app.repository
|
|
||||||
? `${this.app.repository}`
|
|
||||||
: '< no repository >'),
|
|
||||||
m('div.info', [
|
|
||||||
m('p', this.app.active
|
|
||||||
? `Running version: ${this.app.active}`
|
|
||||||
: '< no running version >'),
|
|
||||||
m('p', this.app.latestInstalled
|
|
||||||
? `Latest installed: ${this.app.latestInstalled}`
|
|
||||||
: '< no version installed >'),
|
|
||||||
m('p', this.app.lastActive
|
|
||||||
? `Last stable version: ${this.app.lastActive}`
|
|
||||||
: '< no last stable version >'),
|
|
||||||
m('p', this.app.latestVersion
|
|
||||||
? `Latest version: ${this.app.latestVersion}`
|
|
||||||
: '< no version found >'),
|
|
||||||
]),
|
|
||||||
m('div.console', {
|
|
||||||
onupdate: (vnode) => {
|
|
||||||
if (this.logUpdated) {
|
|
||||||
vnode.dom.scrollTop = vnode.dom.scrollHeight
|
|
||||||
this.logUpdated = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
m('pre', this.app.logs && this.app.logs || '')
|
|
||||||
),
|
|
||||||
this.db
|
|
||||||
? m('div.actions', {
|
|
||||||
hidden: this.status[this.activeApp + 'Updating'],
|
|
||||||
}, [
|
|
||||||
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(),
|
|
||||||
}, 'Start'),
|
|
||||||
m('button', {
|
|
||||||
hidden: !this.db[this.activeApp + 'LastActive']
|
|
||||||
|| this.db[this.activeApp + 'LastActive'] === this.db[this.activeApp + 'Active']
|
|
||||||
}, 'Use Last Version'),
|
|
||||||
])
|
|
||||||
: null,
|
|
||||||
] : null
|
|
||||||
])
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
module.exports = Updater
|
|
|
@ -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)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
{
|
|
||||||
"name": "app",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "",
|
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
|
||||||
"js:build:main": "asbundle app/index.js public/main.js",
|
|
||||||
"dev": "nodemon --watch app --exec \"npm run js:build:main\"",
|
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
|
||||||
},
|
|
||||||
"author": "",
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"asbundle": "^2.6.1",
|
|
||||||
"mithril": "^2.0.4",
|
|
||||||
"node-static": "^0.7.11",
|
|
||||||
"nodemon": "^2.0.4",
|
|
||||||
"socket.io-serveronly": "^2.3.0"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
|
|
||||||
<meta content="utf-8" http-equiv="encoding">
|
|
||||||
<title>Service Core</title>
|
|
||||||
<link href="/main.css" rel="stylesheet" />
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<section id="header"></section>
|
|
||||||
<main id="content"></main>
|
|
||||||
<script src="main.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,270 +0,0 @@
|
||||||
html {
|
|
||||||
box-sizing: border-box;
|
|
||||||
font-size: 16px;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
*, *:before, *:after {
|
|
||||||
box-sizing: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
body, h1, h2, h3, h4, h5, h6, p {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
background: #3d3d3d;
|
|
||||||
color: #f1f1f1;
|
|
||||||
display: flex;
|
|
||||||
font-size: 16px;
|
|
||||||
min-height: 100vh;
|
|
||||||
flex-direction: column;
|
|
||||||
font-family: Helvetica, sans-serif, Arial;
|
|
||||||
}
|
|
||||||
|
|
||||||
a, a:visited {
|
|
||||||
color: #eee;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Container */
|
|
||||||
.container {
|
|
||||||
display: flex;
|
|
||||||
align-items: stretch;
|
|
||||||
flex-direction: column;
|
|
||||||
flex-grow: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.disconnected {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background: rgba(0,0,0,0.8);
|
|
||||||
color: #fff;
|
|
||||||
font-size: 1em;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
[hidden] { display: none !important; }
|
|
||||||
|
|
||||||
main {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: stretch;
|
|
||||||
flex-grow: 2;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
main h1 {
|
|
||||||
text-align: center;
|
|
||||||
font-size: 1.6rem;
|
|
||||||
line-height: 3.6rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
form.login {
|
|
||||||
align-self: center;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
button,
|
|
||||||
a.button {
|
|
||||||
align-self: center;
|
|
||||||
min-width: 200px;
|
|
||||||
background: #ffb843;
|
|
||||||
color: black;
|
|
||||||
padding: 0.5rem;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
text-decoration: none;
|
|
||||||
border: 1px solid hsl(37.3, 80%, 37%);
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************** Header ********************/
|
|
||||||
#header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
width: 100%;
|
|
||||||
background: #292929;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header a {
|
|
||||||
width: 150px;
|
|
||||||
text-align: center;
|
|
||||||
padding: 1rem;
|
|
||||||
color: #ffb843;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header a.active {
|
|
||||||
color: #999;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header .seperator {
|
|
||||||
border-right: 1px solid #999;
|
|
||||||
height: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************** Log ********************/
|
|
||||||
#logger {
|
|
||||||
margin: 0 2rem;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
border: 1px solid #999;
|
|
||||||
padding: 0.5rem;
|
|
||||||
overflow-y: scroll;
|
|
||||||
height: calc(100vh - 160px);
|
|
||||||
background: #0c0c0c;
|
|
||||||
color: #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
#logger div {
|
|
||||||
margin-left: 1rem;
|
|
||||||
text-indent: -1rem;
|
|
||||||
line-height: 1.4rem;
|
|
||||||
margin-bottom: 0.1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
#logger span.white { color: rgb(242,242,242); }
|
|
||||||
#logger span.yellow { color: rgb(193,156,0); }
|
|
||||||
#logger span.cyan { color: rgb(58,150,221); }
|
|
||||||
#logger span.magenta { color: rgb(136,23,152); }
|
|
||||||
#logger span.red { color: rgb(197,15,31); }
|
|
||||||
#logger span.green { color: rgb(19,161,14); }
|
|
||||||
#logger span.inverse {
|
|
||||||
color: #0c0c0c;
|
|
||||||
background: white;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
#logger .padder {
|
|
||||||
height: 0.5rem;
|
|
||||||
flex: 0 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************** Status ********************/
|
|
||||||
#status {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
#status .split {
|
|
||||||
margin-top: 2rem;
|
|
||||||
align-self: center;
|
|
||||||
width: calc(100% - 4rem);
|
|
||||||
display: flex;
|
|
||||||
max-width: 700px;
|
|
||||||
border: 1px solid #999;
|
|
||||||
border-right: none;
|
|
||||||
}
|
|
||||||
#status .item {
|
|
||||||
flex: 2 1 50%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
border-right: 1px solid #999;
|
|
||||||
padding-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
#status .item h4 {
|
|
||||||
font-size: 1.2rem;
|
|
||||||
text-align: center;
|
|
||||||
padding: 1rem;
|
|
||||||
border-bottom: 1px solid #999;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
#status .item p {
|
|
||||||
padding: 0.25rem 1rem;
|
|
||||||
}
|
|
||||||
#status .item p.running {
|
|
||||||
color: rgb(19,161,14);
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
#status .item p.notrunning {
|
|
||||||
color: rgb(197,15,31);
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
#status button {
|
|
||||||
margin-top: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************** Updater ********************/
|
|
||||||
#update {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: calc(100vw - 4rem);
|
|
||||||
align-self: center;
|
|
||||||
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.active {
|
|
||||||
background: transparent;
|
|
||||||
color: #ffb843;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-device-width: 590px) {
|
|
||||||
#update .actions .filler {
|
|
||||||
flex: 2 1 100%;
|
|
||||||
}
|
|
||||||
#update .actions .button {
|
|
||||||
flex: 2 1 50%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#update h4 {
|
|
||||||
text-align: center;
|
|
||||||
padding: 0 1rem 1rem;
|
|
||||||
font-size: 1.2rem;
|
|
||||||
}
|
|
||||||
#update .info {
|
|
||||||
margin-top: 0rem;
|
|
||||||
display: flex;
|
|
||||||
border: 1px solid #999;
|
|
||||||
border-right: none;
|
|
||||||
border-bottom: none;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
#update .info p {
|
|
||||||
flex: 2 1 50%;
|
|
||||||
border-right: 1px solid #999;
|
|
||||||
border-bottom: 1px solid #999;
|
|
||||||
padding: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
#update .console {
|
|
||||||
font-family: "Lucida Console", Monaco, monospace;
|
|
||||||
margin: 1rem 0 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
border: 1px solid #999;
|
|
||||||
padding: 0.5rem;
|
|
||||||
overflow-y: scroll;
|
|
||||||
height: calc(100vh - 160px);
|
|
||||||
background: #0c0c0c;
|
|
||||||
color: #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (min-height: 650px) {
|
|
||||||
#update .console {
|
|
||||||
height: calc(100vh - 340px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue