summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xbin/paste3
-rw-r--r--lib/main.js236
-rw-r--r--lib/paste.js68
-rw-r--r--lib/templates.js46
-rw-r--r--package.json5
l---------static/highlight1
-rw-r--r--static/main.css34
-rw-r--r--templates/base.tmpl16
-rw-r--r--templates/newPaste.tmpl5
-rw-r--r--templates/paste.tmpl5
-rw-r--r--templates/submit-form.tmpl18
11 files changed, 359 insertions, 78 deletions
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 <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}}