# hamprint -- single-container image (plan.md §10).
#
# Runs Gunicorn plus the two periodic jobs (process_submissions every 30 s,
# cleanup_stale every 5 min) in the same process group. No sidecars.
#
# Build:    podman build -t hamprint:latest .
# Run:      podman run --rm -p 8000:8000 --env-file .env hamprint:latest
#
# In production the host typically mounts a volume at /app/media so uploaded
# STLs survive container restarts; the database connection comes from
# DATABASE_URL (Postgres in prod, SQLite if you really want).

FROM docker.io/library/python:3.14-slim

ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1 \
    PIP_DISABLE_PIP_VERSION_CHECK=1 \
    PIP_NO_CACHE_DIR=1 \
    DJANGO_SETTINGS_MODULE=hamprint.settings.prod

WORKDIR /app

# System packages python:3.14-slim doesn't ship:
#   tini    -- PID 1 for clean signal forwarding to gunicorn + the loops
#   libgomp1 -- numpy/numpy-stl runtime on some kernels
#   curl    -- handy for healthchecks if compose.yaml grows one
RUN apt-get update \
    && apt-get install -y --no-install-recommends tini curl libgomp1 \
    && rm -rf /var/lib/apt/lists/*

# Python deps as their own layer so app-code edits don't invalidate the wheel
# cache. psycopg[binary] is added explicitly because requirements.txt is
# kept Postgres-driver-agnostic for the host-venv (SQLite) path.
COPY requirements.txt .
RUN pip install -r requirements.txt 'psycopg[binary]'

# Application code (.dockerignore excludes .venv, .git, db.sqlite3, demo/, etc).
COPY . .

# Build Tailwind CSS, gather everything under STATIC_ROOT for WhiteNoise, then
# drop the ~120 MB Tailwind CLI download so it doesn't ride along. The source
# .css lives at assets/tailwind.source.css (per TAILWIND_CLI_SRC_CSS in
# settings/base.py); only the CLI binary cache is purged.
RUN python manage.py tailwind build --force \
    && python manage.py collectstatic --noinput \
    && rm -rf /app/.django_tailwind_cli

# Default writable dirs. Mount a volume at /app/media in prod for persistence.
RUN mkdir -p /app/media /app/staticfiles

# Drop privileges. uid 1000 maps cleanly to the typical host user in rootless
# podman, so a bind-mounted media volume stays writable without extra fuss.
RUN useradd -m -u 1000 app \
    && chown -R app:app /app
USER app

EXPOSE 8000

# tini reaps zombies + forwards SIGTERM to all children, so when the orchestrator
# stops the container both Gunicorn AND the two background loops get the signal.
ENTRYPOINT ["/usr/bin/tini", "--"]
CMD ["bash", "entrypoint.sh"]
