/** * Funds module — budget flows, river visualization, and treasury management. * * Proxies flow-service API calls and serves the BudgetRiver visualization. */ import { Hono } from "hono"; import { renderShell } from "../../server/shell"; import type { RSpaceModule } from "../../shared/module"; import { getModuleInfoList } from "../../shared/module"; const FLOW_SERVICE_URL = process.env.FLOW_SERVICE_URL || "http://payment-flow:3010"; const routes = new Hono(); // ─── Flow Service API proxy ───────────────────────────── // These proxy to the payment-flow backend so the frontend // can call them from the same origin. routes.get("/api/flows", async (c) => { const owner = c.req.header("X-Owner-Address") || ""; const res = await fetch(`${FLOW_SERVICE_URL}/api/flows?owner=${encodeURIComponent(owner)}`); return c.json(await res.json(), res.status as any); }); routes.get("/api/flows/:flowId", async (c) => { const res = await fetch(`${FLOW_SERVICE_URL}/api/flows/${c.req.param("flowId")}`); return c.json(await res.json(), res.status as any); }); routes.post("/api/flows", async (c) => { const body = await c.req.text(); const res = await fetch(`${FLOW_SERVICE_URL}/api/flows`, { method: "POST", headers: { "Content-Type": "application/json" }, body, }); return c.json(await res.json(), res.status as any); }); routes.post("/api/flows/:flowId/deposit", async (c) => { const body = await c.req.text(); const res = await fetch(`${FLOW_SERVICE_URL}/api/flows/${c.req.param("flowId")}/deposit`, { method: "POST", headers: { "Content-Type": "application/json" }, body, }); return c.json(await res.json(), res.status as any); }); routes.post("/api/flows/:flowId/withdraw", async (c) => { const body = await c.req.text(); const res = await fetch(`${FLOW_SERVICE_URL}/api/flows/${c.req.param("flowId")}/withdraw`, { method: "POST", headers: { "Content-Type": "application/json" }, body, }); return c.json(await res.json(), res.status as any); }); routes.post("/api/flows/:flowId/activate", async (c) => { const res = await fetch(`${FLOW_SERVICE_URL}/api/flows/${c.req.param("flowId")}/activate`, { method: "POST" }); return c.json(await res.json(), res.status as any); }); routes.post("/api/flows/:flowId/pause", async (c) => { const res = await fetch(`${FLOW_SERVICE_URL}/api/flows/${c.req.param("flowId")}/pause`, { method: "POST" }); return c.json(await res.json(), res.status as any); }); routes.post("/api/flows/:flowId/funnels", async (c) => { const body = await c.req.text(); const res = await fetch(`${FLOW_SERVICE_URL}/api/flows/${c.req.param("flowId")}/funnels`, { method: "POST", headers: { "Content-Type": "application/json" }, body, }); return c.json(await res.json(), res.status as any); }); routes.post("/api/flows/:flowId/outcomes", async (c) => { const body = await c.req.text(); const res = await fetch(`${FLOW_SERVICE_URL}/api/flows/${c.req.param("flowId")}/outcomes`, { method: "POST", headers: { "Content-Type": "application/json" }, body, }); return c.json(await res.json(), res.status as any); }); routes.get("/api/flows/:flowId/transactions", async (c) => { const res = await fetch(`${FLOW_SERVICE_URL}/api/flows/${c.req.param("flowId")}/transactions`); return c.json(await res.json(), res.status as any); }); // ─── Page route ───────────────────────────────────────── routes.get("/", (c) => { const spaceSlug = c.req.param("space") || "demo"; return c.html(renderShell({ title: `${spaceSlug} — Funds | rSpace`, moduleId: "funds", spaceSlug, modules: getModuleInfoList(), theme: "light", styles: ``, body: ``, scripts: ``, })); }); export const fundsModule: RSpaceModule = { id: "funds", name: "rFunds", icon: "\uD83C\uDF0A", description: "Budget flows, river visualization, and treasury management", routes, };