From 488809236476a119fc88bd4f76db158a45fc3a05 Mon Sep 17 00:00:00 2001 From: manvalan Date: Thu, 23 Jul 2026 20:26:59 +0000 Subject: [PATCH] Add Docker deployment, remove Clerk auth, and configure OSS mode --- .dockerignore | 2 +- backend/services/llm/anthropic_provider.py | 18 ++++--- docker-compose.yml | 49 +++++++++++++++++++ frontend/dockerfile | 39 +++++++++++++++ frontend/next.config.ts | 4 +- .../src/app/(marketing)/contact/actions.ts | 2 +- frontend/src/lib/api.ts | 2 +- 7 files changed, 102 insertions(+), 14 deletions(-) create mode 100644 docker-compose.yml create mode 100644 frontend/dockerfile diff --git a/.dockerignore b/.dockerignore index 6329830..487a87c 100644 --- a/.dockerignore +++ b/.dockerignore @@ -5,7 +5,7 @@ __pycache__ .pytest_cache .mypy_cache data/ -frontend/ +#frontend/ .git/ .claude/settings.* .claude/plans/ diff --git a/backend/services/llm/anthropic_provider.py b/backend/services/llm/anthropic_provider.py index 677f362..bd6f823 100644 --- a/backend/services/llm/anthropic_provider.py +++ b/backend/services/llm/anthropic_provider.py @@ -269,7 +269,7 @@ class AnthropicProvider(LLMProvider): runs ``validate.py`` server-side via code_execution, and voluntarily calls ``output_tool`` once it has well-formed data. """ - skill_id, version = settings.get_skill(skill_name) + try:\n skill_id, version = settings.get_skill(skill_name)\n except Exception:\n skill_id, version = None, None # Build initial user content user_content: list[dict] = [] @@ -278,13 +278,15 @@ class AnthropicProvider(LLMProvider): user_content.append({"type": "text", "text": user_text}) messages: list[dict] = [{"role": "user", "content": user_content}] - container: dict = { - "skills": [{ - "type": "custom", - "skill_id": skill_id, - "version": version, - }], - } + container: dict | None = None + if skill_id: + container = { + "skills": [{ + "type": "custom", + "skill_id": skill_id, + "version": version, + }], + } total_input = 0 total_output = 0 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..16ed9b2 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,49 @@ +services: + + backend: + build: + context: . + dockerfile: backend/Dockerfile + + container_name: pinscope-backend + restart: unless-stopped + + env_file: + - .env + + environment: + ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY} + + volumes: + - ./data:/app/data + - ./taxonomy:/app/taxonomy + + ports: + - "8080:8080" + + networks: + - pinscope + + frontend: + build: + context: ./frontend + dockerfile: dockerfile + args: + NEXT_PUBLIC_API_URL: "" + + container_name: pinscope-frontend + restart: unless-stopped + + depends_on: + - backend + + ports: + - "3000:3000" + + networks: + - pinscope + + +networks: + pinscope: + driver: bridge diff --git a/frontend/dockerfile b/frontend/dockerfile new file mode 100644 index 0000000..c62d239 --- /dev/null +++ b/frontend/dockerfile @@ -0,0 +1,39 @@ +FROM node:20-alpine AS builder + +WORKDIR /app + +ARG NEXT_PUBLIC_API_URL +ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL + +COPY package*.json ./ +RUN npm ci + +COPY . . + +ENV NEXT_TELEMETRY_DISABLED=1 + +RUN echo "BUILD API URL=$NEXT_PUBLIC_API_URL" +RUN npm run build + +FROM node:20-alpine + +WORKDIR /app + +ARG NEXT_PUBLIC_API_URL +ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL + +ENV NODE_ENV=production +ENV NEXT_TELEMETRY_DISABLED=1 + +COPY --from=builder /app/package*.json ./ +RUN npm ci --omit=dev + +COPY --from=builder /app/.next ./.next +COPY --from=builder /app/public ./public +COPY --from=builder /app/src ./src +COPY --from=builder /app/package.json ./package.json +COPY --from=builder /app/next.config.ts ./next.config.ts + +EXPOSE 3000 + +CMD ["npm","run","start"] diff --git a/frontend/next.config.ts b/frontend/next.config.ts index b9c0143..e0361a7 100644 --- a/frontend/next.config.ts +++ b/frontend/next.config.ts @@ -36,9 +36,7 @@ const nextConfig: NextConfig = { "img-src 'self' data: https: blob:", "font-src 'self' data: https://vercel.live https://assets.vercel.com https://fonts.gstatic.com", "connect-src 'self' blob: https://storage.googleapis.com https://vercel.live wss://ws-us3.pusher.com" + - extra(CSP_CONNECT_HOSTS) + - " " + - (process.env.NEXT_PUBLIC_API_URL?.trim() || "http://localhost:8000"), + extra(CSP_CONNECT_HOSTS), "frame-src 'self' https://vercel.live" + extra(CSP_FRAME_HOSTS), "worker-src 'self' blob:", ].join("; "), diff --git a/frontend/src/app/(marketing)/contact/actions.ts b/frontend/src/app/(marketing)/contact/actions.ts index ecbc27a..64545ec 100644 --- a/frontend/src/app/(marketing)/contact/actions.ts +++ b/frontend/src/app/(marketing)/contact/actions.ts @@ -1,4 +1,4 @@ -const BASE = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000"; +const BASE = process.env.NEXT_PUBLIC_API_URL || ""; export type ActionState = { success: boolean; diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index 7af2acb..90bc954 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -26,7 +26,7 @@ export function safeMpn(mpn: string): string { return mpn.replace(/\//g, "_").replace(/:/g, "_"); } -const BASE = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000"; +const BASE = process.env.NEXT_PUBLIC_API_URL || ""; // Auth token getter — set by useAuthApi hook let _getToken: (() => Promise) | null = null;