# The SPA is compiled with bun and copied into the Python image, so the runtime
# container ends up with one process and no JavaScript toolchain inside it.
FROM oven/bun:1 AS web
WORKDIR /web
COPY web/package.json web/bun.lock ./
RUN bun install --frozen-lockfile
COPY web/ ./
RUN bun run build

FROM python:3.14-slim
ENV PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1 \
    UV_COMPILE_BYTECODE=1 \
    UV_LINK_MODE=copy \
    PATH="/app/.venv/bin:$PATH"

WORKDIR /app

COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv

# Dependencies first, so a code change does not reinstall the world.
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-install-project --no-dev

# LICENSE and README.md are named by pyproject.toml's `license` and `readme`
# fields, so hatchling refuses to build the project without them present.
COPY LICENSE README.md alembic.ini main.py ./
COPY alembic/ ./alembic/
COPY src/ ./src/
RUN uv sync --frozen --no-dev

COPY --from=web /web/build ./web/build

# The SQLite file, attachment blobs and generated keys all live here.
VOLUME ["/app/data"]
EXPOSE 8000

HEALTHCHECK --interval=30s --timeout=5s --start-period=20s \
  CMD python -c "import sys, urllib.request; sys.exit(0 if urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=4).status == 200 else 1)"

CMD ["sh", "-c", "alembic upgrade head && uvicorn main:app --host 0.0.0.0 --port 8000"]
