diff --git a/app.js b/app.js new file mode 100644 index 0000000..4b0f936 --- /dev/null +++ b/app.js @@ -0,0 +1,116 @@ +// Copyright 2013 Sami Samhuri + +var express = require('express') + , Negotiator = require('negotiator') + , kwikemon = require('./kwikemon.js') + , app = module.exports = express() + , version = require('./version.js') + ; + +// Middleware +app.use(express.favicon('/dev/null')); +app.use(express.logger()); +app.use(express.static(__dirname + '/public')); + +// Views +app.set('view engine', 'jade'); +app.set('views', __dirname + '/views'); + +// Routes +app.get('/', route('monitors', getMonitors)); +app.get('/:name', route('monitor', getMonitor)); + +function route(template, buildContext) { + return function(req, res) { + buildContext(req, res, function(err, ctx) { + if (err) { + var message = err.message || String(err) + , status = message == 'not found' ? 404 : 500 + ; + res.format({ + html: function() { + res.render('error', { + version: version, + pageTitle: 'Error', + err: err + }); + }, + text: function() { + res.send(renderText('error', { err: err })); + }, + json: function() { + res.json({ message: message }); + } + }); + } + else { + ctx = ctx || {}; + res.format({ + html: function() { + ctx.version = version; + res.render(template, ctx); + }, + text: function() { + res.send(renderText(template, ctx)); + }, + json: function() { + res.json(ctx); + } + }); + } + }); + }; +} + +// Rendering + +function renderText(template, ctx) { + var text; + switch (template) { + case 'monitor': + text = String(ctx.monitor.text); + break; + + case 'monitors': + text = Object.keys(ctx.monitors).sort().map(function(name) { + return name + ': ' + ctx.monitors[name].text; + }).join('\n'); + break; + + case 'error': + text = ctx.err.message || String(ctx.err); + break; + + default: + throw new Error('unknown text template: ' + template); + } + return text; +} + + +////////////////////// +// Request handlers // +////////////////////// + +function getMonitors(req, res, cb) { + kwikemon.getAll(function(err, monitors) { + cb(err, err ? null : { + pageTitle: 'Monitors', + monitors: monitors + }); + }); +} + +function getMonitor(req, res, cb) { + var name = req.params.name; + kwikemon.get(name, function(err, mon) { + if (!mon) { + err = new Error('not found'); + } + cb(err, err ? null : { + pageTitle: mon.name, + monitor: mon + }); + }); +} + diff --git a/bin/kwikemond b/bin/kwikemond index f6d420f..df42ded 100755 --- a/bin/kwikemond +++ b/bin/kwikemond @@ -1,8 +1,9 @@ #!/usr/bin/env node -var server = require('../server.js') - , port = process.argv[2] - , host = process.argv[3] +var port = process.argv[2] || 1111 + , host = process.argv[3] || '127.0.0.1' + , app = require('../app.js') ; -server.start(port, host); +app.listen(port, host); +console.log('kwikemond listening on ' + host + ':' + port); diff --git a/kwikemon.js b/kwikemon.js index 9e772e7..1d4af98 100644 --- a/kwikemon.js +++ b/kwikemon.js @@ -105,7 +105,8 @@ function set(name, text, options, cb) { ; exists(name, function(err, exists) { var fields = { - text: text + name: name + , text: text , modified: Date.now() } , multi = redis().multi() diff --git a/package.json b/package.json index 40a30d1..6042f0f 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,11 @@ , "kwikemond": "./bin/kwikemond" } , "dependencies": { - "redis": "0.8.x" - , "async": "0.2.x" + "async": "0.2.x" + , "express": "3.0.x" + , "jade": "0.31.x" + , "negotiator": "0.2.x" + , "redis": "0.8.x" , "toml": "0.4.x" } , "devDependencies": { diff --git a/public/css/style.css b/public/css/style.css new file mode 100644 index 0000000..97f15e5 --- /dev/null +++ b/public/css/style.css @@ -0,0 +1,15 @@ +ul#monitors { + list-style: none; +} + +li.monitor { + +} + +#footer { + width: 80%; + margin: 0.5em auto; + padding: 0.3em 1em; + text-align: center; + border-top: solid 1px #aaa; +} diff --git a/server.js b/server.js deleted file mode 100644 index 1c41287..0000000 --- a/server.js +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2013 Sami Samhuri - -module.exports = { - create: create -, start: start -, stop: stop -}; - -var http = require('http') - , kwikemon = require('./kwikemon.js') - , _server - ; - -function create() { - return http.createServer(handleRequest); -} - -function start(port, host) { - port = port || 1111; - host = host || '127.0.0.1'; - _server = create(); - _server.listen(port, host); - console.log('kwikemond listening on ' + host + ':' + port); - return _server; -} - -function stop() { - _server.close(); - _server = null; -} - -function handleRequest(req, res) { - var name = req.url.replace(/^\//, '') - , type = 'html' - , m - ; - if (name == 'favicon.ico') return res.end(); - if (m = name.match(/\.(json|txt)$/)) { - type = m[1]; - name = name.replace(RegExp('\.' + type + '$'), ''); - } - if (name) { - kwikemon.get(name, function(err, text) { - if (err) { - res.end('error: ' + (err.message || 'unknown')); - return; - } - res.end(text); - }); - } - // all - else { - kwikemon.getAll(function(err, monitors) { - if (err) { - res.end('error: ' + (err.message || 'unknown')); - return; - } - Object.keys(monitors).sort().forEach(function(name) { - res.write(name + ': ' + monitors[name] + '\n'); - }); - res.end(); - }); - } -} diff --git a/version.js b/version.js new file mode 100644 index 0000000..058c95f --- /dev/null +++ b/version.js @@ -0,0 +1,3 @@ +// Copyright 2013 Sami Samhuri + +module.exports = JSON.parse(require('fs').readFileSync('./package.json')).version; diff --git a/views/error.jade b/views/error.jade new file mode 100644 index 0000000..85cdefc --- /dev/null +++ b/views/error.jade @@ -0,0 +1,4 @@ +extends layout +block content + p. + error: #{err.message || err} diff --git a/views/layout.jade b/views/layout.jade new file mode 100644 index 0000000..400691e --- /dev/null +++ b/views/layout.jade @@ -0,0 +1,13 @@ +doctype 5 +html(lang="en") + head + title kwikemon + link(rel="stylesheet", href="/css/style.css") + body + block nav + block title + h1= pageTitle || 'kwikemon' + #content + block content + #footer + a(href="https://github.com/samsonjs/kwikemon") kwikemon #{version} diff --git a/views/monitor.jade b/views/monitor.jade new file mode 100644 index 0000000..4f45119 --- /dev/null +++ b/views/monitor.jade @@ -0,0 +1,5 @@ +extends layout +block nav + p: a(href="/") ← monitors +block content + p= monitor.text diff --git a/views/monitors.jade b/views/monitors.jade new file mode 100644 index 0000000..7f15a74 --- /dev/null +++ b/views/monitors.jade @@ -0,0 +1,5 @@ +extends layout +block content + ul#monitors + - each monitor in monitors + li.monitor: a(href="/#{monitor.name}") #{monitor.name}: #{monitor.text}