diff --git a/docker-compose.yml b/docker-compose.yml index 0819a1f..070aacd 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -136,6 +136,10 @@ services: - "traefik.http.routers.rspace-rswag.entrypoints=web" - "traefik.http.routers.rspace-rswag.priority=120" - "traefik.http.routers.rspace-rswag.service=rspace-online" + - "traefik.http.routers.rspace-rsocials.rule=Host(`rsocials.online`)" + - "traefik.http.routers.rspace-rsocials.entrypoints=web" + - "traefik.http.routers.rspace-rsocials.priority=120" + - "traefik.http.routers.rspace-rsocials.service=rspace-online" # Service configuration - "traefik.http.services.rspace-online.loadbalancer.server.port=3000" - "traefik.docker.network=traefik-public" diff --git a/modules/rsocials/mod.ts b/modules/rsocials/mod.ts new file mode 100644 index 0000000..54383f9 --- /dev/null +++ b/modules/rsocials/mod.ts @@ -0,0 +1,199 @@ +/** + * Socials module — federated social feed aggregator. + * + * Aggregates and displays social media activity across community members. + * Supports ActivityPub, RSS, and manual link sharing. + */ + +import { Hono } from "hono"; +import { renderShell } from "../../server/shell"; +import { getModuleInfoList } from "../../shared/module"; +import type { RSpaceModule } from "../../shared/module"; + +const routes = new Hono(); + +// ── API: Health ── +routes.get("/api/health", (c) => { + return c.json({ ok: true, module: "rsocials" }); +}); + +// ── API: Info ── +routes.get("/api/info", (c) => { + return c.json({ + module: "rsocials", + description: "Federated social feed aggregator for communities", + features: [ + "ActivityPub integration", + "RSS feed aggregation", + "Link sharing", + "Community timeline", + ], + }); +}); + +// ── API: Feed — community social timeline ── +routes.get("/api/feed", (c) => { + // Demo feed items + return c.json({ + items: [ + { + id: "demo-1", + type: "post", + author: "Alice", + content: "Just published our community governance proposal!", + source: "fediverse", + timestamp: new Date(Date.now() - 3600_000).toISOString(), + likes: 12, + replies: 3, + }, + { + id: "demo-2", + type: "link", + author: "Bob", + content: "Great article on local-first collaboration", + url: "https://example.com/local-first", + source: "shared", + timestamp: new Date(Date.now() - 7200_000).toISOString(), + likes: 8, + replies: 1, + }, + { + id: "demo-3", + type: "post", + author: "Carol", + content: "Welcome new members! Check out rSpace's tools in the app switcher above.", + source: "local", + timestamp: new Date(Date.now() - 14400_000).toISOString(), + likes: 24, + replies: 7, + }, + ], + demo: true, + }); +}); + +// ── Page route ── +routes.get("/", (c) => { + const space = c.req.param("space") || "demo"; + return c.html( + renderShell({ + title: `${space} — Socials | rSpace`, + moduleId: "rsocials", + spaceSlug: space, + modules: getModuleInfoList(), + theme: "dark", + body: ` +
+ `, + styles: ``, + }), + ); +}); + +export const socialsModule: RSpaceModule = { + id: "rsocials", + name: "rSocials", + icon: "\u{1F4E2}", + description: "Federated social feed aggregator for communities", + routes, + standaloneDomain: "rsocials.online", + feeds: [ + { + id: "social-feed", + name: "Social Feed", + kind: "data", + description: "Community social timeline — posts, links, and activity from connected platforms", + }, + ], + acceptsFeeds: ["data", "trust"], +}; diff --git a/server/index.ts b/server/index.ts index b8dff5b..88fea3b 100644 --- a/server/index.ts +++ b/server/index.ts @@ -62,6 +62,7 @@ import { inboxModule } from "../modules/inbox/mod"; import { dataModule } from "../modules/data/mod"; import { splatModule } from "../modules/splat/mod"; import { photosModule } from "../modules/photos/mod"; +import { socialsModule } from "../modules/rsocials/mod"; import { spaces } from "./spaces"; import { renderShell, renderModuleLanding } from "./shell"; import { fetchLandingPage } from "./landing-proxy"; @@ -91,6 +92,7 @@ registerModule(inboxModule); registerModule(dataModule); registerModule(splatModule); registerModule(photosModule); +registerModule(socialsModule); // ── Config ── const PORT = Number(process.env.PORT) || 3000; @@ -926,17 +928,9 @@ const server = Bun.serve