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 {