fix: resolve all 127 TypeScript errors with Hono type augmentation

Add types/hono.d.ts declaring effectiveSpace, spaceRole, and isOwner
on Hono's ContextVariableMap. Remove 127 "as any" casts across 18
files. Fix ParticipantStatus type in rmaps SyncMessage union.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-03-12 20:25:29 -07:00
parent 2a10277ec8
commit aa6f04e45e
21 changed files with 1150 additions and 192 deletions

View File

@ -104,7 +104,7 @@ const routes = new Hono();
// ── API: List books ──
routes.get("/api/books", async (c) => {
const space = c.req.param("space") || "global";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const search = c.req.query("search")?.toLowerCase();
const tag = c.req.query("tag");
const limit = Math.min(parseInt(c.req.query("limit") || "50"), 100);
@ -157,7 +157,7 @@ routes.get("/api/books", async (c) => {
// ── API: Upload book ──
routes.post("/api/books", async (c) => {
const space = c.req.param("space") || "global";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const token = extractToken(c.req.raw.headers);
if (!token) return c.json({ error: "Authentication required" }, 401);
@ -244,7 +244,7 @@ routes.post("/api/books", async (c) => {
// ── API: Get book details ──
routes.get("/api/books/:id", async (c) => {
const space = c.req.param("space") || "global";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const id = c.req.param("id");
const doc = ensureDoc(dataSpace);
@ -269,7 +269,7 @@ routes.get("/api/books/:id", async (c) => {
// ── API: Serve PDF ──
routes.get("/api/books/:id/pdf", async (c) => {
const space = c.req.param("space") || "global";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const id = c.req.param("id");
const doc = ensureDoc(dataSpace);
@ -307,7 +307,7 @@ routes.get("/api/books/:id/pdf", async (c) => {
// ── Page: Library ──
routes.get("/", (c) => {
const spaceSlug = c.req.param("space") || "personal";
const dataSpace = (c.get("effectiveSpace" as any) as string) || spaceSlug;
const dataSpace = c.get("effectiveSpace") || spaceSlug;
return c.html(renderShell({
title: `${spaceSlug} — Library | rSpace`,
moduleId: "rbooks",
@ -323,7 +323,7 @@ routes.get("/", (c) => {
// ── Page: Book reader ──
routes.get("/read/:id", async (c) => {
const spaceSlug = c.req.param("space") || "personal";
const dataSpace = (c.get("effectiveSpace" as any) as string) || spaceSlug;
const dataSpace = c.get("effectiveSpace") || spaceSlug;
const id = c.req.param("id");
const doc = ensureDoc(dataSpace);

View File

@ -260,7 +260,7 @@ function seedDemoIfEmpty(space: string) {
// GET /api/events — query events with filters
routes.get("/api/events", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const { start, end, source, search, rTool, rEntityId, upcoming } = c.req.query();
const doc = ensureDoc(dataSpace);
@ -313,7 +313,7 @@ routes.post("/api/events", async (c) => {
try { claims = await verifyEncryptIDToken(token); } catch { return c.json({ error: "Invalid token" }, 401); }
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const body = await c.req.json();
const { title, description, start_time, end_time, all_day, timezone, source_id, location_id, location_name,
is_virtual, virtual_url, virtual_platform, r_tool_source, r_tool_entity_id,
@ -398,7 +398,7 @@ routes.post("/api/events", async (c) => {
// GET /api/events/scheduled — query only scheduled knowledge items
routes.get("/api/events/scheduled", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const { date, upcoming, pending_only } = c.req.query();
const doc = ensureDoc(dataSpace);
@ -434,7 +434,7 @@ routes.get("/api/events/scheduled", async (c) => {
// GET /api/events/:id
routes.get("/api/events/:id", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const id = c.req.param("id");
const doc = ensureDoc(dataSpace);
@ -451,7 +451,7 @@ routes.patch("/api/events/:id", async (c) => {
try { claims = await verifyEncryptIDToken(token); } catch { return c.json({ error: "Invalid token" }, 401); }
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const id = c.req.param("id");
const body = await c.req.json();
@ -504,7 +504,7 @@ routes.patch("/api/events/:id", async (c) => {
// DELETE /api/events/:id
routes.delete("/api/events/:id", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const id = c.req.param("id");
const docId = calendarDocId(dataSpace);
@ -521,7 +521,7 @@ routes.delete("/api/events/:id", async (c) => {
routes.get("/api/sources", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const { is_active, is_visible, source_type } = c.req.query();
const doc = ensureDoc(dataSpace);
@ -550,7 +550,7 @@ routes.post("/api/sources", async (c) => {
try { await verifyEncryptIDToken(token); } catch { return c.json({ error: "Invalid token" }, 401); }
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const body = await c.req.json();
const docId = calendarDocId(dataSpace);
ensureDoc(dataSpace);
@ -611,7 +611,7 @@ function deriveLocations(doc: CalendarDoc): DerivedLocation[] {
routes.get("/api/locations", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const { granularity, parent, search, root } = c.req.query();
const doc = ensureDoc(dataSpace);
@ -638,7 +638,7 @@ routes.get("/api/locations", async (c) => {
routes.get("/api/locations/tree", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const doc = ensureDoc(dataSpace);
// Flat list with depth=0 since hierarchical parent_id data is not stored in Automerge
@ -690,7 +690,7 @@ routes.get("/api/lunar", async (c) => {
routes.get("/api/stats", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const doc = ensureDoc(dataSpace);
const events = Object.values(doc.events).length;
@ -704,7 +704,7 @@ routes.get("/api/stats", async (c) => {
routes.get("/api/context/:tool", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const tool = c.req.param("tool");
const entityId = c.req.query("entityId");
if (!entityId) return c.json({ error: "entityId required" }, 400);
@ -721,7 +721,7 @@ routes.get("/api/context/:tool", async (c) => {
// ── Page route ──
routes.get("/", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
return c.html(renderShell({
title: `${space} — Calendar | rSpace`,
moduleId: "rcal",

View File

@ -121,7 +121,7 @@ routes.post("/api/collect", async (c) => {
// ── Page route ──
routes.get("/", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
return c.html(renderShell({
title: `${space} — Data | rSpace`,
moduleId: "rdata",

View File

@ -19,7 +19,7 @@ routes.get("/api/health", (c) => {
routes.get("/", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const view = c.req.query("view");
if (view === "demo") {

View File

@ -19,7 +19,7 @@ routes.get("/api/health", (c) => {
routes.get("/", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const view = c.req.query("view");
if (view === "demo") {

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,20 @@
* Ported from rmaps-online/src/lib/sync.ts (simplified no @/types dependency).
*/
// ── Typed unions ──────────────────────────────────────────────
export type ParticipantStatus = "online" | "away" | "ghost" | "offline";
export type WaypointType = "meeting" | "poi" | "parking" | "food" | "danger" | "custom";
export type LocationSource = "gps" | "network" | "manual" | "ip" | "indoor";
export type PrecisionLevel = "exact" | "building" | "area" | "approximate";
export interface PrivacySettings {
precision: PrecisionLevel;
ghostMode: boolean;
}
// ── State interfaces ──────────────────────────────────────────
export interface RoomState {
id: string;
slug: string;
@ -19,7 +33,7 @@ export interface ParticipantState {
color: string;
joinedAt: string;
lastSeen: string;
status: string;
status: ParticipantStatus;
location?: LocationState;
}
@ -31,7 +45,7 @@ export interface LocationState {
heading?: number;
speed?: number;
timestamp: string;
source: string;
source: LocationSource;
indoor?: { level: number; x: number; y: number; spaceName?: string };
}
@ -44,14 +58,14 @@ export interface WaypointState {
indoor?: { level: number; x: number; y: number };
createdBy: string;
createdAt: string;
type: string;
type: WaypointType;
}
export type SyncMessage =
| { type: "join"; participant: ParticipantState }
| { type: "leave"; participantId: string }
| { type: "location"; participantId: string; location: LocationState }
| { type: "status"; participantId: string; status: string }
| { type: "status"; participantId: string; status: ParticipantStatus }
| { type: "waypoint_add"; waypoint: WaypointState }
| { type: "waypoint_remove"; waypointId: string }
| { type: "full_state"; state: RoomState }
@ -263,7 +277,7 @@ export class RoomSync {
}
}
updateStatus(status: string): void {
updateStatus(status: ParticipantStatus): void {
if (this.state.participants[this.participantId]) {
this.state.participants[this.participantId].status = status;
this.state.participants[this.participantId].lastSeen = new Date().toISOString();

View File

@ -86,22 +86,111 @@ routes.post("/api/push/request-location", async (c) => {
// ── Proxy: routing (OSRM + c3nav) ──
routes.post("/api/routing", async (c) => {
const body = await c.req.json();
const { from, to, mode = "walking" } = body;
const { from, to, mode = "walking", accessibility, indoor } = body;
if (!from?.lat || !from?.lng || !to?.lat || !to?.lng) {
return c.json({ error: "from and to with lat/lng required" }, 400);
}
// Use OSRM for outdoor routing
const segments: { type: string; coordinates: [number, number][]; distance: number; duration: number; steps?: any[] }[] = [];
let totalDistance = 0;
let estimatedTime = 0;
// ── Indoor routing via c3nav ──
if (indoor?.event && (from.indoor || to.indoor)) {
try {
// If both are indoor, route fully indoors
if (from.indoor && to.indoor) {
const c3navRes = await fetch(`https://${indoor.event}.c3nav.de/api/v2/routing/route/`, {
method: "POST",
headers: { "Content-Type": "application/json", "X-API-Key": "anonymous", Accept: "application/json", "User-Agent": "rMaps/1.0" },
body: JSON.stringify({
origin: { level: from.indoor.level, x: from.indoor.x, y: from.indoor.y },
destination: { level: to.indoor.level, x: to.indoor.x, y: to.indoor.y },
...(accessibility?.avoidStairs ? { options: { avoid_stairs: true } } : {}),
...(accessibility?.wheelchair ? { options: { wheelchair: true } } : {}),
}),
signal: AbortSignal.timeout(8000),
});
if (c3navRes.ok) {
const data = await c3navRes.json();
const coords: [number, number][] = data.path?.map((p: any) => [p.lng || p.x, p.lat || p.y]) || [];
const dist = data.distance || 0;
const dur = data.duration || Math.round(dist / 1.2);
segments.push({ type: "indoor", coordinates: coords, distance: dist, duration: dur });
totalDistance += dist;
estimatedTime += dur;
}
} else {
// Mixed indoor/outdoor: transition segment + outdoor OSRM
const outdoorPoint = from.indoor ? to : from;
const indoorPoint = from.indoor ? from : to;
const transitionCoords: [number, number][] = [
[indoorPoint.lng, indoorPoint.lat],
[outdoorPoint.lng, outdoorPoint.lat],
];
segments.push({ type: "transition", coordinates: transitionCoords, distance: 50, duration: 30 });
totalDistance += 50;
estimatedTime += 30;
// OSRM for the outdoor portion
const profile = mode === "driving" ? "car" : "foot";
const osrmRes = await fetch(
`https://router.project-osrm.org/route/v1/${profile}/${outdoorPoint.lng},${outdoorPoint.lat};${(from.indoor ? to : from).lng},${(from.indoor ? to : from).lat}?overview=full&geometries=geojson&steps=true`,
{ signal: AbortSignal.timeout(10000) },
);
if (osrmRes.ok) {
const osrmData = await osrmRes.json();
const route = osrmData.routes?.[0];
if (route) {
segments.push({
type: "outdoor",
coordinates: route.geometry?.coordinates || [],
distance: route.distance || 0,
duration: route.duration || 0,
steps: route.legs?.[0]?.steps,
});
totalDistance += route.distance || 0;
estimatedTime += route.duration || 0;
}
}
}
if (segments.length > 0) {
return c.json({
success: true,
route: { segments, totalDistance, estimatedTime: Math.round(estimatedTime) },
});
}
} catch { /* fall through to outdoor-only routing */ }
}
// ── Outdoor-only routing via OSRM ──
const profile = mode === "driving" ? "car" : "foot";
try {
const res = await fetch(
`https://router.project-osrm.org/route/v1/${profile}/${from.lng},${from.lat};${to.lng},${to.lat}?overview=full&geometries=geojson&steps=true`,
{ signal: AbortSignal.timeout(10000) }
{ signal: AbortSignal.timeout(10000) },
);
if (res.ok) {
const data = await res.json();
return c.json(data);
const route = data.routes?.[0];
if (route) {
return c.json({
success: true,
route: {
segments: [{
type: "outdoor",
coordinates: route.geometry?.coordinates || [],
distance: route.distance || 0,
duration: route.duration || 0,
steps: route.legs?.[0]?.steps,
}],
totalDistance: route.distance || 0,
estimatedTime: Math.round(route.duration || 0),
},
});
}
}
} catch {}
return c.json({ error: "Routing failed" }, 502);
@ -134,7 +223,7 @@ routes.get("/api/c3nav/:event", async (c) => {
// ── Page route ──
routes.get("/", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
return c.html(renderShell({
title: `${space} — Maps | rSpace`,
moduleId: "rmaps",
@ -149,7 +238,7 @@ routes.get("/", (c) => {
// Room-specific page
routes.get("/:room", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const room = c.req.param("room");
return c.html(renderShell({
title: `${room} — Maps | rSpace`,

View File

@ -67,7 +67,7 @@ const CACHE_TTL = 60_000;
// ── API: Health ──
routes.get("/api/health", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const token = getTokenForSpace(dataSpace);
return c.json({ ok: true, module: "network", space, twentyConfigured: !!token });
});
@ -75,7 +75,7 @@ routes.get("/api/health", (c) => {
// ── API: Info ──
routes.get("/api/info", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const token = getTokenForSpace(dataSpace);
return c.json({
module: "network",
@ -90,7 +90,7 @@ routes.get("/api/info", (c) => {
// ── API: People ──
routes.get("/api/people", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const token = getTokenForSpace(dataSpace);
const data = await twentyQuery(`{
people(first: 200) {
@ -116,7 +116,7 @@ routes.get("/api/people", async (c) => {
// ── API: Companies ──
routes.get("/api/companies", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const token = getTokenForSpace(dataSpace);
const data = await twentyQuery(`{
companies(first: 200) {
@ -194,7 +194,7 @@ routes.get("/api/delegations", async (c) => {
// ── API: Graph — transform entities to node/edge format ──
routes.get("/api/graph", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const token = getTokenForSpace(dataSpace);
// Check per-space cache (keyed by space + trust params)
@ -540,7 +540,7 @@ routes.get("/api/workspaces", (c) => {
// ── API: Opportunities ──
routes.get("/api/opportunities", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const token = getTokenForSpace(dataSpace);
const data = await twentyQuery(`{
opportunities(first: 200) {

View File

@ -250,7 +250,7 @@ function extractPlainText(content: string, format?: string): string {
// GET /api/notebooks — list notebooks
routes.get("/api/notebooks", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const notebooks = listNotebooks(dataSpace).map(({ doc }) => notebookToRest(doc));
notebooks.sort((a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime());
@ -260,7 +260,7 @@ routes.get("/api/notebooks", async (c) => {
// POST /api/notebooks — create notebook
routes.post("/api/notebooks", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const token = extractToken(c.req.raw.headers);
if (!token) return c.json({ error: "Authentication required" }, 401);
let claims;
@ -292,7 +292,7 @@ routes.post("/api/notebooks", async (c) => {
// GET /api/notebooks/:id — notebook detail with notes
routes.get("/api/notebooks/:id", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const id = c.req.param("id");
const docId = notebookDocId(dataSpace, id);
@ -314,7 +314,7 @@ routes.get("/api/notebooks/:id", async (c) => {
// PUT /api/notebooks/:id — update notebook
routes.put("/api/notebooks/:id", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const token = extractToken(c.req.raw.headers);
if (!token) return c.json({ error: "Authentication required" }, 401);
let claims;
@ -349,7 +349,7 @@ routes.put("/api/notebooks/:id", async (c) => {
// DELETE /api/notebooks/:id
routes.delete("/api/notebooks/:id", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const id = c.req.param("id");
const docId = notebookDocId(dataSpace, id);
@ -376,7 +376,7 @@ routes.delete("/api/notebooks/:id", async (c) => {
// GET /api/notes — list all notes
routes.get("/api/notes", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const { notebook_id, type, q, limit = "50", offset = "0" } = c.req.query();
let allNotes: ReturnType<typeof noteToRest>[] = [];
@ -412,7 +412,7 @@ routes.get("/api/notes", async (c) => {
// POST /api/notes — create note
routes.post("/api/notes", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const token = extractToken(c.req.raw.headers);
if (!token) return c.json({ error: "Authentication required" }, 401);
let claims;
@ -464,7 +464,7 @@ routes.post("/api/notes", async (c) => {
// GET /api/notes/:id — note detail
routes.get("/api/notes/:id", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const id = c.req.param("id");
const found = findNote(dataSpace, id);
@ -476,7 +476,7 @@ routes.get("/api/notes/:id", async (c) => {
// PUT /api/notes/:id — update note
routes.put("/api/notes/:id", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const id = c.req.param("id");
const body = await c.req.json();
const { title, content, content_format, type, url, language, is_pinned, sort_order } = body;
@ -517,7 +517,7 @@ routes.put("/api/notes/:id", async (c) => {
// DELETE /api/notes/:id
routes.delete("/api/notes/:id", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const id = c.req.param("id");
const found = findNote(dataSpace, id);
@ -601,7 +601,7 @@ function getConnectionDoc(space: string): ConnectionsDoc | null {
// POST /api/import/upload — ZIP upload for Logseq/Obsidian
routes.post("/api/import/upload", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const token = extractToken(c.req.raw.headers);
if (!token) return c.json({ error: "Authentication required" }, 401);
try { await verifyEncryptIDToken(token); } catch { return c.json({ error: "Invalid token" }, 401); }
@ -653,7 +653,7 @@ routes.post("/api/import/upload", async (c) => {
// POST /api/import/notion — Import selected Notion pages
routes.post("/api/import/notion", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const token = extractToken(c.req.raw.headers);
if (!token) return c.json({ error: "Authentication required" }, 401);
try { await verifyEncryptIDToken(token); } catch { return c.json({ error: "Invalid token" }, 401); }
@ -699,7 +699,7 @@ routes.post("/api/import/notion", async (c) => {
// POST /api/import/google-docs — Import selected Google Docs
routes.post("/api/import/google-docs", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const token = extractToken(c.req.raw.headers);
if (!token) return c.json({ error: "Authentication required" }, 401);
try { await verifyEncryptIDToken(token); } catch { return c.json({ error: "Invalid token" }, 401); }
@ -744,7 +744,7 @@ routes.post("/api/import/google-docs", async (c) => {
// GET /api/import/notion/pages — Browse Notion pages for selection
routes.get("/api/import/notion/pages", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const conn = getConnectionDoc(dataSpace);
if (!conn?.notion?.accessToken) {
return c.json({ error: "Notion not connected" }, 400);
@ -787,7 +787,7 @@ routes.get("/api/import/notion/pages", async (c) => {
// GET /api/import/google-docs/list — Browse Google Docs for selection
routes.get("/api/import/google-docs/list", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const conn = getConnectionDoc(dataSpace);
if (!conn?.google?.accessToken) {
return c.json({ error: "Google not connected" }, 400);
@ -818,7 +818,7 @@ routes.get("/api/import/google-docs/list", async (c) => {
// GET /api/export/obsidian — Download Obsidian-format ZIP
routes.get("/api/export/obsidian", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const notebookId = c.req.query("notebookId");
if (!notebookId) return c.json({ error: "notebookId is required" }, 400);
@ -841,7 +841,7 @@ routes.get("/api/export/obsidian", async (c) => {
// GET /api/export/logseq — Download Logseq-format ZIP
routes.get("/api/export/logseq", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const notebookId = c.req.query("notebookId");
if (!notebookId) return c.json({ error: "notebookId is required" }, 400);
@ -864,7 +864,7 @@ routes.get("/api/export/logseq", async (c) => {
// GET /api/export/markdown — Download universal Markdown ZIP
routes.get("/api/export/markdown", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const notebookId = c.req.query("notebookId");
const noteIds = c.req.query("noteIds");
@ -904,7 +904,7 @@ routes.get("/api/export/markdown", async (c) => {
// POST /api/export/notion — Push notes to Notion
routes.post("/api/export/notion", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const token = extractToken(c.req.raw.headers);
if (!token) return c.json({ error: "Authentication required" }, 401);
try { await verifyEncryptIDToken(token); } catch { return c.json({ error: "Invalid token" }, 401); }
@ -944,7 +944,7 @@ routes.post("/api/export/notion", async (c) => {
// POST /api/export/google-docs — Push notes to Google Docs
routes.post("/api/export/google-docs", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const token = extractToken(c.req.raw.headers);
if (!token) return c.json({ error: "Authentication required" }, 401);
try { await verifyEncryptIDToken(token); } catch { return c.json({ error: "Invalid token" }, 401); }
@ -984,7 +984,7 @@ routes.post("/api/export/google-docs", async (c) => {
// GET /api/connections — Status of all integrations
routes.get("/api/connections", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const conn = getConnectionDoc(dataSpace);
return c.json({
@ -1315,7 +1315,7 @@ routes.get("/voice", (c) => {
routes.get("/", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
return c.html(renderShell({
title: `${space} — Notes | rSpace`,
moduleId: "rnotes",

View File

@ -110,7 +110,7 @@ routes.get("/api/assets/:id/original", async (c) => {
// ── Embedded Immich UI ──
routes.get("/album", (c) => {
const spaceSlug = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || spaceSlug;
const dataSpace = c.get("effectiveSpace") || spaceSlug;
return c.html(renderExternalAppShell({
title: `${spaceSlug} — Immich | rSpace`,
moduleId: "rphotos",
@ -125,7 +125,7 @@ routes.get("/album", (c) => {
// ── Page route ──
routes.get("/", (c) => {
const spaceSlug = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || spaceSlug;
const dataSpace = c.get("effectiveSpace") || spaceSlug;
return c.html(renderShell({
title: `${spaceSlug} — Photos | rSpace`,
moduleId: "rphotos",

View File

@ -328,7 +328,7 @@ routes.get("/zine", (c) => {
// ── Page: Editor ──
routes.get("/", (c) => {
const spaceSlug = c.req.param("space") || "personal";
const dataSpace = (c.get("effectiveSpace" as any) as string) || spaceSlug;
const dataSpace = c.get("effectiveSpace") || spaceSlug;
return c.html(renderShell({
title: `${spaceSlug} — rPubs Editor | rSpace`,
moduleId: "rpubs",

View File

@ -799,7 +799,7 @@ function seedDefaultJobs(space: string) {
// GET / — serve schedule UI
routes.get("/", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
return c.html(
renderShell({
title: `${space} — Schedule | rSpace`,
@ -817,7 +817,7 @@ routes.get("/", (c) => {
// GET /api/jobs — list all jobs
routes.get("/api/jobs", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const doc = ensureDoc(dataSpace);
const jobs = Object.values(doc.jobs).map((j) => ({
...j,
@ -830,7 +830,7 @@ routes.get("/api/jobs", (c) => {
// POST /api/jobs — create a new job
routes.post("/api/jobs", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const body = await c.req.json();
const { name, description, cronExpression, timezone, actionType, actionConfig, enabled } = body;
@ -878,7 +878,7 @@ routes.post("/api/jobs", async (c) => {
// GET /api/jobs/:id
routes.get("/api/jobs/:id", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const id = c.req.param("id");
const doc = ensureDoc(dataSpace);
@ -890,7 +890,7 @@ routes.get("/api/jobs/:id", (c) => {
// PUT /api/jobs/:id — update a job
routes.put("/api/jobs/:id", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const id = c.req.param("id");
const body = await c.req.json();
@ -933,7 +933,7 @@ routes.put("/api/jobs/:id", async (c) => {
// DELETE /api/jobs/:id
routes.delete("/api/jobs/:id", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const id = c.req.param("id");
const docId = scheduleDocId(dataSpace);
@ -950,7 +950,7 @@ routes.delete("/api/jobs/:id", (c) => {
// POST /api/jobs/:id/run — manually trigger a job
routes.post("/api/jobs/:id/run", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const id = c.req.param("id");
const docId = scheduleDocId(dataSpace);
@ -997,7 +997,7 @@ routes.post("/api/jobs/:id/run", async (c) => {
// GET /api/log — execution log
routes.get("/api/log", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const doc = ensureDoc(dataSpace);
const log = [...doc.log].reverse(); // newest first
return c.json({ count: log.length, results: log });
@ -1006,7 +1006,7 @@ routes.get("/api/log", (c) => {
// GET /api/log/:jobId — execution log filtered by job
routes.get("/api/log/:jobId", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const jobId = c.req.param("jobId");
const doc = ensureDoc(dataSpace);
const log = doc.log.filter((e) => e.jobId === jobId).reverse();
@ -1156,7 +1156,7 @@ async function executeReminderEmail(
// GET /api/reminders — list reminders
routes.get("/api/reminders", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const doc = ensureDoc(dataSpace);
let reminders = Object.values(doc.reminders);
@ -1185,7 +1185,7 @@ routes.get("/api/reminders", (c) => {
// POST /api/reminders — create a reminder
routes.post("/api/reminders", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const body = await c.req.json();
const { title, description, remindAt, allDay, timezone, notifyEmail, syncToCalendar, cronExpression } = body;
@ -1239,7 +1239,7 @@ routes.post("/api/reminders", async (c) => {
// GET /api/reminders/:id — get single reminder
routes.get("/api/reminders/:id", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const id = c.req.param("id");
const doc = ensureDoc(dataSpace);
@ -1251,7 +1251,7 @@ routes.get("/api/reminders/:id", (c) => {
// PUT /api/reminders/:id — update a reminder
routes.put("/api/reminders/:id", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const id = c.req.param("id");
const body = await c.req.json();
@ -1279,7 +1279,7 @@ routes.put("/api/reminders/:id", async (c) => {
// DELETE /api/reminders/:id — delete (cascades to calendar)
routes.delete("/api/reminders/:id", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const id = c.req.param("id");
const docId = scheduleDocId(dataSpace);
@ -1302,7 +1302,7 @@ routes.delete("/api/reminders/:id", (c) => {
// POST /api/reminders/:id/complete — mark completed
routes.post("/api/reminders/:id/complete", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const id = c.req.param("id");
const docId = scheduleDocId(dataSpace);
@ -1323,7 +1323,7 @@ routes.post("/api/reminders/:id/complete", (c) => {
// POST /api/reminders/:id/snooze — reschedule to a new date
routes.post("/api/reminders/:id/snooze", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const id = c.req.param("id");
const body = await c.req.json();
@ -1384,7 +1384,7 @@ routes.get("/reminders", (c) => {
routes.get("/api/workflows", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const doc = ensureDoc(dataSpace);
// Ensure workflows field exists on older docs
const workflows = Object.values(doc.workflows || {});
@ -1394,7 +1394,7 @@ routes.get("/api/workflows", (c) => {
routes.post("/api/workflows", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const body = await c.req.json();
const docId = scheduleDocId(dataSpace);
@ -1426,7 +1426,7 @@ routes.post("/api/workflows", async (c) => {
routes.get("/api/workflows/:id", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const id = c.req.param("id");
const doc = ensureDoc(dataSpace);
@ -1437,7 +1437,7 @@ routes.get("/api/workflows/:id", (c) => {
routes.put("/api/workflows/:id", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const id = c.req.param("id");
const body = await c.req.json();
@ -1468,7 +1468,7 @@ routes.put("/api/workflows/:id", async (c) => {
routes.delete("/api/workflows/:id", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const id = c.req.param("id");
const docId = scheduleDocId(dataSpace);
@ -1919,7 +1919,7 @@ function appendWorkflowLog(
// POST /api/workflows/:id/run — manual execute
routes.post("/api/workflows/:id/run", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const id = c.req.param("id");
const docId = scheduleDocId(dataSpace);
@ -1947,7 +1947,7 @@ routes.post("/api/workflows/:id/run", async (c) => {
// POST /api/workflows/webhook/:hookId — external webhook trigger
routes.post("/api/workflows/webhook/:hookId", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const hookId = c.req.param("hookId");
const doc = ensureDoc(dataSpace);
@ -1981,7 +1981,7 @@ routes.post("/api/workflows/webhook/:hookId", async (c) => {
// GET /api/workflows/log — workflow execution log
routes.get("/api/workflows/log", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const doc = ensureDoc(dataSpace);
const log = [...(doc.workflowLog || [])].reverse(); // newest first
return c.json({ count: log.length, results: log });

View File

@ -195,7 +195,7 @@ routes.get("/api/feed", (c) =>
routes.post("/api/threads/:id/image", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const id = c.req.param("id");
if (!id || id.includes("..") || id.includes("/")) return c.json({ error: "Invalid ID" }, 400);
@ -240,7 +240,7 @@ routes.post("/api/threads/:id/image", async (c) => {
routes.post("/api/threads/:id/upload-image", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const id = c.req.param("id");
if (!id || id.includes("..") || id.includes("/")) return c.json({ error: "Invalid ID" }, 400);
@ -277,7 +277,7 @@ routes.post("/api/threads/:id/upload-image", async (c) => {
routes.post("/api/threads/:id/tweet/:index/upload-image", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const id = c.req.param("id");
const index = c.req.param("index");
if (!id || id.includes("..") || id.includes("/")) return c.json({ error: "Invalid ID" }, 400);
@ -318,7 +318,7 @@ routes.post("/api/threads/:id/tweet/:index/upload-image", async (c) => {
routes.post("/api/threads/:id/tweet/:index/image", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const id = c.req.param("id");
const index = c.req.param("index");
if (!id || id.includes("..") || id.includes("/")) return c.json({ error: "Invalid ID" }, 400);
@ -374,7 +374,7 @@ routes.post("/api/threads/:id/tweet/:index/image", async (c) => {
routes.delete("/api/threads/:id/tweet/:index/image", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const id = c.req.param("id");
const index = c.req.param("index");
if (!id || id.includes("..") || id.includes("/")) return c.json({ error: "Invalid ID" }, 400);
@ -403,7 +403,7 @@ routes.delete("/api/threads/:id/tweet/:index/image", async (c) => {
routes.delete("/api/threads/:id/images", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const id = c.req.param("id");
if (!id || id.includes("..") || id.includes("/")) return c.json({ error: "Invalid ID" }, 400);
@ -640,7 +640,7 @@ Rules:
routes.get("/api/campaign-workflows", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const doc = ensureDoc(dataSpace);
const workflows = Object.values(doc.campaignWorkflows || {});
workflows.sort((a, b) => a.name.localeCompare(b.name));
@ -649,7 +649,7 @@ routes.get("/api/campaign-workflows", (c) => {
routes.post("/api/campaign-workflows", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const body = await c.req.json();
const docId = socialsDocId(dataSpace);
@ -681,7 +681,7 @@ routes.post("/api/campaign-workflows", async (c) => {
routes.get("/api/campaign-workflows/:id", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const id = c.req.param("id");
const doc = ensureDoc(dataSpace);
@ -692,7 +692,7 @@ routes.get("/api/campaign-workflows/:id", (c) => {
routes.put("/api/campaign-workflows/:id", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const id = c.req.param("id");
const body = await c.req.json();
@ -722,7 +722,7 @@ routes.put("/api/campaign-workflows/:id", async (c) => {
routes.delete("/api/campaign-workflows/:id", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const id = c.req.param("id");
const docId = socialsDocId(dataSpace);
@ -739,7 +739,7 @@ routes.delete("/api/campaign-workflows/:id", (c) => {
// POST /api/campaign-workflows/:id/run — manual execute (stub)
routes.post("/api/campaign-workflows/:id/run", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const id = c.req.param("id");
const doc = ensureDoc(dataSpace);
@ -777,7 +777,7 @@ routes.post("/api/campaign-workflows/:id/run", async (c) => {
routes.post("/api/campaign-workflows/webhook/:hookId", async (c) => {
const hookId = c.req.param("hookId");
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const doc = ensureDoc(dataSpace);
// Find workflow containing a webhook-trigger node with this hookId
@ -831,7 +831,7 @@ function topologicalSortCampaign(nodes: CampaignWorkflowNode[], edges: CampaignW
routes.get("/campaign", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
return c.html(renderShell({
title: `Campaign — rSocials | rSpace`,
moduleId: "rsocials",
@ -846,7 +846,7 @@ routes.get("/campaign", (c) => {
routes.get("/thread/:id", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const id = c.req.param("id");
if (!id || id.includes("..") || id.includes("/")) return c.text("Not found", 404);
@ -889,7 +889,7 @@ routes.get("/thread/:id", async (c) => {
routes.get("/thread-editor/:id/edit", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const id = c.req.param("id");
if (!id || id.includes("..") || id.includes("/")) return c.text("Not found", 404);
@ -912,7 +912,7 @@ routes.get("/thread-editor/:id/edit", async (c) => {
routes.get("/thread-editor", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
return c.html(renderShell({
title: `Thread Editor — rSocials | rSpace`,
moduleId: "rsocials",
@ -927,7 +927,7 @@ routes.get("/thread-editor", (c) => {
routes.get("/threads", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
return c.html(renderShell({
title: `Threads — rSocials | rSpace`,
moduleId: "rsocials",
@ -999,7 +999,7 @@ const POSTIZ_URL = process.env.POSTIZ_URL || "https://demo.rsocials.online";
routes.get("/scheduler", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
return c.html(renderExternalAppShell({
title: `Post Scheduler — rSocials | rSpace`,
moduleId: "rsocials",
@ -1039,7 +1039,7 @@ routes.get("/newsletter-list", async (c) => {
routes.get("/feed", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const isDemo = space === "demo";
const body = isDemo ? renderDemoFeedHTML() : renderLanding();
const styles = isDemo
@ -1058,7 +1058,7 @@ routes.get("/feed", (c) => {
routes.get("/landing", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
return c.html(renderShell({
title: `${space} — rSocials | rSpace`,
moduleId: "rsocials",

View File

@ -205,7 +205,7 @@ const routes = new Hono();
// ── API: List splats ──
routes.get("/api/splats", async (c) => {
const spaceSlug = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || spaceSlug;
const dataSpace = c.get("effectiveSpace") || spaceSlug;
const tag = c.req.query("tag");
const limit = Math.min(parseInt(c.req.query("limit") || "50"), 100);
const offset = parseInt(c.req.query("offset") || "0");
@ -231,7 +231,7 @@ routes.get("/api/splats", async (c) => {
// ── API: Get splat details ──
routes.get("/api/splats/:id", async (c) => {
const spaceSlug = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || spaceSlug;
const dataSpace = c.get("effectiveSpace") || spaceSlug;
const id = c.req.param("id");
const doc = ensureDoc(dataSpace);
@ -256,7 +256,7 @@ routes.get("/api/splats/:id", async (c) => {
// Matches both /api/splats/:id/file and /api/splats/:id/:filename (e.g. rainbow-sphere.splat)
routes.get("/api/splats/:id/:filename", async (c) => {
const spaceSlug = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || spaceSlug;
const dataSpace = c.get("effectiveSpace") || spaceSlug;
const id = c.req.param("id");
const doc = ensureDoc(dataSpace);
@ -310,7 +310,7 @@ routes.post("/api/splats", async (c) => {
}
const spaceSlug = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || spaceSlug;
const dataSpace = c.get("effectiveSpace") || spaceSlug;
const formData = await c.req.formData();
const file = formData.get("file") as File | null;
const title = (formData.get("title") as string || "").trim();
@ -422,7 +422,7 @@ routes.post("/api/splats/from-media", async (c) => {
}
const spaceSlug = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || spaceSlug;
const dataSpace = c.get("effectiveSpace") || spaceSlug;
const formData = await c.req.formData();
const title = (formData.get("title") as string || "").trim();
const description = (formData.get("description") as string || "").trim() || null;
@ -554,7 +554,7 @@ routes.delete("/api/splats/:id", async (c) => {
}
const spaceSlug = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || spaceSlug;
const dataSpace = c.get("effectiveSpace") || spaceSlug;
const id = c.req.param("id");
const doc = ensureDoc(dataSpace);
@ -580,7 +580,7 @@ routes.delete("/api/splats/:id", async (c) => {
// ── Page: Gallery ──
routes.get("/", async (c) => {
const spaceSlug = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || spaceSlug;
const dataSpace = c.get("effectiveSpace") || spaceSlug;
const doc = ensureDoc(dataSpace);
@ -619,7 +619,7 @@ routes.get("/", async (c) => {
// ── Page: Viewer ──
routes.get("/view/:id", async (c) => {
const spaceSlug = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || spaceSlug;
const dataSpace = c.get("effectiveSpace") || spaceSlug;
const id = c.req.param("id");
const doc = ensureDoc(dataSpace);

View File

@ -230,7 +230,7 @@ routes.get("/api/artifact/:id", async (c) => {
// ── Page route: swag designer ──
routes.get("/", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
return c.html(renderShell({
title: `Swag Designer | rSpace`,
moduleId: "rswag",

View File

@ -455,7 +455,7 @@ routes.get("/api/spaces/:slug/activity", async (c) => {
// ── Page route ──
routes.get("/", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
return c.html(renderShell({
title: `${space} — Tasks | rSpace`,
moduleId: "rtasks",

View File

@ -68,7 +68,7 @@ const routes = new Hono();
// GET /api/trips — list trips
routes.get("/api/trips", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const docIds = listTripDocIds(dataSpace);
const rows = docIds.map((docId) => {
@ -103,7 +103,7 @@ routes.post("/api/trips", async (c) => {
if (!title?.trim()) return c.json({ error: "Title required" }, 400);
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const tripId = newId();
const slug = title.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
const now = Date.now();
@ -142,7 +142,7 @@ routes.post("/api/trips", async (c) => {
// GET /api/trips/:id — trip detail with all sub-resources
routes.get("/api/trips/:id", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const tripId = c.req.param("id");
const docId = tripDocId(dataSpace, tripId);
const doc = _syncServer!.getDoc<TripDoc>(docId);
@ -166,7 +166,7 @@ routes.get("/api/trips/:id", async (c) => {
// PUT /api/trips/:id — update trip
routes.put("/api/trips/:id", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const tripId = c.req.param("id");
const docId = tripDocId(dataSpace, tripId);
const doc = _syncServer!.getDoc<TripDoc>(docId);
@ -202,7 +202,7 @@ routes.post("/api/trips/:id/destinations", async (c) => {
try { await verifyEncryptIDToken(token); } catch { return c.json({ error: "Invalid token" }, 401); }
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const tripId = c.req.param("id");
ensureDoc(dataSpace, tripId);
const docId = tripDocId(dataSpace, tripId);
@ -239,7 +239,7 @@ routes.post("/api/trips/:id/itinerary", async (c) => {
try { await verifyEncryptIDToken(token); } catch { return c.json({ error: "Invalid token" }, 401); }
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const tripId = c.req.param("id");
ensureDoc(dataSpace, tripId);
const docId = tripDocId(dataSpace, tripId);
@ -276,7 +276,7 @@ routes.post("/api/trips/:id/bookings", async (c) => {
try { await verifyEncryptIDToken(token); } catch { return c.json({ error: "Invalid token" }, 401); }
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const tripId = c.req.param("id");
ensureDoc(dataSpace, tripId);
const docId = tripDocId(dataSpace, tripId);
@ -314,7 +314,7 @@ routes.post("/api/trips/:id/expenses", async (c) => {
try { await verifyEncryptIDToken(token); } catch { return c.json({ error: "Invalid token" }, 401); }
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const tripId = c.req.param("id");
ensureDoc(dataSpace, tripId);
const docId = tripDocId(dataSpace, tripId);
@ -346,7 +346,7 @@ routes.post("/api/trips/:id/expenses", async (c) => {
routes.get("/api/trips/:id/packing", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const tripId = c.req.param("id");
const docId = tripDocId(dataSpace, tripId);
const doc = _syncServer!.getDoc<TripDoc>(docId);
@ -365,7 +365,7 @@ routes.post("/api/trips/:id/packing", async (c) => {
try { await verifyEncryptIDToken(token); } catch { return c.json({ error: "Invalid token" }, 401); }
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const tripId = c.req.param("id");
ensureDoc(dataSpace, tripId);
const docId = tripDocId(dataSpace, tripId);
@ -394,7 +394,7 @@ routes.post("/api/trips/:id/packing", async (c) => {
routes.patch("/api/packing/:id", async (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
const packingId = c.req.param("id");
// Find the trip doc containing this packing item
@ -434,7 +434,7 @@ routes.post("/api/route", async (c) => {
// ── Route planner page ──
routes.get("/routes", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
return c.html(renderShell({
title: `${space} — Route Planner | rTrips`,
moduleId: "rtrips",
@ -586,7 +586,7 @@ routes.get("/demo", (c) => {
// ── Page route ──
routes.get("/", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
return c.html(renderShell({
title: `${space} — Trips | rSpace`,
moduleId: "rtrips",

View File

@ -192,7 +192,7 @@ routes.get("/api/health", (c) => c.json({ ok: true }));
// ── Page route ──
routes.get("/", (c) => {
const space = c.req.param("space") || "demo";
const dataSpace = (c.get("effectiveSpace" as any) as string) || space;
const dataSpace = c.get("effectiveSpace") || space;
return c.html(renderShell({
title: `${space} — Tube | rSpace`,
moduleId: "rtube",

View File

@ -1663,7 +1663,7 @@ for (const mod of getAllModules()) {
// Resolve effective data space (global vs space-scoped)
const overrides = doc?.meta?.moduleScopeOverrides ?? null;
const effectiveSpace = resolveDataSpace(mod.id, space, overrides);
c.set("effectiveSpace" as any, effectiveSpace);
c.set("effectiveSpace", effectiveSpace);
// Resolve caller's role for write-method blocking
const method = c.req.method;
@ -1675,8 +1675,8 @@ for (const mod of getAllModules()) {
}
const resolved = await resolveCallerRole(space, claims);
if (resolved) {
c.set("spaceRole" as any, resolved.role);
c.set("isOwner" as any, resolved.isOwner);
c.set("spaceRole", resolved.role);
c.set("isOwner", resolved.isOwner);
if (resolved.role === "viewer") {
return c.json({ error: "Write access required" }, 403);
}

9
types/hono.d.ts vendored Normal file
View File

@ -0,0 +1,9 @@
import 'hono';
declare module 'hono' {
interface ContextVariableMap {
effectiveSpace: string;
spaceRole: string;
isOwner: boolean;
}
}