# ── Build stage: Python deps ──────────────────────────────────────────── FROM python:3.12-slim AS builder WORKDIR /build RUN apt-get update && apt-get install -y --no-install-recommends \ gcc libpq-dev curl \ && rm -rf /var/lib/apt/lists/* COPY apps/api/pyproject.toml ./api/pyproject.toml COPY apps/scanner/pyproject.toml ./scanner/pyproject.toml RUN pip install --no-cache-dir --prefix=/install api/. RUN pip install --no-cache-dir --prefix=/install scanner/. \ && PYTHONPATH=/install/lib/python3.12/site-packages \ /install/bin/playwright install chromium --with-deps # ── Build stage: banner bundle ───────────────────────────────────────── FROM node:20-slim AS banner-builder WORKDIR /build/banner COPY apps/banner/package.json apps/banner/package-lock.json ./ RUN npm ci COPY apps/banner/ . RUN npm run build # ── Build stage: admin UI ────────────────────────────────────────────── FROM node:20-slim AS admin-builder WORKDIR /build/admin COPY apps/admin-ui/package.json apps/admin-ui/package-lock.json ./ RUN npm ci COPY apps/admin-ui/ . COPY --from=banner-builder /build/banner/dist/ ./public/ RUN npx vite build # ── Runtime stage ────────────────────────────────────────────────────── FROM python:3.12-slim WORKDIR /app RUN apt-get update && apt-get install -y --no-install-recommends \ libpq5 postgresql-client curl tini supervisor nginx \ && rm -rf /var/lib/apt/lists/* \ && apt-get clean # Copy Python deps from builder COPY --from=builder /install /usr/local # Copy application code COPY apps/api/src ./src COPY apps/api/alembic ./alembic COPY apps/api/alembic.ini ./alembic.ini COPY apps/scanner/src ./src_scanner RUN if [ -d src_scanner ]; then \ cp -r src_scanner/* src/ 2>/dev/null || true; \ fi # Copy built Admin UI static files COPY --from=admin-builder /build/admin/dist /var/www/html # Copy configs COPY apps/admin-ui/nginx.conf /etc/nginx/conf.d/default.conf COPY supervisord.conf /etc/supervisord.conf COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ CMD curl -f http://localhost/health || exit 1 ENTRYPOINT ["/entrypoint.sh"] CMD ["/usr/bin/tini", "--", "supervisord", "-c", "/etc/supervisord.conf"]