- Standardized quotes in pnpm-lock.yaml for improved consistency across dependencies. - Added new dependencies including '@types/node' and 'zod' with specified versions to enhance type safety and validation. - Updated Dockerfile to disable workspace package injection, ensuring symlinks are used for the domain package during installation, which improves build reliability.
110 lines
4.3 KiB
Docker
110 lines
4.3 KiB
Docker
# syntax=docker/dockerfile:1
|
|
# =============================================================================
|
|
# Portal (Next.js) Dockerfile
|
|
# =============================================================================
|
|
# Multi-stage build with standalone output for minimal image size
|
|
# Optimized for fast builds, security, and small production images
|
|
# =============================================================================
|
|
|
|
ARG NODE_VERSION=22
|
|
ARG PNPM_VERSION=10.25.0
|
|
|
|
# =============================================================================
|
|
# Stage 1: Dependencies (cached layer)
|
|
# =============================================================================
|
|
FROM node:${NODE_VERSION}-alpine AS deps
|
|
|
|
ARG PNPM_VERSION
|
|
|
|
# Install build dependencies
|
|
RUN apk add --no-cache libc6-compat \
|
|
&& corepack enable \
|
|
&& corepack prepare pnpm@${PNPM_VERSION} --activate
|
|
|
|
WORKDIR /app
|
|
|
|
# Copy manifests first for dependency caching (all workspace packages)
|
|
COPY .npmrc pnpm-workspace.yaml package.json pnpm-lock.yaml ./
|
|
COPY packages/domain/package.json ./packages/domain/package.json
|
|
COPY apps/portal/package.json ./apps/portal/package.json
|
|
COPY apps/bff/package.json ./apps/bff/package.json
|
|
|
|
# Install only the packages needed for the portal (domain + portal)
|
|
# Disable inject-workspace-packages so symlinks are used (domain dist doesn't exist at install time)
|
|
ENV HUSKY=0
|
|
RUN --mount=type=cache,id=pnpm-portal,target=/root/.local/share/pnpm/store \
|
|
pnpm config set inject-workspace-packages false && \
|
|
pnpm install --frozen-lockfile --filter @customer-portal/domain... --filter @customer-portal/portal...
|
|
|
|
# =============================================================================
|
|
# Stage 2: Builder
|
|
# =============================================================================
|
|
FROM deps AS builder
|
|
|
|
# Copy source files
|
|
COPY tsconfig.json tsconfig.base.json tsconfig.node.json tsconfig.next.json ./
|
|
COPY packages/domain/ ./packages/domain/
|
|
COPY apps/portal/ ./apps/portal/
|
|
|
|
# Build-time environment variables
|
|
ARG NEXT_PUBLIC_API_BASE=/api
|
|
ARG NEXT_PUBLIC_APP_NAME="Customer Portal"
|
|
ARG NEXT_PUBLIC_APP_VERSION=1.0.0
|
|
|
|
ENV NODE_ENV=production \
|
|
NEXT_PUBLIC_API_BASE=${NEXT_PUBLIC_API_BASE} \
|
|
NEXT_PUBLIC_APP_NAME=${NEXT_PUBLIC_APP_NAME} \
|
|
NEXT_PUBLIC_APP_VERSION=${NEXT_PUBLIC_APP_VERSION} \
|
|
NEXT_TELEMETRY_DISABLED=1
|
|
|
|
# Build: domain → Next.js (standalone output)
|
|
# After building domain, manually create symlink to ensure resolution works
|
|
RUN pnpm --filter @customer-portal/domain build \
|
|
&& mkdir -p node_modules/@customer-portal \
|
|
&& ln -sf ../../packages/domain node_modules/@customer-portal/domain \
|
|
&& pnpm --filter @customer-portal/portal build
|
|
|
|
# =============================================================================
|
|
# Stage 3: Production
|
|
# =============================================================================
|
|
FROM node:${NODE_VERSION}-alpine AS production
|
|
|
|
LABEL org.opencontainers.image.title="Customer Portal Frontend" \
|
|
org.opencontainers.image.description="Next.js Customer Portal" \
|
|
org.opencontainers.image.vendor="Customer Portal"
|
|
|
|
# Minimal runtime dependencies + security hardening
|
|
RUN apk add --no-cache dumb-init libc6-compat \
|
|
&& addgroup --system --gid 1001 nodejs \
|
|
&& adduser --system --uid 1001 nextjs \
|
|
# Remove apk cache and unnecessary files
|
|
&& rm -rf /var/cache/apk/* /tmp/* /root/.npm
|
|
|
|
WORKDIR /app
|
|
|
|
# Copy standalone build artifacts with correct ownership
|
|
COPY --from=builder --chown=nextjs:nodejs /app/apps/portal/.next/standalone ./
|
|
COPY --from=builder --chown=nextjs:nodejs /app/apps/portal/.next/static ./apps/portal/.next/static
|
|
COPY --from=builder --chown=nextjs:nodejs /app/apps/portal/public ./apps/portal/public
|
|
|
|
# Security: Run as non-root user
|
|
USER nextjs
|
|
|
|
# Expose frontend port
|
|
EXPOSE 3000
|
|
|
|
# Environment configuration
|
|
ENV NODE_ENV=production \
|
|
NEXT_TELEMETRY_DISABLED=1 \
|
|
PORT=3000 \
|
|
HOSTNAME="0.0.0.0" \
|
|
# Node.js production optimizations
|
|
NODE_OPTIONS="--max-old-space-size=512"
|
|
|
|
# Health check for container orchestration
|
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
|
|
CMD node -e "fetch('http://localhost:3000/_health').then(r=>r.ok||process.exit(1)).catch(()=>process.exit(1))"
|
|
|
|
ENTRYPOINT ["dumb-init", "--"]
|
|
CMD ["node", "apps/portal/server.js"]
|