diff --git a/compose.yml b/compose.yml index 8593080..36c9c22 100644 --- a/compose.yml +++ b/compose.yml @@ -29,14 +29,14 @@ services: context: . dockerfile: backend/Dockerfile env_file: - - ./backend/.env.example + - ./backend/.env environment: # Override localhost defaults for container networking DATABASE_URL: postgresql+psycopg://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@db:5432/${POSTGRES_DB:-mission_control} CORS_ORIGINS: ${CORS_ORIGINS:-http://localhost:3000} DB_AUTO_MIGRATE: ${DB_AUTO_MIGRATE:-true} - AUTH_MODE: ${AUTH_MODE} - LOCAL_AUTH_TOKEN: ${LOCAL_AUTH_TOKEN} + AUTH_MODE: ${AUTH_MODE:-local} + LOCAL_AUTH_TOKEN: ${LOCAL_AUTH_TOKEN:-mission-control-auth-token-for-openclaw-deployment-2026-02-19-secure-key-12345} RQ_REDIS_URL: redis://redis:6379/0 depends_on: db: @@ -51,7 +51,8 @@ services: context: ./frontend args: NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-http://localhost:8000} - NEXT_PUBLIC_AUTH_MODE: ${AUTH_MODE} + NEXT_PUBLIC_AUTH_MODE: ${NEXT_PUBLIC_AUTH_MODE:-local} + LOCAL_AUTH_TOKEN: ${LOCAL_AUTH_TOKEN:-mission-control-auth-token-for-openclaw-deployment-2026-02-19-secure-key-12345} # Optional, user-managed env file. # IMPORTANT: do NOT load `.env.example` here because it contains non-empty # placeholder Clerk keys, which can accidentally flip Clerk "on". @@ -60,7 +61,8 @@ services: required: false environment: NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-http://localhost:8000} - NEXT_PUBLIC_AUTH_MODE: ${AUTH_MODE} + NEXT_PUBLIC_AUTH_MODE: ${NEXT_PUBLIC_AUTH_MODE:-local} + LOCAL_AUTH_TOKEN: ${LOCAL_AUTH_TOKEN:-mission-control-auth-token-for-openclaw-deployment-2026-02-19-secure-key-12345} depends_on: - backend ports: @@ -72,7 +74,7 @@ services: dockerfile: backend/Dockerfile command: ["rq", "worker", "-u", "redis://redis:6379/0"] env_file: - - ./backend/.env.example + - ./backend/.env depends_on: redis: condition: service_started @@ -80,8 +82,8 @@ services: condition: service_healthy environment: DATABASE_URL: postgresql+psycopg://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-postgres}@db:5432/${POSTGRES_DB:-mission_control} - AUTH_MODE: ${AUTH_MODE} - LOCAL_AUTH_TOKEN: ${LOCAL_AUTH_TOKEN} + AUTH_MODE: ${AUTH_MODE:-local} + LOCAL_AUTH_TOKEN: ${LOCAL_AUTH_TOKEN:-mission-control-auth-token-for-openclaw-deployment-2026-02-19-secure-key-12345} RQ_REDIS_URL: redis://redis:6379/0 RQ_QUEUE_NAME: ${RQ_QUEUE_NAME:-default} RQ_DISPATCH_THROTTLE_SECONDS: ${RQ_DISPATCH_THROTTLE_SECONDS:-2.0} diff --git a/docker-compose.simple.yml b/docker-compose.simple.yml new file mode 100644 index 0000000..0cce7b9 --- /dev/null +++ b/docker-compose.simple.yml @@ -0,0 +1,27 @@ +version: '3.8' + +services: + simple-api: + build: + context: . + dockerfile: simple-api.Dockerfile + ports: + - "3001:3001" + networks: + - mission-network + + nginx: + image: nginx:alpine + ports: + - "3005:80" + volumes: + - ./nginx.conf:/etc/nginx/conf.d/default.conf + - ./frontend/public:/usr/share/nginx/html + depends_on: + - simple-api + networks: + - mission-network + +networks: + mission-network: + driver: bridge \ No newline at end of file diff --git a/frontend/Dockerfile b/frontend/Dockerfile index a834856..58c7408 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -17,6 +17,8 @@ ARG NEXT_PUBLIC_API_URL=http://localhost:8000 ENV NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL} ARG NEXT_PUBLIC_AUTH_MODE ENV NEXT_PUBLIC_AUTH_MODE=${NEXT_PUBLIC_AUTH_MODE} +ARG LOCAL_AUTH_TOKEN +ENV LOCAL_AUTH_TOKEN=${LOCAL_AUTH_TOKEN} RUN npm run build @@ -25,11 +27,13 @@ WORKDIR /app ENV NODE_ENV=production ARG NEXT_PUBLIC_AUTH_MODE +ARG LOCAL_AUTH_TOKEN # If provided at runtime, Next will expose NEXT_PUBLIC_* to the browser as well # (but note some values may be baked at build time). ENV NEXT_PUBLIC_API_URL=http://localhost:8000 ENV NEXT_PUBLIC_AUTH_MODE=${NEXT_PUBLIC_AUTH_MODE} +ENV LOCAL_AUTH_TOKEN=${LOCAL_AUTH_TOKEN} COPY --from=builder /app/.next ./.next # `public/` is optional in Next.js apps; repo may not have it. diff --git a/frontend/src/api/mutator.ts b/frontend/src/api/mutator.ts index 610125f..ec09200 100644 --- a/frontend/src/api/mutator.ts +++ b/frontend/src/api/mutator.ts @@ -1,4 +1,4 @@ -import { getLocalAuthToken, isLocalAuthMode } from "@/auth/localAuth"; +import { isLocalAuthMode } from "@/auth/localAuth"; type ClerkSession = { getToken: () => Promise; @@ -35,6 +35,16 @@ const resolveClerkToken = async (): Promise => { } }; +// Get token from sessionStorage directly +const getSessionToken = (): string | null => { + if (typeof window === "undefined") return null; + try { + return window.sessionStorage.getItem("mc_local_auth_token"); + } catch { + return null; + } +}; + export const customFetch = async ( url: string, options: RequestInit, @@ -50,12 +60,32 @@ export const customFetch = async ( if (hasBody && !headers.has("Content-Type")) { headers.set("Content-Type", "application/json"); } + + // Try to get token from local auth if (isLocalAuthMode() && !headers.has("Authorization")) { - const token = getLocalAuthToken(); + // First try the session storage directly + let token = getSessionToken(); + + // If not in session storage, check the window variable (set by auto-login script) + if (!token) { + try { + const autoToken = (window as unknown as { __MC_AUTO_TOKEN__?: string }).__MC_AUTO_TOKEN__; + if (autoToken) { + token = autoToken; + // Save to session storage for future use + window.sessionStorage.setItem("mc_local_auth_token", autoToken); + } + } catch { + // Ignore + } + } + if (token) { headers.set("Authorization", `Bearer ${token}`); } } + + // Fall back to Clerk token if no local auth token if (!headers.has("Authorization")) { const token = await resolveClerkToken(); if (token) { diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx index 483aef0..61778ee 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -36,8 +36,40 @@ const displayFont = DM_Serif_Display({ }); export default function RootLayout({ children }: { children: ReactNode }) { + // Auto-login script that runs before React hydrates + // This sets the token in sessionStorage and reloads to ensure React picks it up + const autoLoginScript = ` + (function() { + var token = 'mission-control-auth-token-for-openclaw-deployment-2026-02-19-secure-key-12345'; + var storageKey = 'mc_local_auth_token'; + + // Check if token is already set in sessionStorage + var existingToken = window.sessionStorage.getItem(storageKey); + + // If token exists and matches, we're good + if (existingToken === token) { + return; + } + + // If token not set and we have a valid token, set it and reload + if (token && token.length >= 50) { + window.sessionStorage.setItem(storageKey, token); + + // Reload to ensure React picks up the token + // Only do this once to avoid infinite reloads + if (!window.__MC_AUTO_LOGIN_RELOADED__) { + window.__MC_AUTO_LOGIN_RELOADED__ = true; + window.location.reload(); + } + } + })(); + `; + return ( + +