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 --- bin/paste | 3 +- lib/main.js | 236 +++++++++++++++++++++++++++++++-------------- lib/paste.js | 68 +++++++++++++ lib/templates.js | 46 +++++++++ package.json | 5 +- static/highlight | 1 + static/main.css | 34 +++++++ templates/base.tmpl | 16 +++ templates/newPaste.tmpl | 5 + templates/paste.tmpl | 5 + templates/submit-form.tmpl | 18 ++++ 11 files changed, 359 insertions(+), 78 deletions(-) create mode 100644 lib/paste.js create mode 100644 lib/templates.js create mode 120000 static/highlight create mode 100644 static/main.css create mode 100644 templates/base.tmpl create mode 100644 templates/newPaste.tmpl create mode 100644 templates/paste.tmpl create mode 100644 templates/submit-form.tmpl diff --git a/bin/paste b/bin/paste index ae31e7b..4a2ea8f 100755 --- a/bin/paste +++ b/bin/paste @@ -1,3 +1,4 @@ #!/usr/bin/env node -require(__dirname + '/../lib/main.js'); +var main = require(__dirname + '/../lib/main.js'); +main.server(); 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 +}; diff --git a/package.json b/package.json index 76e8590..65200e9 100644 --- a/package.json +++ b/package.json @@ -5,9 +5,10 @@ "autor" : "Alexander Sulfrian ", "files" : ["."], "dependencies" : { - "choreographer" : "*", + "beeline" : "*", "querystring" : "*", - "kyoto" : "*" + "kyoto" : "*", + "nun" : "*" }, "main" : "./lib/main.js" } diff --git a/static/highlight b/static/highlight new file mode 120000 index 0000000..0c5b3ef --- /dev/null +++ b/static/highlight @@ -0,0 +1 @@ +../deps/highlight.js/styles/ \ No newline at end of file diff --git a/static/main.css b/static/main.css new file mode 100644 index 0000000..b34c5e8 --- /dev/null +++ b/static/main.css @@ -0,0 +1,34 @@ +body { + width: 80%; + margin: 0 auto; + text-align: center; +} + +textarea { + width: 95%; + height: 50em; + display: block; + margin: 0 auto; + border: 1px solid black; +} + +select#language { + width: 40%; + margin: 0 0 1em; +} + +input.submit { + width: 95%; + height: 2em; + margin: 2em auto 0; + border: 1px solid black; + background: #eee; +} + +input.submit:hover { + background: #ccc; +} + +fieldset { + border: 0; +} diff --git a/templates/base.tmpl b/templates/base.tmpl new file mode 100644 index 0000000..fdee53b --- /dev/null +++ b/templates/base.tmpl @@ -0,0 +1,16 @@ + + + + node-paste + + {{#styles}} + + {{/styles}} + + + + {{+ content}} + Nothing here. + {{/ content}} + + diff --git a/templates/newPaste.tmpl b/templates/newPaste.tmpl new file mode 100644 index 0000000..f6d710a --- /dev/null +++ b/templates/newPaste.tmpl @@ -0,0 +1,5 @@ +{{< base.tmpl}} + +{{+content}} +{{id}} +{{/content}} diff --git a/templates/paste.tmpl b/templates/paste.tmpl new file mode 100644 index 0000000..790ceea --- /dev/null +++ b/templates/paste.tmpl @@ -0,0 +1,5 @@ +{{< base.tmpl}} + +{{+ content}} +
{{&content}}
+{{/ content}} diff --git a/templates/submit-form.tmpl b/templates/submit-form.tmpl new file mode 100644 index 0000000..5755356 --- /dev/null +++ b/templates/submit-form.tmpl @@ -0,0 +1,18 @@ +{{< base.tmpl}} + +{{+ content}} +
+
+ Language: + + + + + +
+
+{{/ content}} -- cgit v1.2.3