diff --git a/modules/rmeets/mod.ts b/modules/rmeets/mod.ts index 853a1d0a..86d507ca 100644 --- a/modules/rmeets/mod.ts +++ b/modules/rmeets/mod.ts @@ -38,6 +38,13 @@ const MI_API_URL = process.env.MEETING_INTELLIGENCE_API_URL || "http://meeting-i const MI_INTERNAL_KEY = process.env.MI_INTERNAL_KEY || ""; const routes = new Hono(); +// Subdomain-aware base for this module: `/rmeets` on {space}.rspace.online, +// `/{space}/rmeets` on localhost or bare domain. +function rmeetsBase(c: any): string { + const space = c.req.param("space") || "demo"; + return c.get("isSubdomain") ? "/rmeets" : `/${escapeHtml(space)}/rmeets`; +} + // ── Meeting Intelligence API helper ── async function miApiFetch(path: string, options?: { method?: string; body?: any }): Promise<{ ok: boolean; data?: any; error?: string }> { @@ -184,7 +191,7 @@ routes.get("/meet", (c) => { routes.get("/recordings", async (c) => { const space = c.req.param("space") || "demo"; - const base = `/${escapeHtml(space)}/rmeets`; + const base = rmeetsBase(c); const prefix = encodeURIComponent(space + "_"); const result = await miApiFetch(`/meetings?limit=50&sort=-created_at&conference_prefix=${prefix}`); @@ -240,7 +247,7 @@ routes.get("/recordings/:id", (c) => { routes.get("/recordings/:id/:tab", async (c) => { const space = c.req.param("space") || "demo"; - const base = `/${escapeHtml(space)}/rmeets`; + const base = rmeetsBase(c); const id = c.req.param("id"); const tab = c.req.param("tab") || "overview"; const validTabs = ["overview", "transcript", "summary", "speakers"]; @@ -365,7 +372,7 @@ ${speakerList}`; routes.get("/search", async (c) => { const space = c.req.param("space") || "demo"; - const base = `/${escapeHtml(space)}/rmeets`; + const base = rmeetsBase(c); const q = c.req.query("q") || ""; let resultsHtml = ""; @@ -445,7 +452,7 @@ routes.all("/api/mi-proxy/*", async (c) => { routes.get("/", (c) => { const space = c.req.param("space") || "demo"; - const base = `/${escapeHtml(space)}/rmeets`; + const base = rmeetsBase(c); const randomId = Math.random().toString(36).slice(2, 10); return c.html(renderShell({ title: `rMeets — ${space} | rSpace`, @@ -526,7 +533,7 @@ routes.get("/", (c) => { routes.get("/meeting-intelligence", async (c) => { const space = c.req.param("space") || "demo"; - const base = `/${escapeHtml(space)}/rmeets`; + const base = rmeetsBase(c); const prefix = encodeURIComponent(space + "_"); // Fetch space-scoped meetings from MI API @@ -674,7 +681,7 @@ routes.get("/:room", (c) => { // Default: clean full-screen Jitsi — no rSpace shell, mobile-friendly const jitsiRoom = encodeURIComponent(space + "_" + room); - const meetsBase = `/${escapeHtml(space)}/rmeets`; + const meetsBase = rmeetsBase(c); return c.html(` diff --git a/server/shell.ts b/server/shell.ts index 7a605906..0b367c31 100644 --- a/server/shell.ts +++ b/server/shell.ts @@ -369,7 +369,7 @@ export function renderShell(opts: ShellOptions): string { // Service worker registration + update detection if ("serviceWorker" in navigator && location.hostname !== "localhost") { - navigator.serviceWorker.register("/sw.js?v=8").then((reg) => { + navigator.serviceWorker.register("/sw.js?v=9").then((reg) => { function showUpdateBanner() { if (!isStandalone) return; // Only show update prompt in installed PWA if (sessionStorage.getItem('rspace_update_dismissed')) return; // dismissed this session @@ -2304,7 +2304,7 @@ export function renderModuleLanding(opts: ModuleLandingOptions): string {