FROM node:20-alpine AS base RUN corepack enable && corepack prepare pnpm@9.15.4 --activate WORKDIR /app FROM base AS builder COPY package.json pnpm-workspace.yaml ./ COPY apps/master-api ./apps/master-api COPY packages ./packages RUN pnpm install --no-frozen-lockfile RUN pnpm --filter @media-cluster/shared-types build RUN pnpm --filter @media-cluster/protocol build WORKDIR /app/apps/master-api RUN pnpm exec prisma generate RUN pnpm build # Assemble a flat runtime directory (avoids broken pnpm symlinks in final image) WORKDIR /app RUN mkdir -p /out/node_modules/@media-cluster \ && cp -R apps/master-api/dist /out/dist \ && cp -R apps/master-api/prisma /out/prisma \ && cp apps/master-api/package.json /out/package.json \ && cp -R packages/shared-types /out/node_modules/@media-cluster/shared-types \ && cp -R packages/protocol /out/node_modules/@media-cluster/protocol FROM node:20-alpine AS runner WORKDIR /app ENV NODE_ENV=production RUN apk add --no-cache openssl wget python3 make g++ COPY --from=builder /out/ ./ # Drop workspace: protocol deps; keep prisma CLI for migrate RUN node -e "\ const fs=require('fs');\ const p=JSON.parse(fs.readFileSync('package.json','utf8'));\ delete p.dependencies['@media-cluster/protocol'];\ delete p.dependencies['@media-cluster/shared-types'];\ p.dependencies.prisma = (p.devDependencies && p.devDependencies.prisma) || '6.3.1';\ delete p.devDependencies;\ delete p.scripts;\ fs.writeFileSync('package.json', JSON.stringify(p, null, 2));" RUN npm install --omit=dev \ && npx prisma generate \ && apk del python3 make g++ EXPOSE 3000 HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=5 \ CMD wget -qO- http://127.0.0.1:3000/health || exit 1 CMD ["sh", "-c", "npx prisma migrate deploy && node dist/app.js"]