1// This is the main file for paperclover.net's server.
2const app = new Hono();
3const console = log.scoped("http");
4
5// Middleware
6app.use(trimTrailingSlash());
7app.use(removeDuplicateSlashes);
8app.use(logger((msg) => msg.startsWith("-->") && console.info(msg.slice(4))));
9app.use(admin.middleware);
10
11// Backends
12app.route("", require("./blog/backend.ts").app);
13app.route("", require("./q+a/backend.ts").app);
14app.route("", require("./file-viewer/backend.ts").app);
15app.route("", require("./friend-auth.ts").app);
16app.route("", require("./backend-extra.ts").app);
17
18// Asset middleware has least precedence
19app.use(assets.middleware);
20
21// Asset Aliases
22app.get(
23 "/apple-touch-icon-precomposed.png",
24 (c) => assets.serveAsset(c, "/apple-touch-icon.png"),
25);
26app.get(
27 "/apple-touch-icon-57x57.png",
28 (c) => assets.serveAsset(c, "/apple-touch-icon.png"),
29);
30app.get(
31 "/apple-touch-icon-57x57-precomposed.png",
32 (c) => assets.serveAsset(c, "/apple-touch-icon.png"),
33);
34
35// Redirects
36app.get(
37 "/hotewig-reanimated",
38 (c) =>
39 c.redirect(
40 "https://docs.google.com/forms/d/e/1FAIpQLSdMdwotGKmrDYT8stYPm5uoOlmqZPLApYp8MuABDmcAwW6Fig/viewform?usp=header",
41 ),
42);
43
44// Handlers
45app.notFound(assets.notFound);
46
47export default app;
48
49async function removeDuplicateSlashes(c: Context, next: Next) {
50 const path = c.req.path;
51 if (/\/\/+/.test(path)) {
52 const normalizedPath = path.replace(/\/\/+/g, "/");
53 const query = c.req.query();
54 const queryString = Object.keys(query).length > 0
55 ? "?" + new URLSearchParams(query).toString()
56 : "";
57 return c.redirect(normalizedPath + queryString, 301);
58 }
59 await next();
60}
61
62import * as assets from "#sitegen/assets";
63import * as log from "@clo/lib/log";
64import { type Context, Hono, type Next } from "hono";
65import { logger } from "hono/logger";
66import { trimTrailingSlash } from "hono/trailing-slash";
67import * as admin from "./admin.ts";
68import "./file-viewer/sync.ts";