/**
* rSocials module — campaign strategy workflow builder.
*
* Proxies campaign API + embeds the Next.js campaign editor from rsocials:3000.
* Page routes render an iframe inside the rSpace shell so the campaign builder
* gets the full rSpace header/nav while the Next.js app runs independently.
*/
import { Hono } from "hono";
import { renderShell } from "../../server/shell";
import type { RSpaceModule } from "../../shared/module";
import { getModuleInfoList } from "../../shared/module";
const RSOCIALS_INTERNAL = process.env.RSOCIALS_URL || "http://rsocials:3000";
const RSOCIALS_PUBLIC = "https://rsocials.online";
const routes = new Hono();
// ── API proxy ────────────────────────────────────────────
// Proxy all campaign API calls to the rsocials container
routes.get("/api/campaigns", async (c) => {
const res = await fetch(`${RSOCIALS_INTERNAL}/api/campaigns`);
return c.json(await res.json(), res.status as StatusCode);
});
routes.get("/api/campaigns/:id", async (c) => {
const res = await fetch(`${RSOCIALS_INTERNAL}/api/campaigns/${c.req.param("id")}`);
return c.json(await res.json(), res.status as StatusCode);
});
routes.post("/api/campaigns", async (c) => {
const body = await c.req.text();
const res = await fetch(`${RSOCIALS_INTERNAL}/api/campaigns`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body,
});
return c.json(await res.json(), res.status as StatusCode);
});
routes.put("/api/campaigns/:id", async (c) => {
const body = await c.req.text();
const res = await fetch(`${RSOCIALS_INTERNAL}/api/campaigns/${c.req.param("id")}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body,
});
return c.json(await res.json(), res.status as StatusCode);
});
routes.delete("/api/campaigns/:id", async (c) => {
const res = await fetch(`${RSOCIALS_INTERNAL}/api/campaigns/${c.req.param("id")}`, {
method: "DELETE",
});
return c.json(await res.json(), res.status as StatusCode);
});
// ── Page routes ──────────────────────────────────────────
function campaignFrame(src: string, space: string, title: string) {
return renderShell({
title,
moduleId: "rsocials",
spaceSlug: space,
modules: getModuleInfoList(),
theme: "dark",
styles: ``,
body: `
`,
});
}
// /rsocials/ → campaign list
routes.get("/", (c) => {
const space = c.req.param("space") || "demo";
return c.html(
campaignFrame(`${RSOCIALS_PUBLIC}/campaigns`, space, `Campaigns | rSocials`)
);
});
// /rsocials/campaign → campaign list (alias)
routes.get("/campaign", (c) => {
const space = c.req.param("space") || "demo";
return c.html(
campaignFrame(`${RSOCIALS_PUBLIC}/campaigns`, space, `Campaigns | rSocials`)
);
});
// /rsocials/campaign/:id → specific campaign editor
routes.get("/campaign/:id", (c) => {
const space = c.req.param("space") || "demo";
const id = c.req.param("id");
return c.html(
campaignFrame(
`${RSOCIALS_PUBLIC}/campaigns/${id}`,
space,
`Campaign Editor | rSocials`
)
);
});
type StatusCode = 200 | 201 | 204 | 400 | 401 | 403 | 404 | 409 | 500 | 502;
export const rsocialsModule: RSpaceModule = {
id: "rsocials",
name: "rSocials",
icon: "\uD83D\uDCE2",
description: "Campaign strategy workflow builder",
routes,
standaloneDomain: "rsocials.online",
};