# =============================================================================
# 0pt web console — Dockerfile för Coolify (SIAX Cloud)
# =============================================================================
# Referensimplementation: sax3l/siax-io, som redan körs självhostat på flottan
# (Hetzner + Coolify + Traefik). Samma form återanvänds här i stället för att
# uppfinna ett eget mönster: flerstegsbygge, node:22-alpine, libc6-compat för
# sharp, icke-root, HEALTHCHECK, och npm/npx borttaget ur runtime-imagen.
#
# Den deployerbara tjänsten i det här repot är Next.js 16-appen i repo-roten.
# Liveness: GET /api/health (svarar alltid 200 så länge processen lever).
# Readiness: GET /api/ready (503 tills DATABASE_URL pekar på en nåbar Postgres)
# — peka Coolifys readiness-probe dit, INTE HEALTHCHECK nedan.
#
# VAD SOM ÄNDRADES MOT FÖREGÅENDE VERSION AV DEN HÄR FILEN, och varför:
#
#  1. next.config.mjs sätter nu `output: "standalone"`. Runtime-steget bär
#     därför .next/standalone (server.js + spårade moduler) i stället för hela
#     node_modules. Mätt på det här repot: den gamla imagen var 1,33 GB på disk
#     / 315 MB komprimerat innehåll.
#  2. node:24-alpine -> node:22-alpine. Båda CI-workflowsen (.gitea/ och
#     .github/workflows/ci.yml) kör `next build` på node 22; siax-io kör 22 i
#     produktion. Att bygga och köra på samma major som grinden testar tar bort
#     en version-skew utan uppsida.
#  3. `--ignore-workspace` borttaget. Kommentaren som motiverade flaggan
#     ("pnpm-workspace.yaml ... carries only an `allowBuilds:` block and NO
#     `packages:` field") är inaktuell: filen har ett giltigt `packages:`-fält
#     och packages/schemas är en riktig workspace-medlem med egen package.json
#     sedan BACKLOG P1-21. Utan flaggan blir installationen här BIT FÖR BIT
#     samma kommando som CI kör (`pnpm install --frozen-lockfile`).
#  4. `--prod=false` tillagt. Coolify injicerar NODE_ENV=production vid byggtid,
#     vilket får pnpm att hoppa över devDependencies (typescript, tailwindcss)
#     som `next build` kräver. Samma fallgrop dokumenterad i siax-io:s
#     Dockerfile för npm's `--include=dev`.
#  5. Eget `sharp`-steg, se nedan.
#  6. npm/npx strippat ur runtime-imagen (SIO-019-mönstret från siax-io).
# =============================================================================

# ---- beroenden ---------------------------------------------------------------
FROM node:22-alpine AS deps
WORKDIR /app

ENV NEXT_TELEMETRY_DISABLED=1

# sharps förbyggda binär och libvips kräver libc-kompatibilitet på Alpine.
RUN apk add --no-cache libc6-compat

# corepack-fetch-flaken (server6-byggen, ECONNRESET mot registry.npmjs.org):
# retry-loop 8 försök à 15 s — samma mönster som c0nf0rm #56 / k0b.
RUN corepack enable && for i in 1 2 3 4 5 6 7 8; do corepack prepare pnpm@9 --activate && break || { echo "corepack retry $i/8"; sleep 15; }; done

# Manifesten först, för lagercache. pnpm-workspace.yaml OCH
# packages/schemas/package.json måste med: pnpm-lock.yaml har två importers
# (`.` och `packages/schemas`), och --frozen-lockfile vägrar om medlemmens
# manifest saknas på disk.
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./
# Every workspace importer in pnpm-lock.yaml must have its manifest on disk,
# otherwise --frozen-lockfile refuses to install. Keep this list in sync with
# `packages/*/package.json`.
COPY packages/schemas/package.json ./packages/schemas/package.json
COPY packages/decision/package.json ./packages/decision/package.json
COPY packages/siax-access/package.json ./packages/siax-access/package.json

# --prod=false: se punkt 4 ovan. Utan den dör `next build` på saknad typescript
# så fort byggmiljön råkar ha NODE_ENV=production satt.
RUN pnpm install --frozen-lockfile --prod=false

