# ── Stage 1: build the React SPA ───────────────────────────────────── FROM node:22-alpine AS frontend-builder WORKDIR /frontend COPY frontend/package.json frontend/package-lock.json frontend/.npmrc ./ RUN npm ci COPY frontend/ ./ RUN npm run build # ── Stage 2: install backend Python deps ───────────────────────────── FROM python:3.12-slim AS backend-builder ENV POETRY_VERSION=1.8.4 \ POETRY_VIRTUALENVS_IN_PROJECT=true \ PIP_NO_CACHE_DIR=1 RUN pip install --no-cache-dir "poetry==${POETRY_VERSION}" WORKDIR /app COPY pyproject.toml poetry.lock* README.md ./ RUN poetry install --only main --no-root COPY fire_planner ./fire_planner COPY alembic ./alembic COPY alembic.ini ./alembic.ini RUN poetry install --only main # ── Stage 3: runtime ───────────────────────────────────────────────── FROM python:3.12-slim WORKDIR /app RUN useradd --system --uid 10003 --home /app --shell /usr/sbin/nologin firep COPY --from=backend-builder --chown=firep:firep /app /app COPY --from=frontend-builder --chown=firep:firep /frontend/dist /app/frontend_dist ENV PATH="/app/.venv/bin:${PATH}" \ PYTHONUNBUFFERED=1 \ FRONTEND_DIST=/app/frontend_dist EXPOSE 8080 USER firep ENTRYPOINT ["python", "-m", "fire_planner"] CMD ["serve"]