|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- var formidable = require("formidable");
- var crypto = require("crypto");
- var zlib = require("zlib");
- var fs = require("fs");
- var preprocess = require("./preprocess.js");
-
- var sessions = {};
- var sessionTimeouts = {};
-
- if (fs.existsSync(".sessions")) {
- sessions = JSON.parse(fs.readFileSync(".sessions", "utf8"));
-
- //Set appropriate timeouts.
- Object.keys(sessions).forEach(function(i) {
- sessionTimeouts[i] = setTimeout(function() {
- delete sessions[i];
- }, 1000*60*10);
- });
- }
-
- function saveSessions() {
- fs.writeFileSync(".sessions", JSON.stringify(sessions));
- process.exit();
- }
-
- process.on("SIGINT", saveSessions);
- process.on("SIGTERM", saveSessions);
-
- function templatify(str, args, ctx, env) {
- env.url = ctx.req.url;
-
- var env = {
- session: function(key) {
- ctx.cachable = false;
- return ctx.session[key];
- },
- arg: function(key) {
- return ctx.htmlEntities(args[key]);
- },
- noescape: args,
- env: env,
- template: function(key) {
- return ctx.template(key, args);
- }
- }
-
- str = preprocess(str, env);
-
- return str;
- }
-
- var cache = {};
-
- module.exports = function(options) {
- this.req = options.req;
- this.res = options.res;
- this.templates = options.templates;
- this.views = options.views;
- this.db = options.db;
- this.conf = options.conf;
- this.query = this.req.url.split("?")[1] || "";
- this.shouldGzip = /gzip/.test(this.req.headers["accept-encoding"]);
-
- this.cachable = true;
- this.statusCode = 200;
- this.headers = {};
- if (this.conf.debug)
- this.startTime = new Date();
-
- //Handle cookies
- this.cookies = {};
- this.req.headers.cookie = this.req.headers.cookie || "";
- this.req.headers.cookie.split(/;\s*/).forEach(function(elem) {
- var pair = elem.split("=");
- this.cookies[pair[0]] = decodeURIComponent(pair[1]);
- }.bind(this));
-
- //Handle sessions
- var key;
- if (sessions[this.cookies.session]) {
- key = this.cookies.session;
- } else {
- do {
- key = crypto.randomBytes(64).toString("hex");
- } while (sessions[key]);
-
- sessions[key] = {};
- this.res.setHeader("Set-Cookie", "session="+key);
- }
- this.session = sessions[key];
-
- //Reset session delete timer
- if (sessionTimeouts[key])
- clearTimeout(sessionTimeouts[key]);
-
- sessionTimeouts[key] = setTimeout(function() {
- delete sessions[key];
- }, this.conf.session_timeout);
- }
-
- module.exports.prototype = {
- _end: function(str) {
- if (this.conf.debug) {
- var ms = (new Date().getTime() - this.startTime.getTime());
- console.log(
- ms+" millisecond(s)\t"+
- (this.statusCode || 200)+"\t"+
- this.req.url
- );
- } else {
- console.log(this.req.url);
- }
-
- this.res.writeHead(this.statusCode, this.headers);
- this.res.end(str);
- },
-
- end: function(str, alreadyGzipped) {
- if (!this.shouldGzip)
- return this._end(str);
-
- this.setHeader("Content-Encoding", "gzip");
-
- if (alreadyGzipped)
- return this._end(str);
-
- zlib.gzip(str, function(err, res) {
- if (err)
- throw err;
-
- this._end(res);
- }.bind(this));
- },
-
- succeed: function(obj) {
- obj = obj || {};
- obj.success = true;
-
- this.setHeader("Content-Type", "application/json");
-
- this.end(JSON.stringify(obj));
- },
-
- fail: function(err) {
- console.log("Sending error to client:");
- console.trace(err);
-
- this.setHeader("Content-Type", "application/json");
-
- obj = {};
- obj.success = false;
- obj.error = err.toString();
- this.end(JSON.stringify(obj));
- },
-
- err404: function() {
- this.setStatus(404);
- this.end(this.view("404"));
- },
-
- template: function(name, args) {
- var str = this.templates[name];
- if (!str)
- throw new Error("No such template: "+name);
-
- return templatify(str, args, this, {template: name});
- },
-
- view: function(name, args) {
- var str = this.views[name];
- if (!str)
- throw new Error("No such view: "+name);
-
- return templatify(str, args, this, {view: name});
- },
-
- getPostData: function(cb) {
- if (this.postData)
- return cb(null, this.postData.data, this.postData.files);
-
- if (this.req.method.toUpperCase() != "POST")
- return cb(new Error("Expected POST request, got "+this.req.method));
-
- var form = new formidable.IncomingForm();
- form.parse(this.req, function(err, data, files) {
- if (err) return cb(err);
-
- this.postData = {
- data: data,
- files: files
- }
-
- cb(null, data, files);
- }.bind(this));
- },
-
- setStatus: function(code) {
- this.statusCode = code;
- },
-
- setHeader: function(key, val) {
- this.headers[key] = val;
- },
-
- login: function(username, id) {
- this.session.loggedIn = true;
- this.session.username = username;
- this.session.userId = id;
- },
-
- logout: function() {
- this.session.loggedIn = false;
- delete this.session.username;
- delete this.session.userId;
- },
-
- async: function(n, cb) {
- if (typeof n !== "number")
- throw new Error("Expected number, got "+typeof n);
-
- if (n < 1)
- return cb();
-
- var res = {};
- var errs = {};
- var errnum = 0;
-
- return function(key, val, err) {
- if (key)
- res[key] = val;
- if (err)
- errs[key] = err;
-
- if (n === 1)
- cb((errnum ? errs : null), res);
- else
- n -= 1;
- }
- },
-
- htmlEntities: function(arg) {
- if (typeof arg === "string") {
- return arg.replace(/&/g, "&")
- .replace(/</g, "<")
- .replace(/>/g, ">")
- .replace(/"/g, """);
- } else {
- return arg;
- }
- }
- }
|