From badc6ce9e6f4f28849ccdc60a0a4e114265bcdb5 Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Thu, 26 Feb 2026 21:49:07 -0600 Subject: [PATCH] fix(web): resolve dashboard widget errors and deployment config - Add workspace ID to ActiveProjectsWidget API calls (fixes 401/403) - Add ORCHESTRATOR_URL and ORCHESTRATOR_API_KEY to web service in swarm compose (fixes 503 on orchestrator proxy routes) - Add internal network to web service for orchestrator connectivity - Update .env.example domain examples to single-level subdomains - Fix version display on login page from v0.1 to v0.0.20 Co-Authored-By: Claude Opus 4.6 --- .env.example | 12 ++++++------ apps/web/src/app/(auth)/login/page.tsx | 2 +- .../widgets/ActiveProjectsWidget.tsx | 18 ++++++++++++++---- docker-compose.swarm.portainer.yml | 4 ++++ 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/.env.example b/.env.example index 6c4e43e..36dbd73 100644 --- a/.env.example +++ b/.env.example @@ -79,7 +79,7 @@ OIDC_CLIENT_ID=your-client-id-here OIDC_CLIENT_SECRET=your-client-secret-here # Redirect URI must match what's configured in Authentik # Development: http://localhost:3001/auth/oauth2/callback/authentik -# Production: https://api.mosaicstack.dev/auth/oauth2/callback/authentik +# Production: https://mosaic-api.woltje.com/auth/oauth2/callback/authentik OIDC_REDIRECT_URI=http://localhost:3001/auth/oauth2/callback/authentik # Authentik PostgreSQL Database @@ -361,17 +361,17 @@ RATE_LIMIT_STORAGE=redis # a single workspace. MATRIX_HOMESERVER_URL=http://synapse:8008 MATRIX_ACCESS_TOKEN= -MATRIX_BOT_USER_ID=@mosaic-bot:matrix.example.com -MATRIX_SERVER_NAME=matrix.example.com -# MATRIX_CONTROL_ROOM_ID=!roomid:matrix.example.com +MATRIX_BOT_USER_ID=@mosaic-bot:matrix.woltje.com +MATRIX_SERVER_NAME=matrix.woltje.com +# MATRIX_CONTROL_ROOM_ID=!roomid:matrix.woltje.com # MATRIX_WORKSPACE_ID=your-workspace-uuid # ====================== # Matrix / Synapse Deployment # ====================== # Domains for Traefik routing to Matrix services -MATRIX_DOMAIN=matrix.example.com -ELEMENT_DOMAIN=chat.example.com +MATRIX_DOMAIN=matrix.woltje.com +ELEMENT_DOMAIN=chat.woltje.com # Synapse database (created automatically by synapse-db-init in the swarm compose) SYNAPSE_POSTGRES_DB=synapse diff --git a/apps/web/src/app/(auth)/login/page.tsx b/apps/web/src/app/(auth)/login/page.tsx index d5c09e5..467b647 100644 --- a/apps/web/src/app/(auth)/login/page.tsx +++ b/apps/web/src/app/(auth)/login/page.tsx @@ -326,7 +326,7 @@ function LoginPageContent(): ReactElement {
- +
diff --git a/apps/web/src/components/widgets/ActiveProjectsWidget.tsx b/apps/web/src/components/widgets/ActiveProjectsWidget.tsx index 1db97d5..068ed71 100644 --- a/apps/web/src/components/widgets/ActiveProjectsWidget.tsx +++ b/apps/web/src/components/widgets/ActiveProjectsWidget.tsx @@ -7,6 +7,7 @@ import { useState, useEffect } from "react"; import { FolderOpen, Bot, Activity, Clock, AlertCircle, CheckCircle2 } from "lucide-react"; import type { WidgetProps } from "@mosaic/shared"; import { apiPost } from "@/lib/api/client"; +import { useWorkspaceId } from "@/lib/hooks"; interface ActiveProject { id: string; @@ -34,6 +35,7 @@ interface AgentSession { } export function ActiveProjectsWidget({ id: _id, config: _config }: WidgetProps): React.JSX.Element { + const workspaceId = useWorkspaceId(); const [projects, setProjects] = useState([]); const [agentSessions, setAgentSessions] = useState([]); const [isLoadingProjects, setIsLoadingProjects] = useState(true); @@ -48,7 +50,11 @@ export function ActiveProjectsWidget({ id: _id, config: _config }: WidgetProps): try { setProjectsError(null); // Use API client to ensure CSRF token is included - const data = await apiPost("/api/widgets/data/active-projects"); + const data = await apiPost( + "/api/widgets/data/active-projects", + undefined, + workspaceId ?? undefined + ); setProjects(data); } catch (error) { console.error("Failed to fetch active projects:", error); @@ -67,7 +73,7 @@ export function ActiveProjectsWidget({ id: _id, config: _config }: WidgetProps): return (): void => { clearInterval(interval); }; - }, []); + }, [workspaceId]); // Fetch agent chains useEffect(() => { @@ -75,7 +81,11 @@ export function ActiveProjectsWidget({ id: _id, config: _config }: WidgetProps): try { setAgentsError(null); // Use API client to ensure CSRF token is included - const data = await apiPost("/api/widgets/data/agent-chains"); + const data = await apiPost( + "/api/widgets/data/agent-chains", + undefined, + workspaceId ?? undefined + ); setAgentSessions(data); } catch (error) { console.error("Failed to fetch agent sessions:", error); @@ -94,7 +104,7 @@ export function ActiveProjectsWidget({ id: _id, config: _config }: WidgetProps): return (): void => { clearInterval(interval); }; - }, []); + }, [workspaceId]); const getStatusIcon = (status: string): React.JSX.Element => { const statusUpper = status.toUpperCase(); diff --git a/docker-compose.swarm.portainer.yml b/docker-compose.swarm.portainer.yml index 3f90de0..0cd84aa 100644 --- a/docker-compose.swarm.portainer.yml +++ b/docker-compose.swarm.portainer.yml @@ -176,6 +176,9 @@ services: NODE_ENV: production PORT: ${WEB_PORT:-3000} NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL} + # Server-side orchestrator proxy (API routes forward to orchestrator service) + ORCHESTRATOR_URL: http://orchestrator:3001 + ORCHESTRATOR_API_KEY: ${ORCHESTRATOR_API_KEY} healthcheck: test: [ @@ -187,6 +190,7 @@ services: retries: 3 start_period: 40s networks: + - internal - traefik-public deploy: restart_policy: