/** * rEvents module — event aggregation & parsing. * * Aggregates events from Luma, Meetup, iCal feeds, and unstructured text. * The functional app runs as a standalone Next.js container (revents-online:3000) * and is embedded via externalApp iframe in rSpace. */ import { Hono } from "hono"; import { renderShell, renderExternalAppShell, escapeHtml } from "../../server/shell"; import { getModuleInfoList } from "../../shared/module"; import type { RSpaceModule } from "../../shared/module"; import { renderLanding } from "./landing"; const REVENTS_URL = process.env.REVENTS_URL || "http://revents-online:3000"; const REVENTS_PUBLIC_URL = process.env.REVENTS_PUBLIC_URL || "https://revents.online"; const routes = new Hono(); // ── Proxy API calls to revents-online ── routes.get("/api/events", async (c) => { const qs = new URL(c.req.url).search; try { const res = await fetch(`${REVENTS_URL}/api/events${qs}`); const data = await res.json(); return c.json(data); } catch { return c.json({ count: 0, results: [] }); } }); routes.get("/api/sources", async (c) => { try { const res = await fetch(`${REVENTS_URL}/api/sources`); const data = await res.json(); return c.json(data); } catch { return c.json([]); } }); routes.post("/api/parse", async (c) => { try { const body = await c.req.json(); const res = await fetch(`${REVENTS_URL}/api/parse`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }); const data = await res.json(); return c.json(data); } catch (e: any) { return c.json({ error: e.message }, 500); } }); // ── Explore page (embedded) ── routes.get("/explore", (c) => { const space = c.req.param("space") || "demo"; return c.html(renderExternalAppShell({ title: `Explore Events — rEvents | rSpace`, moduleId: "revents", spaceSlug: space, modules: getModuleInfoList(), appUrl: `${REVENTS_PUBLIC_URL}/explore`, appName: "rEvents", theme: "dark", })); }); // ── Sources page (embedded) ── routes.get("/sources", (c) => { const space = c.req.param("space") || "demo"; return c.html(renderExternalAppShell({ title: `Event Sources — rEvents | rSpace`, moduleId: "revents", spaceSlug: space, modules: getModuleInfoList(), appUrl: `${REVENTS_PUBLIC_URL}/sources`, appName: "rEvents", theme: "dark", })); }); // ── Hub page ── routes.get("/", (c) => { const space = c.req.param("space") || "demo"; const base = `/${escapeHtml(space)}/revents`; return c.html(renderShell({ title: `rEvents — ${space} | rSpace`, moduleId: "revents", spaceSlug: space, modules: getModuleInfoList(), styles: ``, body: `

rEvents

Aggregate events from Luma, Meetup, iCal, and more

`, })); }); export const eventsModule: RSpaceModule = { id: "revents", name: "rEvents", icon: "🎪", description: "Event aggregation & discovery from Luma, Meetup, iCal and more", scoping: { defaultScope: "space", userConfigurable: true }, routes, standaloneDomain: "revents.online", landingPage: renderLanding, externalApp: { url: REVENTS_PUBLIC_URL, name: "rEvents" }, outputPaths: [ { path: "explore", name: "Explore", icon: "🔍", description: "Browse upcoming events" }, { path: "sources", name: "Sources", icon: "🔗", description: "Connected event sources" }, ], feeds: [ { id: "events", name: "Events", kind: "data", description: "Aggregated events from external platforms and manual input", filterable: true, }, ], acceptsFeeds: ["data"], };