diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..2e7a629 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,63 @@ +# ============================================================================== +# AI Photo Edit - Unified Container +# Builds React frontend and serves it alongside FastAPI backend +# ============================================================================== + +# Stage 1: Build React frontend +FROM node:20-alpine AS frontend-build + +WORKDIR /frontend + +# Copy package files and install dependencies +COPY frontend/package.json frontend/package-lock.json* ./ +RUN npm install + +# Copy frontend source and build +COPY frontend/ ./ +RUN npm run build + +# Stage 2: Python backend with frontend static files +FROM python:3.11-slim + +WORKDIR /app + +# Install system dependencies for OpenCV, rembg, SAM, and image processing +RUN apt-get update && apt-get install -y \ + libgl1 \ + libglib2.0-0 \ + libsm6 \ + libxext6 \ + libxrender-dev \ + libgomp1 \ + wget \ + git \ + && rm -rf /var/lib/apt/lists/* + +# Copy requirements and install Python dependencies +COPY backend/requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Pre-download rembg model (u2net) to avoid first-run delay +RUN python -c "from rembg import remove; print('rembg model downloaded')" || true + +# Copy backend application +COPY backend/ . + +# Copy entrypoint script +COPY backend/entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + +# Copy scripts +COPY scripts/ /scripts/ + +# Copy built frontend from stage 1 +COPY --from=frontend-build /frontend/dist /app/static + +# Create data directories +RUN mkdir -p /app/data/projects /app/data/patches /app/data/models + +# Expose port +EXPOSE 8000 + +# Use entrypoint script +ENTRYPOINT ["/entrypoint.sh"] diff --git a/backend/app/main.py b/backend/app/main.py index f6a95fd..540f490 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -1,7 +1,10 @@ -from fastapi import FastAPI +from fastapi import FastAPI, Request from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles +from fastapi.responses import FileResponse, HTMLResponse from contextlib import asynccontextmanager +from pathlib import Path +import os from app.config import settings from app.database import init_db @@ -40,9 +43,9 @@ app.include_router(generate.router) app.include_router(tools.router) -@app.get("/") -def root(): - """API root endpoint""" +@app.get("/api") +def api_root(): + """API info endpoint""" return { "name": "AI Photo Edit API", "version": "1.0.0", @@ -54,3 +57,44 @@ def root(): def health(): """Health check endpoint""" return {"status": "healthy"} + + +# Static files directory +STATIC_DIR = Path("/app/static") + + +# Serve static assets (JS, CSS, images) +if STATIC_DIR.exists(): + app.mount("/assets", StaticFiles(directory=STATIC_DIR / "assets"), name="assets") + + +@app.get("/", response_class=HTMLResponse) +async def serve_spa(): + """Serve React SPA index.html""" + index_path = STATIC_DIR / "index.html" + if index_path.exists(): + return FileResponse(index_path) + return HTMLResponse("