From ce7c7773cd59ba3723df211d701355a33601bada Mon Sep 17 00:00:00 2001 From: barsa Date: Wed, 10 Dec 2025 18:42:33 +0900 Subject: [PATCH] Refactor Dockerfiles and enhance Next.js configuration for improved build and security - Updated Dockerfiles for BFF and Portal to optimize dependency installation and build processes, focusing on specific package filters. - Enhanced Next.js configuration to improve security headers and Content Security Policy for development environments. - Adjusted Docker Compose files to support dynamic image tagging for better version control. - Cleaned up deployment scripts to include environment variable management for image tagging. --- apps/bff/Dockerfile | 21 +++++++++++-------- apps/portal/Dockerfile | 11 +++++----- apps/portal/next.config.mjs | 30 ++++++++++++++++++++++++++-- docker/portainer/docker-compose.yml | 4 ++-- portal-backend.latest.tar.gz.sha256 | 1 + portal-frontend.latest.tar.gz.sha256 | 1 + scripts/plesk-deploy.sh | 2 ++ 7 files changed, 53 insertions(+), 17 deletions(-) create mode 100644 portal-backend.latest.tar.gz.sha256 create mode 100644 portal-frontend.latest.tar.gz.sha256 diff --git a/apps/bff/Dockerfile b/apps/bff/Dockerfile index 0dcd68bc..69735149 100644 --- a/apps/bff/Dockerfile +++ b/apps/bff/Dockerfile @@ -24,15 +24,16 @@ RUN apk add --no-cache python3 make g++ openssl libc6-compat \ WORKDIR /app -# Copy manifests first for dependency caching +# 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/ -COPY apps/bff/package.json ./apps/bff/ +COPY packages/domain/package.json ./packages/domain/package.json +COPY apps/bff/package.json ./apps/bff/package.json +COPY apps/portal/package.json ./apps/portal/package.json -# Install all dependencies with cache mount (separate layer for better caching) +# Install only the packages needed for the BFF (domain + bff) ENV HUSKY=0 RUN --mount=type=cache,id=pnpm-bff,target=/root/.local/share/pnpm/store \ - pnpm install --frozen-lockfile + pnpm install --frozen-lockfile --filter @customer-portal/domain... --filter @customer-portal/bff... # ============================================================================= # Stage 2: Builder @@ -47,7 +48,9 @@ COPY packages/domain/ ./packages/domain/ COPY apps/bff/ ./apps/bff/ # Build: domain → Prisma generate → BFF (single RUN for better layer efficiency) -RUN pnpm --filter @customer-portal/domain build \ +# Clean tsbuildinfo to force fresh emit; checked-in files can skip output otherwise +RUN rm -f apps/bff/tsconfig*.tsbuildinfo \ + && pnpm --filter @customer-portal/domain build \ && pnpm --filter @customer-portal/bff exec prisma generate \ && pnpm --filter @customer-portal/bff build @@ -84,8 +87,10 @@ ENV PRISMA_SCHEMA_PATH=/app/prisma/schema.prisma COPY --from=builder --chown=nestjs:nodejs /app/deploy ./ # Regenerate Prisma client for production paths and cleanup -RUN rm -rf node_modules/.prisma \ - && npx --yes prisma@${PRISMA_VERSION} generate --schema=${PRISMA_SCHEMA_PATH} \ +RUN npm install -g prisma@${PRISMA_VERSION} \ + && rm -rf node_modules/.prisma \ + && prisma generate --schema=${PRISMA_SCHEMA_PATH} \ + && mkdir -p /app/node_modules/.prisma \ # Create symlink for backward compatibility with any hardcoded paths && mkdir -p /app/apps/bff/prisma \ && ln -sf /app/prisma/schema.prisma /app/apps/bff/prisma/schema.prisma \ diff --git a/apps/portal/Dockerfile b/apps/portal/Dockerfile index ccd16456..2b5278e9 100644 --- a/apps/portal/Dockerfile +++ b/apps/portal/Dockerfile @@ -23,15 +23,16 @@ RUN apk add --no-cache libc6-compat \ WORKDIR /app -# Copy manifests first for dependency caching +# 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/ -COPY apps/portal/package.json ./apps/portal/ +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 dependencies with cache mount (unique id per app for better caching) +# Install only the packages needed for the portal (domain + portal) ENV HUSKY=0 RUN --mount=type=cache,id=pnpm-portal,target=/root/.local/share/pnpm/store \ - pnpm install --frozen-lockfile + pnpm install --frozen-lockfile --filter @customer-portal/domain... --filter @customer-portal/portal... # ============================================================================= # Stage 2: Builder diff --git a/apps/portal/next.config.mjs b/apps/portal/next.config.mjs index cae63fb0..dd14c603 100644 --- a/apps/portal/next.config.mjs +++ b/apps/portal/next.config.mjs @@ -47,11 +47,37 @@ const nextConfig = { async headers() { const isDev = process.env.NODE_ENV === "development"; - const connectSources = ["'self'", "https:"]; if (isDev) { - connectSources.push("http://localhost:*"); + const devConnectSources = ["'self'", "https:", "http://localhost:*", "ws://localhost:*"]; + const devScriptSrc = ["'self'", "'unsafe-inline'", "'unsafe-eval'", "blob:"].join(" "); + + return [ + { + source: "/(.*)", + headers: [ + { key: "X-Frame-Options", value: "DENY" }, + { key: "X-Content-Type-Options", value: "nosniff" }, + { key: "Referrer-Policy", value: "strict-origin-when-cross-origin" }, + { key: "X-XSS-Protection", value: "1; mode=block" }, + { + key: "Content-Security-Policy", + value: [ + "default-src 'self'", + `script-src ${devScriptSrc}`, + "style-src 'self' 'unsafe-inline'", + "img-src 'self' data: https:", + "font-src 'self' data:", + `connect-src ${devConnectSources.join(" ")}`, + "frame-ancestors 'none'", + ].join("; "), + }, + ], + }, + ]; } + const connectSources = ["'self'", "https:"]; + return [ { source: "/(.*)", diff --git a/docker/portainer/docker-compose.yml b/docker/portainer/docker-compose.yml index 8bb0c31b..ca5439f3 100644 --- a/docker/portainer/docker-compose.yml +++ b/docker/portainer/docker-compose.yml @@ -10,7 +10,7 @@ services: # Frontend (Next.js) # --------------------------------------------------------------------------- frontend: - image: ${FRONTEND_IMAGE:-portal-frontend:latest} + image: ${FRONTEND_IMAGE:-portal-frontend}:${IMAGE_TAG:-latest} container_name: portal-frontend ports: - "127.0.0.1:${FRONTEND_PORT:-3000}:3000" @@ -35,7 +35,7 @@ services: # Backend (NestJS BFF) # --------------------------------------------------------------------------- backend: - image: ${BACKEND_IMAGE:-portal-backend:latest} + image: ${BACKEND_IMAGE:-portal-backend}:${IMAGE_TAG:-latest} container_name: portal-backend ports: - "127.0.0.1:${BACKEND_PORT:-4000}:4000" diff --git a/portal-backend.latest.tar.gz.sha256 b/portal-backend.latest.tar.gz.sha256 new file mode 100644 index 00000000..cf79af19 --- /dev/null +++ b/portal-backend.latest.tar.gz.sha256 @@ -0,0 +1 @@ +8c26832b5a788235f2fb461eeeb10a33f34f40830d04fb1235aed449b67ebe1c /home/barsa/projects/customer_portal/customer-portal/portal-backend.latest.tar.gz diff --git a/portal-frontend.latest.tar.gz.sha256 b/portal-frontend.latest.tar.gz.sha256 new file mode 100644 index 00000000..2e0a1a49 --- /dev/null +++ b/portal-frontend.latest.tar.gz.sha256 @@ -0,0 +1 @@ +8b0b76418ba09fc6cb3134db6ac87d350463f88fae6ffae4fd536111d15fa0cf /home/barsa/projects/customer_portal/customer-portal/portal-frontend.latest.tar.gz diff --git a/scripts/plesk-deploy.sh b/scripts/plesk-deploy.sh index 8e0ec3a5..c8608c82 100755 --- a/scripts/plesk-deploy.sh +++ b/scripts/plesk-deploy.sh @@ -292,6 +292,8 @@ while [[ $# -gt 0 ]]; do esac done +export IMAGE_TAG + # ============================================================================= # Main # =============================================================================