Simple image host.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

context.js 4.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. var formidable = require("formidable");
  2. var crypto = require("crypto");
  3. var zlib = require("zlib");
  4. var preprocess = require("./preprocess.js");
  5. var sessions = {};
  6. function templatify(str, args, ctx, env) {
  7. env.url = ctx.req.url;
  8. str = preprocess(str, {
  9. session: function(key) {
  10. ctx.cachable = false;
  11. return ctx.session[key];
  12. },
  13. arg: function(key) {
  14. return ctx.htmlEntities(args[key]);
  15. },
  16. noescape: args,
  17. env: env,
  18. template: function(key) {
  19. return ctx.template(key);
  20. }
  21. });
  22. return str;
  23. }
  24. var cache = {};
  25. module.exports = function(options) {
  26. this.req = options.req;
  27. this.res = options.res;
  28. this.templates = options.templates;
  29. this.views = options.views;
  30. this.db = options.db;
  31. this.conf = options.conf;
  32. this.query = this.req.url.split("?")[1] || "";
  33. this.shouldGzip = /gzip/.test(this.req.headers["accept-encoding"]);
  34. this.cachable = true;
  35. this.statusCode = 200;
  36. this.headers = {};
  37. if (this.conf.debug)
  38. this.startTime = new Date();
  39. //Handle cookies
  40. this.cookies = {};
  41. this.req.headers.cookie = this.req.headers.cookie || "";
  42. this.req.headers.cookie.split(/;\s*/).forEach(function(elem) {
  43. var pair = elem.split("=");
  44. this.cookies[pair[0]] = decodeURIComponent(pair[1]);
  45. }.bind(this));
  46. //Handle sessions
  47. var key;
  48. if (sessions[this.cookies.session]) {
  49. key = this.cookies.session;
  50. } else {
  51. do {
  52. key = crypto.randomBytes(64).toString("hex");
  53. } while (sessions[key]);
  54. sessions[key] = {};
  55. this.res.setHeader("Set-Cookie", "session="+key);
  56. //Delete session after a while
  57. setTimeout(function() {
  58. delete sessions[key];
  59. }, this.conf.session_timeout);
  60. }
  61. this.session = sessions[key];
  62. //Reset session delete timer
  63. if (this.session._timeout)
  64. clearTimeout(this.session._timeout);
  65. this.session._timeout = setTimeout(function() {
  66. delete sessions[key];
  67. }, this.conf.session_timeout);
  68. }
  69. module.exports.prototype = {
  70. _end: function(str) {
  71. if (this.conf.debug) {
  72. var ms = (new Date().getTime() - this.startTime.getTime());
  73. console.log(
  74. ms+" millisecond(s)\t"+
  75. (this.statusCode || 200)+"\t"+
  76. this.req.url
  77. );
  78. } else {
  79. console.log(this.req.url);
  80. }
  81. this.res.writeHead(this.statusCode, this.headers);
  82. this.res.end(str);
  83. },
  84. end: function(str, alreadyGzipped) {
  85. if (!this.shouldGzip)
  86. return this._end(str);
  87. this.setHeader("Content-Encoding", "gzip");
  88. if (alreadyGzipped)
  89. return this._end(str);
  90. zlib.gzip(str, function(err, res) {
  91. if (err)
  92. throw err;
  93. this._end(res);
  94. }.bind(this));
  95. },
  96. succeed: function(obj) {
  97. obj = obj || {};
  98. obj.success = true;
  99. this.setHeader("Content-Type", "application/json");
  100. this.end(JSON.stringify(obj));
  101. },
  102. fail: function(err) {
  103. console.log("Sending error to client:");
  104. console.trace(err);
  105. this.setHeader("Content-Type", "application/json");
  106. obj = {};
  107. obj.success = false;
  108. obj.error = err.toString();
  109. this.end(JSON.stringify(obj));
  110. },
  111. err404: function() {
  112. this.setStatus(404);
  113. this.end(this.view("404"));
  114. },
  115. template: function(name, args) {
  116. var str = this.templates[name];
  117. if (!str)
  118. throw new Error("No such template: "+name);
  119. return templatify(str, args, this, {template: name});
  120. },
  121. view: function(name, args) {
  122. var str = this.views[name];
  123. if (!str)
  124. throw new Error("No such view: "+name);
  125. return templatify(str, args, this, {view: name});
  126. },
  127. getPostData: function(cb) {
  128. if (this.postData)
  129. return cb(null, this.postData.data, this.postData.files);
  130. if (this.req.method.toUpperCase() != "POST")
  131. return cb(new Error("Expected POST request, got "+this.req.method));
  132. var form = new formidable.IncomingForm();
  133. form.parse(this.req, function(err, data, files) {
  134. if (err) return cb(err);
  135. this.postData = {
  136. data: data,
  137. files: files
  138. }
  139. cb(null, data, files);
  140. }.bind(this));
  141. },
  142. setStatus: function(code) {
  143. this.statusCode = code;
  144. },
  145. setHeader: function(key, val) {
  146. this.headers[key] = val;
  147. },
  148. login: function(username, id) {
  149. this.session.loggedIn = true;
  150. this.session.username = username;
  151. this.session.userId = id;
  152. },
  153. logout: function() {
  154. this.session.loggedIn = false;
  155. delete this.session.username;
  156. delete this.session.userId;
  157. },
  158. async: function(n, cb) {
  159. if (typeof n !== "number")
  160. throw new Error("Expected number, got "+typeof n);
  161. if (n < 1)
  162. return cb();
  163. var res = {};
  164. var errs = {};
  165. var errnum = 0;
  166. return function(key, val, err) {
  167. if (key)
  168. res[key] = val;
  169. if (err)
  170. errs[key] = err;
  171. if (n === 1)
  172. cb((errnum ? errs : null), res);
  173. else
  174. n -= 1;
  175. }
  176. },
  177. htmlEntities: function(arg) {
  178. if (typeof arg === "string") {
  179. return arg.replace(/&/g, "&amp;")
  180. .replace(/</g, "&lt;")
  181. .replace(/>/g, "&gt;")
  182. .replace(/"/g, "&quot;");
  183. } else {
  184. return arg;
  185. }
  186. }
  187. }