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.

server.js 4.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. var http = require("http");
  2. var https = require("https");
  3. var fs = require("fs");
  4. var domain = require("domain");
  5. var zlib = require("zlib");
  6. var loader = require("./lib/loader.js");
  7. var pg = require("pg");
  8. var Context = require("./lib/context.js");
  9. var conf = JSON.parse(fs.readFileSync("conf.json"));
  10. var endpoints = {
  11. //General
  12. "/favicon.ico": "favicon.node.js",
  13. "/global.css": "global.css",
  14. "/global.js": "global.js",
  15. "/404": "404.node.js",
  16. //Index
  17. "/": "index/index.node.js",
  18. "/index/script.js": "index/script.js",
  19. "/index/style.css": "index/style.css",
  20. //Register
  21. "/register": "register/index.node.js",
  22. "/register/style.css": "register/style.css",
  23. "/register/script.js": "register/script.js",
  24. //Profile
  25. "/profile": "profile/index.node.js",
  26. "/profile/style.css": "profile/style.css",
  27. "/profile/script.js": "profile/script.js",
  28. //Settings
  29. "/settings": "settings/index.node.js",
  30. "/settings/style.css": "settings/style.css",
  31. "/settings/script.js": "settings/script.js",
  32. //Viewer
  33. "/view": "view/index.node.js",
  34. "/view/style.css": "view/style.css",
  35. "/view/script.js": "view/script.js",
  36. //Plain images
  37. "/i": "i/index.node.js",
  38. //API
  39. "/api/template": "api/template.node.js",
  40. "/api/image_create": "api/image_create.node.js",
  41. "/api/collection_create": "api/collection_create.node.js",
  42. "/api/account_create": "api/account_create.node.js",
  43. "/api/account_login": "api/account_login.node.js",
  44. "/api/account_logout": "api/account_logout.node.js",
  45. "/api/account_change_password": "api/account_change_password.node.js"
  46. }
  47. //We cache static resources for a long time. However, we want to invalidate
  48. //the browser's cache whenever a file updates. Therefore, we append
  49. //a number to all static files, and the number increases every time we start
  50. //the server. currentRun is that number.
  51. var currentRun;
  52. try {
  53. currentRun = parseInt(fs.readFileSync(".currentRun", "utf8"));
  54. } catch (err) {
  55. if (err.code === "ENOENT")
  56. currentRun = 0;
  57. else
  58. throw err;
  59. }
  60. currentRun = (currentRun >= conf.max_runs ? 0 : currentRun);
  61. currentRun = (currentRun || 0) + 1;
  62. conf.current_run = currentRun.toString();
  63. fs.writeFileSync(".currentRun", currentRun, "utf8");
  64. var loaded = loader.load(endpoints, conf);
  65. var db = new pg.Client(conf.db);
  66. var gzipCache = {};
  67. //Function to run on each request
  68. function onRequest(req, res) {
  69. var ctx = new Context({
  70. req: req,
  71. res: res,
  72. templates: loaded.templates,
  73. views: loaded.views,
  74. db: db,
  75. conf: conf
  76. });
  77. var ep = loaded.endpoints[req.url.split("?")[0]];
  78. //If the file doesn't exist, we 404.
  79. if (ep === undefined) {
  80. ep = loaded.endpoints["/404"];
  81. ctx.setStatus(404);
  82. }
  83. //Execute if it's a .node.js, or just respond with the contents of the file
  84. if (typeof ep == "function") {
  85. ep(ctx);
  86. } else {
  87. //Cache content for a while
  88. ctx.setHeader("Cache-Control", "public, max-age="+conf.cache_max_age);
  89. //Gzip and such
  90. if (ctx.shouldGzip && gzipCache[req.url]) {
  91. ctx.end(gzipCache[req.url], true);
  92. } else if (ctx.shouldGzip) {
  93. zlib.gzip(ep, function(err, res) {
  94. gzipCache[req.url] = res;
  95. ctx.end(res, true);
  96. });
  97. } else {
  98. ctx.end(ep);
  99. }
  100. }
  101. }
  102. //Initiate a postgresql client
  103. db.connect(function() {
  104. //Create HTTP or HTTPS server
  105. var server;
  106. if (conf.use_https) {
  107. server = https.createServer(conf.https, onRequest);
  108. } else {
  109. server = http.createServer(onRequest);
  110. }
  111. server.listen(conf.port);
  112. console.log("Listening on port "+conf.port+".");
  113. purgeCollections();
  114. });
  115. //On an interval, delete old collections from anonymous users
  116. function purgeCollections() {
  117. var timeout = conf.purge_collections_timeout;
  118. db.query(
  119. "DELETE FROM collections "+
  120. "WHERE user_id IS NULL "+
  121. "AND date_created < NOW() - INTERVAL '"+timeout+"'",
  122. function(err, res) {
  123. if (err)
  124. throw err;
  125. if (res.rowCount > 0) {
  126. console.log(
  127. "Deleted "+res.rowCount+" collections "+
  128. "from over "+timeout+" ago."
  129. );
  130. }
  131. }
  132. );
  133. }
  134. setTimeout(purgeCollections, conf.purge_collections_interval);
  135. //We don't want to crash even if something throws an uncaught exception.
  136. if (!conf.debug) {
  137. var d = domain.create();
  138. d.on("error", function(err) {
  139. console.trace(err);
  140. });
  141. process.on("uncaughtException", function(err) {
  142. console.trace(err);
  143. });
  144. }