Compare commits
1 Commits
main
...
backup/net
| Author | SHA1 | Date |
|---|---|---|
|
|
96a00a6f36 |
|
|
@ -0,0 +1,89 @@
|
|||
/**
|
||||
* rEvents landing page — event aggregation & discovery.
|
||||
*/
|
||||
export function renderLanding(): string {
|
||||
return `
|
||||
<!-- Hero -->
|
||||
<div class="rl-hero">
|
||||
<span class="rl-tagline" style="color:#a78bfa;background:rgba(167,139,250,0.1);border-color:rgba(167,139,250,0.2)">
|
||||
Event Aggregator
|
||||
</span>
|
||||
<h1 class="rl-heading" style="background:linear-gradient(to right,#a78bfa,#ec4899);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text">
|
||||
All your events. One place.
|
||||
</h1>
|
||||
<p class="rl-subtitle">
|
||||
Aggregate events from Luma, Meetup, iCal feeds, and more into a unified stream for your community.
|
||||
</p>
|
||||
<p class="rl-subtext">
|
||||
rEvents is the <span style="color:#a78bfa;font-weight:600">event ingestion layer</span> for the rStack.
|
||||
Connect external event platforms, parse unstructured text into structured events,
|
||||
and keep your community's calendar in sync — automatically.
|
||||
</p>
|
||||
<div class="rl-cta-row">
|
||||
<a href="https://demo.rspace.online/revents" class="rl-cta-primary" id="ml-primary"
|
||||
style="background:linear-gradient(to right,#a78bfa,#ec4899);color:#0b1120">
|
||||
Try the Demo
|
||||
</a>
|
||||
<a href="#features" class="rl-cta-secondary">Learn More</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Features (3-card grid) -->
|
||||
<section class="rl-section" style="border-top:none" id="features">
|
||||
<div class="rl-container">
|
||||
<div class="rl-grid-3">
|
||||
<div class="rl-card rl-card--center" style="padding:2rem">
|
||||
<div class="rl-icon-box" style="background:rgba(167,139,250,0.12);font-size:1.5rem">
|
||||
<span style="font-size:1.5rem">🔗</span>
|
||||
</div>
|
||||
<h3>Aggregate</h3>
|
||||
<p>Pull events from Luma, Meetup, iCal feeds, and RSS into one unified stream. Sync on demand or on a schedule.</p>
|
||||
</div>
|
||||
<div class="rl-card rl-card--center" style="padding:2rem">
|
||||
<div class="rl-icon-box" style="background:rgba(236,72,153,0.12);font-size:1.5rem">
|
||||
<span style="font-size:1.5rem">✨</span>
|
||||
</div>
|
||||
<h3>Parse</h3>
|
||||
<p>Paste unstructured text — emails, messages, flyers — and extract structured event data with dates, locations, and more.</p>
|
||||
</div>
|
||||
<div class="rl-card rl-card--center" style="padding:2rem">
|
||||
<div class="rl-icon-box" style="background:rgba(96,165,250,0.12);font-size:1.5rem">
|
||||
<span style="font-size:1.5rem">📅</span>
|
||||
</div>
|
||||
<h3>Connect</h3>
|
||||
<p>Push events to rCal, surface them in rInbox, coordinate across the entire rStack ecosystem seamlessly.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Supported Sources -->
|
||||
<section class="rl-section">
|
||||
<div class="rl-container">
|
||||
<h2 class="rl-section-title" style="text-align:center;margin-bottom:2rem">Supported Sources</h2>
|
||||
<div class="rl-grid-4">
|
||||
<div class="rl-card rl-card--center" style="padding:1.5rem">
|
||||
<div style="font-size:2rem;margin-bottom:0.5rem">🎄</div>
|
||||
<h3 style="font-size:1rem">Luma</h3>
|
||||
<p style="font-size:0.8rem">lu.ma calendar events</p>
|
||||
</div>
|
||||
<div class="rl-card rl-card--center" style="padding:1.5rem">
|
||||
<div style="font-size:2rem;margin-bottom:0.5rem">🏅</div>
|
||||
<h3 style="font-size:1rem">Meetup</h3>
|
||||
<p style="font-size:0.8rem">Meetup.com groups</p>
|
||||
</div>
|
||||
<div class="rl-card rl-card--center" style="padding:1.5rem">
|
||||
<div style="font-size:2rem;margin-bottom:0.5rem">📅</div>
|
||||
<h3 style="font-size:1rem">iCal / ICS</h3>
|
||||
<p style="font-size:0.8rem">Any .ics feed URL</p>
|
||||
</div>
|
||||
<div class="rl-card rl-card--center" style="padding:1.5rem">
|
||||
<div style="font-size:2rem;margin-bottom:0.5rem">✍</div>
|
||||
<h3 style="font-size:1rem">Manual / Text</h3>
|
||||
<p style="font-size:0.8rem">Paste & parse anything</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
`;
|
||||
}
|
||||
|
|
@ -0,0 +1,164 @@
|
|||
/**
|
||||
* 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: `<style>
|
||||
.rs-hub{max-width:720px;margin:2rem auto;padding:0 1.5rem}
|
||||
.rs-hub h1{font-size:1.8rem;margin-bottom:.5rem;color:var(--rs-text-primary)}
|
||||
.rs-hub>p{color:var(--rs-text-secondary);margin-bottom:2rem}
|
||||
.rs-nav{display:flex;flex-direction:column;gap:1rem}
|
||||
.rs-nav a{display:flex;align-items:center;gap:1rem;padding:1.25rem 1.5rem;border-radius:12px;background:var(--rs-bg-surface);border:1px solid var(--rs-border);text-decoration:none;color:inherit;transition:border-color .15s,background .15s}
|
||||
.rs-nav a:hover{border-color:var(--rs-accent);background:var(--rs-bg-hover)}
|
||||
.rs-nav .nav-icon{font-size:2rem;flex-shrink:0}
|
||||
.rs-nav .nav-body h3{margin:0 0 .25rem;font-size:1.1rem;color:var(--rs-text-primary)}
|
||||
.rs-nav .nav-body p{margin:0;font-size:.85rem;color:var(--rs-text-secondary)}
|
||||
@media(max-width:600px){.rs-hub{margin:1rem auto;padding:0 .75rem}.rs-nav a{padding:1rem;gap:.75rem}.rs-nav .nav-icon{font-size:1.5rem}}
|
||||
</style>`,
|
||||
body: `<div class="rs-hub">
|
||||
<h1>rEvents</h1>
|
||||
<p>Aggregate events from Luma, Meetup, iCal, and more</p>
|
||||
<nav class="rs-nav">
|
||||
<a href="${base}/explore">
|
||||
<span class="nav-icon">🔍</span>
|
||||
<div class="nav-body">
|
||||
<h3>Explore Events</h3>
|
||||
<p>Browse and search upcoming events from all connected sources</p>
|
||||
</div>
|
||||
</a>
|
||||
<a href="${base}/sources">
|
||||
<span class="nav-icon">🔗</span>
|
||||
<div class="nav-body">
|
||||
<h3>Event Sources</h3>
|
||||
<p>Connect Luma, Meetup, iCal feeds and manage sync</p>
|
||||
</div>
|
||||
</a>
|
||||
<a href="${REVENTS_PUBLIC_URL}" target="_blank" rel="noopener">
|
||||
<span class="nav-icon">🎪</span>
|
||||
<div class="nav-body">
|
||||
<h3>Open rEvents</h3>
|
||||
<p>Full standalone app at revents.online</p>
|
||||
</div>
|
||||
</a>
|
||||
</nav>
|
||||
</div>`,
|
||||
}));
|
||||
});
|
||||
|
||||
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"],
|
||||
};
|
||||
Loading…
Reference in New Issue