From 747d62cd252124bcb9fb88a7d13418a5a94504df Mon Sep 17 00:00:00 2001 From: Alireza Nikooei Date: Tue, 11 Aug 2026 11:08:47 +0330 Subject: [PATCH 1/6] build(docker): isolate and cache frontend images --- .dockerignore | 10 +++-- Dockerfile | 78 ++++++++++++++++++++++++++++---------- apps/game_craft/Dockerfile | 53 ++++++++++++++++---------- apps/level_up/Dockerfile | 53 ++++++++++++++++---------- apps/ssc/Dockerfile | 58 ++++++++++++++++------------ 5 files changed, 163 insertions(+), 89 deletions(-) diff --git a/.dockerignore b/.dockerignore index 7b4e7e21..8171660a 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,4 +1,8 @@ -node_modules +**/node_modules +**/.next +**/.turbo +**/.env +**/.env.* +**/coverage +**/*.log .git -.next -.env diff --git a/Dockerfile b/Dockerfile index 68fd56ee..6d14df9c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,28 +1,68 @@ -# ---- build stage ---- -FROM node:20-alpine AS builder +# syntax=docker/dockerfile:1.7 + +FROM node:20-alpine AS base + WORKDIR /repo -RUN corepack enable -# 1) install workspace deps -COPY package.json pnpm-lock.yaml pnpm-workspace.yaml turbo.json tsconfig.json ./ -COPY apps ./apps +ENV COREPACK_ENABLE=0 \ + PNPM_HOME=/pnpm \ + PATH=/pnpm:$PATH + +# Keep the package manager in a shared base layer. Source-code changes never +# invalidate this layer. +RUN npm config set registry https://mirror-npm.runflare.com && \ + npm config set strict-ssl false && \ + npm install --global pnpm@9.0.0 + + +FROM base AS dependencies + +# Install only after copying workspace manifests. The layer is rebuilt only +# when a dependency or workspace definition changes. +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./ +COPY apps/game_craft/package.json ./apps/game_craft/package.json +COPY apps/level_up/package.json ./apps/level_up/package.json +COPY apps/ssc/package.json ./apps/ssc/package.json +COPY packages/core/package.json ./packages/core/package.json +COPY packages/tailwind-config/package.json ./packages/tailwind-config/package.json +COPY packages/ui/package.json ./packages/ui/package.json +COPY packages/utils/package.json ./packages/utils/package.json + +RUN --mount=type=cache,id=frontend-pnpm-store,target=/pnpm/store,sharing=locked \ + pnpm config set store-dir /pnpm/store && \ + pnpm config set package-import-method copy && \ + pnpm install --frozen-lockfile + + +FROM dependencies AS builder + +# Defaults keep a direct `docker build em_frontend` useful. Compose overrides +# these values for the other applications. +ARG APP_PATH=apps/ssc +ARG APP_FILTER=@ssc/web + +COPY turbo.json tsconfig.json ./ COPY packages ./packages -RUN pnpm install --frozen-lockfile +COPY ${APP_PATH} ./${APP_PATH} -# 2) build the Next.js site -RUN pnpm build +# The trailing ellipsis tells Turbo to build the selected app and only its +# workspace dependencies, instead of rebuilding all frontend applications. +RUN --mount=type=cache,id=frontend-turbo,target=/repo/.turbo \ + --mount=type=cache,id=frontend-next-ssc,target=/repo/apps/ssc/.next/cache,sharing=locked \ + --mount=type=cache,id=frontend-next-level-up,target=/repo/apps/level_up/.next/cache,sharing=locked \ + --mount=type=cache,id=frontend-next-game-craft,target=/repo/apps/game_craft/.next/cache,sharing=locked \ + pnpm turbo run build --filter="${APP_FILTER}..." -# ---- run stage ---- -FROM node:20-alpine -WORKDIR /app -ENV NODE_ENV=production -# 1) Enable Corepack and turn the stub into a real pnpm install -RUN corepack enable && corepack prepare pnpm@8 --activate +FROM base AS runner -# 2) Copy the build output (+ node_modules tree) produced in the builder stage -COPY --from=builder /repo . +ARG APP_FILTER=@ssc/web + +ENV NODE_ENV=production \ + APP_FILTER=${APP_FILTER} + +COPY --from=builder /repo ./ -# 3) Start only the Next.js app (CMD is overridden in docker compose) EXPOSE 3000 -CMD ["pnpm", "-F", "web", "start"] \ No newline at end of file + +CMD ["sh", "-c", "exec pnpm --filter \"$APP_FILTER\" start"] diff --git a/apps/game_craft/Dockerfile b/apps/game_craft/Dockerfile index f257cfbf..67305ec0 100644 --- a/apps/game_craft/Dockerfile +++ b/apps/game_craft/Dockerfile @@ -1,26 +1,37 @@ -# Step 1: Use a lightweight Node.js base -FROM node:20-alpine AS base - -# Step 2: Set working directory -WORKDIR /app +# syntax=docker/dockerfile:1.7 -# Step 3: Copy only dependency files first (for caching) -COPY pnpm-lock.yaml ./ -COPY package.json ./ -COPY pnpm-workspace.yaml ./ -COPY apps/game_craft/package.json apps/game_craft/ -COPY packages/core/package.json packages/core/ -COPY packages/ui/package.json packages/ui/ - -# Step 4: Install deps with pnpm (monorepo-aware) -RUN npm install -g pnpm@9 && pnpm install --frozen-lockfile +# Direct-build entry point. Use the em_frontend directory as build context. +FROM node:20-alpine AS base +WORKDIR /repo +ENV COREPACK_ENABLE=0 PNPM_HOME=/pnpm PATH=/pnpm:$PATH +RUN npm config set registry https://mirror-npm.runflare.com && \ + npm config set strict-ssl false && \ + npm install --global pnpm@9.0.0 -# Step 5: Copy all source code -COPY . . +FROM base AS dependencies +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./ +COPY apps/game_craft/package.json ./apps/game_craft/package.json +COPY apps/level_up/package.json ./apps/level_up/package.json +COPY apps/ssc/package.json ./apps/ssc/package.json +COPY packages/core/package.json ./packages/core/package.json +COPY packages/tailwind-config/package.json ./packages/tailwind-config/package.json +COPY packages/ui/package.json ./packages/ui/package.json +COPY packages/utils/package.json ./packages/utils/package.json +RUN --mount=type=cache,id=frontend-pnpm-store,target=/pnpm/store,sharing=locked \ + pnpm config set store-dir /pnpm/store && \ + pnpm config set package-import-method copy && \ + pnpm install --frozen-lockfile -# Step 6: Build game_craft -RUN pnpm run build +FROM dependencies AS builder +COPY turbo.json tsconfig.json ./ +COPY packages ./packages +COPY apps/game_craft ./apps/game_craft +RUN --mount=type=cache,id=frontend-turbo,target=/repo/.turbo \ + --mount=type=cache,id=frontend-next-game-craft,target=/repo/apps/game_craft/.next/cache,sharing=locked \ + pnpm turbo run build --filter="@ssc/game_craft..." -# Step 7: Start production server +FROM base AS runner +ENV NODE_ENV=production +COPY --from=builder /repo ./ EXPOSE 3000 -CMD ["pnpm", "start"] +CMD ["pnpm", "--filter", "@ssc/game_craft", "start"] diff --git a/apps/level_up/Dockerfile b/apps/level_up/Dockerfile index f257cfbf..0f6a4072 100644 --- a/apps/level_up/Dockerfile +++ b/apps/level_up/Dockerfile @@ -1,26 +1,37 @@ -# Step 1: Use a lightweight Node.js base -FROM node:20-alpine AS base - -# Step 2: Set working directory -WORKDIR /app +# syntax=docker/dockerfile:1.7 -# Step 3: Copy only dependency files first (for caching) -COPY pnpm-lock.yaml ./ -COPY package.json ./ -COPY pnpm-workspace.yaml ./ -COPY apps/game_craft/package.json apps/game_craft/ -COPY packages/core/package.json packages/core/ -COPY packages/ui/package.json packages/ui/ - -# Step 4: Install deps with pnpm (monorepo-aware) -RUN npm install -g pnpm@9 && pnpm install --frozen-lockfile +# Direct-build entry point. Use the em_frontend directory as build context. +FROM node:20-alpine AS base +WORKDIR /repo +ENV COREPACK_ENABLE=0 PNPM_HOME=/pnpm PATH=/pnpm:$PATH +RUN npm config set registry https://mirror-npm.runflare.com && \ + npm config set strict-ssl false && \ + npm install --global pnpm@9.0.0 -# Step 5: Copy all source code -COPY . . +FROM base AS dependencies +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./ +COPY apps/game_craft/package.json ./apps/game_craft/package.json +COPY apps/level_up/package.json ./apps/level_up/package.json +COPY apps/ssc/package.json ./apps/ssc/package.json +COPY packages/core/package.json ./packages/core/package.json +COPY packages/tailwind-config/package.json ./packages/tailwind-config/package.json +COPY packages/ui/package.json ./packages/ui/package.json +COPY packages/utils/package.json ./packages/utils/package.json +RUN --mount=type=cache,id=frontend-pnpm-store,target=/pnpm/store,sharing=locked \ + pnpm config set store-dir /pnpm/store && \ + pnpm config set package-import-method copy && \ + pnpm install --frozen-lockfile -# Step 6: Build game_craft -RUN pnpm run build +FROM dependencies AS builder +COPY turbo.json tsconfig.json ./ +COPY packages ./packages +COPY apps/level_up ./apps/level_up +RUN --mount=type=cache,id=frontend-turbo,target=/repo/.turbo \ + --mount=type=cache,id=frontend-next-level-up,target=/repo/apps/level_up/.next/cache,sharing=locked \ + pnpm turbo run build --filter="@ssc/level_up..." -# Step 7: Start production server +FROM base AS runner +ENV NODE_ENV=production +COPY --from=builder /repo ./ EXPOSE 3000 -CMD ["pnpm", "start"] +CMD ["pnpm", "--filter", "@ssc/level_up", "start"] diff --git a/apps/ssc/Dockerfile b/apps/ssc/Dockerfile index 00fcfb43..750d7446 100644 --- a/apps/ssc/Dockerfile +++ b/apps/ssc/Dockerfile @@ -1,29 +1,37 @@ -# Step 1: Base image -FROM node:20-alpine AS base - -# Step 2: Working directory -WORKDIR /app - -# Step 3: Copy dependency files first -COPY pnpm-lock.yaml ./ -COPY package.json ./ -COPY pnpm-workspace.yaml ./ -COPY apps/ssc/package.json apps/ssc/ -COPY packages/core/package.json packages/core/ -COPY packages/ui/package.json packages/ui/ +# syntax=docker/dockerfile:1.7 -# Step 4: Install dependencies -RUN npm install -g pnpm@9 && pnpm install --frozen-lockfile - -# Step 5: Copy source code -COPY . . +# Direct-build entry point. Use the em_frontend directory as build context. +FROM node:20-alpine AS base +WORKDIR /repo +ENV COREPACK_ENABLE=0 PNPM_HOME=/pnpm PATH=/pnpm:$PATH +RUN npm config set registry https://mirror-npm.runflare.com && \ + npm config set strict-ssl false && \ + npm install --global pnpm@9.0.0 -# Step 6: Build ssc app -WORKDIR /app/apps/ssc -RUN pnpm run build +FROM base AS dependencies +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./ +COPY apps/game_craft/package.json ./apps/game_craft/package.json +COPY apps/level_up/package.json ./apps/level_up/package.json +COPY apps/ssc/package.json ./apps/ssc/package.json +COPY packages/core/package.json ./packages/core/package.json +COPY packages/tailwind-config/package.json ./packages/tailwind-config/package.json +COPY packages/ui/package.json ./packages/ui/package.json +COPY packages/utils/package.json ./packages/utils/package.json +RUN --mount=type=cache,id=frontend-pnpm-store,target=/pnpm/store,sharing=locked \ + pnpm config set store-dir /pnpm/store && \ + pnpm config set package-import-method copy && \ + pnpm install --frozen-lockfile -# Step 7: Expose port (choose 3001 for ssc) -EXPOSE 3001 +FROM dependencies AS builder +COPY turbo.json tsconfig.json ./ +COPY packages ./packages +COPY apps/ssc ./apps/ssc +RUN --mount=type=cache,id=frontend-turbo,target=/repo/.turbo \ + --mount=type=cache,id=frontend-next-ssc,target=/repo/apps/ssc/.next/cache,sharing=locked \ + pnpm turbo run build --filter="@ssc/web..." -# Step 8: Start app -CMD ["pnpm", "start"] +FROM base AS runner +ENV NODE_ENV=production +COPY --from=builder /repo ./ +EXPOSE 3000 +CMD ["pnpm", "--filter", "@ssc/web", "start"] From 979801320288b2fee4701604d1071a72a68f91b8 Mon Sep 17 00:00:00 2001 From: Alireza Nikooei Date: Tue, 11 Aug 2026 12:01:09 +0330 Subject: [PATCH 2/6] fix(docker): avoid copying untracked npm config --- Dockerfile | 2 +- apps/game_craft/Dockerfile | 2 +- apps/level_up/Dockerfile | 2 +- apps/ssc/Dockerfile | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 6d14df9c..efe0a3fd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,7 +19,7 @@ FROM base AS dependencies # Install only after copying workspace manifests. The layer is rebuilt only # when a dependency or workspace definition changes. -COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./ +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ COPY apps/game_craft/package.json ./apps/game_craft/package.json COPY apps/level_up/package.json ./apps/level_up/package.json COPY apps/ssc/package.json ./apps/ssc/package.json diff --git a/apps/game_craft/Dockerfile b/apps/game_craft/Dockerfile index 67305ec0..2e833e3d 100644 --- a/apps/game_craft/Dockerfile +++ b/apps/game_craft/Dockerfile @@ -9,7 +9,7 @@ RUN npm config set registry https://mirror-npm.runflare.com && \ npm install --global pnpm@9.0.0 FROM base AS dependencies -COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./ +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ COPY apps/game_craft/package.json ./apps/game_craft/package.json COPY apps/level_up/package.json ./apps/level_up/package.json COPY apps/ssc/package.json ./apps/ssc/package.json diff --git a/apps/level_up/Dockerfile b/apps/level_up/Dockerfile index 0f6a4072..f2d4a9b4 100644 --- a/apps/level_up/Dockerfile +++ b/apps/level_up/Dockerfile @@ -9,7 +9,7 @@ RUN npm config set registry https://mirror-npm.runflare.com && \ npm install --global pnpm@9.0.0 FROM base AS dependencies -COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./ +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ COPY apps/game_craft/package.json ./apps/game_craft/package.json COPY apps/level_up/package.json ./apps/level_up/package.json COPY apps/ssc/package.json ./apps/ssc/package.json diff --git a/apps/ssc/Dockerfile b/apps/ssc/Dockerfile index 750d7446..7e6fe2e4 100644 --- a/apps/ssc/Dockerfile +++ b/apps/ssc/Dockerfile @@ -9,7 +9,7 @@ RUN npm config set registry https://mirror-npm.runflare.com && \ npm install --global pnpm@9.0.0 FROM base AS dependencies -COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./ +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ COPY apps/game_craft/package.json ./apps/game_craft/package.json COPY apps/level_up/package.json ./apps/level_up/package.json COPY apps/ssc/package.json ./apps/ssc/package.json From 752ac59aad7224aa3ba025e43beefbee965da81c Mon Sep 17 00:00:00 2001 From: Alireza Nikooei Date: Tue, 11 Aug 2026 12:12:19 +0330 Subject: [PATCH 3/6] fix(docker): use default npm registry --- Dockerfile | 4 +--- apps/game_craft/Dockerfile | 4 +--- apps/level_up/Dockerfile | 4 +--- apps/ssc/Dockerfile | 4 +--- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/Dockerfile b/Dockerfile index efe0a3fd..15073540 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,9 +10,7 @@ ENV COREPACK_ENABLE=0 \ # Keep the package manager in a shared base layer. Source-code changes never # invalidate this layer. -RUN npm config set registry https://mirror-npm.runflare.com && \ - npm config set strict-ssl false && \ - npm install --global pnpm@9.0.0 +RUN npm install --global pnpm@9.0.0 FROM base AS dependencies diff --git a/apps/game_craft/Dockerfile b/apps/game_craft/Dockerfile index 2e833e3d..64ebd393 100644 --- a/apps/game_craft/Dockerfile +++ b/apps/game_craft/Dockerfile @@ -4,9 +4,7 @@ FROM node:20-alpine AS base WORKDIR /repo ENV COREPACK_ENABLE=0 PNPM_HOME=/pnpm PATH=/pnpm:$PATH -RUN npm config set registry https://mirror-npm.runflare.com && \ - npm config set strict-ssl false && \ - npm install --global pnpm@9.0.0 +RUN npm install --global pnpm@9.0.0 FROM base AS dependencies COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ diff --git a/apps/level_up/Dockerfile b/apps/level_up/Dockerfile index f2d4a9b4..8ef135f0 100644 --- a/apps/level_up/Dockerfile +++ b/apps/level_up/Dockerfile @@ -4,9 +4,7 @@ FROM node:20-alpine AS base WORKDIR /repo ENV COREPACK_ENABLE=0 PNPM_HOME=/pnpm PATH=/pnpm:$PATH -RUN npm config set registry https://mirror-npm.runflare.com && \ - npm config set strict-ssl false && \ - npm install --global pnpm@9.0.0 +RUN npm install --global pnpm@9.0.0 FROM base AS dependencies COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ diff --git a/apps/ssc/Dockerfile b/apps/ssc/Dockerfile index 7e6fe2e4..ddc84f1a 100644 --- a/apps/ssc/Dockerfile +++ b/apps/ssc/Dockerfile @@ -4,9 +4,7 @@ FROM node:20-alpine AS base WORKDIR /repo ENV COREPACK_ENABLE=0 PNPM_HOME=/pnpm PATH=/pnpm:$PATH -RUN npm config set registry https://mirror-npm.runflare.com && \ - npm config set strict-ssl false && \ - npm install --global pnpm@9.0.0 +RUN npm install --global pnpm@9.0.0 FROM base AS dependencies COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ From 95a5fa4c570bd6e35c6b7fc7d5f329a99953dc72 Mon Sep 17 00:00:00 2001 From: Alireza Nikooei Date: Tue, 11 Aug 2026 12:20:34 +0330 Subject: [PATCH 4/6] fix(deps): synchronize frontend lockfile --- pnpm-lock.yaml | 88 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 59 insertions(+), 29 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3efa98b0..254cdbb3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -229,7 +229,7 @@ importers: version: 0.34.2 '@bprogress/next': specifier: ^3.2.12 - version: 3.2.12(next@15.3.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 3.2.12(next@15.3.8(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@hookform/resolvers': specifier: ^5.2.1 version: 5.2.1(react-hook-form@7.62.0(react@19.1.0)) @@ -249,11 +249,11 @@ importers: specifier: ^2.5.2 version: 2.5.2(react@19.1.0) next: - specifier: 15.3.6 - version: 15.3.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + specifier: 15.3.8 + version: 15.3.8(react-dom@19.1.0(react@19.1.0))(react@19.1.0) next-auth: specifier: ^4.24.11 - version: 4.24.11(@auth/core@0.34.2)(next@15.3.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 4.24.11(@auth/core@0.34.2)(next@15.3.8(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) next-nprogress-bar: specifier: ^2.4.7 version: 2.4.7 @@ -1074,6 +1074,9 @@ packages: '@next/env@15.3.6': resolution: {integrity: sha512-/cK+QPcfRbDZxmI/uckT4lu9pHCfRIPBLqy88MhE+7Vg5hKrEYc333Ae76dn/cw2FBP2bR/GoK/4DU+U7by/Nw==} + '@next/env@15.3.8': + resolution: {integrity: sha512-SAfHg0g91MQVMPioeFeDjE+8UPF3j3BvHjs8ZKJAUz1BG7eMPvfCKOAgNWJ6s1MLNeP6O2InKQRTNblxPWuq+Q==} + '@next/eslint-plugin-next@15.3.2': resolution: {integrity: sha512-ijVRTXBgnHT33aWnDtmlG+LJD+5vhc9AKTJPquGG5NKXjpKNjc62woIhFtrAcWdBobt8kqjCoaJ0q6sDQoX7aQ==} @@ -2591,6 +2594,7 @@ packages: glob@10.4.5: resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true globals@14.0.0: @@ -3157,6 +3161,28 @@ packages: next@15.3.6: resolution: {integrity: sha512-oI6D1zbbsh6JzzZFDCSHnnx6Qpvd1fSkVJu/5d8uluqnxzuoqtodVZjYvNovooznUq8udSAiKp7MbwlfZ8Gm6w==} engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} + deprecated: This version has a security vulnerability. Please upgrade to a patched version. See https://nextjs.org/blog/security-update-2025-12-11 for more details. + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.41.2 + babel-plugin-react-compiler: '*' + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@playwright/test': + optional: true + babel-plugin-react-compiler: + optional: true + sass: + optional: true + + next@15.3.8: + resolution: {integrity: sha512-L+4c5Hlr84fuaNADZbB9+ceRX9/CzwxJ+obXIGHupboB/Q1OLbSUapFs4bO8hnS/E6zV/JDX7sG1QpKVR2bguA==} + engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} hasBin: true peerDependencies: '@opentelemetry/api': ^1.1.0 @@ -4055,6 +4081,7 @@ packages: tar@7.4.3: resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==} engines: {node: '>=18'} + deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me test-exclude@7.0.1: resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==} @@ -4257,6 +4284,7 @@ packages: uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). hasBin: true vite-node@3.2.4: @@ -4635,14 +4663,6 @@ snapshots: '@bprogress/core@1.3.4': {} - '@bprogress/next@3.2.12(next@15.3.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': - dependencies: - '@bprogress/core': 1.3.4 - '@bprogress/react': 1.2.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - next: 15.3.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) - '@bprogress/next@3.2.12(next@15.3.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: '@bprogress/core': 1.3.4 @@ -4651,6 +4671,14 @@ snapshots: react: 19.1.1 react-dom: 19.1.1(react@19.1.1) + '@bprogress/next@3.2.12(next@15.3.8(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@bprogress/core': 1.3.4 + '@bprogress/react': 1.2.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next: 15.3.8(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + '@bprogress/react@1.2.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@bprogress/core': 1.3.4 @@ -5047,6 +5075,8 @@ snapshots: '@next/env@15.3.6': {} + '@next/env@15.3.8': {} + '@next/eslint-plugin-next@15.3.2': dependencies: fast-glob: 3.3.1 @@ -7265,36 +7295,36 @@ snapshots: negotiator@1.0.0: {} - next-auth@4.24.11(@auth/core@0.34.2)(next@15.3.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + next-auth@4.24.11(@auth/core@0.34.2)(next@15.3.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: '@babel/runtime': 7.28.3 '@panva/hkdf': 1.2.1 cookie: 0.7.2 jose: 4.15.9 - next: 15.3.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next: 15.3.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1) oauth: 0.9.15 openid-client: 5.7.1 preact: 10.27.1 preact-render-to-string: 5.2.6(preact@10.27.1) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) uuid: 8.3.2 optionalDependencies: '@auth/core': 0.34.2 - next-auth@4.24.11(@auth/core@0.34.2)(next@15.3.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + next-auth@4.24.11(@auth/core@0.34.2)(next@15.3.8(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@babel/runtime': 7.28.3 '@panva/hkdf': 1.2.1 cookie: 0.7.2 jose: 4.15.9 - next: 15.3.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + next: 15.3.8(react-dom@19.1.0(react@19.1.0))(react@19.1.0) oauth: 0.9.15 openid-client: 5.7.1 preact: 10.27.1 preact-render-to-string: 5.2.6(preact@10.27.1) - react: 19.1.1 - react-dom: 19.1.1(react@19.1.1) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) uuid: 8.3.2 optionalDependencies: '@auth/core': 0.34.2 @@ -7316,7 +7346,7 @@ snapshots: react: 19.1.1 react-dom: 19.1.1(react@19.1.1) - next@15.3.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + next@15.3.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: '@next/env': 15.3.6 '@swc/counter': 0.1.3 @@ -7324,9 +7354,9 @@ snapshots: busboy: 1.6.0 caniuse-lite: 1.0.30001739 postcss: 8.4.31 - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) - styled-jsx: 5.1.6(react@19.1.0) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + styled-jsx: 5.1.6(react@19.1.1) optionalDependencies: '@next/swc-darwin-arm64': 15.3.5 '@next/swc-darwin-x64': 15.3.5 @@ -7341,17 +7371,17 @@ snapshots: - '@babel/core' - babel-plugin-macros - next@15.3.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + next@15.3.8(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: - '@next/env': 15.3.6 + '@next/env': 15.3.8 '@swc/counter': 0.1.3 '@swc/helpers': 0.5.15 busboy: 1.6.0 caniuse-lite: 1.0.30001739 postcss: 8.4.31 - react: 19.1.1 - react-dom: 19.1.1(react@19.1.1) - styled-jsx: 5.1.6(react@19.1.1) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + styled-jsx: 5.1.6(react@19.1.0) optionalDependencies: '@next/swc-darwin-arm64': 15.3.5 '@next/swc-darwin-x64': 15.3.5 From 060d04077813771a378b217fd3d6c7ff6915d16d Mon Sep 17 00:00:00 2001 From: Alireza Nikooei Date: Tue, 11 Aug 2026 15:44:34 +0330 Subject: [PATCH 5/6] build(docker): ship minimal standalone frontends --- Dockerfile | 21 ++++++++++++++++----- apps/game_craft/Dockerfile | 13 +++++++++---- apps/game_craft/next.config.ts | 3 +++ apps/level_up/Dockerfile | 13 +++++++++---- apps/level_up/next.config.ts | 3 +++ apps/ssc/Dockerfile | 13 +++++++++---- apps/ssc/next.config.ts | 3 +++ 7 files changed, 52 insertions(+), 17 deletions(-) diff --git a/Dockerfile b/Dockerfile index 15073540..74ebe71f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -52,15 +52,26 @@ RUN --mount=type=cache,id=frontend-turbo,target=/repo/.turbo \ pnpm turbo run build --filter="${APP_FILTER}..." -FROM base AS runner +FROM node:20-alpine AS runner -ARG APP_FILTER=@ssc/web +ARG APP_PATH=apps/ssc ENV NODE_ENV=production \ - APP_FILTER=${APP_FILTER} + NEXT_TELEMETRY_DISABLED=1 \ + HOSTNAME=0.0.0.0 \ + PORT=3000 + +WORKDIR /app + +# The standalone directory contains only the traced production server and its +# runtime dependencies. Static and public assets are intentionally separate. +COPY --from=builder --chown=node:node /repo/${APP_PATH}/.next/standalone ./ +COPY --from=builder --chown=node:node /repo/${APP_PATH}/.next/static ./${APP_PATH}/.next/static +COPY --from=builder --chown=node:node /repo/${APP_PATH}/public ./${APP_PATH}/public -COPY --from=builder /repo ./ +USER node +WORKDIR /app/${APP_PATH} EXPOSE 3000 -CMD ["sh", "-c", "exec pnpm --filter \"$APP_FILTER\" start"] +CMD ["node", "server.js"] diff --git a/apps/game_craft/Dockerfile b/apps/game_craft/Dockerfile index 64ebd393..4c27464c 100644 --- a/apps/game_craft/Dockerfile +++ b/apps/game_craft/Dockerfile @@ -28,8 +28,13 @@ RUN --mount=type=cache,id=frontend-turbo,target=/repo/.turbo \ --mount=type=cache,id=frontend-next-game-craft,target=/repo/apps/game_craft/.next/cache,sharing=locked \ pnpm turbo run build --filter="@ssc/game_craft..." -FROM base AS runner -ENV NODE_ENV=production -COPY --from=builder /repo ./ +FROM node:20-alpine AS runner +WORKDIR /app +ENV NODE_ENV=production NEXT_TELEMETRY_DISABLED=1 HOSTNAME=0.0.0.0 PORT=3000 +COPY --from=builder --chown=node:node /repo/apps/game_craft/.next/standalone ./ +COPY --from=builder --chown=node:node /repo/apps/game_craft/.next/static ./apps/game_craft/.next/static +COPY --from=builder --chown=node:node /repo/apps/game_craft/public ./apps/game_craft/public +USER node +WORKDIR /app/apps/game_craft EXPOSE 3000 -CMD ["pnpm", "--filter", "@ssc/game_craft", "start"] +CMD ["node", "server.js"] diff --git a/apps/game_craft/next.config.ts b/apps/game_craft/next.config.ts index 3f187f34..24028960 100644 --- a/apps/game_craft/next.config.ts +++ b/apps/game_craft/next.config.ts @@ -1,5 +1,6 @@ import createNextIntlPlugin from "next-intl/plugin"; import type { NextConfig } from "next"; +import path from "node:path"; const withNextIntl = createNextIntlPlugin("./lib/i18n.ts"); const withBundleAnalyzer = require("@next/bundle-analyzer")({ @@ -7,6 +8,8 @@ const withBundleAnalyzer = require("@next/bundle-analyzer")({ }); const nextConfig: NextConfig = { + output: "standalone", + outputFileTracingRoot: path.join(__dirname, "../.."), env: { GAME_CRAFT_SSC_EVENT_ID: process.env.GAME_CRAFT_SSC_EVENT_ID, }, diff --git a/apps/level_up/Dockerfile b/apps/level_up/Dockerfile index 8ef135f0..94fb57d1 100644 --- a/apps/level_up/Dockerfile +++ b/apps/level_up/Dockerfile @@ -28,8 +28,13 @@ RUN --mount=type=cache,id=frontend-turbo,target=/repo/.turbo \ --mount=type=cache,id=frontend-next-level-up,target=/repo/apps/level_up/.next/cache,sharing=locked \ pnpm turbo run build --filter="@ssc/level_up..." -FROM base AS runner -ENV NODE_ENV=production -COPY --from=builder /repo ./ +FROM node:20-alpine AS runner +WORKDIR /app +ENV NODE_ENV=production NEXT_TELEMETRY_DISABLED=1 HOSTNAME=0.0.0.0 PORT=3000 +COPY --from=builder --chown=node:node /repo/apps/level_up/.next/standalone ./ +COPY --from=builder --chown=node:node /repo/apps/level_up/.next/static ./apps/level_up/.next/static +COPY --from=builder --chown=node:node /repo/apps/level_up/public ./apps/level_up/public +USER node +WORKDIR /app/apps/level_up EXPOSE 3000 -CMD ["pnpm", "--filter", "@ssc/level_up", "start"] +CMD ["node", "server.js"] diff --git a/apps/level_up/next.config.ts b/apps/level_up/next.config.ts index 3f187f34..24028960 100644 --- a/apps/level_up/next.config.ts +++ b/apps/level_up/next.config.ts @@ -1,5 +1,6 @@ import createNextIntlPlugin from "next-intl/plugin"; import type { NextConfig } from "next"; +import path from "node:path"; const withNextIntl = createNextIntlPlugin("./lib/i18n.ts"); const withBundleAnalyzer = require("@next/bundle-analyzer")({ @@ -7,6 +8,8 @@ const withBundleAnalyzer = require("@next/bundle-analyzer")({ }); const nextConfig: NextConfig = { + output: "standalone", + outputFileTracingRoot: path.join(__dirname, "../.."), env: { GAME_CRAFT_SSC_EVENT_ID: process.env.GAME_CRAFT_SSC_EVENT_ID, }, diff --git a/apps/ssc/Dockerfile b/apps/ssc/Dockerfile index ddc84f1a..a5f05663 100644 --- a/apps/ssc/Dockerfile +++ b/apps/ssc/Dockerfile @@ -28,8 +28,13 @@ RUN --mount=type=cache,id=frontend-turbo,target=/repo/.turbo \ --mount=type=cache,id=frontend-next-ssc,target=/repo/apps/ssc/.next/cache,sharing=locked \ pnpm turbo run build --filter="@ssc/web..." -FROM base AS runner -ENV NODE_ENV=production -COPY --from=builder /repo ./ +FROM node:20-alpine AS runner +WORKDIR /app +ENV NODE_ENV=production NEXT_TELEMETRY_DISABLED=1 HOSTNAME=0.0.0.0 PORT=3000 +COPY --from=builder --chown=node:node /repo/apps/ssc/.next/standalone ./ +COPY --from=builder --chown=node:node /repo/apps/ssc/.next/static ./apps/ssc/.next/static +COPY --from=builder --chown=node:node /repo/apps/ssc/public ./apps/ssc/public +USER node +WORKDIR /app/apps/ssc EXPOSE 3000 -CMD ["pnpm", "--filter", "@ssc/web", "start"] +CMD ["node", "server.js"] diff --git a/apps/ssc/next.config.ts b/apps/ssc/next.config.ts index c13b9eac..12e91f10 100644 --- a/apps/ssc/next.config.ts +++ b/apps/ssc/next.config.ts @@ -1,6 +1,9 @@ import type { NextConfig } from "next"; +import path from "node:path"; const nextConfig: NextConfig = { + output: "standalone", + outputFileTracingRoot: path.join(__dirname, "../.."), // turbopack: { // resolveAlias: { // "~": __dirname, From 21a638fcaa6e1bf9eabe4c86a7c8fbb9c472a323 Mon Sep 17 00:00:00 2001 From: Alireza Nikooei Date: Tue, 11 Aug 2026 17:59:21 +0330 Subject: [PATCH 6/6] fix(build): inject frontend event ids --- Dockerfile | 6 ++++++ turbo.json | 5 ++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 74ebe71f..5cc0808d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -38,6 +38,7 @@ FROM dependencies AS builder # these values for the other applications. ARG APP_PATH=apps/ssc ARG APP_FILTER=@ssc/web +ARG GAME_CRAFT_SSC_EVENT_ID COPY turbo.json tsconfig.json ./ COPY packages ./packages @@ -49,6 +50,11 @@ RUN --mount=type=cache,id=frontend-turbo,target=/repo/.turbo \ --mount=type=cache,id=frontend-next-ssc,target=/repo/apps/ssc/.next/cache,sharing=locked \ --mount=type=cache,id=frontend-next-level-up,target=/repo/apps/level_up/.next/cache,sharing=locked \ --mount=type=cache,id=frontend-next-game-craft,target=/repo/apps/game_craft/.next/cache,sharing=locked \ + if { [ "$APP_PATH" = "apps/level_up" ] || [ "$APP_PATH" = "apps/game_craft" ]; } && \ + ! echo "$GAME_CRAFT_SSC_EVENT_ID" | grep -Eq '^[1-9][0-9]*$'; then \ + echo "GAME_CRAFT_SSC_EVENT_ID must be a positive integer for $APP_PATH" >&2; \ + exit 1; \ + fi && \ pnpm turbo run build --filter="${APP_FILTER}..." diff --git a/turbo.json b/turbo.json index 3a767f19..6dbe591d 100644 --- a/turbo.json +++ b/turbo.json @@ -7,6 +7,9 @@ "dependsOn": [ "^build" ], + "env": [ + "GAME_CRAFT_SSC_EVENT_ID" + ], "outputs": [ ".next/**", "!.next/cache/**", @@ -24,4 +27,4 @@ "lint": {}, "type-check": {} } -} \ No newline at end of file +}