FROM python:3.12-slim AS build
WORKDIR /app
COPY server-up/requirements.txt .
RUN pip install --no-cache-dir --prefix=/inst -r requirements.txt

FROM python:3.12-slim
# De volgorde hieronder is met opzet: alles wat zelden verandert staat boven,
# alles wat bij elke commit verandert onderaan. Docker hergebruikt een laag
# alleen zolang niets ervóór is gewijzigd, dus één regel op de verkeerde plek
# kost bij elke build opnieuw de hele rit.
#
# Wat hier eerder misging: `ARG SU_VERSION` met een LABEL erachter stond
# bovenaan. Die waarde verandert per release, dus elke uitgave gooide álle
# lagen erna weg — apt-get, de Docker-CLI van 70 MB, de front-end-bestanden,
# alles. Vandaar dat versie en label nu helemaal onderaan staan.

# util-linux levert `setpriv`, waarmee het entrypoint naar een gewone gebruiker
# afzakt zonder de capabilities te verliezen die Server Up nodig heeft. Het is
# een Essential-pakket en dus al aanwezig, maar expliciet noemen zorgt dat een
# uitgeklede basis-image het niet stilletjes wegbezuinigt.
RUN apt-get update && apt-get install -y --no-install-recommends \
    git openssh-client curl tar gzip ca-certificates util-linux passwd \
    && rm -rf /var/lib/apt/lists/* \
    && setpriv --help >/dev/null

# Install Docker CLI + docker-compose inside container
RUN DPKG_ARCH=$(dpkg --print-architecture) \
 && case "$DPKG_ARCH" in \
      amd64) DOCKER_ARCH=x86_64; COMPOSE_ARCH=x86_64 ;; \
      arm64) DOCKER_ARCH=aarch64; COMPOSE_ARCH=aarch64 ;; \
      armhf) DOCKER_ARCH=armhf; COMPOSE_ARCH=armv7 ;; \
      *) DOCKER_ARCH=$DPKG_ARCH; COMPOSE_ARCH=$DPKG_ARCH ;; \
    esac \
 && curl -fsSL "https://download.docker.com/linux/static/stable/${DOCKER_ARCH}/docker-27.5.1.tgz" \
    | tar xz --strip-components=1 -C /usr/local/bin docker/docker \
 && chmod +x /usr/local/bin/docker \
 && curl -fsSL "https://github.com/docker/compose/releases/download/v2.32.4/docker-compose-linux-${COMPOSE_ARCH}" \
    -o /usr/local/bin/docker-compose \
 && chmod +x /usr/local/bin/docker-compose \
 && mkdir -p /usr/local/lib/docker/cli-plugins /usr/libexec/docker/cli-plugins \
 && ln -sf /usr/local/bin/docker-compose /usr/local/lib/docker/cli-plugins/docker-compose \
 && ln -sf /usr/local/bin/docker-compose /usr/libexec/docker/cli-plugins/docker-compose

COPY --from=build /inst /usr/local
WORKDIR /app

# De front-end-bestanden staan vóór de broncode: hun versies staan hier vast,
# dus deze lagen blijven staan zolang je ze niet zelf wijzigt. Stonden ze erna,
# dan haalde elke commit ze opnieuw van het CDN én werden ze bij elke push
# opnieuw geüpload.
RUN mkdir -p static/fonts \
 && curl -fsSL "https://cdn.jsdelivr.net/npm/@mdi/font@7.4.47/css/materialdesignicons.min.css" -o static/fonts/mdi.min.css \
 && curl -fsSL "https://cdn.jsdelivr.net/npm/@mdi/font@7.4.47/fonts/materialdesignicons-webfont.woff2" -o static/fonts/materialdesignicons-webfont.woff2 \
 && sed -i "s|https://cdn.jsdelivr.net/npm/@mdi/font@7.4.47/fonts/||g" static/fonts/mdi.min.css

# Front-end libraries meeleveren in plaats van vanaf een CDN laden. Zo kan de
# Content-Security-Policy op 'self' blijven, is er geen supply-chain-risico van
# unpkg/jsdelivr op runtime, en werkt de UI ook zonder internetverbinding.
RUN mkdir -p static/vendor \
 && curl -fsSL "https://cdn.tailwindcss.com/3.4.16?plugins=forms,typography" \
      -o static/vendor/tailwind.js \
 && curl -fsSL "https://cdn.jsdelivr.net/npm/alpinejs@3.14.1/dist/cdn.min.js" \
      -o static/vendor/alpine.min.js \
 && curl -fsSL "https://cdn.jsdelivr.net/npm/htmx.org@2.0.3/dist/htmx.min.js" \
      -o static/vendor/htmx.min.js \
 && for f in static/vendor/tailwind.js static/vendor/alpine.min.js static/vendor/htmx.min.js; do \
      test -s "$f" || { echo "LEEG: $f"; exit 1; }; \
    done

# Directories + git safe.directory (fix dubious ownership)
RUN mkdir -p /data/stacks /data/appdata /data/backups /data/git modules /root \
 && printf '[safe]\n\tdirectory = *\n' > /root/.gitconfig

# GIT_SSH_COMMAND: StrictHostKeyChecking=accept-new vertrouwt een host bij de
# eerste verbinding en bewaart de sleutel in /data/known_hosts. Wijzigt die
# sleutel later, dan faalt de verbinding — met `no` was elke MITM onzichtbaar.
ENV PYTHONUNBUFFERED=1 PORT=5000 HOME=/root \
    SU_CONFIG=/data/config.json SU_AUDIT=/data/audit.db SU_GIT_CACHE=/data/git \
    SU_SECRET=/data/secret.key SU_JOBS=/data/jobs \
    GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/data/known_hosts"
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
RUN chmod +x /usr/local/bin/docker-entrypoint.sh

EXPOSE 5000
HEALTHCHECK --interval=30s --timeout=8s --start-period=15s CMD curl -fs http://localhost:5000/healthz || exit 1
# Het entrypoint start als root, zet /data klaar en zakt daarna af naar
# SU_UID:SU_GID. Zonder die variabelen verandert er niets: dan blijft het root.
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
CMD ["python","app.py"]

# ── Vanaf hier verandert er iets bij elke commit ─────────────────────────────
# Alles hierboven blijft in de cache staan; alleen deze laatste lagen worden
# opnieuw gebouwd. Daardoor duurt een release een halve minuut in plaats van
# een kwartier, en hoeft de push alleen deze lagen te uploaden.
COPY server-up/ ./
COPY modules/ ./modules-bundled/
COPY VERSION ./VERSION

# Build-arg: zet bij build naar de git-tag, zodat de image z'n eigen versie kent.
#   docker build --build-arg SU_VERSION=$(git describe --tags --always) ...
# Forgejo Actions doet dit automatisch (zie server-up-deploy/README.md).
# Helemaal onderaan, want deze waarde verandert per uitgave en zou alles
# eronder ongeldig maken.
ARG SU_VERSION=dev
ENV SU_VERSION=${SU_VERSION}
LABEL org.opencontainers.image.title="Server Up" \
      org.opencontainers.image.version="${SU_VERSION}" \
      org.opencontainers.image.source="https://git.example.com/bes-r/server-up"
