/** * rBooks standalone server — independent deployment at rbooks.online. * * Wraps the books module routes in a minimal Hono server with * standalone shell (just EncryptID identity, no app/space switcher). * * Usage: bun run modules/books/standalone.ts */ import { Hono } from "hono"; import { cors } from "hono/cors"; import { resolve } from "node:path"; import { booksModule } from "./mod"; import { renderStandaloneShell } from "../../server/shell"; const PORT = Number(process.env.PORT) || 3000; const DIST_DIR = resolve(import.meta.dir, "../../dist"); const app = new Hono(); app.use("/api/*", cors()); // WebAuthn related origins (passkey sharing with rspace.online) app.get("/.well-known/webauthn", (c) => { return c.json( { origins: ["https://rspace.online"] }, 200, { "Access-Control-Allow-Origin": "*", "Cache-Control": "public, max-age=3600" } ); }); // Mount books module routes at root app.route("/", booksModule.routes); // Static asset serving function getContentType(path: string): string { if (path.endsWith(".html")) return "text/html"; if (path.endsWith(".js")) return "application/javascript"; if (path.endsWith(".css")) return "text/css"; if (path.endsWith(".json")) return "application/json"; if (path.endsWith(".svg")) return "image/svg+xml"; if (path.endsWith(".png")) return "image/png"; if (path.endsWith(".jpg") || path.endsWith(".jpeg")) return "image/jpeg"; if (path.endsWith(".ico")) return "image/x-icon"; return "application/octet-stream"; } Bun.serve({ port: PORT, async fetch(req) { const url = new URL(req.url); // Static assets if (url.pathname !== "/" && !url.pathname.startsWith("/api/")) { const assetPath = url.pathname.slice(1); if (assetPath.includes(".")) { const filePath = resolve(DIST_DIR, assetPath); const file = Bun.file(filePath); if (await file.exists()) { return new Response(file, { headers: { "Content-Type": getContentType(assetPath) } }); } } } // Hono handles all routes return app.fetch(req); }, }); console.log(`rBooks standalone server running on http://localhost:${PORT}`);