# Das Abbild: ein Prozess, eine Platte, sonst nichts.
#
# NODE 22 IST FESTGENAGELT, und das ist kein Zufall. better-sqlite3 ist ein
# nativer Baustein und laedt beim Installieren eine fertige Binaerdatei
# herunter (`prebuild-install || node-gyp rebuild`). Fuer 11.10.0 gibt es die
# nur fuer diese ABI-Versionen:
#
#     Node 20 -> ABI 115   vorhanden
#     Node 22 -> ABI 127   vorhanden   <- wir
#     Node 24 -> ABI 137   FEHLT
#
# Wer auf 24 hochzieht, faellt auf node-gyp zurueck, und das braucht python3,
# make und g++ im Abbild. Der Bau bricht dann ab -- laut und sofort, immerhin.
# Vor einem Versionssprung also nachsehen, ob es die ABI als Binaerdatei gibt:
# https://github.com/WiseLibs/better-sqlite3/releases
#
# bookworm-slim und nicht alpine: musl-Binaerdateien gibt es zwar auch, aber
# der glibc-Weg ist der ausgetretene. Das Abbild ist dafuer groesser -- bei
# einem Container auf dem eigenen Server ist das der guenstigere Tausch.

# ------------------------------------------------------------ Abhaengigkeiten
FROM node:22-bookworm-slim AS deps
WORKDIR /app
COPY package.json package-lock.json ./
# Nur was der Betrieb braucht. Hier laeuft prebuild-install und holt die
# native Binaerdatei fuer diese Node-Version.
RUN npm ci --omit=dev

# --------------------------------------------------------------- Oberflaeche
# Eigene Stufe, damit ein geaendertes Dashboard nicht den Server neu uebersetzt
# und umgekehrt. Was hier entsteht, ist reines HTML/CSS/JS -- im Betrieb laeuft
# davon nichts, es wird nur ausgeliefert.
FROM node:22-bookworm-slim AS web
WORKDIR /ui
COPY ui/package.json ui/package-lock.json ./
# Hier bewusst OHNE --omit=dev: vite und tsc sind devDependencies, und ohne sie
# gibt es nichts zu bauen.
RUN npm ci
COPY ui/ ./
# vite.config.ts schreibt nach ../web, also /web. Wer das dort aendert, aendert
# die Zeile im Betriebsabbild mit.
RUN npm run build

# -------------------------------------------------------------------- Bauen
FROM node:22-bookworm-slim AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY tsconfig.json ./
COPY src ./src
RUN npm run build

# ------------------------------------------------------------------ Betrieb
FROM node:22-bookworm-slim AS runtime
ENV NODE_ENV=production
WORKDIR /app

# ffmpeg -- fuer EIN EINZIGES Bild je Video, und trotzdem die groesste einzelne
# Zutat dieses Abbilds.
#
# DIE ABWAEGUNG, ausgesprochen: eine Kachel ohne Vorschau ist ein Fragezeichen.
# Bei 3600 Bildern faellt das nicht auf, bei einer Handvoll Clips ist die
# Galerie dann eine Liste von Dateinamen -- und dafuer braucht es keine
# Galerie. Ein Standbild kann sharp nicht liefern: libvips dekodiert kein
# Video, das ist kein Einstellungsproblem.
#
# Gerechnet wird NUR beim Hochladen und beim Nachziehen, nie beim Ausliefern.
# ffmpeg laeuft also nicht im Anfrageweg.
#
# WARUM NICHT `apt-get install ffmpeg` -- nachgemessen und wieder verworfen:
# Debians Paket schaltet jeden Filter frei und zieht die Abhaengigkeiten dafuer
# mit. Das Abbild wuchs von 460 MB auf 1,11 GB, und die groessten Brocken
# waren libllvm15 (114 MB), Mesa samt libgl1-mesa-dri (25 MB), libz3 (22 MB)
# und libflite1 (27 MB, ein SPRACHSYNTHESIZER). Alles fuer Filter, die ein
# Dienst ohne Bildschirm nie anfasst; --no-install-recommends half nicht, denn
# es sind harte Abhaengigkeiten.
#
# Also die statischen Binaerdateien, auf eine Fassung FESTGENAGELT. Der Preis
# dafuer ist ehrlich zu benennen: Aktualisierungen kommen nicht mehr ueber apt,
# sondern nur, wenn jemand diese Zeile anfasst. Vertretbar, weil ffmpeg hier
# nur eigene, bereits abgelegte Dateien anfasst und nicht am Netz haengt.
# NUR ffmpeg, NICHT ffprobe: das zweite Werkzeug waegt noch einmal 135 MB und
# haette hier eine einzige Aufgabe gehabt -- die Laenge zu nennen. Die steht
# auch im Kopf, den ffmpeg beim Oeffnen druckt; nachgemessen an beiden Dateien,
# auf zwei Nachkommastellen identisch (siehe medienDauer in storage.ts).
COPY --from=mwader/static-ffmpeg:7.1 /ffmpeg /usr/local/bin/

COPY --from=deps  /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
COPY package.json ./

# Die gebaute Oberflaeche. Der Dienst liefert sie aus, wenn sie da ist, und
# sagt sonst, dass sie fehlt (siehe index.ts) -- diese Auskunft bleibt als
# Netz, falls die Stufe oben je leer ausgeht.
COPY --from=web /web ./web

# Ein Ordner fuer Bilder UND media.db, damit eine Sicherung aus genau einem
# Kopiervorgang ueber ein Volume besteht.
ENV DATA_DIR=/data
RUN mkdir -p /data && chown -R node:node /data /app

# Nicht als root. Der Dienst nimmt Dateien von aussen entgegen und schreibt sie
# auf die Platte -- das ist die Stelle, an der man das nicht will.
USER node

ENV PORT=8080
EXPOSE 8080

# Ohne curl im Abbild: Node kann das selbst, fetch ist ab 18 eingebaut.
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
    CMD node -e "fetch('http://127.0.0.1:'+(process.env.PORT||8080)+'/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"

CMD ["node", "dist/index.js"]
