Serve static files

This commit is contained in:
Starbeamrainbowlabs 2022-02-24 00:39:33 +00:00
parent b0fd916eb4
commit d5a9668e17
Signed by: sbrl
GPG key ID: 1BE5172E637709C2
6 changed files with 1156 additions and 964 deletions

1994
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -5,7 +5,7 @@
"main": "src/index.mjs",
"scripts": {
"test": "echo \"No tests have been implemented yet\"",
"build": "node src/static/esbuild.mjs"
"build": "node src/static/esbuild.mjs"
},
"repository": {
"type": "git",
@ -21,15 +21,18 @@
"@ltd/j-toml": "^1.29.0",
"applause-cli": "^1.8.0",
"jpake": "^1.0.1",
"mime": "^3.0.0",
"nexline": "^1.2.2",
"p-queue": "^7.2.0",
"p-reflect": "^3.0.0",
"p-retry": "^5.0.0",
"powahroot": "^1.1.1",
"pretty-ms": "^7.0.1",
"systeminformation": "^5.11.2",
"tweetnacl": "^1.0.3"
},
"devDependencies": {
"@types/mime": "^2.0.3",
"esbuild": "^0.14.23",
"fork-awesome": "^1.2.0"
}

View file

@ -2,12 +2,31 @@
import ServerRouter from 'powahroot/Server.mjs';
import middleware_log from './routes/middleware_log.mjs';
import middleware_catch_errors from './routes/middleware_errors.mjs';
import route_table from './routes/table.mjs';
import route_static from './routes/static.mjs';
export default function(sys) {
const router = new ServerRouter();
router.on_all(middleware_catch_errors.bind(this, sys.config.verbose));
router.on_all(middleware_log);
///
// API
///
router.get(`/api/table/:table_name`, route_table.bind(this, sys));
///
// Web interface
///
router.get(`/`, async (ctx, next) => {
ctx.params.filepath = `index.html`;
await route_static(ctx, next);
});
router.get(`/static/::filepath`, route_static);
return router;
}

View file

@ -0,0 +1,37 @@
"use strict";
import pretty_ms from 'pretty-ms';
import log from '../../../../io/NamespacedLog.mjs'; const l = log("http:request");
/**
* Handles errors thrown by handlers further down the chain.
* @param {RequestContext} context The RequestContext object.
* @param {Function} next The function to call to invoke the next middleware item
*/
async function middleware_catch_errors(verbose, context, next) {
try {
await next();
} catch(error) {
handle_error(verbose, error, context);
}
}
/**
* Handles a given error thrown by a given RequestContext.
* @param {Error} error The error that was thrown.
* @param {RequestContext} context The RequestContext from which the error was thrown.
*/
function handle_error(verbose, error, context) {
l.info(`[?ms] [${context.request.method} 503] ${context.request.connection.remoteAddress} -> ${context.request.url}`);
console.error(error.stack); // TODO: colourise this?
// TODO: Send a better error page - perhaps with an error id that's uploaded to some remote system, or the prettified stack trace & offending code encrypted with a key?
if(!verbose)
context.send.plain(503, "Error caught", "Oops! An error ocurred. Please report this to bugs@starbeamrainbowlabs.com");
else
context.send.plain(503, `*** Server Error ***\n${error.stack}\n`);
}
export default middleware_catch_errors;

View file

@ -0,0 +1,15 @@
"use strict";
import pretty_ms from 'pretty-ms';
import log from '../../../../io/NamespacedLog.mjs'; const l = log("http:request");
async function middleware_log_request(ctx, next) {
let start = new Date();
await next();
l.info(`[${pretty_ms(new Date() - start)}] [${ctx.request.method} ${ctx.response.statusCode}] ${ctx.request.connection.remoteAddress} -> ${ctx.request.url}`);
}
export default middleware_log_request;

View file

@ -0,0 +1,50 @@
"use strict";
import path from 'path';
import fs from 'fs';
import mime from 'mime';
const __dirname = import.meta.url.slice(7, import.meta.url.lastIndexOf("/"));
const root_dir = path.resolve(__dirname, "../../../../../static-dist");
function make_path_safe(path) {
return path.replace(/[^a-zA-Z0-9\-\._\/]/g, "")
.replace(/\.+/g, ".");
}
async function route_files(context, _next) {
let safe_url = make_path_safe(context.params.filepath);
let file_path = path.join(root_dir, safe_url);
try {
let info = await fs.promises.stat(file_path);
if(info && info.isFile()) {
context.response.writeHead(200, {
"content-type": mime.getType(file_path),
"content-length": info.size
});
fs.createReadStream(file_path)
.pipe(context.response);
}
else {
send_404(context, safe_url);
return;
}
} catch(error) {
if(error.code == "ENOENT") {
send_404(context, safe_url);
return;
}
else {
throw error;
}
}
}
function send_404(context, url) {
context.send.plain(404, `Error: ${url} couldn't be found.`);
}
export { route_files };
export default route_files;