# syntax=docker/dockerfile:1.7 ARG NODE_IMAGE=node:24.18.0-bookworm-slim # ============================================================ # 1. Dependencias # ============================================================ FROM ${NODE_IMAGE} AS deps WORKDIR /app # Cambiar este valor invalida únicamente la capa de dependencias. # Se incrementó para descartar la capa corrupta anterior. ARG DEPS_CACHE_REV=20260720-2 ENV NEXT_TELEMETRY_DISABLED=1 \ NPM_CONFIG_REGISTRY=https://registry.npmjs.org/ \ NPM_CONFIG_REPLACE_REGISTRY_HOST=always \ NPM_CONFIG_AUDIT=false \ NPM_CONFIG_FUND=false \ NPM_CONFIG_FETCH_RETRIES=2 \ NPM_CONFIG_FETCH_RETRY_MINTIMEOUT=5000 \ NPM_CONFIG_FETCH_RETRY_MAXTIMEOUT=30000 \ NPM_CONFIG_FETCH_TIMEOUT=120000 \ NPM_CONFIG_MAXSOCKETS=8 \ NPM_CONFIG_LOGLEVEL=info COPY package.json package-lock.json .npmrc ./ # ------------------------------------------------------------ # Normalizar URLs antiguas del registry # ------------------------------------------------------------ RUN node <<'NODE' const fs = require("node:fs"); const filename = "package-lock.json"; const publicRegistry = "https://registry.npmjs.org/"; let content = fs.readFileSync(filename, "utf8"); let replacements = 0; const internalRegistries = [ /https?:\/\/packages\.applied-caas-gateway1\.internal\.api\.openai\.org\/artifactory\/api\/npm\/npm-public\//g, ]; for (const pattern of internalRegistries) { content = content.replace(pattern, () => { replacements += 1; return publicRegistry; }); } fs.writeFileSync( filename, `${content.trimEnd()}\n`, "utf8" ); const normalized = fs.readFileSync(filename, "utf8"); const forbiddenHosts = [ "internal.api.openai.org", "packages.applied-caas-gateway1", ]; const detectedHosts = forbiddenHosts.filter((host) => normalized.includes(host) ); if (detectedHosts.length > 0) { console.error( `ERROR: registries no permitidos: ${detectedHosts.join(", ")}` ); process.exit(1); } const lockfile = JSON.parse(normalized); console.log( `Lockfile válido: ${lockfile.name}` ); console.log( `lockfileVersion: ${lockfile.lockfileVersion}` ); console.log( `Referencias normalizadas: ${replacements}` ); NODE # ------------------------------------------------------------ # Validar conectividad con npm # ------------------------------------------------------------ RUN node --version \ && npm --version \ && npm config get registry \ && npm config get replace-registry-host \ && npm ping # ------------------------------------------------------------ # Instalar y verificar dependencias # ------------------------------------------------------------ RUN --mount=type=cache,target=/root/.npm,sharing=private \ echo "DEPS_CACHE_REV=${DEPS_CACHE_REV}" \ && timeout \ --signal=TERM \ --kill-after=30s \ 900s \ npm ci \ --no-audit \ --no-fund \ --prefer-online \ --loglevel=info \ --timing \ && test -d /app/node_modules \ && test -f /app/node_modules/next/package.json \ && test -f /app/node_modules/react/package.json \ && node -e \ "console.log('Next.js:', require('./node_modules/next/package.json').version)" \ && node -e \ "console.log('React:', require('./node_modules/react/package.json').version)" \ && du -sh /app/node_modules # ============================================================ # 2. Compilación Next.js # ============================================================ FROM ${NODE_IMAGE} AS builder WORKDIR /app ENV NODE_ENV=production \ NEXT_TELEMETRY_DISABLED=1 \ NODE_OPTIONS=--max-old-space-size=2048 COPY --from=deps \ /app/node_modules \ ./node_modules COPY package.json package-lock.json ./ COPY . . RUN test -d /app/node_modules \ && test -f /app/node_modules/next/package.json RUN --mount=type=cache,target=/app/.next/cache,sharing=private \ npm run build RUN test -f /app/.next/standalone/server.js \ && test -d /app/.next/static # ============================================================ # 3. Runtime de producción # ============================================================ FROM ${NODE_IMAGE} AS runner WORKDIR /app ENV DEBIAN_FRONTEND=noninteractive \ NODE_ENV=production \ NEXT_TELEMETRY_DISABLED=1 \ HOSTNAME=0.0.0.0 \ PORT=80 RUN apt-get update \ && apt-get install -y \ --no-install-recommends \ ca-certificates \ libcap2-bin \ && rm -rf /var/lib/apt/lists/* \ && groupadd \ --gid 1001 \ nodejs \ && useradd \ --uid 1001 \ --gid 1001 \ --create-home \ --shell /usr/sbin/nologin \ nextjs \ && setcap \ 'cap_net_bind_service=+ep' \ /usr/local/bin/node COPY --from=builder \ --chown=nextjs:nodejs \ /app/public \ ./public COPY --from=builder \ --chown=nextjs:nodejs \ /app/.next/standalone \ ./ COPY --from=builder \ --chown=nextjs:nodejs \ /app/.next/static \ ./.next/static USER nextjs EXPOSE 80 HEALTHCHECK \ --interval=30s \ --timeout=8s \ --start-period=30s \ --retries=3 \ CMD ["node", "-e", "fetch('http://127.0.0.1/api/health').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))"] CMD ["node", "server.js"]