# Imagen de la aplicación, en dos etapas (T006).
#
# La primera compila los assets con Node; la segunda ejecuta Python y sólo se
# lleva el resultado. Node no queda en la imagen final: son unos cuantos cientos
# de megabytes que en producción no ejecutan nada, y cada uno de ellos es
# superficie que hay que parchear.
#
# El orden de las capas está elegido para que un cambio de código no vuelva a
# instalar dependencias: primero los manifiestos, después el código.

# --- Etapa 1: los assets ----------------------------------------------------
FROM node:24-slim AS assets

WORKDIR /app

# `npm ci` y no `npm install`: instala exactamente lo que dice el candado, y
# falla si el candado no coincide con package.json en vez de resolverlo de otra
# manera. Una compilación que resuelve versiones sola no es reproducible.
COPY package.json package-lock.json ./
RUN npm ci

COPY vite.config.ts tsconfig*.json ./
COPY frontend/ ./frontend/
COPY static/ ./static/

# Deja `static/dist/` con el manifiesto que django-vite lee en producción.
RUN npm run build


# --- Etapa 2: la aplicación -------------------------------------------------
FROM python:3.13-slim AS app

# Sin escritura de .pyc y sin buffer en la salida: en un contenedor, los
# registros se leen por la salida estándar, y con buffer una línea puede tardar
# minutos en aparecer —o perderse si el proceso muere—.
ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1 \
    UV_COMPILE_BYTECODE=1 \
    UV_LINK_MODE=copy \
    UV_PROJECT_ENVIRONMENT=/usr/local

COPY --from=ghcr.io/astral-sh/uv:0.9 /uv /usr/local/bin/uv

WORKDIR /app

# Las dependencias en su propia capa: cambian con `uv.lock`, no con cada línea
# de código que se toque.
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev --no-install-project

COPY manage.py ./
COPY config/ ./config/
COPY apps/ ./apps/
COPY static/ ./static/
COPY --from=assets /app/static/dist/ ./static/dist/

# `collectstatic` corre acá y no al arrancar: es trabajo que no depende del
# entorno y hacerlo en cada arranque retrasaría cada despliegue y cada
# reinicio. Necesita una clave para poder importar la configuración, y la de
# acá no se usa para nada: no cifra ni firma nada que sobreviva a esta línea.
RUN SECRET_KEY=solo-para-collectstatic \
    SETTINGS_ENCRYPTION_KEY=Ry1lTnFXTHZLSllJWklVbHVCbHZIRlVvVDRfZDNZa0E= \
    DATABASE_URL=postgres://nadie@localhost/nada \
    ALLOWED_HOSTS=localhost \
    DJANGO_SETTINGS_MODULE=config.settings.prod \
    python manage.py collectstatic --noinput --clear

# Los archivos de exportación viven acá y se comparten entre el proceso web y el
# trabajador: uno los escribe y el otro los sirve. Sin un volumen montado en
# este punto, la descarga da 404 en cuanto el trabajador corre en otro contenedor.
RUN mkdir -p /app/var/exports

# Sin privilegios: si algo se escapa del proceso, que no se escape como root.
RUN useradd --system --create-home --uid 1001 relay \
    && chown -R relay:relay /app
USER relay

EXPOSE 8000

# Dos trabajadores y dos hilos alcanzan para una instalación chica; lo que
# importa es que sea un servidor de verdad y no el de desarrollo. El tiempo
# máximo es generoso porque la exportación por debajo del umbral se transmite
# dentro de la misma respuesta.
CMD ["gunicorn", "config.wsgi:application", \
     "--bind", "0.0.0.0:8000", \
     "--workers", "2", \
     "--threads", "2", \
     "--timeout", "120", \
     "--access-logfile", "-", \
     "--error-logfile", "-"]
