From bec553fd7689b236d1ec5fb37e04966c45ec19df Mon Sep 17 00:00:00 2001 From: "Khoa.vo" Date: Tue, 13 Jan 2026 08:11:28 +0700 Subject: [PATCH] v3.2.0: Fix CORS - add nginx reverse proxy for production - Add nginx to Dockerfile as reverse proxy - Route /api/* to FastAPI, / to Next.js on single port 80 - Update all frontend components to use /api prefix in production - Simplify docker-compose to single port 80 - Fixes CORS errors when deployed to remote servers --- Dockerfile | 60 +++++++++++++++++++++++++++++++----- components/Gallery.tsx | 4 +-- components/PromptHero.tsx | 4 +-- components/PromptLibrary.tsx | 4 +-- components/UploadHistory.tsx | 4 +-- docker-compose.yml | 3 +- 6 files changed, 62 insertions(+), 17 deletions(-) diff --git a/Dockerfile b/Dockerfile index 70026f0..eef9157 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,13 +21,14 @@ RUN pip install --no-cache-dir -r requirements.txt # Copy backend source COPY backend/ ./ -# Stage 3: Production image with supervisor +# Stage 3: Production image with nginx + supervisor FROM python:3.11-slim AS runner WORKDIR /app -# Install Node.js and supervisor +# Install Node.js, nginx, and supervisor RUN apt-get update && apt-get install -y \ curl \ + nginx \ supervisor \ && curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \ && apt-get install -y nodejs \ @@ -50,6 +51,42 @@ COPY --from=backend-builder /backend ./backend # Copy data directory for prompts COPY --from=frontend-builder /app/data ./data +# Create nginx config - reverse proxy /api to FastAPI +RUN cat > /etc/nginx/sites-available/default <<'EOF' +server { + listen 80; + server_name _; + + # Proxy /api/* to FastAPI backend (strip /api prefix) + location /api/ { + proxy_pass http://127.0.0.1:8000/; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_cache_bypass $http_upgrade; + proxy_read_timeout 300s; + proxy_connect_timeout 75s; + } + + # Proxy everything else to Next.js + location / { + proxy_pass http://127.0.0.1:3000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_cache_bypass $http_upgrade; + } +} +EOF + # Create supervisor config RUN mkdir -p /var/log/supervisor COPY < { diff --git a/components/PromptHero.tsx b/components/PromptHero.tsx index 6d7911e..21de4cf 100644 --- a/components/PromptHero.tsx +++ b/components/PromptHero.tsx @@ -5,8 +5,8 @@ import { useStore, ReferenceCategory } from "@/lib/store"; import { cn } from "@/lib/utils"; import { Sparkles, Maximize2, X, Hash, AlertTriangle, Upload, Brain, Settings, Settings2 } from "lucide-react"; -// FastAPI backend URL -const API_BASE = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'; +// FastAPI backend URL - /api in production (nginx proxy), localhost in dev +const API_BASE = process.env.NEXT_PUBLIC_API_URL || (typeof window !== 'undefined' && window.location.hostname !== 'localhost' ? '/api' : 'http://localhost:8000'); const IMAGE_COUNTS = [1, 2, 4]; diff --git a/components/PromptLibrary.tsx b/components/PromptLibrary.tsx index 982e12b..a903793 100644 --- a/components/PromptLibrary.tsx +++ b/components/PromptLibrary.tsx @@ -7,8 +7,8 @@ import { cn } from '@/lib/utils'; import { Prompt, PromptCache } from '@/lib/types'; import { motion, AnimatePresence } from 'framer-motion'; -// FastAPI backend URL -const API_BASE = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'; +// FastAPI backend URL - /api in production (nginx proxy), localhost in dev +const API_BASE = process.env.NEXT_PUBLIC_API_URL || (typeof window !== 'undefined' && window.location.hostname !== 'localhost' ? '/api' : 'http://localhost:8000'); export function PromptLibrary({ onSelect }: { onSelect?: (prompt: string) => void }) { const { setPrompt, settings } = useStore(); diff --git a/components/UploadHistory.tsx b/components/UploadHistory.tsx index d4622d6..62fd84b 100644 --- a/components/UploadHistory.tsx +++ b/components/UploadHistory.tsx @@ -5,8 +5,8 @@ import { useStore, ReferenceCategory } from '@/lib/store'; import { Clock, Upload, Trash2, CheckCircle, X, Film, Check } from 'lucide-react'; import { cn } from '@/lib/utils'; -// FastAPI backend URL -const API_BASE = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000'; +// FastAPI backend URL - /api in production (nginx proxy), localhost in dev +const API_BASE = process.env.NEXT_PUBLIC_API_URL || (typeof window !== 'undefined' && window.location.hostname !== 'localhost' ? '/api' : 'http://localhost:8000'); export function UploadHistory() { const { diff --git a/docker-compose.yml b/docker-compose.yml index 0da81ec..cd59c02 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,8 +4,7 @@ services: container_name: kv-pix restart: unless-stopped ports: - - "3000:3000" # Next.js frontend - - "8000:8000" # FastAPI backend + - "80:80" # nginx serves both frontend and API environment: - NODE_ENV=production volumes: