|
|
@@ -0,0 +1,184 @@ |
|
|
|
var http = require("http"); |
|
|
|
var urllib = require("url"); |
|
|
|
var fs = require("fs"); |
|
|
|
|
|
|
|
var res404 = "404 not found: {{pathname}}"; |
|
|
|
var res403 = "403 forbidden: {{pathname}}"; |
|
|
|
|
|
|
|
function template(tpml, args) { |
|
|
|
for (var i in args) { |
|
|
|
tpml = tpml.split("{{"+i+"}}").join(args[i]); |
|
|
|
} |
|
|
|
return tpml; |
|
|
|
} |
|
|
|
|
|
|
|
class App { |
|
|
|
constructor(options) { |
|
|
|
options = options || {}; |
|
|
|
|
|
|
|
// 403 and 404 response |
|
|
|
this.res404 = options.res404 || res404; |
|
|
|
this.res403 = options.res403 || res403; |
|
|
|
|
|
|
|
// Script to be served as /webby.js |
|
|
|
this.clientScript = ""; |
|
|
|
|
|
|
|
// Create server |
|
|
|
if (options.server !== undefined) { |
|
|
|
this.server = options.server; |
|
|
|
} else { |
|
|
|
this.server = http.createServer(); |
|
|
|
|
|
|
|
var port = options.port || process.env.PORT | 8080; |
|
|
|
var host = options.host || "127.0.0.1"; |
|
|
|
|
|
|
|
this.server.listen(port, host); |
|
|
|
this.info("Listening on "+host+":"+port); |
|
|
|
} |
|
|
|
|
|
|
|
this._routeMap = {}; |
|
|
|
this._routes = []; |
|
|
|
|
|
|
|
// Add scripts |
|
|
|
if (options.client_utils) { |
|
|
|
this.addScript("client_utils", |
|
|
|
fs.readFileSync(__dirname+"/client/utils.js", "utf8")); |
|
|
|
} |
|
|
|
|
|
|
|
// Serve /webby.js |
|
|
|
this.get("/webby.js", (req, res) => { |
|
|
|
res.writeHead(200, { |
|
|
|
"Content-Type": "application/javascript" |
|
|
|
}); |
|
|
|
res.end(this.clientScript); |
|
|
|
}); |
|
|
|
|
|
|
|
// Listen for requests |
|
|
|
this.server.on("request", (req, res) => { |
|
|
|
var url = urllib.parse(req.url); |
|
|
|
req.urlobj = url; |
|
|
|
|
|
|
|
var route = null; |
|
|
|
|
|
|
|
// If the route is in the hash map, use that |
|
|
|
var r = this._routeMap[url.pathname]; |
|
|
|
if (r && (r.method === "ALL" || r.method === req.method)) { |
|
|
|
route = r; |
|
|
|
|
|
|
|
// Search through the routes list and look for matching routes |
|
|
|
} else { |
|
|
|
for (var i in this._routes) { |
|
|
|
var r = this._routes[i]; |
|
|
|
if (r.pattern.test(req.urlobj.pathname)) { |
|
|
|
route = r; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// If we still have no route, 404 |
|
|
|
if (route === null) { |
|
|
|
res.writeHead(404); |
|
|
|
res.end(template(this.res404, |
|
|
|
{ pathname: req.urlobj.pathname })); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// Run all the before stuff if applicable |
|
|
|
var self = this; |
|
|
|
if (route.before) { |
|
|
|
var cbs = route.before.length; |
|
|
|
function cb() { |
|
|
|
if (--cbs === 0) |
|
|
|
route.func(req, res, self); |
|
|
|
} |
|
|
|
|
|
|
|
for (var i in route.before) { |
|
|
|
route.before[i](req, res, cb); |
|
|
|
} |
|
|
|
|
|
|
|
// Just run the function if there's no before |
|
|
|
} else { |
|
|
|
route.func(req, res, this); |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
* Add route. |
|
|
|
* Args: |
|
|
|
* method: "GET", "POST", "PUT", "DELETE", "ALL" |
|
|
|
* path: path, |
|
|
|
* func: function |
|
|
|
*/ |
|
|
|
route(method, path, before, func) { |
|
|
|
if (method !== "GET" && method !== "POST" && method !== "PUT" && |
|
|
|
method !== "DELETE" && method !== "ALL") { |
|
|
|
throw new Error("Invalid method."); |
|
|
|
} |
|
|
|
|
|
|
|
// Before is optional |
|
|
|
if (func === undefined) { |
|
|
|
func = before; |
|
|
|
before = undefined; |
|
|
|
} |
|
|
|
|
|
|
|
// All necessary arguments must exist |
|
|
|
if (typeof path !== "string") |
|
|
|
throw new TypeError("Path must be a string."); |
|
|
|
if (typeof func !== "function") |
|
|
|
throw new TypeError("Func must be a function."); |
|
|
|
|
|
|
|
// Add to routes array or route map |
|
|
|
if (path[0] === "^") { |
|
|
|
var pat = new RegExp(path); |
|
|
|
this._routes.push({ |
|
|
|
method: method, |
|
|
|
func: func, |
|
|
|
pattern: pat, |
|
|
|
before: before |
|
|
|
}); |
|
|
|
} else { |
|
|
|
this._routeMap[path] = { |
|
|
|
method: method, |
|
|
|
func: func, |
|
|
|
before: before |
|
|
|
}; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
* Add code to be served as /webby.js |
|
|
|
*/ |
|
|
|
addScript(name, str) { |
|
|
|
var start = "(function "+name+"() {\n"; |
|
|
|
var end = "\n})();\n" |
|
|
|
|
|
|
|
str = str.trim(); |
|
|
|
this.clientScript += start + str + end; |
|
|
|
} |
|
|
|
|
|
|
|
get(path, before, func) { this.route("GET", path, before, func); } |
|
|
|
post(path, before, func) { this.route("POST", path, before, func); } |
|
|
|
put(path, before, func) { this.route("PUT", path, before, func); } |
|
|
|
delete(path, before, func) { this.route("DELETE", path, before, func); } |
|
|
|
all(path, before, func) { this.route("ALL", path, before, func); } |
|
|
|
|
|
|
|
info(str) { |
|
|
|
console.log("Info: "+str); |
|
|
|
} |
|
|
|
notice(str) { |
|
|
|
console.log("Notice: "+str); |
|
|
|
} |
|
|
|
warning(str) { |
|
|
|
console.log("Warning: "+str); |
|
|
|
} |
|
|
|
panic(str) { |
|
|
|
console.log("PANIC: "+str); |
|
|
|
process.exit(1); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
exports.static = require("./js/static"); |
|
|
|
exports.middleware = require("./js/middleware");; |
|
|
|
exports.App = App; |