diff --git a/api/core/ioroutes.mjs b/api/core/ioroutes.mjs index 8c1855d..69aabb8 100644 --- a/api/core/ioroutes.mjs +++ b/api/core/ioroutes.mjs @@ -1,3 +1,4 @@ +import defaults from '../defaults.mjs' import { formatLog } from './loghelper.mjs' /* @@ -6,7 +7,16 @@ import { formatLog } from './loghelper.mjs' * Get config */ export async function config(ctx, data, cb) { - cb(ctx.config) + 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) } /* @@ -94,8 +104,8 @@ export async function updatestart(ctx, data, cb) { */ 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()) + ctx.socket.emit('core.db', ctx.db.data.core) + ctx.socket.emit('core.status', {}) } /* diff --git a/api/core/loghelper.mjs b/api/core/loghelper.mjs index 59e878f..97869d4 100644 --- a/api/core/loghelper.mjs +++ b/api/core/loghelper.mjs @@ -1,4 +1,4 @@ -import _ from 'lodash' +import defaults from '../defaults.mjs' import { format } from 'util' // http://en.wikipedia.org/wiki/ANSI_escape_code#graphics @@ -64,7 +64,7 @@ function indent(s) { } export function formatLog(data) { - let rec = _.cloneDeep(data) + let rec = defaults(data) delete rec.v; diff --git a/api/defaults.mjs b/api/defaults.mjs new file mode 100644 index 0000000..a2ef666 --- /dev/null +++ b/api/defaults.mjs @@ -0,0 +1,34 @@ + +// taken from isobject npm library +function isObject(val) { + return val != null && typeof val === 'object' && Array.isArray(val) === false +} + +export default 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 +} diff --git a/api/routerio.mjs b/api/routerio.mjs index 3f13c9b..74ddaf9 100644 --- a/api/routerio.mjs +++ b/api/routerio.mjs @@ -21,32 +21,31 @@ function register(ctx, name, method) { } -function onConnection(server, config, db, log, coreService, data) { +function onConnection(server, ctx, data) { const io = server const socket = data - const child = log.child({ + const child = ctx.log.child({ id: socket.id, }) - child.event = log.event + child.event = ctx.log.event child.info('Got new socket connection') - let ctx = { - config, - io, - socket, + let ioCtx = { + io: io, + socket: socket, log: child, - db, - core: coreService, - logroot: log, + db: ctx.db, + core: ctx.core, + logroot: ctx.log, } - ctx.socket.on('disconnect', function() { + ioCtx.socket.on('disconnect', function() { child.info('Closed connection') }) - register(ctx, 'core', core) + register(ioCtx, 'core', core) } export default onConnection diff --git a/api/server.mjs b/api/server.mjs index cdd1ef9..8cec5b1 100644 --- a/api/server.mjs +++ b/api/server.mjs @@ -1,84 +1,75 @@ -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 coremonitor from './core/coremonitor.mjs' import onConnection from './routerio.mjs' -export function run(config, db, log, core, http, port) { - return new Promise(function(resolve, reject) { - core.startMonitor() +export function run(http, port, ctx) { + let localUtil = new ctx.sc.Util(import.meta.url) - const __dirname = path.dirname(fileURLToPath(import.meta.url)) - const staticRoot = path.join(__dirname,'../public') + const staticRoot = localUtil.getPathFromRoot('../public') - const fileServer = new nStatic.Server(staticRoot) - const server = http.createServer(function (req, res) { - const child = log.child({}) + const fileServer = new nStatic.Server(staticRoot) + const server = http.createServer(function (req, res) { + const child = ctx.log.child({}) - const d1 = new Date().getTime() + 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 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}`) + let level = 'debug' + if (res.statusCode >= 400) { + level = 'warn' + } + if (res.statusCode >= 500) { + level = 'error' } - - res.addListener('finish', done); - res.addListener('close', done); - req.addListener('end', function () { - if (req.url === '/') { - res.writeHead(302, { Location: '/index.html' }) - return res.end() - } + let status = '' + if (res.statusCode >= 400) { + status = res.statusCode + ' ' + } - fileServer.serve(req, res, function (err) { - if (err) { - if (err.status !== 404) { - log.error(err, req.url); - } + child[level]({ + duration: requestTime, + status: res.statusCode, + }, `<-- ${status}${req.method} ${req.url}`) + } + + res.addListener('finish', done); + res.addListener('close', done); - res.writeHead(err.status, err.headers); - res.end(err.message); + 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) { + ctx.log.error(err, req.url); } - }); - }).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(port, '0.0.0.0', function(err) { - if (err) { - return reject(err) - } - log.event.info(`Server is listening on ${port} serving files on ${staticRoot}`) - log.info(`Server is listening on ${port} serving files on ${staticRoot}`) - resolve() - }) + res.writeHead(err.status, err.headers); + res.end(err.message); + } + }); + }).resume() }) + + const io = new socket(server) + io.on('connection', onConnection.bind(this, io, ctx)) + + // coremonitor(io, config, db, log, core) + + return server.listenAsync(port) + .then(function() { + ctx.log.info('Server is listening on port ' + port) + }) } \ No newline at end of file diff --git a/client/status/status.js b/client/status/status.js index 27de0f7..5ac8674 100644 --- a/client/status/status.js +++ b/client/status/status.js @@ -5,61 +5,43 @@ const Module = require('../module') const Status = Module({ init: function() { this._name = '...loading...' - this._management = { - name: 'manage', - port: null, - repository: null, - active: null, - latestInstalled: null, - latestVersion: null, - running: null, - updating: null, - starting: null, - } - this._app = { - name: 'app', - port: null, - repository: null, - active: null, - latestInstalled: null, - latestVersion: null, - running: null, - updating: null, - starting: null, - } + this._apps = [] 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 + 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) => { - this._management.active = res.manageActive - this._management.latestInstalled = res.manageLatestInstalled - this._management.latestVersion = res.manageLatestVersion - this._app.active = res.appActive - this._app.latestInstalled = res.appLatestInstalled - this._app.latestVersion = res.appLatestVersion + console.log('db', res) m.redraw() }) this.on('core.status', (res) => { console.log(res) - this._management.running = res.manage + /*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 + this._app.starting = res.appStarting*/ m.redraw() }) @@ -97,36 +79,36 @@ const Status = Module({ return m('div#status', [ m('h1.header', this._name), m('div.split', [ - loopOver.map((group) => { + this._apps.map((app) => { return m('div.item', [ - m('h4', group[0]), - m('p', this[group[1]].port - ? `Port: ${this[group[1]].port}` + m('h4', app.name), + m('p', app.config.port + ? `Port: ${app.config.port}` : ''), - m('p', this[group[1]].repository - ? `${this[group[1]].repository}` + m('p', app.config.repository + ? `${app.config.repository}` : '< no repository >'), - m('p', this[group[1]].active - ? `Running version: ${this[group[1]].active}` + m('p', app.config.active + ? `Running version: ${app.config.active}` : '< no running version >'), - m('p', this[group[1]].latestInstalled - ? `Latest installed: ${this[group[1]].latestInstalled}` + m('p', app.config.latestInstalled + ? `Latest installed: ${app.config.latestInstalled}` : '< no version installed >'), - m('p', this[group[1]].latestVersion - ? `Latest version: ${this[group[1]].latestVersion}` + m('p', app.config.latestVersion + ? `Latest version: ${app.config.latestVersion}` : '< no version found >'), - this[group[1]].running !== null && this[group[1]].repository + app.config.running !== null && app.config.repository ? m('p', - { class: this[group[1]].running ? 'running' : 'notrunning' }, - this[group[1]].running ? 'Running' : 'Not Running' + { class: app.config.running ? 'running' : 'notrunning' }, + app.config.running ? 'Running' : 'Not Running' ) : null, - !this[group[1]].running && (this[group[1]].updating || this[group[1]].starting) - ? m('div.status', this.getStatus(this[group[1]])) + !app.config.running && (app.config.updating || app.config.starting) + ? m('div.status', this.getStatus(app.config)) : null, m('button', { - hidden: this[group[1]].running || this[group[1]].updating || this[group[1]].starting || !this[group[1]].repository, - onclick: () => this.start(this[group[1]].name), + hidden: app.config.running || app.config.updating || app.config.starting || !app.config.repository, + onclick: () => this.start(app.config.name), }, 'Update/Start') ]) }), diff --git a/dev.mjs b/dev.mjs new file mode 100644 index 0000000..c283432 --- /dev/null +++ b/dev.mjs @@ -0,0 +1,42 @@ +import { ServiceCore, Application, getLog, Util, Core } from 'service-core' +import * as index from './index.mjs' + +let util = new Util(import.meta.url) + +const helloWorldConfig = { + "provider": "git", + "url": "https://git.nfp.is/api/v1/repos/thething/sc-helloworld/releases", + "port": 8888, +} + +var core = new ServiceCore('sc-manager', import.meta.url) +core.config['hello-world'] = helloWorldConfig +core.setConfig({ + port: 4271, +}) + +core.init(index).then(function() { + let provConstructor = Core.providers.get(core.db.config['hello-world'].provider) + let provider = new provConstructor(core.db.config['hello-world']) + + let helloWorld = new Application({ + db: core.db, + util: util, + log: getLog('hello-world'), + core: core.core, + }, provider, 'hello-world') + + core.core.applications.push(helloWorld) + core.core.applicationMap.set('hello-world', helloWorld) + + helloWorld.startAutoupdater() + + return core.run() +}).then(function() { + return core.core.applicationMap.get('hello-world').update() +}).then(function() { + return core.core.runApplication(core.core.applicationMap.get('hello-world')) +}).then(function() { + let helloApp = core.core.applicationMap.get('hello-world') + helloApp.on('updated', core.core.runApplication.bind(core.core, helloApp)) +}) \ No newline at end of file diff --git a/hello-world/.gitkeep b/hello-world/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/index.mjs b/index.mjs index ac92217..c8155c4 100644 --- a/index.mjs +++ b/index.mjs @@ -1,5 +1,5 @@ -export function start(config, db, log, core, http, port) { - return import('./api/server.mjs').then(function(module) { - return module.run(config, db, log, core, http, port) - }) -} \ No newline at end of file +import { run } from "./api/server.mjs" + +export function start(http, port, ctx) { + return run(http, port, ctx) +} diff --git a/package.json b/package.json index 8b8f47d..ff11a1a 100644 --- a/package.json +++ b/package.json @@ -4,24 +4,42 @@ "description": "", "main": "index.js", "scripts": { - "js:build:main": "asbundle client/index.js public/main.js", - "dev:server": "nodemon --watch runner.mjs --watch api runner.mjs | bunyan", - "dev:client": "nodemon --watch client --exec \"npm run js:build:main\"", - "build": "npm run js:build:main", + "build:main": "asbundle client/index.js public/main.js", + "build:main:watch": "npm-watch build:main", + "dev:server": "node dev.mjs | bunyan", + "dev:server:watch": "npm-watch dev:server", + "dev": "npm run dev:server && npm run dev:client", + "build": "npm run build:main", "test": "echo \"Error: no test specified\" && exit 1" }, + "watch": { + "dev:server": { + "patterns": [ + "api/*" + ], + "extensions": "js,mjs", + "quiet": true, + "inherit": true + }, + "build:main": { + "patterns": [ + "client/*" + ], + "extensions": "js,mjs", + "quiet": true, + "inherit": true + } + }, "author": "", "license": "WTFPL", "dependencies": { - "lodash": "^4.17.20", + "flaska": "^1.2.1", "node-static": "^0.7.11", "socket.io-serveronly": "^2.3.0" }, "devDependencies": { "asbundle": "^2.6.1", - "bunyan-lite": "^1.0.1", "mithril": "^2.0.4", - "nodemon": "^2.0.4", - "service-core": "^2.0.0" + "service-core": "^3.0.0-beta.11" } }