# syntax=docker/dockerfile:1.7
#
# Packet Station image. One image runs the app, the worker, migrations and doctor.
# Build:  docker build --build-arg PS_BUILD=sha-<sha> -t gt.pescoinc.biz/spencerm/packetstation:sha-<sha> .
# No password is ever passed at build time. Secrets arrive at run time as compose file secrets.

ARG PYTHON_IMAGE=python:3.12-slim-bookworm
ARG NODE_IMAGE=node:22-bookworm-slim

# ---------------------------------------------------------------------------
# Stage 1: frontend. Vite build into frontend/dist.
# ---------------------------------------------------------------------------
FROM ${NODE_IMAGE} AS frontend
WORKDIR /src/frontend
# Lockfile first so the npm ci layer is cached until dependencies change.
COPY frontend/package.json frontend/package-lock.json ./
RUN npm ci --no-audit --no-fund
COPY frontend/ ./
RUN npm run build

# ---------------------------------------------------------------------------
# Stage 2: builder. Python venv in /opt/venv with every dependency and the package.
# ---------------------------------------------------------------------------
FROM ${PYTHON_IMAGE} AS builder
ENV PIP_DISABLE_PIP_VERSION_CHECK=1 \
    PIP_NO_CACHE_DIR=1 \
    PYTHONDONTWRITEBYTECODE=1
# PESCO's private index on Gitea. It serves visualkit, mfileskit and their dependencies only.
ARG PESCO_INDEX_URL=https://gt.pescoinc.biz/api/packages/spencerm/pypi/simple/
RUN python -m venv /opt/venv
ENV PATH=/opt/venv/bin:$PATH
WORKDIR /src

# Public dependencies from PyPI.
COPY server/requirements.txt server/requirements.txt
RUN pip install -r server/requirements.txt

# PESCO libraries from the Gitea index only. Always --index-url, never --extra-index-url,
# so a public package with the same name can never be picked instead.
COPY server/requirements-pesco.txt server/requirements-pesco.txt
RUN pip install --index-url "${PESCO_INDEX_URL}" -r server/requirements-pesco.txt

# The package itself. Dependencies are already in the venv.
COPY pyproject.toml README* ./
COPY server/ server/
RUN pip install --no-deps . \
 && pip check

# ---------------------------------------------------------------------------
# Stage 3: runtime.
# ---------------------------------------------------------------------------
FROM ${PYTHON_IMAGE} AS runtime

# ODBC Driver 18 from Microsoft's Debian 12 repository, plus tini and tzdata.
# curl and gnupg are only needed to fetch the signing key, so they are removed again.
RUN set -eux; \
    apt-get update; \
    apt-get install -y --no-install-recommends curl gnupg ca-certificates; \
    curl -fsSL https://packages.microsoft.com/keys/microsoft.asc \
      | gpg --dearmor -o /usr/share/keyrings/microsoft-prod.gpg; \
    echo "deb [arch=amd64 signed-by=/usr/share/keyrings/microsoft-prod.gpg] https://packages.microsoft.com/debian/12/prod bookworm main" \
      > /etc/apt/sources.list.d/microsoft-prod.list; \
    apt-get update; \
    ACCEPT_EULA=Y apt-get install -y --no-install-recommends \
      msodbcsql18 unixodbc tini tzdata ca-certificates; \
    apt-get purge -y --auto-remove curl gnupg; \
    rm -rf /var/lib/apt/lists/*; \
    odbcinst -q -d -n "ODBC Driver 18 for SQL Server"

# Service user. Group 10001 matches the host secret files (root:10001 0440).
RUN groupadd -g 10001 ps \
 && useradd -u 10001 -g 10001 -r -M -d /app -s /usr/sbin/nologin ps

COPY --from=builder /opt/venv /opt/venv
COPY --from=frontend /src/frontend/dist /app/static
COPY --chmod=0755 docker/entrypoint.sh /app/entrypoint.sh
# Guard against CRLF line endings from a Windows checkout.
RUN sed -i 's/\r$//' /app/entrypoint.sh

# /data/assets is a named volume. Docker copies this ownership into a new volume on first use.
# /backups is mounted read-only into the worker; it only needs to exist.
RUN mkdir -p /data/assets /backups \
 && chown 10001:10001 /data/assets \
 && chmod 0750 /data/assets

ARG PS_BUILD=dev
ENV PATH=/opt/venv/bin:$PATH \
    PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1 \
    PS_BUILD=${PS_BUILD} \
    STATIC_DIR=/app/static \
    ASSETS_DIR=/data/assets \
    BACKUP_DIR=/backups
LABEL org.opencontainers.image.title="packetstation" \
      org.opencontainers.image.source="https://gt.pescoinc.biz/spencerm/packetstation" \
      org.opencontainers.image.version="${PS_BUILD}" \
      biz.pescoinc.packetstation.build="${PS_BUILD}"

WORKDIR /app
USER ps
EXPOSE 8000
# No HEALTHCHECK here. Compose sets one per service (app and worker check different things).
ENTRYPOINT ["tini", "--", "/app/entrypoint.sh"]
CMD ["app"]
