# Stage 1 — build the React SPA. Node 24: package-lock.json is written by
# npm 11, which older bundled npms (node 22's 10.x) refuse as out-of-sync.
FROM node:24-alpine AS frontend-build
WORKDIR /build
COPY frontend/package.json frontend/package-lock.json ./
RUN npm ci
COPY frontend/ ./
RUN npm run build

# Stage 2 — runtime. Every remaining dependency ships a wheel, so there is no
# compile step and no toolchain to keep out of the image. The wheel-building
# stage that used to sit here existed for quickjs, which compiled from source
# and which M2 removed with campaign scripting.
FROM python:3.12-slim
WORKDIR /app

COPY backend/requirements.txt /tmp/requirements.txt
RUN pip install --no-cache-dir -r /tmp/requirements.txt && rm /tmp/requirements.txt

# Layout mirrors the repo: main.py finds the SPA at ../../frontend/dist
# relative to backend/app/main.py.
COPY backend/app /app/backend/app
COPY --from=frontend-build /build/dist /app/frontend/dist

# Database lives on a volume; parent dir is created by the app if missing.
ENV AIDND_DB_PATH=/data/data.db
VOLUME /data

EXPOSE 8000
# Publish this port to loopback only — `-p 127.0.0.1:8000:8000`, which is what
# docker-compose.yml does. The listener below is 0.0.0.0 because that is the
# only address a published port can reach inside a container; it is not an
# invitation to put the storyteller on the LAN, which is single-user and
# unauthenticated in local mode.
WORKDIR /app/backend
# Single worker on purpose: the turn lock and the debug log are in-process
# state.
#
# No --proxy-headers. That existed for a hosted deployment behind a platform
# edge, along with the per-IP rate limiting that read X-Forwarded-For. Neither
# survives M2, and trusting a forwarded header on a loopback-published port
# would be a way to lie to the app rather than a feature.
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
