Web framework.
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.

index.js 4.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. var http = require("http");
  2. var urllib = require("url");
  3. var fs = require("fs");
  4. var res404 = "404 not found: {{method}} {{pathname}}";
  5. var res403 = "403 forbidden: {{method}} {{pathname}}";
  6. function template(tpml, args) {
  7. for (var i in args) {
  8. tpml = tpml.split("{{"+i+"}}").join(args[i]);
  9. }
  10. return tpml;
  11. }
  12. function resJson(obj) {
  13. this.writeHead(200, {
  14. "content-type": "application/json"
  15. });
  16. this.end(JSON.stringify(obj));
  17. }
  18. class App {
  19. constructor(options) {
  20. options = options || {};
  21. // 403 and 404 response
  22. this.res404 = options.res404 || res404;
  23. this.res403 = options.res403 || res403;
  24. // Script to be served as /webframe.js
  25. this.clientScript = "";
  26. // Create server
  27. if (options.server !== undefined) {
  28. this.server = options.server;
  29. } else {
  30. this.server = http.createServer();
  31. var port = options.port || process.env.PORT | 8080;
  32. var host = options.host || "127.0.0.1";
  33. this.server.listen(port, host);
  34. this.info("Listening on "+host+":"+port);
  35. }
  36. this._routeMap = {};
  37. this._routes = [];
  38. // Add scripts
  39. if (options.client_utils) {
  40. this.addScript("client_utils",
  41. fs.readFileSync(__dirname+"/client/utils.js", "utf8"));
  42. }
  43. // Serve /webframe.js
  44. this.get("/webframe.js", (req, res) => {
  45. res.writeHead(200, {
  46. "Content-Type": "application/javascript"
  47. });
  48. res.end(this.clientScript);
  49. });
  50. // Listen for requests
  51. this.server.on("request", (req, res) => {
  52. res.json = resJson;
  53. var url = urllib.parse(req.url);
  54. req.urlobj = url;
  55. var route = null;
  56. // If the route is in the hash map, use that
  57. var r = this._routeMap[url.pathname];
  58. if (r && (r.method === "ALL" || r.method === req.method)) {
  59. route = r;
  60. // Search through the routes list and look for matching routes
  61. } else {
  62. for (var i in this._routes) {
  63. var r = this._routes[i];
  64. if (r.method !== "ALL" && r.method !== req.method)
  65. continue;
  66. if (r.pattern.test(req.urlobj.pathname)) {
  67. route = r;
  68. break;
  69. }
  70. }
  71. }
  72. // If we still have no route, 404
  73. if (route === null) {
  74. res.writeHead(404);
  75. res.end(template(this.res404,
  76. { method: req.method, pathname: req.urlobj.pathname }));
  77. return;
  78. }
  79. // Run all the middleware stuff if applicable
  80. var self = this;
  81. if (route.middleware) {
  82. var cbs = route.middleware.length;
  83. function cb() {
  84. if (--cbs === 0)
  85. route.func(req, res, self);
  86. }
  87. for (var i in route.middleware) {
  88. route.middleware[i](req, res, cb);
  89. }
  90. // Just run the function if there's no middleware
  91. } else {
  92. route.func(req, res, this);
  93. }
  94. });
  95. }
  96. /*
  97. * Add route.
  98. * Args:
  99. * method: "GET", "POST", "PUT", "DELETE", "ALL"
  100. * path: path,
  101. * func: function
  102. */
  103. route(method, path, middleware, func) {
  104. if (method !== "GET" && method !== "POST" && method !== "PUT" &&
  105. method !== "DELETE" && method !== "ALL") {
  106. throw new Error("Invalid method.");
  107. }
  108. // Before is optional
  109. if (func === undefined) {
  110. func = middleware;
  111. middleware = undefined;
  112. }
  113. // All necessary arguments must exist
  114. if (typeof path !== "string")
  115. throw new TypeError("Path must be a string.");
  116. if (typeof func !== "function")
  117. throw new TypeError("Func must be a function.");
  118. // All middlewares must exist
  119. if (middleware) {
  120. for (var i in middleware) {
  121. if (typeof middleware[i] !== "function") {
  122. this.panic(
  123. "Middleware "+i+" for "+method+" "+path+
  124. " is "+(typeof middleware[i])+", expected function.");
  125. return;
  126. }
  127. }
  128. }
  129. // Add to routes array or route map
  130. if (path[0] === "^") {
  131. var pat = new RegExp(path);
  132. this._routes.push({
  133. method: method,
  134. func: func,
  135. pattern: pat,
  136. middleware: middleware
  137. });
  138. } else {
  139. this._routeMap[path] = {
  140. method: method,
  141. func: func,
  142. middleware: middleware
  143. };
  144. }
  145. }
  146. /*
  147. * Add code to be served as /webframe.js
  148. */
  149. addScript(name, str) {
  150. var start = "(function "+name+"() {\n";
  151. var end = "\n})();\n"
  152. str = str.trim();
  153. this.clientScript += start + str + end;
  154. }
  155. /*
  156. * Template string
  157. */
  158. template(tmpl, args) { return template(tmpl, args); }
  159. get(path, middleware, func) { this.route("GET", path, middleware, func); }
  160. post(path, middleware, func) { this.route("POST", path, middleware, func); }
  161. put(path, middleware, func) { this.route("PUT", path, middleware, func); }
  162. delete(path, middleware, func) { this.route("DELETE", path, middleware, func); }
  163. all(path, middleware, func) { this.route("ALL", path, middleware, func); }
  164. info(str) {
  165. console.log("Info: "+str);
  166. }
  167. notice(str) {
  168. console.log("Notice: "+str);
  169. }
  170. warning(str) {
  171. console.log("Warning: "+str);
  172. }
  173. panic(str) {
  174. console.log("PANIC: "+str);
  175. process.exit(1);
  176. }
  177. }
  178. exports.static = require("./js/static");
  179. exports.middleware = require("./js/middleware");;
  180. exports.App = App;