51 lines
1.8 KiB
JavaScript
51 lines
1.8 KiB
JavaScript
// Simple server adapted from https://developer.mozilla.org/en-US/docs/Learn/Server-side/Node_server_without_framework:
|
|
import * as fs from "fs";
|
|
import * as http from "http";
|
|
import * as path from "path";
|
|
const MIME_TYPES = {
|
|
default: "application/octet-stream",
|
|
html: "text/html; charset=UTF-8",
|
|
js: "application/javascript; charset=UTF-8",
|
|
mjs: "application/javascript; charset=UTF-8",
|
|
css: "text/css",
|
|
png: "image/png",
|
|
jpg: "image/jpg",
|
|
gif: "image/gif",
|
|
ico: "image/x-icon",
|
|
svg: "image/svg+xml",
|
|
};
|
|
|
|
const STATIC_PATH = path.join(process.cwd(), "./");
|
|
const toBool = [() => true, () => false];
|
|
|
|
export default function serve(port) {
|
|
if (!port)
|
|
throw new Error("Port is required");
|
|
|
|
const prepareFile = async (url) => {
|
|
const paths = [STATIC_PATH, url];
|
|
if (url.endsWith("/"))
|
|
paths.push("index.html");
|
|
const filePath = path.join(...paths);
|
|
const pathTraversal = !filePath.startsWith(STATIC_PATH);
|
|
const exists = await fs.promises.access(filePath).then(...toBool);
|
|
const found = !pathTraversal && exists;
|
|
const streamPath = found ? filePath : `${STATIC_PATH}/index.html`;
|
|
const ext = path.extname(streamPath).substring(1).toLowerCase();
|
|
const stream = fs.createReadStream(streamPath);
|
|
return { found, ext, stream };
|
|
};
|
|
|
|
const server = http
|
|
.createServer(async (req, res) => {
|
|
const file = await prepareFile(req.url);
|
|
const statusCode = file.found ? 200 : 404;
|
|
const mimeType = MIME_TYPES[file.ext] || MIME_TYPES.default;
|
|
res.writeHead(statusCode, { "Content-Type": mimeType });
|
|
file.stream.pipe(res);
|
|
})
|
|
.listen(port);
|
|
|
|
console.log(`Server running at http://127.0.0.1:${port}/`);
|
|
return server;
|
|
}
|