Something went wrong. Try again.
Share secure logs from the command line
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127#!/usr/bin/env nodeimport http from "node:http";import fs from "node:fs/promises";import { buffer } from "node:stream/consumers";import { encrypt } from "./static/subtle.mjs";import { getGist, addGist } from "./src/database.js";import { PORT, BIND, BASE_URL, LIMIT_SIZE } from "./src/constants.js";
// load static files into memory at startupconst staticCache = { "styles.css": [await fs.readFile("./static/styles.css"), "text/css"], "home.mjs": [ await fs.readFile("./static/home.mjs"), "application/javascript", ], "view.mjs": [ await fs.readFile("./static/view.mjs"), "application/javascript", ], "subtle.mjs": [ await fs.readFile("./static/subtle.mjs"), "application/javascript", ],};
// simple template engineconst render = async (template) => { const layout = await fs.readFile("./src/views/_layout.html", "utf8"); const body = await fs.readFile(`./src/views/${template}.html`, "utf8"); return layout.replace("{{body}}", body).replaceAll("{{BASE_URL}}", BASE_URL);};
// route handlersconst serveStatic = (_req, res, match) => { const [contents, type] = staticCache[match.pathname.groups.file] || []; if (!contents) throw { code: 404, message: "Not found" };
res.setHeader("Content-Type", type); res.setHeader("Cache-Control", "max-age=2592000"); res.end(contents);};
const home = async (_req, res) => { res.setHeader("Content-Type", "text/html"); res.end(await render("home"));};
const view = async (_req, res) => { res.setHeader("Content-Type", "text/html"); res.end(await render("view"));};
const gistData = (_req, res, match) => { const gist = getGist(match.pathname.groups.uuid); if (!gist) throw { code: 404, message: "Not found" };
res.setHeader("Cache-Control", "max-age=2592000"); res.setHeader("X-IV", gist.iv); res.end(Buffer.from(gist.cipherText));};
const createGist = async (req, res) => { const requestSize = parseInt(req.headers["content-length"]); if (isNaN(requestSize) || requestSize > LIMIT_SIZE) throw { code: 413, message: "too large" }; if (requestSize === 0) throw { code: 406, message: "empty file" };
const body = await buffer(req); const { iv, k, cipherText } = await encrypt(body); const uuid = addGist(iv, cipherText);
res.statusCode = 201; res.end(`${BASE_URL}/${uuid}#${k}`);};
// routes using URLPattern (the order can be relevant for matching)const routes = [ { method: "GET", pattern: new URLPattern({ pathname: "/" }), handler: home }, { method: "GET", pattern: new URLPattern({ pathname: "/static/:file" }), handler: serveStatic, }, { method: "GET", pattern: new URLPattern({ pathname: "/data/:uuid" }), handler: gistData, }, { method: "GET", pattern: new URLPattern({ pathname: "/:uuid" }), handler: view, }, { method: "POST", pattern: new URLPattern({ pathname: "/" }), handler: createGist, },];
// request handlerconst handleRequests = async (req, res) => { const match = routes.find( (route) => req.method === route.method && route.pattern.test(req.url), ); if (match) { const route = match.pattern.exec(req.url); return await match.handler(req, res, route); }
throw { code: 404, message: "Not found" };};
// serverconst server = http.createServer(async (req, res) => { try { await handleRequests(req, res); } catch (err) { res.statusCode = err.code || 500; res.end(err.message || "Server error"); }});
server.listen(PORT, BIND, () => { console.log(`Listen on http://${BIND}:${PORT}`);});