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.
This commit is contained in:
barsa 2025-12-10 18:42:33 +09:00
parent bc5c7c9bd4
commit ce7c7773cd
7 changed files with 53 additions and 17 deletions

View File

@ -24,15 +24,16 @@ RUN apk add --no-cache python3 make g++ openssl libc6-compat \
WORKDIR /app 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 .npmrc pnpm-workspace.yaml package.json pnpm-lock.yaml ./
COPY packages/domain/package.json ./packages/domain/ COPY packages/domain/package.json ./packages/domain/package.json
COPY apps/bff/package.json ./apps/bff/ 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 ENV HUSKY=0
RUN --mount=type=cache,id=pnpm-bff,target=/root/.local/share/pnpm/store \ 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 # Stage 2: Builder
@ -47,7 +48,9 @@ COPY packages/domain/ ./packages/domain/
COPY apps/bff/ ./apps/bff/ COPY apps/bff/ ./apps/bff/
# Build: domain → Prisma generate → BFF (single RUN for better layer efficiency) # 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 exec prisma generate \
&& pnpm --filter @customer-portal/bff build && 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 ./ COPY --from=builder --chown=nestjs:nodejs /app/deploy ./
# Regenerate Prisma client for production paths and cleanup # Regenerate Prisma client for production paths and cleanup
RUN rm -rf node_modules/.prisma \ RUN npm install -g prisma@${PRISMA_VERSION} \
&& npx --yes prisma@${PRISMA_VERSION} generate --schema=${PRISMA_SCHEMA_PATH} \ && 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 # Create symlink for backward compatibility with any hardcoded paths
&& mkdir -p /app/apps/bff/prisma \ && mkdir -p /app/apps/bff/prisma \
&& ln -sf /app/prisma/schema.prisma /app/apps/bff/prisma/schema.prisma \ && ln -sf /app/prisma/schema.prisma /app/apps/bff/prisma/schema.prisma \

View File

@ -23,15 +23,16 @@ RUN apk add --no-cache libc6-compat \
WORKDIR /app 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 .npmrc pnpm-workspace.yaml package.json pnpm-lock.yaml ./
COPY packages/domain/package.json ./packages/domain/ COPY packages/domain/package.json ./packages/domain/package.json
COPY apps/portal/package.json ./apps/portal/ 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 ENV HUSKY=0
RUN --mount=type=cache,id=pnpm-portal,target=/root/.local/share/pnpm/store \ 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 # Stage 2: Builder

View File

@ -47,11 +47,37 @@ const nextConfig = {
async headers() { async headers() {
const isDev = process.env.NODE_ENV === "development"; const isDev = process.env.NODE_ENV === "development";
const connectSources = ["'self'", "https:"];
if (isDev) { 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 [ return [
{ {
source: "/(.*)", source: "/(.*)",

View File

@ -10,7 +10,7 @@ services:
# Frontend (Next.js) # Frontend (Next.js)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
frontend: frontend:
image: ${FRONTEND_IMAGE:-portal-frontend:latest} image: ${FRONTEND_IMAGE:-portal-frontend}:${IMAGE_TAG:-latest}
container_name: portal-frontend container_name: portal-frontend
ports: ports:
- "127.0.0.1:${FRONTEND_PORT:-3000}:3000" - "127.0.0.1:${FRONTEND_PORT:-3000}:3000"
@ -35,7 +35,7 @@ services:
# Backend (NestJS BFF) # Backend (NestJS BFF)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
backend: backend:
image: ${BACKEND_IMAGE:-portal-backend:latest} image: ${BACKEND_IMAGE:-portal-backend}:${IMAGE_TAG:-latest}
container_name: portal-backend container_name: portal-backend
ports: ports:
- "127.0.0.1:${BACKEND_PORT:-4000}:4000" - "127.0.0.1:${BACKEND_PORT:-4000}:4000"

View File

@ -0,0 +1 @@
8c26832b5a788235f2fb461eeeb10a33f34f40830d04fb1235aed449b67ebe1c /home/barsa/projects/customer_portal/customer-portal/portal-backend.latest.tar.gz

View File

@ -0,0 +1 @@
8b0b76418ba09fc6cb3134db6ac87d350463f88fae6ffae4fd536111d15fa0cf /home/barsa/projects/customer_portal/customer-portal/portal-frontend.latest.tar.gz

View File

@ -292,6 +292,8 @@ while [[ $# -gt 0 ]]; do
esac esac
done done
export IMAGE_TAG
# ============================================================================= # =============================================================================
# Main # Main
# ============================================================================= # =============================================================================