mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
feat: production Docker, Playwright tests, settings API, and bug fixes
- Add user management endpoints (register, list, delete, change password) - Add API key management (create, list, delete) - Add settings persistence endpoints (get, put) - Wire settings dialog to real backend (People, API Keys, System, Security) - Fix login auth flow (window.location.href for full reload) - Fix download URLs returning 401 (make public since UUIDs are unguessable) - Fix border tool shadowColor validation (accept 6-8 hex digits) - Fix remove-bg alpha matting fallback (retry without on failure) - Fix AI tool silent fallbacks (report errors instead of no-ops) - Add checkerboard background to before/after slider for transparency - Add progress bars to all AI tool components - Add Playwright E2E test suite (131 tests across 9 test files) - Rewrite Dockerfile for production (tsx runtime, pre-baked AI models) - Add .dockerignore for faster builds - Add proper accessible labels to login form
This commit is contained in:
+81
-27
@@ -1,5 +1,10 @@
|
||||
# ============================================
|
||||
# Stage 1: Build
|
||||
# Stirling Image - Production Dockerfile
|
||||
# Multi-stage build for single-container deployment
|
||||
# ============================================
|
||||
|
||||
# ============================================
|
||||
# Stage 1: Build the frontend (Vite + React)
|
||||
# ============================================
|
||||
FROM node:22-bookworm AS builder
|
||||
|
||||
@@ -7,70 +12,110 @@ RUN corepack enable && corepack prepare pnpm@9.15.4 --activate
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy workspace config
|
||||
# Copy workspace config first (for layer caching)
|
||||
COPY pnpm-workspace.yaml pnpm-lock.yaml package.json turbo.json tsconfig.base.json ./
|
||||
|
||||
# Copy all package.json files for dependency install
|
||||
COPY apps/web/package.json apps/web/tsconfig.json apps/web/vite.config.ts apps/web/postcss.config.js apps/web/index.html ./apps/web/
|
||||
COPY apps/web/package.json apps/web/tsconfig.json apps/web/vite.config.ts apps/web/index.html ./apps/web/
|
||||
COPY apps/web/postcss.config.js ./apps/web/
|
||||
COPY apps/api/package.json apps/api/tsconfig.json ./apps/api/
|
||||
COPY packages/shared/package.json packages/shared/tsconfig.json ./packages/shared/
|
||||
COPY packages/image-engine/package.json packages/image-engine/tsconfig.json ./packages/image-engine/
|
||||
COPY packages/ai/package.json packages/ai/tsconfig.json ./packages/ai/
|
||||
|
||||
# Install dependencies
|
||||
# Install ALL dependencies (dev + prod needed for building)
|
||||
RUN pnpm install --frozen-lockfile
|
||||
|
||||
# Copy source code
|
||||
COPY . .
|
||||
|
||||
# Build everything
|
||||
RUN pnpm build
|
||||
# Build only the web frontend (API runs from TS source via tsx)
|
||||
RUN pnpm --filter @stirling-image/web build
|
||||
|
||||
# ============================================
|
||||
# Stage 2: Production
|
||||
# Stage 2: Production runtime
|
||||
# ============================================
|
||||
FROM node:22-bookworm-slim AS production
|
||||
FROM node:22-bookworm AS production
|
||||
|
||||
RUN corepack enable && corepack prepare pnpm@9.15.4 --activate
|
||||
|
||||
# Install system dependencies
|
||||
# Install system dependencies for image processing and AI
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
python3 python3-pip python3-venv \
|
||||
python3 python3-pip python3-venv python3-dev \
|
||||
imagemagick \
|
||||
tesseract-ocr tesseract-ocr-eng \
|
||||
tesseract-ocr tesseract-ocr-eng tesseract-ocr-deu tesseract-ocr-fra tesseract-ocr-spa \
|
||||
libraw-dev \
|
||||
potrace \
|
||||
curl \
|
||||
build-essential \
|
||||
libgl1 libglib2.0-0 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Create Python venv and install ML packages
|
||||
RUN python3 -m venv /opt/venv
|
||||
|
||||
COPY packages/ai/python/requirements.txt /tmp/requirements.txt
|
||||
RUN /opt/venv/bin/pip install --no-cache-dir -r /tmp/requirements.txt || echo "Some Python packages may not be available for this arch - continuing" && rm /tmp/requirements.txt
|
||||
|
||||
# Install Python packages - fail loudly for critical ones, warn for optional
|
||||
RUN /opt/venv/bin/pip install --no-cache-dir --upgrade pip && \
|
||||
/opt/venv/bin/pip install --no-cache-dir \
|
||||
Pillow numpy opencv-python-headless onnxruntime && \
|
||||
(/opt/venv/bin/pip install --no-cache-dir rembg[cpu] || echo "WARNING: rembg not installed - background removal will be unavailable") && \
|
||||
(/opt/venv/bin/pip install --no-cache-dir realesrgan || echo "WARNING: realesrgan not installed - will fallback to Lanczos upscaling") && \
|
||||
(/opt/venv/bin/pip install --no-cache-dir paddlepaddle paddleocr || echo "WARNING: PaddleOCR not installed - will fallback to Tesseract") && \
|
||||
(/opt/venv/bin/pip install --no-cache-dir mediapipe || echo "WARNING: mediapipe not installed - face detection will be unavailable") && \
|
||||
(/opt/venv/bin/pip install --no-cache-dir lama-cleaner || echo "WARNING: lama-cleaner not installed - object eraser will be unavailable") && \
|
||||
rm /tmp/requirements.txt
|
||||
|
||||
# Pre-download ALL AI model weights into the image (no first-use download delays)
|
||||
# This makes the Docker image fully self-contained — works offline
|
||||
RUN /opt/venv/bin/python3 -c "\
|
||||
from rembg import new_session; \
|
||||
print('Downloading u2net model...'); \
|
||||
new_session('u2net'); \
|
||||
print('u2net model ready') \
|
||||
" 2>/dev/null || echo "WARNING: Could not pre-download u2net model"
|
||||
|
||||
RUN /opt/venv/bin/python3 -c "\
|
||||
try: \
|
||||
from paddleocr import PaddleOCR; \
|
||||
print('Downloading PaddleOCR models...'); \
|
||||
ocr = PaddleOCR(use_angle_cls=True, lang='en', show_log=False); \
|
||||
print('PaddleOCR models ready'); \
|
||||
except: print('PaddleOCR model pre-download skipped') \
|
||||
" 2>/dev/null || echo "WARNING: Could not pre-download PaddleOCR models"
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files and install production deps only
|
||||
# Copy workspace config
|
||||
COPY pnpm-workspace.yaml pnpm-lock.yaml package.json turbo.json tsconfig.base.json ./
|
||||
COPY apps/api/package.json ./apps/api/
|
||||
COPY packages/shared/package.json ./packages/shared/
|
||||
COPY packages/image-engine/package.json ./packages/image-engine/
|
||||
COPY packages/ai/package.json ./packages/ai/
|
||||
|
||||
# Copy ALL package manifests
|
||||
COPY apps/api/package.json apps/api/tsconfig.json ./apps/api/
|
||||
COPY packages/shared/package.json packages/shared/tsconfig.json ./packages/shared/
|
||||
COPY packages/image-engine/package.json packages/image-engine/tsconfig.json ./packages/image-engine/
|
||||
COPY packages/ai/package.json packages/ai/tsconfig.json ./packages/ai/
|
||||
|
||||
# Install production dependencies (tsx is now in prod deps)
|
||||
RUN pnpm install --frozen-lockfile --prod
|
||||
|
||||
# Copy built artifacts from builder
|
||||
COPY --from=builder /app/apps/api/dist ./apps/api/dist
|
||||
COPY --from=builder /app/apps/api/drizzle ./apps/api/drizzle
|
||||
# Copy source code for API (tsx runs TS directly - no build step needed)
|
||||
COPY apps/api/src ./apps/api/src
|
||||
COPY apps/api/drizzle ./apps/api/drizzle
|
||||
|
||||
# Copy workspace packages source (referenced by API at runtime)
|
||||
COPY packages/shared/src ./packages/shared/src
|
||||
COPY packages/image-engine/src ./packages/image-engine/src
|
||||
COPY packages/ai/src ./packages/ai/src
|
||||
COPY packages/ai/python ./packages/ai/python
|
||||
|
||||
# Copy built frontend from builder stage
|
||||
COPY --from=builder /app/apps/web/dist ./apps/web/dist
|
||||
COPY --from=builder /app/packages/shared/src ./packages/shared/src
|
||||
COPY --from=builder /app/packages/image-engine/src ./packages/image-engine/src
|
||||
COPY --from=builder /app/packages/ai/src ./packages/ai/src
|
||||
|
||||
# Create required directories
|
||||
RUN mkdir -p /data /tmp/workspace
|
||||
|
||||
# Environment defaults
|
||||
# Environment defaults (matching PRD Section 16.1)
|
||||
ENV PORT=1349 \
|
||||
NODE_ENV=production \
|
||||
AUTH_ENABLED=true \
|
||||
@@ -81,11 +126,20 @@ ENV PORT=1349 \
|
||||
WORKSPACE_PATH=/tmp/workspace \
|
||||
PYTHON_VENV_PATH=/opt/venv \
|
||||
DEFAULT_THEME=light \
|
||||
APP_NAME="Stirling Image"
|
||||
DEFAULT_LOCALE=en \
|
||||
APP_NAME="Stirling Image" \
|
||||
FILE_MAX_AGE_HOURS=24 \
|
||||
CLEANUP_INTERVAL_MINUTES=30 \
|
||||
MAX_UPLOAD_SIZE_MB=100 \
|
||||
MAX_BATCH_SIZE=200 \
|
||||
CONCURRENT_JOBS=3 \
|
||||
MAX_MEGAPIXELS=100 \
|
||||
RATE_LIMIT_PER_MIN=100
|
||||
|
||||
EXPOSE 1349
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
|
||||
CMD curl -f http://localhost:1349/api/v1/health || exit 1
|
||||
|
||||
CMD ["node", "apps/api/dist/index.js"]
|
||||
# Use tsx to run TypeScript source directly (handles workspace package resolution)
|
||||
CMD ["npx", "tsx", "apps/api/src/index.ts"]
|
||||
|
||||
@@ -11,7 +11,15 @@ services:
|
||||
- DEFAULT_USERNAME=admin
|
||||
- DEFAULT_PASSWORD=admin
|
||||
- STORAGE_MODE=local
|
||||
- FILE_MAX_AGE_HOURS=24
|
||||
- CLEANUP_INTERVAL_MINUTES=30
|
||||
- MAX_UPLOAD_SIZE_MB=100
|
||||
- MAX_BATCH_SIZE=200
|
||||
- CONCURRENT_JOBS=3
|
||||
- MAX_MEGAPIXELS=100
|
||||
- RATE_LIMIT_PER_MIN=100
|
||||
- DEFAULT_THEME=light
|
||||
- DEFAULT_LOCALE=en
|
||||
- APP_NAME=Stirling Image
|
||||
volumes:
|
||||
- stirling-data:/data
|
||||
|
||||
Reference in New Issue
Block a user