# ---- Frontend compilado ---- FROM node:22-slim AS web-build WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # ---- Runtime ---- FROM node:22-slim WORKDIR /app ENV NODE_ENV=production ENV HOST=0.0.0.0 ENV PLATFORM_PORT=3100 # La zona del contenedor se fija a UTC, que es como Postgres almacena los # instantes. La lógica de agenda no depende de ella —usa `businesses.timezone`— # y dejarla en UTC hace que una recaída a la zona del proceso se note aquí en # vez de esconderse en el equipo de desarrollo, que está en México. ENV TZ=UTC COPY package*.json ./ # `--omit=dev` a secas dejaría fuera a `tsx`, que este backend NECESITA en # tiempo de ejecución porque corre TypeScript directo. Por eso `tsx` está en # `dependencies` y no en `devDependencies`: arrancar con `npx tsx` lo bajaría # de npm en cada arranque, sin versión fijada y sobre todo el código del # servidor. Es un riesgo de cadena de suministro que no compensa. RUN npm ci --omit=dev && npm cache clean --force COPY --from=web-build /app/dist ./dist COPY platform ./platform COPY shared ./shared COPY scripts ./scripts COPY tsconfig.json ./ # `platform/.env` está gitignorado y NO se copia: las credenciales llegan como # variables de entorno al desplegar. Si alguna vez aparece dentro de la imagen, # es un fallo del `.dockerignore`. EXPOSE 3100 # Consulta la base, no solo el puerto: un proceso vivo con Postgres caido no # esta sano, y sin esto el orquestador lo daria por bueno y no reiniciaria. HEALTHCHECK --interval=30s --timeout=5s --start-period=40s --retries=3 CMD node -e "fetch('http://127.0.0.1:3100/api/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))" # El contenedor no corre como root. USER node # Las migraciones NO se ejecutan aquí: se lanzan como paso explícito del # despliegue. Correrlas al arrancar hace que dos réplicas migren a la vez sobre # la misma base, y que un arranque fallido deje el esquema a medias. CMD ["node", "scripts/run-tsx.mjs", "platform/index.ts"]