diff options
Diffstat (limited to '')
-rwxr-xr-x | bin/paste | 3 | ||||
-rw-r--r-- | lib/main.js | 236 | ||||
-rw-r--r-- | lib/paste.js | 68 | ||||
-rw-r--r-- | lib/templates.js | 46 | ||||
-rw-r--r-- | package.json | 5 | ||||
l--------- | static/highlight | 1 | ||||
-rw-r--r-- | static/main.css | 34 | ||||
-rw-r--r-- | templates/base.tmpl | 16 | ||||
-rw-r--r-- | templates/newPaste.tmpl | 5 | ||||
-rw-r--r-- | templates/paste.tmpl | 5 | ||||
-rw-r--r-- | templates/submit-form.tmpl | 18 |
11 files changed, 359 insertions, 78 deletions
@@ -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 <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 @@ +<!doctype html> +<html> +<head> + <title>node-paste</title> + <link href="/static/main.css" rel="stylesheet" type="text/css" /> + {{#styles}} + <link href="{{name}}" rel="stylesheet" type="text/css" /> + {{/styles}} +</head> + +<body> + {{+ content}} + Nothing here. + {{/ content}} +</body> +</html> 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}} +<a href="/get/{{id}}">{{id}}</a> +{{/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}} +<pre>{{&content}}</pre> +{{/ 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}} + <form action="/add" method="POST"> + <fieldset> + <lable for="language">Language:</lable> + <select id="language" name="language"> + <option value="plain">plain text</option> + <option value="javascript">JavaScript</option> + <option value="auto">auto detection</option> + </select> + + <textarea name="content"></textarea> + + <input class="submit" type="submit" value="paste" /> + </fieldset> + </form> +{{/ content}} |