fix(infra): consolidate external service URLs to rspace.online (TASK-51.1)
- Replace rdata.online/collect.js with /collect.js proxy route (7 files) - Update MAPS_SYNC_URL to wss://maps-sync.rspace.online - Fix legacy TWENTY_API_URL to crm.rspace.online - Add Traefik host exclusions for maps-sync, crm, analytics, newsletter - Close TASK-23 (feature parity audit) and TASK-51.2 (301 redirects) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
1460d2b579
commit
751a2c8e7b
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
id: TASK-23
|
||||
title: 'Feature parity audit: 13 overlapping shapes'
|
||||
status: To Do
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-18 19:49'
|
||||
labels:
|
||||
|
|
@ -37,7 +37,40 @@ For each pair: read both implementations, note feature gaps, classify as critica
|
|||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [ ] #1 All 13 shape pairs compared side-by-side
|
||||
- [ ] #2 Feature gaps documented with severity (critical/nice-to-have)
|
||||
- [ ] #3 Critical gaps identified for immediate fix
|
||||
- [x] #1 All 13 shape pairs compared side-by-side
|
||||
- [x] #2 Feature gaps documented with severity (critical/nice-to-have)
|
||||
- [x] #3 Critical gaps identified for immediate fix
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
### Audit Findings Summary
|
||||
|
||||
Completed feature-parity audit across all 13 shape pairs. Identified critical gaps in 7 shapes requiring backend/architecture work:
|
||||
|
||||
**Critical Gaps by Shape:**
|
||||
|
||||
1. **folk-chat**: No real-time backend sync (local-only messages). Messages only persist in client-side Automerge store; no server broadcast or presence system.
|
||||
|
||||
2. **folk-video-chat**: No peer-to-peer video infrastructure. Missing Jitsi integration; only local camera preview available.
|
||||
|
||||
3. **folk-markdown**: Plain textarea implementation vs canvas-website's MDXEditor. No table support, syntax highlighting, or rich markdown features.
|
||||
|
||||
4. **folk-slide**: No slide navigation or presentation system. Currently a decorative container only; lacks deck traversal and full-screen mode.
|
||||
|
||||
5. **folk-prompt**: No streaming response support. No arrow-binding template substitution for dynamic prompt construction from shape references.
|
||||
|
||||
6. **folk-obs-note**: No vault sync mechanism (Quartz/GitHub integration). Plain textarea editor without multi-format obsidian compatibility.
|
||||
|
||||
7. **folk-map**: No collaborative presence indicators, pin annotations, routing/directions, or multi-style layer support (satellite, terrain).
|
||||
|
||||
**Pervasive Gap (All 13 Shapes)**:
|
||||
StandardizedToolWrapper features missing across all implementations: pin/minimize/maximize/tags. These foundational window-management features need framework-level integration.
|
||||
|
||||
**Best Parity Shapes** (closest feature coverage):
|
||||
- folk-google-item: Good feature alignment with canvas-website GoogleItem
|
||||
- folk-embed: Functional iframe/external content support
|
||||
- folk-transcription: Core audio-to-text parity achieved
|
||||
|
||||
**Recommendation**:
|
||||
Prioritize real-time sync infrastructure (folk-chat, folk-video-chat) and StandardizedToolWrapper integration as foundational for improving parity across all shapes. Markdown and slide features require moderate lift; map and obs-note features are lower priority for MVP.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
id: TASK-51.1
|
||||
title: 'Phase 2: Fix external service URLs (analytics, maps sync, Twenty CRM)'
|
||||
status: To Do
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-25 07:47'
|
||||
labels:
|
||||
|
|
@ -25,7 +25,27 @@ DECISION NEEDED: Is Twenty CRM (rnetwork.online) a separate container or proxied
|
|||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [ ] #1 Analytics collect.js loads from relative path on rspace.online
|
||||
- [ ] #2 Maps sync WebSocket connects via new URL
|
||||
- [ ] #3 Network module reaches Twenty CRM without depending on rnetwork.online domain
|
||||
- [x] #1 Analytics collect.js loads from relative path on rspace.online
|
||||
- [x] #2 Maps sync WebSocket connects via new URL
|
||||
- [x] #3 Network module reaches Twenty CRM without depending on rnetwork.online domain
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
<!-- SECTION:NOTES:BEGIN -->
|
||||
### AC#1 — collect.js
|
||||
- Added root-level `/collect.js` proxy route in `server/index.ts` (proxies from Umami at `analytics.rspace.online`)
|
||||
- Replaced `https://rdata.online/collect.js` → `/collect.js` in: server/landing-proxy.ts, server/shell.ts (2x), server/landing.ts (2x), website/create-space.html, website/index.html, website/public/landing.html
|
||||
|
||||
### AC#2 — Maps sync
|
||||
- Changed `MAPS_SYNC_URL` in docker-compose.yml: `wss://sync.rmaps.online` → `wss://maps-sync.rspace.online`
|
||||
- Changed fallback in `modules/rmaps/mod.ts` to `wss://maps-sync.rspace.online`
|
||||
- Added `Host(maps-sync.rspace.online)` to rmaps-sync Traefik labels on Netcup
|
||||
- Added `maps-sync.rspace.online` CNAME in Cloudflare DNS
|
||||
- Added `!Host(maps-sync.rspace.online)` exclusion to rspace-canvas wildcard Traefik rule
|
||||
|
||||
### AC#3 — Twenty CRM
|
||||
- Already resolved: main docker-compose uses `TWENTY_API_URL=http://twenty-ch-server:3000` (internal Docker DNS)
|
||||
- Fallback in rnetwork module already points to `crm.rspace.online`
|
||||
- Fixed legacy docker-compose.standalone.yml: `https://rnetwork.online` → `https://crm.rspace.online`
|
||||
<!-- SECTION:NOTES:END -->
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
id: TASK-51.2
|
||||
title: 'Phase 1: Convert standalone domain rewrite to 301 redirects'
|
||||
status: To Do
|
||||
status: Done
|
||||
assignee: []
|
||||
created_date: '2026-02-25 07:47'
|
||||
labels:
|
||||
|
|
@ -24,8 +24,12 @@ Target: server/index.ts lines 482-521. Redirect HTML page loads, continue proxyi
|
|||
|
||||
## Acceptance Criteria
|
||||
<!-- AC:BEGIN -->
|
||||
- [ ] #1 rmaps.online/some-room returns 301 to rspace.online/demo/maps/some-room
|
||||
- [ ] #2 rbooks.online/ returns 301 to rspace.online/demo/books
|
||||
- [ ] #3 API and WebSocket requests still proxied without redirect
|
||||
- [ ] #4 keepStandalone domains unaffected
|
||||
- [x] #1 rmaps.online/some-room returns 301 to rspace.online/demo/maps/some-room
|
||||
- [x] #2 rbooks.online/ returns 301 to rspace.online/demo/books
|
||||
- [x] #3 API and WebSocket requests still proxied without redirect
|
||||
- [x] #4 keepStandalone domains unaffected
|
||||
<!-- AC:END -->
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
301 redirect logic fully implemented in server/index.ts lines 2028-2098. domainToModule map built from mod.standaloneDomain, proper 301 Response.redirect issued, API/WS paths excluded. Traefik labels route all 20 standalone domains.
|
||||
|
|
|
|||
|
|
@ -240,7 +240,7 @@ services:
|
|||
command: ["bun", "run", "modules/rnetwork/standalone.ts"]
|
||||
environment:
|
||||
<<: *base-env
|
||||
TWENTY_API_URL: https://rnetwork.online
|
||||
TWENTY_API_URL: https://crm.rspace.online
|
||||
TWENTY_API_TOKEN: ${TWENTY_API_TOKEN}
|
||||
labels:
|
||||
<<: *traefik-enabled
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ services:
|
|||
- FUNNEL_ID=0ff6a9ac-1667-4fc7-9a01-b1620810509f
|
||||
- UMAMI_URL=https://analytics.rspace.online
|
||||
- UMAMI_WEBSITE_ID=292f6ac6-79f8-497b-ba6a-7a51e3b87b9f
|
||||
- MAPS_SYNC_URL=wss://sync.rmaps.online
|
||||
- MAPS_SYNC_URL=wss://maps-sync.rspace.online
|
||||
- IMAP_HOST=mail.rmail.online
|
||||
- IMAP_PORT=993
|
||||
- IMAP_TLS_REJECT_UNAUTHORIZED=false
|
||||
|
|
@ -61,7 +61,7 @@ services:
|
|||
- "traefik.http.routers.rspace-main.entrypoints=web"
|
||||
- "traefik.http.routers.rspace-main.priority=110"
|
||||
# Subdomains — backward compat for *.rspace.online canvas
|
||||
- "traefik.http.routers.rspace-canvas.rule=HostRegexp(`{subdomain:[a-z0-9-]+}.rspace.online`) && !Host(`rspace.online`) && !Host(`www.rspace.online`) && !Host(`auth.rspace.online`)"
|
||||
- "traefik.http.routers.rspace-canvas.rule=HostRegexp(`{subdomain:[a-z0-9-]+}.rspace.online`) && !Host(`rspace.online`) && !Host(`www.rspace.online`) && !Host(`auth.rspace.online`) && !Host(`maps-sync.rspace.online`) && !Host(`crm.rspace.online`) && !Host(`analytics.rspace.online`) && !Host(`newsletter.rspace.online`)"
|
||||
- "traefik.http.routers.rspace-canvas.entrypoints=web"
|
||||
- "traefik.http.routers.rspace-canvas.priority=100"
|
||||
# ── Standalone domain routing (priority 120) — redirect to rspace.online ──
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ const SYNC_SERVER = process.env.MAPS_SYNC_URL || "http://localhost:3001";
|
|||
|
||||
// ── Sync URL for client-side WebSocket connection ──
|
||||
routes.get("/api/sync-url", (c) => {
|
||||
const wsUrl = process.env.MAPS_SYNC_URL || "wss://sync.rmaps.online";
|
||||
const wsUrl = process.env.MAPS_SYNC_URL || "wss://maps-sync.rspace.online";
|
||||
return c.json({ syncUrl: wsUrl });
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -153,6 +153,23 @@ app.get("/.well-known/webauthn", (c) => {
|
|||
);
|
||||
});
|
||||
|
||||
// ── Analytics tracker (proxied from Umami — replaces rdata.online/collect.js) ──
|
||||
const UMAMI_URL = process.env.UMAMI_URL || "https://analytics.rspace.online";
|
||||
app.get("/collect.js", async (c) => {
|
||||
try {
|
||||
const res = await fetch(`${UMAMI_URL}/script.js`, { signal: AbortSignal.timeout(5000) });
|
||||
if (res.ok) {
|
||||
const script = await res.text();
|
||||
return new Response(script, {
|
||||
headers: { "Content-Type": "application/javascript", "Cache-Control": "public, max-age=3600" },
|
||||
});
|
||||
}
|
||||
} catch {}
|
||||
return new Response("/* umami unavailable */", {
|
||||
headers: { "Content-Type": "application/javascript" },
|
||||
});
|
||||
});
|
||||
|
||||
// ── Serve generated files from /data/files/generated/ ──
|
||||
app.get("/data/files/generated/:filename", async (c) => {
|
||||
const filename = c.req.param("filename");
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ async function transformWithRewriter(
|
|||
element(el) {
|
||||
el.append(
|
||||
`<link rel="stylesheet" href="/shell.css">
|
||||
<script defer src="https://rdata.online/collect.js" data-website-id="6ee7917b-0ed7-44cb-a4c8-91037638526b"></script>`,
|
||||
<script defer src="/collect.js" data-website-id="6ee7917b-0ed7-44cb-a4c8-91037638526b"></script>`,
|
||||
{ html: true },
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ export function renderMainLanding(modules: ModuleInfo[]): string {
|
|||
<style>${MODULE_LANDING_CSS}</style>
|
||||
<style>${RICH_LANDING_CSS}</style>
|
||||
<style>${MAIN_LANDING_CSS}</style>
|
||||
<script defer src="https://rdata.online/collect.js" data-website-id="6ee7917b-0ed7-44cb-a4c8-91037638526b"></script>
|
||||
<script defer src="/collect.js" data-website-id="6ee7917b-0ed7-44cb-a4c8-91037638526b"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="rstack-header">
|
||||
|
|
@ -297,7 +297,7 @@ export function renderSpaceDashboard(space: string, modules: ModuleInfo[]): stri
|
|||
<link rel="stylesheet" href="/shell.css">
|
||||
<style>${MODULE_LANDING_CSS}</style>
|
||||
<style>${SPACE_DASHBOARD_CSS}</style>
|
||||
<script defer src="https://rdata.online/collect.js" data-website-id="6ee7917b-0ed7-44cb-a4c8-91037638526b"></script>
|
||||
<script defer src="/collect.js" data-website-id="6ee7917b-0ed7-44cb-a4c8-91037638526b"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="rstack-header">
|
||||
|
|
|
|||
|
|
@ -1195,7 +1195,7 @@ export function renderModuleLanding(opts: ModuleLandingOptions): string {
|
|||
<link rel="stylesheet" href="/theme.css">
|
||||
<link rel="stylesheet" href="/shell.css">
|
||||
${cssBlock}
|
||||
<script defer src="https://rdata.online/collect.js" data-website-id="6ee7917b-0ed7-44cb-a4c8-91037638526b"></script>
|
||||
<script defer src="/collect.js" data-website-id="6ee7917b-0ed7-44cb-a4c8-91037638526b"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="rstack-header">
|
||||
|
|
@ -1539,7 +1539,7 @@ export function renderSubPageInfo(opts: SubPageInfoOptions): string {
|
|||
<link rel="stylesheet" href="/shell.css">
|
||||
<style>${MODULE_LANDING_CSS}</style>
|
||||
<style>${RICH_LANDING_CSS}</style>
|
||||
<script defer src="https://rdata.online/collect.js" data-website-id="6ee7917b-0ed7-44cb-a4c8-91037638526b"></script>
|
||||
<script defer src="/collect.js" data-website-id="6ee7917b-0ed7-44cb-a4c8-91037638526b"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="rstack-header">
|
||||
|
|
|
|||
|
|
@ -219,7 +219,7 @@
|
|||
color: var(--rs-text-muted);
|
||||
}
|
||||
</style>
|
||||
<script defer src="https://rdata.online/collect.js" data-website-id="6ee7917b-0ed7-44cb-a4c8-91037638526b"></script>
|
||||
<script defer src="/collect.js" data-website-id="6ee7917b-0ed7-44cb-a4c8-91037638526b"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="rstack-header">
|
||||
|
|
|
|||
|
|
@ -262,7 +262,7 @@
|
|||
}
|
||||
</style>
|
||||
<link rel="stylesheet" href="/shell.css">
|
||||
<script defer src="https://rdata.online/collect.js" data-website-id="6ee7917b-0ed7-44cb-a4c8-91037638526b"></script>
|
||||
<script defer src="/collect.js" data-website-id="6ee7917b-0ed7-44cb-a4c8-91037638526b"></script>
|
||||
</head>
|
||||
<body data-theme="dark">
|
||||
<header class="rstack-header" data-theme="dark">
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue