/**
* 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,
};