From f9bda6c35da65c6a0eff4073e9c5ac6cf9317132 Mon Sep 17 00:00:00 2001 From: Jeff Emmett Date: Wed, 25 Feb 2026 18:45:41 -0800 Subject: [PATCH] =?UTF-8?q?refactor:=20remove=20iframe=20shell=20=E2=80=94?= =?UTF-8?q?=20render=20all=20modules=20directly=20via=20web=20components?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every module except canvas was using renderIframeShell() to embed standalone domains (rdata.online, rwork.online, etc.) via iframe. None of these domains had independent deployments — they routed back to the same container, causing infinite redirect loops or 404s. Now all 22 modules render their web components directly inside renderShell(), eliminating cross-origin failures, iframe loading spinners, and ~820 lines of dead code. Standalone domain requests are internally rewritten to module routes instead of 301 redirecting. - Remove renderIframeShell(), renderStandaloneShell(), IframeShellOptions - Remove keepStandalone set; rewrite standalone domains internally - Convert all module GET / handlers to renderShell + components - Delete 20 standalone.ts entry points (circular/broken) Co-Authored-By: Claude Opus 4.6 --- modules/books/mod.ts | 8 +- modules/books/standalone.ts | 71 ---------- modules/cal/mod.ts | 8 +- modules/cal/standalone.ts | 17 --- modules/cart/mod.ts | 8 +- modules/cart/standalone.ts | 58 -------- modules/choices/mod.ts | 8 +- modules/choices/standalone.ts | 45 ------ modules/data/mod.ts | 8 +- modules/data/standalone.ts | 17 --- modules/files/mod.ts | 8 +- modules/files/standalone.ts | 23 --- modules/forum/mod.ts | 8 +- modules/forum/standalone.ts | 23 --- modules/funds/mod.ts | 10 +- modules/funds/standalone.ts | 45 ------ modules/inbox/mod.ts | 8 +- modules/inbox/standalone.ts | 17 --- modules/maps/mod.ts | 8 +- modules/maps/standalone.ts | 17 --- modules/network/mod.ts | 8 +- modules/network/standalone.ts | 17 --- modules/notes/mod.ts | 8 +- modules/notes/standalone.ts | 17 --- modules/photos/mod.ts | 8 +- modules/providers/mod.ts | 8 +- modules/providers/standalone.ts | 54 ------- modules/pubs/mod.ts | 8 +- modules/pubs/standalone.ts | 57 -------- modules/swag/mod.ts | 8 +- modules/swag/standalone.ts | 54 ------- modules/trips/mod.ts | 8 +- modules/trips/standalone.ts | 17 --- modules/tube/mod.ts | 8 +- modules/tube/standalone.ts | 17 --- modules/vote/mod.ts | 8 +- modules/vote/standalone.ts | 17 --- modules/wallet/mod.ts | 8 +- modules/wallet/standalone.ts | 17 --- modules/work/mod.ts | 8 +- modules/work/standalone.ts | 17 --- server/index.ts | 48 ++----- server/shell.ts | 241 ++------------------------------ 43 files changed, 129 insertions(+), 947 deletions(-) delete mode 100644 modules/books/standalone.ts delete mode 100644 modules/cal/standalone.ts delete mode 100644 modules/cart/standalone.ts delete mode 100644 modules/choices/standalone.ts delete mode 100644 modules/data/standalone.ts delete mode 100644 modules/files/standalone.ts delete mode 100644 modules/forum/standalone.ts delete mode 100644 modules/funds/standalone.ts delete mode 100644 modules/inbox/standalone.ts delete mode 100644 modules/maps/standalone.ts delete mode 100644 modules/network/standalone.ts delete mode 100644 modules/notes/standalone.ts delete mode 100644 modules/providers/standalone.ts delete mode 100644 modules/pubs/standalone.ts delete mode 100644 modules/swag/standalone.ts delete mode 100644 modules/trips/standalone.ts delete mode 100644 modules/tube/standalone.ts delete mode 100644 modules/vote/standalone.ts delete mode 100644 modules/wallet/standalone.ts delete mode 100644 modules/work/standalone.ts diff --git a/modules/books/mod.ts b/modules/books/mod.ts index c331011..c9490fa 100644 --- a/modules/books/mod.ts +++ b/modules/books/mod.ts @@ -10,7 +10,7 @@ import { resolve } from "node:path"; import { mkdir, readFile } from "node:fs/promises"; import { randomUUID } from "node:crypto"; import { sql } from "../../shared/db/pool"; -import { renderShell, renderIframeShell } from "../../server/shell"; +import { renderShell } from "../../server/shell"; import { getModuleInfoList } from "../../shared/module"; import type { RSpaceModule } from "../../shared/module"; import { @@ -204,13 +204,15 @@ routes.get("/api/books/:id/pdf", async (c) => { // ── Page: Library ── routes.get("/", (c) => { const spaceSlug = c.req.param("space") || "personal"; - return c.html(renderIframeShell({ + return c.html(renderShell({ title: `${spaceSlug} — Library | rSpace`, moduleId: "books", spaceSlug, modules: getModuleInfoList(), theme: "dark", - standaloneDomain: "rbooks.online", + body: ``, + scripts: ``, + styles: ``, })); }); diff --git a/modules/books/standalone.ts b/modules/books/standalone.ts deleted file mode 100644 index 7b9df85..0000000 --- a/modules/books/standalone.ts +++ /dev/null @@ -1,71 +0,0 @@ -/** - * 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}`); diff --git a/modules/cal/mod.ts b/modules/cal/mod.ts index eeec1d7..4309bd1 100644 --- a/modules/cal/mod.ts +++ b/modules/cal/mod.ts @@ -9,7 +9,7 @@ import { Hono } from "hono"; import { readFileSync } from "node:fs"; import { resolve } from "node:path"; import { sql } from "../../shared/db/pool"; -import { renderShell, renderIframeShell } from "../../server/shell"; +import { renderShell } from "../../server/shell"; import { getModuleInfoList } from "../../shared/module"; import type { RSpaceModule } from "../../shared/module"; import { verifyEncryptIDToken, extractToken } from "@encryptid/sdk/server"; @@ -375,13 +375,15 @@ routes.get("/api/context/:tool", async (c) => { // ── Page route ── routes.get("/", (c) => { const space = c.req.param("space") || "demo"; - return c.html(renderIframeShell({ + return c.html(renderShell({ title: `${space} — Calendar | rSpace`, moduleId: "cal", spaceSlug: space, modules: getModuleInfoList(), theme: "dark", - standaloneDomain: "rcal.online", + body: ``, + scripts: ``, + styles: ``, })); }); diff --git a/modules/cal/standalone.ts b/modules/cal/standalone.ts deleted file mode 100644 index 9f4346a..0000000 --- a/modules/cal/standalone.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Standalone server for the Cal module. - * Serves rcal.online independently. - */ - -import { Hono } from "hono"; -import { serveStatic } from "hono/bun"; -import { calModule } from "./mod"; - -const app = new Hono(); - -app.use("/modules/cal/*", serveStatic({ root: "./dist" })); -app.use("/*", serveStatic({ root: "./dist" })); -app.route("/", calModule.routes); - -console.log(`[rCal Standalone] Listening on :3000`); -export default { port: 3000, fetch: app.fetch }; diff --git a/modules/cart/mod.ts b/modules/cart/mod.ts index 3d07441..f52227b 100644 --- a/modules/cart/mod.ts +++ b/modules/cart/mod.ts @@ -10,7 +10,7 @@ import { Hono } from "hono"; import { readFileSync } from "node:fs"; import { resolve } from "node:path"; import { sql } from "../../shared/db/pool"; -import { renderShell, renderIframeShell } from "../../server/shell"; +import { renderShell } from "../../server/shell"; import { getModuleInfoList } from "../../shared/module"; import { depositOrderRevenue } from "./flow"; import type { RSpaceModule } from "../../shared/module"; @@ -441,13 +441,15 @@ routes.post("/api/fulfill/resolve", async (c) => { // ── Page route: shop ── routes.get("/", (c) => { const space = c.req.param("space") || "demo"; - return c.html(renderIframeShell({ + return c.html(renderShell({ title: `Shop | rSpace`, moduleId: "cart", spaceSlug: space, modules: getModuleInfoList(), theme: "dark", - standaloneDomain: "rcart.online", + body: ``, + scripts: ``, + styles: ``, })); }); diff --git a/modules/cart/standalone.ts b/modules/cart/standalone.ts deleted file mode 100644 index 518903f..0000000 --- a/modules/cart/standalone.ts +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Cart standalone server — independent deployment at rcart.online. - */ - -import { Hono } from "hono"; -import { cors } from "hono/cors"; -import { resolve } from "node:path"; -import { cartModule } from "./mod"; - -const PORT = Number(process.env.PORT) || 3000; -const DIST_DIR = resolve(import.meta.dir, "../../dist"); - -const app = new Hono(); -app.use("/api/*", cors({ - origin: "*", - exposeHeaders: ["X-PAYMENT-REQUIRED", "X-PAYMENT-RESPONSE"], - allowHeaders: ["Content-Type", "Authorization", "X-PAYMENT", "X-PAYMENT-RESPONSE"], -})); - -app.get("/.well-known/webauthn", (c) => { - return c.json( - { origins: ["https://rspace.online"] }, - 200, - { "Access-Control-Allow-Origin": "*", "Cache-Control": "public, max-age=3600" } - ); -}); - -app.route("/", cartModule.routes); - -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(".ico")) return "image/x-icon"; - return "application/octet-stream"; -} - -Bun.serve({ - port: PORT, - async fetch(req) { - const url = new URL(req.url); - if (url.pathname !== "/" && !url.pathname.startsWith("/api/")) { - const assetPath = url.pathname.slice(1); - if (assetPath.includes(".")) { - const file = Bun.file(resolve(DIST_DIR, assetPath)); - if (await file.exists()) { - return new Response(file, { headers: { "Content-Type": getContentType(assetPath) } }); - } - } - } - return app.fetch(req); - }, -}); - -console.log(`rCart standalone server running on http://localhost:${PORT}`); diff --git a/modules/choices/mod.ts b/modules/choices/mod.ts index ff26119..c4310bb 100644 --- a/modules/choices/mod.ts +++ b/modules/choices/mod.ts @@ -9,7 +9,7 @@ */ import { Hono } from "hono"; -import { renderShell, renderIframeShell } from "../../server/shell"; +import { renderShell } from "../../server/shell"; import type { RSpaceModule } from "../../shared/module"; import { getModuleInfoList } from "../../shared/module"; import { getDocumentData } from "../../server/community-store"; @@ -48,13 +48,15 @@ routes.get("/api/choices", async (c) => { // GET / — choices page routes.get("/", (c) => { const spaceSlug = c.req.param("space") || "demo"; - return c.html(renderIframeShell({ + return c.html(renderShell({ title: `${spaceSlug} — Choices | rSpace`, moduleId: "choices", spaceSlug, modules: getModuleInfoList(), theme: "dark", - standaloneDomain: "rchoices.online", + body: ``, + scripts: ``, + styles: ``, })); }); diff --git a/modules/choices/standalone.ts b/modules/choices/standalone.ts deleted file mode 100644 index ee08863..0000000 --- a/modules/choices/standalone.ts +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Choices standalone server — independent deployment at choices.jeffemmett.com. - */ - -import { Hono } from "hono"; -import { resolve } from "node:path"; -import { choicesModule } from "./mod"; - -const PORT = Number(process.env.PORT) || 3000; -const DIST_DIR = resolve(import.meta.dir, "../../dist"); - -const app = new Hono(); - -app.get("/.well-known/webauthn", (c) => { - return c.json( - { origins: ["https://rspace.online"] }, - 200, - { "Access-Control-Allow-Origin": "*", "Cache-Control": "public, max-age=3600" } - ); -}); - -app.route("/", choicesModule.routes); - -Bun.serve({ - port: PORT, - async fetch(req) { - const url = new URL(req.url); - if (url.pathname !== "/" && !url.pathname.startsWith("/api/")) { - const assetPath = url.pathname.slice(1); - if (assetPath.includes(".")) { - const file = Bun.file(resolve(DIST_DIR, assetPath)); - if (await file.exists()) { - const ct = assetPath.endsWith(".js") ? "application/javascript" : - assetPath.endsWith(".css") ? "text/css" : - assetPath.endsWith(".html") ? "text/html" : - "application/octet-stream"; - return new Response(file, { headers: { "Content-Type": ct } }); - } - } - } - return app.fetch(req); - }, -}); - -console.log(`rChoices standalone server running on http://localhost:${PORT}`); diff --git a/modules/data/mod.ts b/modules/data/mod.ts index e8c0aec..426d029 100644 --- a/modules/data/mod.ts +++ b/modules/data/mod.ts @@ -6,7 +6,7 @@ */ import { Hono } from "hono"; -import { renderShell, renderIframeShell } from "../../server/shell"; +import { renderShell } from "../../server/shell"; import { getModuleInfoList } from "../../shared/module"; import type { RSpaceModule } from "../../shared/module"; @@ -120,13 +120,15 @@ routes.post("/api/collect", async (c) => { // ── Page route ── routes.get("/", (c) => { const space = c.req.param("space") || "demo"; - return c.html(renderIframeShell({ + return c.html(renderShell({ title: `${space} — Data | rSpace`, moduleId: "data", spaceSlug: space, modules: getModuleInfoList(), theme: "dark", - standaloneDomain: "rdata.online", + body: ``, + scripts: ``, + styles: ``, })); }); diff --git a/modules/data/standalone.ts b/modules/data/standalone.ts deleted file mode 100644 index 4a9b82b..0000000 --- a/modules/data/standalone.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Standalone server for the Data module. - * Serves rdata.online independently. - */ - -import { Hono } from "hono"; -import { serveStatic } from "hono/bun"; -import { dataModule } from "./mod"; - -const app = new Hono(); - -app.use("/modules/data/*", serveStatic({ root: "./dist" })); -app.use("/*", serveStatic({ root: "./dist" })); -app.route("/", dataModule.routes); - -console.log(`[rData Standalone] Listening on :3000`); -export default { port: 3000, fetch: app.fetch }; diff --git a/modules/files/mod.ts b/modules/files/mod.ts index 90a279a..47f83f0 100644 --- a/modules/files/mod.ts +++ b/modules/files/mod.ts @@ -9,7 +9,7 @@ import { resolve } from "node:path"; import { mkdir, writeFile, unlink } from "node:fs/promises"; import { createHash, randomBytes } from "node:crypto"; import { sql } from "../../shared/db/pool"; -import { renderShell, renderIframeShell } from "../../server/shell"; +import { renderShell } from "../../server/shell"; import { getModuleInfoList } from "../../shared/module"; import type { RSpaceModule } from "../../shared/module"; import { verifyEncryptIDToken, extractToken } from "@encryptid/sdk/server"; @@ -366,13 +366,15 @@ routes.delete("/api/cards/:id", async (c) => { // ── Page route ── routes.get("/", (c) => { const spaceSlug = c.req.param("space") || "demo"; - return c.html(renderIframeShell({ + return c.html(renderShell({ title: `${spaceSlug} — Files | rSpace`, moduleId: "files", spaceSlug, modules: getModuleInfoList(), theme: "dark", - standaloneDomain: "rfiles.online", + body: ``, + scripts: ``, + styles: ``, })); }); diff --git a/modules/files/standalone.ts b/modules/files/standalone.ts deleted file mode 100644 index 0d39cb9..0000000 --- a/modules/files/standalone.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Standalone server for the Files module. - * Serves rfiles.online independently. - */ - -import { Hono } from "hono"; -import { serveStatic } from "hono/bun"; -import { filesModule } from "./mod"; - -const app = new Hono(); - -// Serve static module assets -app.use("/modules/files/*", serveStatic({ root: "./dist" })); -app.use("/*", serveStatic({ root: "./dist" })); - -// Mount files routes at root -app.route("/", filesModule.routes); - -console.log(`[rFiles Standalone] Listening on :3000`); -export default { - port: 3000, - fetch: app.fetch, -}; diff --git a/modules/forum/mod.ts b/modules/forum/mod.ts index 08c3314..b166b6d 100644 --- a/modules/forum/mod.ts +++ b/modules/forum/mod.ts @@ -7,7 +7,7 @@ import { Hono } from "hono"; import { readFileSync } from "node:fs"; import { resolve } from "node:path"; import { sql } from "../../shared/db/pool"; -import { renderShell, renderIframeShell } from "../../server/shell"; +import { renderShell } from "../../server/shell"; import { getModuleInfoList } from "../../shared/module"; import { provisionInstance, destroyInstance } from "./lib/provisioner"; import type { RSpaceModule } from "../../shared/module"; @@ -157,13 +157,15 @@ routes.get("/api/health", (c) => { // ── Page route ── routes.get("/", (c) => { const spaceSlug = c.req.param("space") || "demo"; - return c.html(renderIframeShell({ + return c.html(renderShell({ title: `${spaceSlug} — Forum | rSpace`, moduleId: "forum", spaceSlug, modules: getModuleInfoList(), theme: "dark", - standaloneDomain: "rforum.online", + body: ``, + scripts: ``, + styles: ``, })); }); diff --git a/modules/forum/standalone.ts b/modules/forum/standalone.ts deleted file mode 100644 index a449669..0000000 --- a/modules/forum/standalone.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Standalone server for the Forum module. - * Serves rforum.online independently. - */ - -import { Hono } from "hono"; -import { serveStatic } from "hono/bun"; -import { forumModule } from "./mod"; - -const app = new Hono(); - -// Serve static module assets -app.use("/modules/forum/*", serveStatic({ root: "./dist" })); -app.use("/*", serveStatic({ root: "./dist" })); - -// Mount forum routes at root -app.route("/", forumModule.routes); - -console.log(`[rForum Standalone] Listening on :3000`); -export default { - port: 3000, - fetch: app.fetch, -}; diff --git a/modules/funds/mod.ts b/modules/funds/mod.ts index 509dd95..353d10e 100644 --- a/modules/funds/mod.ts +++ b/modules/funds/mod.ts @@ -8,7 +8,7 @@ import { Hono } from "hono"; import { readFileSync } from "node:fs"; import { resolve } from "node:path"; import { sql } from "../../shared/db/pool"; -import { renderShell, renderIframeShell } from "../../server/shell"; +import { renderShell } from "../../server/shell"; import type { RSpaceModule } from "../../shared/module"; import { getModuleInfoList } from "../../shared/module"; import { verifyEncryptIDToken, extractToken } from "@encryptid/sdk/server"; @@ -193,16 +193,18 @@ const fundsScripts = ` const fundsStyles = ``; -// Landing page — iframes the standalone rfunds.online app +// Landing page routes.get("/", (c) => { const spaceSlug = c.req.param("space") || "demo"; - return c.html(renderIframeShell({ + return c.html(renderShell({ title: `rFunds — TBFF Flow Funding | rSpace`, moduleId: "funds", spaceSlug, modules: getModuleInfoList(), theme: "dark", - standaloneDomain: "rfunds.online", + body: ``, + scripts: ``, + styles: ``, })); }); diff --git a/modules/funds/standalone.ts b/modules/funds/standalone.ts deleted file mode 100644 index 6ff0eab..0000000 --- a/modules/funds/standalone.ts +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Funds standalone server — independent deployment at rfunds.online. - */ - -import { Hono } from "hono"; -import { resolve } from "node:path"; -import { fundsModule } from "./mod"; - -const PORT = Number(process.env.PORT) || 3000; -const DIST_DIR = resolve(import.meta.dir, "../../dist"); - -const app = new Hono(); - -app.get("/.well-known/webauthn", (c) => { - return c.json( - { origins: ["https://rspace.online"] }, - 200, - { "Access-Control-Allow-Origin": "*", "Cache-Control": "public, max-age=3600" } - ); -}); - -app.route("/", fundsModule.routes); - -Bun.serve({ - port: PORT, - async fetch(req) { - const url = new URL(req.url); - // Serve static assets (JS, CSS, etc.) from dist/ - const assetPath = url.pathname.slice(1); - if (assetPath.includes(".")) { - const file = Bun.file(resolve(DIST_DIR, assetPath)); - if (await file.exists()) { - const ct = assetPath.endsWith(".js") ? "application/javascript" : - assetPath.endsWith(".css") ? "text/css" : - assetPath.endsWith(".html") ? "text/html" : - "application/octet-stream"; - return new Response(file, { headers: { "Content-Type": ct } }); - } - } - // All other routes (/, /demo, /flow/:id, /api/*) handled by Hono - return app.fetch(req); - }, -}); - -console.log(`rFunds standalone server running on http://localhost:${PORT}`); diff --git a/modules/inbox/mod.ts b/modules/inbox/mod.ts index 4f97d33..6edb0b9 100644 --- a/modules/inbox/mod.ts +++ b/modules/inbox/mod.ts @@ -9,7 +9,7 @@ import { Hono } from "hono"; import { readFileSync } from "node:fs"; import { resolve } from "node:path"; import { sql } from "../../shared/db/pool"; -import { renderShell, renderIframeShell } from "../../server/shell"; +import { renderShell } from "../../server/shell"; import { getModuleInfoList } from "../../shared/module"; import type { RSpaceModule } from "../../shared/module"; import { verifyEncryptIDToken, extractToken } from "@encryptid/sdk/server"; @@ -530,13 +530,15 @@ runSyncLoop(); // ── Page route ── routes.get("/", (c) => { const space = c.req.param("space") || "demo"; - return c.html(renderIframeShell({ + return c.html(renderShell({ title: `${space} — Inbox | rSpace`, moduleId: "inbox", spaceSlug: space, modules: getModuleInfoList(), theme: "dark", - standaloneDomain: "rinbox.online", + body: ``, + scripts: ``, + styles: ``, })); }); diff --git a/modules/inbox/standalone.ts b/modules/inbox/standalone.ts deleted file mode 100644 index 0ef63e3..0000000 --- a/modules/inbox/standalone.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Standalone server for the Inbox module. - * Serves rinbox.online independently. - */ - -import { Hono } from "hono"; -import { serveStatic } from "hono/bun"; -import { inboxModule } from "./mod"; - -const app = new Hono(); - -app.use("/modules/inbox/*", serveStatic({ root: "./dist" })); -app.use("/*", serveStatic({ root: "./dist" })); -app.route("/", inboxModule.routes); - -console.log(`[rInbox Standalone] Listening on :3000`); -export default { port: 3000, fetch: app.fetch }; diff --git a/modules/maps/mod.ts b/modules/maps/mod.ts index 955c497..b828032 100644 --- a/modules/maps/mod.ts +++ b/modules/maps/mod.ts @@ -7,7 +7,7 @@ */ import { Hono } from "hono"; -import { renderShell, renderIframeShell } from "../../server/shell"; +import { renderShell } from "../../server/shell"; import { getModuleInfoList } from "../../shared/module"; import type { RSpaceModule } from "../../shared/module"; @@ -133,13 +133,15 @@ routes.get("/api/c3nav/:event", async (c) => { // ── Page route ── routes.get("/", (c) => { const space = c.req.param("space") || "demo"; - return c.html(renderIframeShell({ + return c.html(renderShell({ title: `${space} — Maps | rSpace`, moduleId: "maps", spaceSlug: space, modules: getModuleInfoList(), theme: "dark", - standaloneDomain: "rmaps.online", + body: ``, + scripts: ``, + styles: ``, })); }); diff --git a/modules/maps/standalone.ts b/modules/maps/standalone.ts deleted file mode 100644 index 928f286..0000000 --- a/modules/maps/standalone.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Standalone server for the Maps module. - * Serves rmaps.online independently. - */ - -import { Hono } from "hono"; -import { serveStatic } from "hono/bun"; -import { mapsModule } from "./mod"; - -const app = new Hono(); - -app.use("/modules/maps/*", serveStatic({ root: "./dist" })); -app.use("/*", serveStatic({ root: "./dist" })); -app.route("/", mapsModule.routes); - -console.log(`[rMaps Standalone] Listening on :3000`); -export default { port: 3000, fetch: app.fetch }; diff --git a/modules/network/mod.ts b/modules/network/mod.ts index 890793c..972d82b 100644 --- a/modules/network/mod.ts +++ b/modules/network/mod.ts @@ -7,7 +7,7 @@ */ import { Hono } from "hono"; -import { renderShell, renderIframeShell } from "../../server/shell"; +import { renderShell } from "../../server/shell"; import { getModuleInfoList } from "../../shared/module"; import type { RSpaceModule } from "../../shared/module"; @@ -216,13 +216,15 @@ routes.get("/api/workspaces", (c) => { // ── Page route ── routes.get("/", (c) => { const space = c.req.param("space") || "demo"; - return c.html(renderIframeShell({ + return c.html(renderShell({ title: `${space} — Network | rSpace`, moduleId: "network", spaceSlug: space, modules: getModuleInfoList(), theme: "dark", - standaloneDomain: "rnetwork.online", + body: ``, + scripts: ``, + styles: ``, })); }); diff --git a/modules/network/standalone.ts b/modules/network/standalone.ts deleted file mode 100644 index 1262620..0000000 --- a/modules/network/standalone.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Standalone server for the Network module. - * Serves rnetwork.online independently. - */ - -import { Hono } from "hono"; -import { serveStatic } from "hono/bun"; -import { networkModule } from "./mod"; - -const app = new Hono(); - -app.use("/modules/network/*", serveStatic({ root: "./dist" })); -app.use("/*", serveStatic({ root: "./dist" })); -app.route("/", networkModule.routes); - -console.log(`[rNetwork Standalone] Listening on :3000`); -export default { port: 3000, fetch: app.fetch }; diff --git a/modules/notes/mod.ts b/modules/notes/mod.ts index 06b5e1c..a9fed40 100644 --- a/modules/notes/mod.ts +++ b/modules/notes/mod.ts @@ -9,7 +9,7 @@ import { Hono } from "hono"; import { readFileSync } from "node:fs"; import { resolve } from "node:path"; import { sql } from "../../shared/db/pool"; -import { renderShell, renderIframeShell } from "../../server/shell"; +import { renderShell } from "../../server/shell"; import { getModuleInfoList } from "../../shared/module"; import type { RSpaceModule } from "../../shared/module"; import { verifyEncryptIDToken, extractToken } from "@encryptid/sdk/server"; @@ -361,13 +361,15 @@ routes.delete("/api/notes/:id", async (c) => { // ── Page route ── routes.get("/", (c) => { const space = c.req.param("space") || "demo"; - return c.html(renderIframeShell({ + return c.html(renderShell({ title: `${space} — Notes | rSpace`, moduleId: "notes", spaceSlug: space, modules: getModuleInfoList(), theme: "dark", - standaloneDomain: "rnotes.online", + body: ``, + scripts: ``, + styles: ``, })); }); diff --git a/modules/notes/standalone.ts b/modules/notes/standalone.ts deleted file mode 100644 index 3541db8..0000000 --- a/modules/notes/standalone.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Standalone server for the Notes module. - * Serves rnotes.online independently. - */ - -import { Hono } from "hono"; -import { serveStatic } from "hono/bun"; -import { notesModule } from "./mod"; - -const app = new Hono(); - -app.use("/modules/notes/*", serveStatic({ root: "./dist" })); -app.use("/*", serveStatic({ root: "./dist" })); -app.route("/", notesModule.routes); - -console.log(`[rNotes Standalone] Listening on :3000`); -export default { port: 3000, fetch: app.fetch }; diff --git a/modules/photos/mod.ts b/modules/photos/mod.ts index 5073588..b96a1af 100644 --- a/modules/photos/mod.ts +++ b/modules/photos/mod.ts @@ -7,7 +7,7 @@ */ import { Hono } from "hono"; -import { renderShell, renderIframeShell } from "../../server/shell"; +import { renderShell } from "../../server/shell"; import { getModuleInfoList } from "../../shared/module"; import type { RSpaceModule } from "../../shared/module"; @@ -108,13 +108,15 @@ routes.get("/api/assets/:id/original", async (c) => { // ── Page route ── routes.get("/", (c) => { const spaceSlug = c.req.param("space") || "demo"; - return c.html(renderIframeShell({ + return c.html(renderShell({ title: `${spaceSlug} — Photos | rSpace`, moduleId: "photos", spaceSlug, modules: getModuleInfoList(), theme: "dark", - standaloneDomain: "rphotos.online", + body: ``, + scripts: ``, + styles: ``, })); }); diff --git a/modules/providers/mod.ts b/modules/providers/mod.ts index 7a87734..4ce9f55 100644 --- a/modules/providers/mod.ts +++ b/modules/providers/mod.ts @@ -9,7 +9,7 @@ import { Hono } from "hono"; import { readFileSync } from "node:fs"; import { resolve } from "node:path"; import { sql } from "../../shared/db/pool"; -import { renderShell, renderIframeShell } from "../../server/shell"; +import { renderShell } from "../../server/shell"; import { getModuleInfoList } from "../../shared/module"; import type { RSpaceModule } from "../../shared/module"; import { verifyEncryptIDToken, extractToken } from "@encryptid/sdk/server"; @@ -348,13 +348,15 @@ routes.delete("/api/providers/:id", async (c) => { // ── Page route: browse providers ── routes.get("/", (c) => { const space = c.req.param("space") || "demo"; - return c.html(renderIframeShell({ + return c.html(renderShell({ title: `Providers | rSpace`, moduleId: "providers", spaceSlug: space, modules: getModuleInfoList(), theme: "dark", - standaloneDomain: "providers.mycofi.earth", + body: ``, + scripts: ``, + styles: ``, })); }); diff --git a/modules/providers/standalone.ts b/modules/providers/standalone.ts deleted file mode 100644 index dd68bab..0000000 --- a/modules/providers/standalone.ts +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Providers standalone server — independent deployment at providers.mycofi.earth. - */ - -import { Hono } from "hono"; -import { cors } from "hono/cors"; -import { resolve } from "node:path"; -import { providersModule } from "./mod"; - -const PORT = Number(process.env.PORT) || 3000; -const DIST_DIR = resolve(import.meta.dir, "../../dist"); - -const app = new Hono(); -app.use("/api/*", cors()); - -app.get("/.well-known/webauthn", (c) => { - return c.json( - { origins: ["https://rspace.online"] }, - 200, - { "Access-Control-Allow-Origin": "*", "Cache-Control": "public, max-age=3600" } - ); -}); - -app.route("/", providersModule.routes); - -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(".ico")) return "image/x-icon"; - return "application/octet-stream"; -} - -Bun.serve({ - port: PORT, - async fetch(req) { - const url = new URL(req.url); - if (url.pathname !== "/" && !url.pathname.startsWith("/api/")) { - const assetPath = url.pathname.slice(1); - if (assetPath.includes(".")) { - const file = Bun.file(resolve(DIST_DIR, assetPath)); - if (await file.exists()) { - return new Response(file, { headers: { "Content-Type": getContentType(assetPath) } }); - } - } - } - return app.fetch(req); - }, -}); - -console.log(`Providers standalone server running on http://localhost:${PORT}`); diff --git a/modules/pubs/mod.ts b/modules/pubs/mod.ts index f9f7593..d09c531 100644 --- a/modules/pubs/mod.ts +++ b/modules/pubs/mod.ts @@ -13,7 +13,7 @@ import { parseMarkdown } from "./parse-document"; import { compileDocument } from "./typst-compile"; import { getFormat, FORMATS, listFormats } from "./formats"; import type { BookFormat } from "./formats"; -import { renderShell, renderIframeShell } from "../../server/shell"; +import { renderShell } from "../../server/shell"; import { getModuleInfoList } from "../../shared/module"; import type { RSpaceModule } from "../../shared/module"; @@ -321,13 +321,15 @@ routes.get("/api/artifact/:id/pdf", async (c) => { // ── Page: Editor ── routes.get("/", (c) => { const spaceSlug = c.req.param("space") || "personal"; - return c.html(renderIframeShell({ + return c.html(renderShell({ title: `${spaceSlug} — rPubs Editor | rSpace`, moduleId: "pubs", spaceSlug, modules: getModuleInfoList(), theme: "dark", - standaloneDomain: "rpubs.online", + body: ``, + scripts: ``, + styles: ``, })); }); diff --git a/modules/pubs/standalone.ts b/modules/pubs/standalone.ts deleted file mode 100644 index c0561f0..0000000 --- a/modules/pubs/standalone.ts +++ /dev/null @@ -1,57 +0,0 @@ -/** - * rPubs standalone server — independent deployment at rpubs.online. - * - * Usage: bun run modules/pubs/standalone.ts - */ - -import { Hono } from "hono"; -import { cors } from "hono/cors"; -import { resolve } from "node:path"; -import { pubsModule } from "./mod"; - -const PORT = Number(process.env.PORT) || 3000; -const DIST_DIR = resolve(import.meta.dir, "../../dist"); - -const app = new Hono(); - -app.use("/api/*", cors()); - -app.get("/.well-known/webauthn", (c) => { - return c.json( - { origins: ["https://rspace.online"] }, - 200, - { "Access-Control-Allow-Origin": "*", "Cache-Control": "public, max-age=3600" } - ); -}); - -app.route("/", pubsModule.routes); - -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(".ico")) return "image/x-icon"; - return "application/octet-stream"; -} - -Bun.serve({ - port: PORT, - async fetch(req) { - const url = new URL(req.url); - if (url.pathname !== "/" && !url.pathname.startsWith("/api/")) { - const assetPath = url.pathname.slice(1); - if (assetPath.includes(".")) { - const file = Bun.file(resolve(DIST_DIR, assetPath)); - if (await file.exists()) { - return new Response(file, { headers: { "Content-Type": getContentType(assetPath) } }); - } - } - } - return app.fetch(req); - }, -}); - -console.log(`rPubs standalone server running on http://localhost:${PORT}`); diff --git a/modules/swag/mod.ts b/modules/swag/mod.ts index 1c6e29a..684793d 100644 --- a/modules/swag/mod.ts +++ b/modules/swag/mod.ts @@ -11,7 +11,7 @@ import { mkdir, writeFile, readFile, readdir, stat } from "node:fs/promises"; import { join } from "node:path"; import { getProduct, PRODUCTS } from "./products"; import { processImage } from "./process-image"; -import { renderShell, renderIframeShell } from "../../server/shell"; +import { renderShell } from "../../server/shell"; import { getModuleInfoList } from "../../shared/module"; import type { RSpaceModule } from "../../shared/module"; @@ -228,13 +228,15 @@ routes.get("/api/artifact/:id", async (c) => { // ── Page route: swag designer ── routes.get("/", (c) => { const space = c.req.param("space") || "demo"; - return c.html(renderIframeShell({ + return c.html(renderShell({ title: `Swag Designer | rSpace`, moduleId: "swag", spaceSlug: space, modules: getModuleInfoList(), theme: "dark", - standaloneDomain: "swag.mycofi.earth", + body: ``, + scripts: ``, + styles: ``, })); }); diff --git a/modules/swag/standalone.ts b/modules/swag/standalone.ts deleted file mode 100644 index 6c2e017..0000000 --- a/modules/swag/standalone.ts +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Swag standalone server — independent deployment at swag.mycofi.earth. - */ - -import { Hono } from "hono"; -import { cors } from "hono/cors"; -import { resolve } from "node:path"; -import { swagModule } from "./mod"; - -const PORT = Number(process.env.PORT) || 3000; -const DIST_DIR = resolve(import.meta.dir, "../../dist"); - -const app = new Hono(); -app.use("/api/*", cors()); - -app.get("/.well-known/webauthn", (c) => { - return c.json( - { origins: ["https://rspace.online"] }, - 200, - { "Access-Control-Allow-Origin": "*", "Cache-Control": "public, max-age=3600" } - ); -}); - -app.route("/", swagModule.routes); - -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(".ico")) return "image/x-icon"; - return "application/octet-stream"; -} - -Bun.serve({ - port: PORT, - async fetch(req) { - const url = new URL(req.url); - if (url.pathname !== "/" && !url.pathname.startsWith("/api/")) { - const assetPath = url.pathname.slice(1); - if (assetPath.includes(".")) { - const file = Bun.file(resolve(DIST_DIR, assetPath)); - if (await file.exists()) { - return new Response(file, { headers: { "Content-Type": getContentType(assetPath) } }); - } - } - } - return app.fetch(req); - }, -}); - -console.log(`Swag standalone server running on http://localhost:${PORT}`); diff --git a/modules/trips/mod.ts b/modules/trips/mod.ts index 6df7ad8..63c311c 100644 --- a/modules/trips/mod.ts +++ b/modules/trips/mod.ts @@ -9,7 +9,7 @@ import { Hono } from "hono"; import { readFileSync } from "node:fs"; import { resolve } from "node:path"; import { sql } from "../../shared/db/pool"; -import { renderShell, renderIframeShell } from "../../server/shell"; +import { renderShell } from "../../server/shell"; import { getModuleInfoList } from "../../shared/module"; import type { RSpaceModule } from "../../shared/module"; import { verifyEncryptIDToken, extractToken } from "@encryptid/sdk/server"; @@ -253,13 +253,15 @@ routes.get("/routes", (c) => { // ── Page route ── routes.get("/", (c) => { const space = c.req.param("space") || "demo"; - return c.html(renderIframeShell({ + return c.html(renderShell({ title: `${space} — Trips | rSpace`, moduleId: "trips", spaceSlug: space, modules: getModuleInfoList(), theme: "dark", - standaloneDomain: "rtrips.online", + body: ``, + scripts: ``, + styles: ``, })); }); diff --git a/modules/trips/standalone.ts b/modules/trips/standalone.ts deleted file mode 100644 index 3df0737..0000000 --- a/modules/trips/standalone.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Standalone server for the Trips module. - * Serves rtrips.online independently. - */ - -import { Hono } from "hono"; -import { serveStatic } from "hono/bun"; -import { tripsModule } from "./mod"; - -const app = new Hono(); - -app.use("/modules/trips/*", serveStatic({ root: "./dist" })); -app.use("/*", serveStatic({ root: "./dist" })); -app.route("/", tripsModule.routes); - -console.log(`[rTrips Standalone] Listening on :3000`); -export default { port: 3000, fetch: app.fetch }; diff --git a/modules/tube/mod.ts b/modules/tube/mod.ts index 94d8333..aea5863 100644 --- a/modules/tube/mod.ts +++ b/modules/tube/mod.ts @@ -6,7 +6,7 @@ */ import { Hono } from "hono"; -import { renderShell, renderIframeShell } from "../../server/shell"; +import { renderShell } from "../../server/shell"; import { getModuleInfoList } from "../../shared/module"; import type { RSpaceModule } from "../../shared/module"; import { verifyEncryptIDToken, extractToken } from "@encryptid/sdk/server"; @@ -191,13 +191,15 @@ routes.get("/api/health", (c) => c.json({ ok: true })); // ── Page route ── routes.get("/", (c) => { const space = c.req.param("space") || "demo"; - return c.html(renderIframeShell({ + return c.html(renderShell({ title: `${space} — Tube | rSpace`, moduleId: "tube", spaceSlug: space, modules: getModuleInfoList(), theme: "dark", - standaloneDomain: "rtube.online", + body: ``, + scripts: ``, + styles: ``, })); }); diff --git a/modules/tube/standalone.ts b/modules/tube/standalone.ts deleted file mode 100644 index 98bbbf8..0000000 --- a/modules/tube/standalone.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Standalone server for the Tube module. - * Serves rtube.online independently. - */ - -import { Hono } from "hono"; -import { serveStatic } from "hono/bun"; -import { tubeModule } from "./mod"; - -const app = new Hono(); - -app.use("/modules/tube/*", serveStatic({ root: "./dist" })); -app.use("/*", serveStatic({ root: "./dist" })); -app.route("/", tubeModule.routes); - -console.log(`[rTube Standalone] Listening on :3000`); -export default { port: 3000, fetch: app.fetch }; diff --git a/modules/vote/mod.ts b/modules/vote/mod.ts index 35d705d..d0aadff 100644 --- a/modules/vote/mod.ts +++ b/modules/vote/mod.ts @@ -9,7 +9,7 @@ import { Hono } from "hono"; import { readFileSync } from "node:fs"; import { resolve } from "node:path"; import { sql } from "../../shared/db/pool"; -import { renderShell, renderIframeShell } from "../../server/shell"; +import { renderShell } from "../../server/shell"; import { getModuleInfoList } from "../../shared/module"; import type { RSpaceModule } from "../../shared/module"; import { verifyEncryptIDToken, extractToken } from "@encryptid/sdk/server"; @@ -327,13 +327,15 @@ routes.post("/api/proposals/:id/final-vote", async (c) => { // ── Page route ── routes.get("/", (c) => { const space = c.req.param("space") || "demo"; - return c.html(renderIframeShell({ + return c.html(renderShell({ title: `${space} — Vote | rSpace`, moduleId: "vote", spaceSlug: space, modules: getModuleInfoList(), theme: "dark", - standaloneDomain: "rvote.online", + body: ``, + scripts: ``, + styles: ``, })); }); diff --git a/modules/vote/standalone.ts b/modules/vote/standalone.ts deleted file mode 100644 index 55625fd..0000000 --- a/modules/vote/standalone.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Standalone server for the Vote module. - * Serves rvote.online independently. - */ - -import { Hono } from "hono"; -import { serveStatic } from "hono/bun"; -import { voteModule } from "./mod"; - -const app = new Hono(); - -app.use("/modules/vote/*", serveStatic({ root: "./dist" })); -app.use("/*", serveStatic({ root: "./dist" })); -app.route("/", voteModule.routes); - -console.log(`[rVote Standalone] Listening on :3000`); -export default { port: 3000, fetch: app.fetch }; diff --git a/modules/wallet/mod.ts b/modules/wallet/mod.ts index 1b9af12..3c435b3 100644 --- a/modules/wallet/mod.ts +++ b/modules/wallet/mod.ts @@ -6,7 +6,7 @@ */ import { Hono } from "hono"; -import { renderShell, renderIframeShell } from "../../server/shell"; +import { renderShell } from "../../server/shell"; import { getModuleInfoList } from "../../shared/module"; import type { RSpaceModule } from "../../shared/module"; @@ -94,13 +94,15 @@ function getSafePrefix(chainId: string): string | null { // ── Page route ── routes.get("/", (c) => { const spaceSlug = c.req.param("space") || "demo"; - return c.html(renderIframeShell({ + return c.html(renderShell({ title: `${spaceSlug} — Wallet | rSpace`, moduleId: "wallet", spaceSlug, modules: getModuleInfoList(), theme: "dark", - standaloneDomain: "rwallet.online", + body: ``, + scripts: ``, + styles: ``, })); }); diff --git a/modules/wallet/standalone.ts b/modules/wallet/standalone.ts deleted file mode 100644 index b20b8e2..0000000 --- a/modules/wallet/standalone.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Standalone server for the Wallet module. - * Serves rwallet.online independently. - */ - -import { Hono } from "hono"; -import { serveStatic } from "hono/bun"; -import { walletModule } from "./mod"; - -const app = new Hono(); - -app.use("/modules/wallet/*", serveStatic({ root: "./dist" })); -app.use("/*", serveStatic({ root: "./dist" })); -app.route("/", walletModule.routes); - -console.log(`[rWallet Standalone] Listening on :3000`); -export default { port: 3000, fetch: app.fetch }; diff --git a/modules/work/mod.ts b/modules/work/mod.ts index 1cbade7..0ec97e1 100644 --- a/modules/work/mod.ts +++ b/modules/work/mod.ts @@ -9,7 +9,7 @@ import { Hono } from "hono"; import { readFileSync } from "node:fs"; import { resolve } from "node:path"; import { sql } from "../../shared/db/pool"; -import { renderShell, renderIframeShell } from "../../server/shell"; +import { renderShell } from "../../server/shell"; import { getModuleInfoList } from "../../shared/module"; import type { RSpaceModule } from "../../shared/module"; import { verifyEncryptIDToken, extractToken } from "@encryptid/sdk/server"; @@ -217,13 +217,15 @@ routes.get("/api/spaces/:slug/activity", async (c) => { // ── Page route ── routes.get("/", (c) => { const space = c.req.param("space") || "demo"; - return c.html(renderIframeShell({ + return c.html(renderShell({ title: `${space} — Work | rSpace`, moduleId: "work", spaceSlug: space, modules: getModuleInfoList(), theme: "dark", - standaloneDomain: "rwork.online", + body: ``, + scripts: ``, + styles: ``, })); }); diff --git a/modules/work/standalone.ts b/modules/work/standalone.ts deleted file mode 100644 index 19c2b26..0000000 --- a/modules/work/standalone.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Standalone server for the Work module. - * Serves rwork.online independently. - */ - -import { Hono } from "hono"; -import { serveStatic } from "hono/bun"; -import { workModule } from "./mod"; - -const app = new Hono(); - -app.use("/modules/work/*", serveStatic({ root: "./dist" })); -app.use("/*", serveStatic({ root: "./dist" })); -app.route("/", workModule.routes); - -console.log(`[rWork Standalone] Listening on :3000`); -export default { port: 3000, fetch: app.fetch }; diff --git a/server/index.ts b/server/index.ts index 200df3d..042b568 100644 --- a/server/index.ts +++ b/server/index.ts @@ -626,14 +626,6 @@ for (const mod of getAllModules()) { domainToModule.set(mod.standaloneDomain, mod.id); } } -// Domains we keep on their own containers (do NOT rewrite) -const keepStandalone = new Set([ - "rcart.online", - "rdata.online", - "rfiles.online", - "swag.mycofi.earth", - "providers.mycofi.earth", -]); // ── Bun.serve: WebSocket + fetch delegation ── const server = Bun.serve({ @@ -645,35 +637,19 @@ const server = Bun.serve({ const hostClean = host?.split(":")[0] || ""; const subdomain = getSubdomain(host); - // ── Standalone domain → 301 redirect to canonical subdomain URL ── + // ── Standalone domain → internal rewrite to module routes ── const standaloneModuleId = domainToModule.get(hostClean); - if (standaloneModuleId && !keepStandalone.has(hostClean)) { - // WebSocket: rewrite for backward compat (WS can't follow redirects) - if (url.pathname.startsWith("/ws/")) { - const communitySlug = url.pathname.split("/")[2]; - if (communitySlug) { - const spaceConfig = await getSpaceConfig(communitySlug); - const claims = await authenticateWSUpgrade(req); - let readOnly = false; - if (spaceConfig) { - const vis = spaceConfig.visibility; - if (vis === "authenticated" || vis === "members_only") { - if (!claims) return new Response("Authentication required", { status: 401 }); - } else if (vis === "public_read") { - readOnly = !claims; - } - } - const peerId = generatePeerId(); - const mode = url.searchParams.get("mode") === "json" ? "json" : "automerge"; - const upgraded = server.upgrade(req, { - data: { communitySlug, peerId, claims, readOnly, mode } as WSData, - }); - if (upgraded) return undefined; + if (standaloneModuleId) { + // Static assets pass through + if (url.pathname !== "/" && !url.pathname.startsWith("/api/") && !url.pathname.startsWith("/ws/")) { + const assetPath = url.pathname.slice(1); + if (assetPath.includes(".")) { + const staticResponse = await serveStatic(assetPath); + if (staticResponse) return staticResponse; } - return new Response("WebSocket upgrade failed", { status: 400 }); } - // Everything else: 301 redirect to {space}.rspace.online/{moduleId} + // Rewrite path internally: / → /demo/{moduleId} const pathParts = url.pathname.split("/").filter(Boolean); let space = "demo"; let suffix = ""; @@ -690,8 +666,10 @@ const server = Bun.serve({ suffix = url.pathname; } - const canonical = `https://${space}.rspace.online/${standaloneModuleId}${suffix}${url.search}`; - return Response.redirect(canonical, 301); + const rewrittenPath = `/${space}/${standaloneModuleId}${suffix}`; + const rewrittenUrl = new URL(rewrittenPath + url.search, `http://localhost:${PORT}`); + const rewrittenReq = new Request(rewrittenUrl, req); + return app.fetch(rewrittenReq); } // ── WebSocket upgrade ── diff --git a/server/shell.ts b/server/shell.ts index 0091008..d618605 100644 --- a/server/shell.ts +++ b/server/shell.ts @@ -3,9 +3,6 @@ * * Wraps module content in the shared rSpace layout: header with app/space * switchers + identity,
with module content, shell script + styles. - * - * In standalone mode, modules call renderStandaloneShell() which omits the - * app/space switchers and only includes identity. */ import type { ModuleInfo } from "../shared/module"; @@ -57,6 +54,16 @@ export function renderShell(opts: ShellOptions): string { ${escapeHtml(title)} + + ${styles} ${head} @@ -288,52 +295,6 @@ export function renderShell(opts: ShellOptions): string { `; } -/** Minimal shell for standalone module deployments (no app/space switcher) */ -export function renderStandaloneShell(opts: { - title: string; - body: string; - scripts?: string; - styles?: string; - theme?: "dark" | "light"; - head?: string; -}): string { - const { title, body, scripts = "", styles = "", theme = "dark", head = "" } = opts; - - return ` - - - - - ${escapeHtml(title)} - - ${styles} - ${head} - - -
- -
- -
-
- -
-
-
- ${body} -
- - ${scripts} - -`; -} - // ── Welcome overlay (quarter-screen popup for first-time visitors on demo) ── function renderWelcomeOverlay(): string { @@ -436,188 +397,6 @@ const WELCOME_CSS = ` } `; -/** - * Shell that embeds a standalone app via iframe. - * - * Wraps the independent app's domain in the shared rSpace header/nav - * so users get the latest code from the standalone repo while preserving - * the unified space/identity experience. - */ -export interface IframeShellOptions extends Omit { - /** The standalone app domain, e.g. "rvote.online" */ - standaloneDomain: string; - /** Extra path to append after the domain root (default: "") */ - path?: string; -} - -export function renderIframeShell(opts: IframeShellOptions): string { - const { standaloneDomain, path = "", ...shellOpts } = opts; - const iframeSrc = `https://${standaloneDomain}${path}`; - - const moduleName = shellOpts.modules.find(m => m.id === shellOpts.moduleId)?.name || shellOpts.moduleId; - - return renderShell({ - ...shellOpts, - body: `
-
-

Loading ${escapeHtml(moduleName)}...

-
- - `, - styles: ``, - scripts: ``, - }); -} function escapeHtml(s: string): string { return s.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """);