From ec2f803a306c7843a8814029b5e77a9dadffd136 Mon Sep 17 00:00:00 2001 From: Alexander Sulfrian Date: Thu, 30 Jun 2011 23:48:38 +0200 Subject: progress: templates/css, new router, paste module as db abstraction --- lib/main.js | 236 +++++++++++++++++++++++++++++++++++++------------------ lib/paste.js | 68 ++++++++++++++++ lib/templates.js | 46 +++++++++++ 3 files changed, 275 insertions(+), 75 deletions(-) create mode 100644 lib/paste.js create mode 100644 lib/templates.js (limited to 'lib') diff --git a/lib/main.js b/lib/main.js index 6e2f36a..191dbaa 100644 --- a/lib/main.js +++ b/lib/main.js @@ -1,103 +1,189 @@ var http = require('http'), -router = require('choreographer').router(), +fs = require('fs'), +path = require('path'), +bee = require('beeline'), qs = require('querystring'), -uuid = require(__dirname + '/uuid'), hl = require(__dirname + '/highlight'), -kyoto = require('kyoto'); +tmpl = require(__dirname + '/templates'), +pasteInit = require(__dirname + '/paste'); -var db = new kyoto.open(__dirname + '/../db/pastes.kch', 'a+', kyotoOpen); +var config = { + port: 8080, + database: __dirname + '/../db/pastes.kch' +}; -function generateId(callback) { - var id = uuid.generate(14); - db.get(id, function(err, value) { - if (value) { - generateId(); - } - else { - callback(id); - } +var paste; + +function _200(res, contentType, data) { + res.writeHead(200, {'Content-Type': contentType}); + res.write(data); + res.end('\n'); +} + +function _404(res) { + res.writeHead(404, {'Content-Type': 'text/plain'}); + res.end('404: Not found.\n'); +} + +function _500(res, err) { + res.writeHead(404, {'Content-Type': 'text/plain'}); + res.write('500: Internal Server Error'); + + if (err) { + res.write(':\n'); + res.write(JSON.stringify(err)); + } + + res.end('\n'); +} + +function parsePost(req, callback) { + var content = ''; + + req.on('data', function(chunk) { + content += chunk; + }); + + req.on('end', function() { + var post = qs.parse(content); + callback(post); }); } -function getPaste(plain, req, res, paste) { - db.get(paste, function(err, value) { - if (err) { - res.writeHead(404, {'Content-Type': 'text/plain'}); - res.end('404: ' + req.url + ' not found:\n' + err + '\n'); - } - else { - var data = JSON.parse(value); - if (plain) { - res.writeHead(200, {'Content-Type': 'text/plain'}); - res.write(data.content); +function getContentType(ext) { + contentType = { + '.css': 'text/css', + '.js': 'text/javascript', + '.png': 'image/png' + }; + + if (contentType[ext]) { + return contentType[ext]; + } + else { + return 'unknown'; + } +} + +function checkPath(file, base) { + var normFile = path.normalize(file); + var normBase = path.normalize(base); + + if (normFile.indexOf(normBase) === 0) { + return true; + } + else { + return false; + } +} + +function serveStaticFiles(res, filepath) { + var base = path.join(__dirname, '..', 'static'); + var file = path.join(base, filepath); + + if (checkPath(file, base)) { + path.exists(file, function(exists) { + if (exists) { + var ext = path.extname(file); + var contentType = getContentType(ext); + + fs.readFile(file, function (err, data) { + if (err) { + _500(res, err); + } + else { + _200(res, contentType, data); + } + }); } else { - res.writeHead(200, {'Content-Type': 'text/html'}); - res.write(hl.highlight(data.content, data.language)); + _404(res); } - res.end('\n'); - } - }); + }); + } + else { + _404(res); + } } -router - .get('/plain/*', function(req, res, paste) { - getPaste(true, req, res, paste); - }) - .get('/get/*', function(req, res, paste) { - getPaste(false, req, res, paste); - }) - .get('/', function(req, res) { - }) - .post('/add', function(req, res) { - var content = ''; - - req.on('data', function(chunk) { - content += chunk; +var router = bee.route({ + "/": function(req, res) { + tmpl.renderHtml('submit-form.tmpl', {}, res); + }, + + "r`^/plain/([^/]+)$`": function(req, res, id) { + paste.get(id[0], function(data) { + if (data) { + _200(res, 'text/plain', data.content); + } + else { + _404(res); + } + }); + }, + + "r`^/get/([^/]+)$`": function(req, res, id) { + paste.get(id[0], function(data) { + if (data) { + data.styles = [{name: '/static/highlight/github.css'}]; + data.content = hl.highlight(data.content, data.language); + + tmpl.renderHtml('paste.tmpl', data, res); + } + else { + _404(res); + } }); + }, - req.on('end', function() { - var post = qs.parse(content); + "r`^/static/(.+)$`": function(req, res, filepath) { + serveStaticFiles(res, filepath[0]); + }, - generateId(function(id) { - var data = { - content: post.content, - language: post.language, - time: Date() - }; - db.set(id, JSON.stringify(data), function(err) { + "/add": { + POST: function(req, res) { + parsePost(req, function(post) { + paste.add(post, function(err, id) { if (err) { - res.writeHead(500, {'Content-Type': 'text/plain'}); - res.write(err); - res.end('\n'); + _500(res, err); } else { console.log('new paste: %s', id); - res.writeHead(200, {'Content-Type': 'text/plain'}); - res.write(id); - res.end('\n'); + tmpl.renderHtml('newPaste.tmpl', {id: id}, res); } }); }); - }); - }) - .notFound(function(req, res) { - res.writeHead(404, {'Content-Type': 'text/plain'}); - res.end('404: ' + req.url + ' not found.\n'); - }); - + }, -function kyotoOpen(err) { - if (err) throw err; + GET: function(req, res) { + _404(res); + } + }, - http.createServer(router).listen(8080); - console.log('Listening on port 8080...'); -} + "`404`": function(req, res) { + _404(res); + }, -process.on('uncaughtException', function(exeption) { - process.exit(1); + "`503`": function(req, res, err) { + _500(res, err); + } }); -process.on('exit', function() { - db.close(function(err) { console.log(err); }); -}); +var server = function(port) { + port = port || config.port; + + pasteInit.init(config, function(func) { + paste = func; + + http.createServer(router).listen(port); + console.log('Listening on port %d...', port); + }); +}; + +if (typeof module == "object" && typeof require == "function") { + exports.server = server; + exports.config = config; +} +if (module === require.main) { + server(); +} diff --git a/lib/paste.js b/lib/paste.js new file mode 100644 index 0000000..a60bee2 --- /dev/null +++ b/lib/paste.js @@ -0,0 +1,68 @@ +var kyoto = require('kyoto'), +uuid = require(__dirname + '/uuid'); + +var db; + +var generateId = function(callback) { + var id = uuid.generate(14); + + db.get(id, function(err, value) { + if (value) { + generateId(); + } + else { + callback(id); + } + }); +}; + +var get = function(id, callback) { + db.get(id, function(err, value) { + if (value) { + value = JSON.parse(value); + } + + callback(value); + }); +}; + +var add = function(post, callback) { + generateId(function(id) { + var data = { + content: post.content, + language: post.language, + time: new Date() + }; + + db.set(id, JSON.stringify(data), function(err) { + callback(err, id); + }); + }); +}; + +var init = function(config, callback) { + db = new kyoto.open(config.database, 'a+', function(err) { + if (err) throw err; + + process.on('uncaughtException', function(exeption) { + console.error('%j', exeption); + process.exit(1); + }); + + process.on('exit', function() { + db.close(function(err) { console.log(err); }); + }); + + var thismodule = { + get: get, + add: add + }; + + callback(thismodule); + + }); +}; + +module.exports = { + init: init +}; diff --git a/lib/templates.js b/lib/templates.js new file mode 100644 index 0000000..c470454 --- /dev/null +++ b/lib/templates.js @@ -0,0 +1,46 @@ +var nun = require('nun'); + +var templates = {}; + +var render = function(template, args, callback) { + if (template in templates) { + callback(undefined, templates[template](args)); + } + else { + nun.compile(__dirname + '/../templates/' + template, "", function(err, tmpl) { + if (err) { + callback(err); + } + else { + templates[template] = tmpl; + callback(err, tmpl(args)); + } + }); + } +}; + +var renderHtml = function(template, args, res) { + render(template, args, function(err, events) { + if (err) { + res.writeHead(500, {'Content-Type': 'text/plain'}); + res.write(JSON.stringify(err)); + res.end('\n'); + } + else { + res.writeHead(200, {'Content-Type': 'text/html'}); + + events.on('data', function(data) { + res.write(data); + }); + + events.on('end', function() { + res.end('\n'); + }); + } + }); +}; + +module.exports = { + render: render, + renderHtml: renderHtml +}; -- cgit v1.2.3