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.3KB

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