# syntax=docker/dockerfile:1 # ============ Stage 1: Frontend build ============ FROM node:18-bookworm-slim AS frontend-builder WORKDIR /app/frontend COPY package*.json ./ RUN npm ci COPY src ./src COPY next-env.d.ts ./ COPY next.config.mjs ./ COPY postcss.config.mjs ./ COPY tailwind.config.ts ./ COPY tsconfig.json ./ # The stable baseline has no public directory. Keep one in the image so the # runtime copy is deterministic and future static assets have a fixed home. RUN mkdir -p public RUN npm run build RUN npm prune --omit=dev # ============ Stage 2: Backend + runtime ============ FROM python:3.11-slim AS runtime ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ PYTHONPATH=/app/backend \ NODE_ENV=production WORKDIR /app RUN apt-get update && apt-get install -y --no-install-recommends \ gcc \ g++ \ libpq-dev \ nodejs \ npm \ postgresql-client \ && rm -rf /var/lib/apt/lists/* COPY backend/requirements.txt ./backend/ RUN pip install --no-cache-dir -r backend/requirements.txt COPY backend ./backend COPY --from=frontend-builder /app/frontend/.next ./frontend/.next COPY --from=frontend-builder /app/frontend/public ./frontend/public COPY --from=frontend-builder /app/frontend/package.json ./frontend/package.json COPY --from=frontend-builder /app/frontend/node_modules ./frontend/node_modules RUN mkdir -p /app/backend/xingbang_data/reviewers \ /app/backend/xingbang_data/history \ /app/backend/xingbang_data/team_experience \ /app/backend/logs COPY docker-entrypoint.sh /app/docker-entrypoint.sh RUN chmod +x /app/docker-entrypoint.sh EXPOSE 8002 3000 HEALTHCHECK --interval=30s --timeout=5s --start-period=120s --retries=3 \ CMD node -e "Promise.all([fetch('http://127.0.0.1:3000/'), fetch('http://127.0.0.1:8002/')]).then(rs => process.exit(rs.every(r => r.ok) ? 0 : 1)).catch(() => process.exit(1))" ENTRYPOINT ["/app/docker-entrypoint.sh"]