# ---- sharp (bildoptimering) --------------------------------------------------
# Nexts bildoptimerare laddar `sharp` med en runtime-`require`, så `next build`
# spårar den INTE in i .next/standalone. Verifierat i det här repot: ett bygge
# med enbart `output: "standalone"` gav ett .next/standalone/node_modules som
# bara innehöll next/, react/ och react-dom/.
#
# `outputFileTracingIncludes` går inte att använda som lösning: globben
# "./node_modules/@img/**/*" får Turbopack att panika på pnpms katalogsymlänkar
# i node_modules/.pnpm ("Is a directory (os error 21)") och bygget faller med
# exit 1. Reproducerat, inte antaget.
#
# Därför ett eget steg med npm i stället för pnpm: npm lägger ett PLATT,
# symlänkfritt node_modules, vilket är precis vad runtime-steget kan kopiera rakt
# av. Versionen läses ur pnpm-lock.yaml så den aldrig kan glida ifrån vad resten
# av bygget löste ut, och steget faller hårt om utläsningen inte ger något.
# Eftersom det körs i en Alpine-image blir det musl-binärerna (@img/*-linuxmusl-*)
# som hamnar där, inte värdmaskinens.
FROM node:22-alpine AS sharp
WORKDIR /sharp
RUN apk add --no-cache libc6-compat
COPY pnpm-lock.yaml ./
RUN set -eu; \
    SHARP_VERSION="$(sed -n 's/^  sharp@\([0-9][^(:]*\).*/\1/p' pnpm-lock.yaml | sort -u | head -1)"; \
    if [ -z "$SHARP_VERSION" ]; then \
      echo "FEL: hittade ingen sharp-version i pnpm-lock.yaml — vägrar bygga en image vars bildoptimering tyst skulle vara trasig." >&2; \
      exit 1; \
    fi; \
    echo "sharp pinnad från pnpm-lock.yaml: ${SHARP_VERSION}"; \
    npm install --no-save --no-audit --no-fund "sharp@${SHARP_VERSION}"; \
    node -e "require('sharp'); console.log('sharp laddar OK i byggsteget')"

# ---- bygge -------------------------------------------------------------------
FROM node:22-alpine AS build
WORKDIR /app

RUN apk add --no-cache libc6-compat
# corepack-fetch-flaken (server6-byggen, ECONNRESET mot registry.npmjs.org):
# retry-loop 8 försök à 15 s — samma mönster som c0nf0rm #56 / k0b.
RUN corepack enable && for i in 1 2 3 4 5 6 7 8; do corepack prepare pnpm@9 --activate && break || { echo "corepack retry $i/8"; sleep 15; }; done

# Endast rot-projektets node_modules kopieras. packages/schemas har noll
# beroenden (den paketerar bara JSON Schema-filer som konsumeras via sökväg),
# så pnpm skapar aldrig något packages/schemas/node_modules att kopiera —
# verifierat med `ls` efter en frozen install.
COPY --from=deps /app/node_modules ./node_modules
COPY . .

ENV NEXT_TELEMETRY_DISABLED=1
# Måste vara production. devDependencies behövs inte som NODE_ENV här — de ligger
# redan i node_modules från deps-steget. NODE_ENV=development skulle dessutom
# dra in Reacts utvecklingsruntime under prerendering (se siax-io:s Dockerfile,
# där det är uppmätt åt båda hållen).
ENV NODE_ENV=production
# Bygget får inte kräva riktiga secrets: lib/db.ts och lib/stripe.ts är lata och
# env-skyddade, och middleware.ts:s startgrind (assertAuthConfigSafe) klagar bara
# om AUTH_ENFORCE uttryckligen sätts till "false" i produktion. Verifierat: hela
# bygget går igenom utan en enda env-variabel satt.
RUN pnpm run build

# ---- runtime -----------------------------------------------------------------
FROM node:22-alpine AS runtime
WORKDIR /app

# `apk upgrade` hämtar Alpines senaste patchade paket (t.ex. libssl3/libcrypto3 —
# bastaggen släpar efter uppströms säkerhetsfixar) utan att röra den pinnade
# Node-versionen. wget behövs för HEALTHCHECK: bara node:*-alpine har varken
# curl eller wget.
RUN apk add --no-cache libc6-compat wget && apk upgrade --no-cache

ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
ENV PORT=3000
ENV HOSTNAME=0.0.0.0

# standalone-utdatan kopierar varken public/ eller .next/static — de måste med
# explicit, annars serveras appen utan statiska assets.
COPY --from=build --chown=node:node /app/public ./public
COPY --from=build --chown=node:node /app/.next/standalone ./
COPY --from=build --chown=node:node /app/.next/static ./.next/static

# sharp läggs BREDVID standalone-utdatans egna node_modules (next/react/react-dom).
# Docker slår ihop katalogerna, så inget skrivs över. `require('sharp')` från
# next/dist/... löses ut genom att vandra uppåt till /app/node_modules/sharp.
COPY --from=sharp --chown=node:node /sharp/node_modules ./node_modules

# Bas-imagen node:22-alpine skeppar npms egen globala installation
# (/usr/local/lib/node_modules/npm) med ett eget beroendeträd som regelbundet
# bär kända HIGH/CRITICAL-CVE:er, helt utan koppling till den här appens kod.
# Runtime-steget kör `node server.js` — varken RUN, CMD eller HEALTHCHECK här
# anropar npm eller npx — så trädet kan tas bort i stället för att accepteras.
RUN rm -rf /usr/local/lib/node_modules/npm /usr/local/bin/npm /usr/local/bin/npx /usr/local/share/man/man1/npm*

USER node

EXPOSE 3000

# LIVENESS. /api/health svarar 200 så länge processen kan svara — rätt signal för
# "starta om containern". Peka INTE hit en readiness-probe: /api/ready svarar 503
# tills DATABASE_URL är satt och Postgres är nåbar, och en HEALTHCHECK mot den
# skulle få Coolify att döda en container som fungerar precis som avsett.
HEALTHCHECK --interval=30s --timeout=5s --start-period=25s --retries=3 \
  CMD wget -qO- http://127.0.0.1:3000/api/health >/dev/null 2>&1 || exit 1

CMD ["node", "server.js"]
