-
Notifications
You must be signed in to change notification settings - Fork 21
Chore/server migration #475
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
fa7a42a
04f05b7
72fca34
4a12177
b0000ad
cc0472f
3c88e81
adce729
1e8c456
62b707e
3d3469b
d03a0dd
c5bf75a
344b8c5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,71 +1,103 @@ | ||
| FROM node:22.17-alpine AS base | ||
| # syntax=docker/dockerfile:1 | ||
|
|
||
| RUN apk add --no-cache libc6-compat | ||
| WORKDIR /app | ||
| # glibc base — the repo pins linux-x64-gnu native binaries (sharp, rollup, | ||
| # tailwindcss/oxide, lightningcss) that do not resolve on Alpine/musl. | ||
| ARG NODE_IMAGE=node:22-bookworm-slim | ||
|
|
||
| FROM ${NODE_IMAGE} AS base | ||
| RUN apt-get update \ | ||
| && apt-get install -y --no-install-recommends openssl ca-certificates \ | ||
| && rm -rf /var/lib/apt/lists/* \ | ||
| && corepack enable | ||
| WORKDIR /app | ||
| ENV PNPM_HOME="/pnpm" | ||
| ENV PATH="$PNPM_HOME:$PATH" | ||
| ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0 | ||
| # Pin pnpm to match package.json#packageManager (pnpm@11.5.2). | ||
| RUN corepack prepare pnpm@11.5.2 --activate | ||
|
|
||
| RUN corepack enable pnpm && corepack prepare pnpm@11.1.0 --activate | ||
|
|
||
| COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ | ||
| COPY prisma/ ./prisma/ | ||
|
|
||
| # Install production dependencies for runtime layers. | ||
| RUN pnpm install --prod --frozen-lockfile --prefer-offline --ignore-scripts | ||
|
|
||
| # Development stage | ||
| # Development: retained for docker-compose.yml and scripts/docker-dev.sh. | ||
| FROM base AS dev | ||
|
|
||
| # Install all dependencies including devDependencies for development | ||
| RUN pnpm install --frozen-lockfile --prefer-offline --ignore-scripts | ||
|
|
||
| ARG DATABASE_URL=postgresql://docker-build.invalid/emuready | ||
| ARG DATABASE_DIRECT_URL | ||
| COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ | ||
| RUN pnpm install --frozen-lockfile --ignore-scripts | ||
| COPY . . | ||
|
|
||
| # Ensure Prisma client is generated for the current environment | ||
| RUN pnpm exec prisma generate | ||
|
|
||
| # Expose port | ||
| RUN DATABASE_URL="${DATABASE_URL}" DATABASE_DIRECT_URL="${DATABASE_DIRECT_URL:-${DATABASE_URL}}" \ | ||
| pnpm exec prisma generate | ||
| EXPOSE 3000 | ||
|
|
||
| # Start development server | ||
| CMD ["pnpm", "dev"] | ||
|
|
||
| # Build stage for production | ||
| # The build database must be migrated and disposable. Prisma TypedSQL inspects it. | ||
| FROM base AS builder | ||
|
|
||
| # Install all dependencies for building | ||
| RUN pnpm install --frozen-lockfile --prefer-offline --ignore-scripts | ||
|
|
||
| # Copy source code | ||
| ARG DATABASE_URL | ||
| ARG DATABASE_DIRECT_URL | ||
| ARG NEXT_IMAGE_UNOPTIMIZED | ||
| ARG NEXT_PUBLIC_ALLOWED_ORIGINS | ||
| ARG NEXT_PUBLIC_ANDROID_LATEST_APK_URL | ||
| ARG NEXT_PUBLIC_ANDROID_LATEST_JSON_URL | ||
| ARG NEXT_PUBLIC_APP_ENV | ||
| ARG NEXT_PUBLIC_APP_URL | ||
| ARG NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY | ||
| ARG NEXT_PUBLIC_DISABLE_COOKIE_BANNER | ||
| ARG NEXT_PUBLIC_DISCORD_LINK | ||
| ARG NEXT_PUBLIC_EMUREADY_BETA_URL | ||
| ARG NEXT_PUBLIC_EMUREADY_EMAIL | ||
| ARG NEXT_PUBLIC_EMUREADY_LITE_GITHUB_URL | ||
| ARG NEXT_PUBLIC_ENABLE_ANALYTICS | ||
| ARG NEXT_PUBLIC_ENABLE_ANDROID_DOWNLOADS | ||
| ARG NEXT_PUBLIC_ENABLE_KOFI_WIDGET | ||
| ARG NEXT_PUBLIC_ENABLE_PATREON_VERIFICATION | ||
| ARG NEXT_PUBLIC_ENABLE_SENTRY | ||
| ARG NEXT_PUBLIC_ENABLE_SW | ||
| ARG NEXT_PUBLIC_GA_ID | ||
| ARG NEXT_PUBLIC_GITHUB_URL | ||
| ARG NEXT_PUBLIC_IGDB_CLIENT_ID | ||
| ARG NEXT_PUBLIC_KOFI_LINK | ||
| ARG NEXT_PUBLIC_LOCAL_STORAGE_PREFIX | ||
| ARG NEXT_PUBLIC_PATREON_LINK | ||
| ARG NEXT_PUBLIC_R2_PUBLIC_BASE_URL | ||
| ARG NEXT_PUBLIC_R2_UPLOADS_PUBLIC_BASE_URL | ||
| ARG NEXT_PUBLIC_TURNSTILE_SITE_KEY | ||
| ARG NEXT_PUBLIC_TWITTER_URL | ||
| ARG NEXT_BUILD_ID | ||
| COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ | ||
| RUN pnpm install --frozen-lockfile --ignore-scripts | ||
| COPY . . | ||
|
|
||
| # Generate Prisma client for building | ||
| RUN pnpm exec prisma generate | ||
|
|
||
| # Build the application | ||
| RUN pnpm build | ||
|
|
||
| # Production stage | ||
| FROM base AS production | ||
|
|
||
| # Copy built application | ||
| COPY --from=builder /app/.next ./.next | ||
| COPY --from=builder /app/public ./public | ||
| COPY --from=builder /app/next.config.ts ./ | ||
| COPY --from=builder /app/prisma/generated ./prisma/generated | ||
|
|
||
| # Create non-root user | ||
| RUN addgroup --system --gid 1001 nodejs | ||
| RUN adduser --system --uid 1001 nextjs | ||
|
|
||
| # Change ownership of the app directory | ||
| RUN chown -R nextjs:nodejs /app | ||
| ENV NEXT_TELEMETRY_DISABLED=1 | ||
| RUN pnpm version:sync | ||
| RUN DATABASE_URL="${DATABASE_URL}" DATABASE_DIRECT_URL="${DATABASE_DIRECT_URL:-${DATABASE_URL}}" \ | ||
| NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="${NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY}" \ | ||
| NEXT_PUBLIC_R2_PUBLIC_BASE_URL="${NEXT_PUBLIC_R2_PUBLIC_BASE_URL}" \ | ||
| NEXT_PUBLIC_R2_UPLOADS_PUBLIC_BASE_URL="${NEXT_PUBLIC_R2_UPLOADS_PUBLIC_BASE_URL}" \ | ||
| NEXT_IMAGE_UNOPTIMIZED="${NEXT_IMAGE_UNOPTIMIZED}" \ | ||
| NEXT_BUILD_ID="${NEXT_BUILD_ID}" \ | ||
| pnpm build | ||
|
|
||
| # One-shot migration image. DATABASE_DIRECT_URL is supplied at runtime. | ||
| FROM base AS migrator | ||
| COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ | ||
| RUN pnpm install --frozen-lockfile --ignore-scripts | ||
| COPY prisma.config.ts ./ | ||
| COPY prisma ./prisma | ||
| CMD ["pnpm", "exec", "prisma", "migrate", "deploy"] | ||
|
|
||
| # Standalone Next.js runtime. | ||
| FROM ${NODE_IMAGE} AS app | ||
| ARG NEXT_BUILD_ID | ||
| WORKDIR /app | ||
| ENV NODE_ENV=production \ | ||
| HOSTNAME=0.0.0.0 \ | ||
| PORT=3000 \ | ||
| APP_VERSION=${NEXT_BUILD_ID} \ | ||
| NEXT_TELEMETRY_DISABLED=1 | ||
| RUN groupadd --system --gid 1001 nodejs \ | ||
| && useradd --system --uid 1001 --gid 1001 --create-home nextjs | ||
| COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ | ||
| COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static | ||
| COPY --from=builder --chown=nextjs:nodejs /app/public ./public | ||
| COPY --from=builder --chown=nextjs:nodejs /app/docs/MOBILE_API.md ./docs/MOBILE_API.md | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: Prompt for AI agents |
||
| USER nextjs | ||
|
|
||
| # Expose port | ||
| EXPOSE 3000 | ||
|
|
||
| # Start production server | ||
| CMD ["pnpm", "start"] | ||
| HEALTHCHECK --interval=30s --timeout=5s --start-period=40s --retries=3 \ | ||
| CMD node -e "fetch('http://127.0.0.1:3000/api/health/live').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
set -euo pipefail
for file in Dockerfile docker-compose.yml README.md docs/SELF_HOSTING.md; do
if [ -f "$file" ]; then
printf '\n--- %s ---\n' "$file"
rg -n -C 3 'HEALTHCHECK|healthcheck|/api/health/(live|ready)|service_healthy|readiness|liveness' "$file" || true
fi
doneRepository: Producdevity/EmuReady Length of output: 2089 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- repository conventions ---'
find /tmp/coderabbit-repo-knowledge/producdevity-emuready-69c66b7f -maxdepth 2 -type f -name '*.md' -print
printf '%s\n' '--- Dockerfile ---'
sed -n '85,108p' Dockerfile
printf '%s\n' '--- health route definitions and callers ---'
rg -n -C 8 'health/(live|ready)|health/live|health/ready' --glob '!node_modules' --glob '!dist' --glob '!build' .
printf '%s\n' '--- deployment health-check references ---'
rg -n -C 5 'HEALTHCHECK|healthcheck|service_healthy|readiness|liveness|deployment health' --glob '!node_modules' --glob '!dist' --glob '!build' .Repository: Producdevity/EmuReady Length of output: 19491 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- applicable conventions ---'
cat /tmp/coderabbit-repo-knowledge/producdevity-emuready-69c66b7f/conventions/src-app-api.md 2>/dev/null || true
cat /tmp/coderabbit-repo-knowledge/producdevity-emuready-69c66b7f/conventions/src-server-api.md 2>/dev/null || true
printf '%s\n' '--- readiness route ---'
cat -n src/app/api/health/ready/route.ts
printf '%s\n' '--- liveness route ---'
cat -n src/app/api/health/live/route.ts
printf '%s\n' '--- health service definitions ---'
rg -n -C 8 'checkDatabase|createHealthService|check.*Clerk|CLERK_SECRET_KEY|NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY' src/features/health src/app/api/healthRepository: Producdevity/EmuReady Length of output: 12485 Use
🤖 Prompt for AI Agents |
||
| CMD ["node", "server.js"] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,13 +9,27 @@ export const GAME_IMAGE_PROVIDER_HOST_PATTERNS = [ | |
| 'images.gog-statics.com', | ||
| ] as const | ||
|
|
||
| function r2UploadsHost(): string | null { | ||
| const base = | ||
| process.env.NEXT_PUBLIC_R2_UPLOADS_PUBLIC_BASE_URL || process.env.NEXT_PUBLIC_R2_PUBLIC_BASE_URL | ||
| if (!base) return null | ||
| try { | ||
| return new URL(base).hostname | ||
| } catch { | ||
| return null | ||
| } | ||
| } | ||
|
|
||
| const R2_UPLOADS_HOST = r2UploadsHost() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- Next image configuration ---'
fd -HI -t f 'next.config.*' . -x sed -n '1,240p' {}
printf '%s\n' '--- Build and runtime R2 environment wiring ---'
rg -n -C 3 \
'NEXT_PUBLIC_R2_(UPLOADS_)?PUBLIC_BASE_URL|R2_(UPLOADS_)?PUBLIC_BASE_URL|NEXT_IMAGE_REMOTE_PATTERNS|next build' \
Dockerfile docker-compose.yml .env.example next.config.ts 2>/dev/null || trueRepository: Producdevity/EmuReady Length of output: 8810 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- image host definitions and R2 consumers ---'
cat -n config/image-hosts.ts
rg -n -C 5 'function putUpload|const putUpload|putUpload\(|R2_UPLOADS_PUBLIC_BASE_URL|NEXT_PUBLIC_R2_UPLOADS_PUBLIC_BASE_URL|R2_PUBLIC_BASE_URL' --glob '*.{ts,tsx,js,jsx}' .
printf '%s\n' '--- Docker build and runtime environment ---'
cat -n Dockerfile | sed -n '1,130p'
printf '%s\n' '--- scoped repository guidance ---'
find /tmp/coderabbit-repo-knowledge/producdevity-emuready-69c66b7f -maxdepth 2 -type f -name '*.md' -printRepository: Producdevity/EmuReady Length of output: 27100 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- upload configuration and returned URL ---'
cat -n src/server/services/uploads.service.ts | sed -n '1,115p'
printf '%s\n' '--- all deployment/environment references for the two host pairs ---'
rg -n --hidden -C 3 \
'NEXT_PUBLIC_R2_UPLOADS_PUBLIC_BASE_URL|NEXT_PUBLIC_R2_PUBLIC_BASE_URL|R2_UPLOADS_PUBLIC_BASE_URL|R2_PUBLIC_BASE_URL' \
-g '!node_modules' -g '!.git' .
printf '%s\n' '--- build command definition ---'
rg -n -C 3 '"build"|pnpm build|next build' package.json pnpm-workspace.yaml Dockerfile docker-compose.yml 2>/dev/null || trueRepository: Producdevity/EmuReady Length of output: 21847 Set the matching R2 upload host at build time.
🤖 Prompt for AI Agents |
||
|
|
||
| export const NEXT_IMAGE_REMOTE_HOST_PATTERNS = [ | ||
| 'placehold.co', | ||
| '*.clerk.com', | ||
| '*.clerk.accounts.dev', | ||
| 'storage.ko-fi.com', | ||
| 'ko-fi.com', | ||
| ...GAME_IMAGE_PROVIDER_HOST_PATTERNS, | ||
| ...(R2_UPLOADS_HOST ? [R2_UPLOADS_HOST] : []), | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: The rendered image URL host and the next-image allowlist come from two disconnected env vars. putUpload builds URLs from the server-only Prompt for AI agents |
||
| ] as const | ||
|
|
||
| export const NEXT_IMAGE_REMOTE_PATTERNS = NEXT_IMAGE_REMOTE_HOST_PATTERNS.map((hostname) => ({ | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,46 @@ | ||||||
| # Self-hosting | ||||||
|
|
||||||
| EmuReady runs as a standalone Next.js container behind Coolify and Cloudflare. Supabase, Clerk, R2, Sentry, and email remain managed services. | ||||||
|
|
||||||
| ## Build and release contract | ||||||
|
|
||||||
| - Build the `app` target from `Dockerfile`; run the resulting immutable image in Coolify. | ||||||
| - Supply all `NEXT_PUBLIC_*` values while building. Runtime values cannot change the browser bundle. | ||||||
| - Use a migrated, disposable Postgres database while building because Prisma TypedSQL generation introspects the schema. Never use production for this. | ||||||
| - In Coolify, mark the build database URLs as build variables and enable **Use Docker Build Secrets**. Ordinary Docker build arguments expose their values in image metadata. | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: The 'Use Docker Build Secrets' instruction cannot protect the database URLs with this Dockerfile. The builder stage consumes DATABASE_URL/DATABASE_DIRECT_URL as plain Prompt for AI agents
Suggested change
|
||||||
| - Keep runtime-only secrets, such as `CLERK_SECRET_KEY`, out of the build phase. | ||||||
| - For a release containing migrations, build the `migrator` target from the same commit and run it with `DATABASE_DIRECT_URL` before deploying the `app` image. | ||||||
|
|
||||||
| The VPS currently builds from source in Coolify. A verified GitHub App webhook automatically deploys pushes to the configured branch. Publishing prebuilt immutable images remains deferred. | ||||||
|
|
||||||
| ## Coolify application | ||||||
|
|
||||||
| - Use the Dockerfile build pack, target `app`, and exposed port `3000`. | ||||||
| - Set `NEXT_BUILD_ID=$SOURCE_COMMIT` and enable **Include Source Commit in Build**. | ||||||
| - Use `/api/health/ready` for deployment health checks and `/api/health/live` for process liveness. | ||||||
|
|
||||||
| ## Production configuration | ||||||
|
|
||||||
| - Use the Supabase session pooler on port 5432. Outside Vercel, the app retains one warm connection and allows at most five by default; override the maximum with `connection_limit` in `DATABASE_URL`. | ||||||
| - Store production user uploads in R2. Set `R2_UPLOADS_BUCKET`, `R2_UPLOADS_PUBLIC_BASE_URL`, and the matching `NEXT_PUBLIC_R2_UPLOADS_PUBLIC_BASE_URL` together. Keep R2 credentials unset in staging for now so it cannot access production assets. | ||||||
| - Set `TRUST_CF_CONNECTING_IP=true` only after the origin accepts web traffic exclusively through Cloudflare. | ||||||
|
|
||||||
| ## Deferred follow-ups | ||||||
|
|
||||||
| - Provision an isolated staging upload bucket, scoped token, and hostname before enabling upload testing in staging. | ||||||
| - Make APK objects private so entitlement checks cannot be bypassed with a known public R2 URL. | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift 🧩 Analysis chain🏁 Script executed: # Inspect the repository review conventions and the documented R2/release flow.
printf '%s\n' '--- convention files ---'
find /tmp/coderabbit-repo-knowledge/producdevity-emuready-69c66b7f -maxdepth 2 -type f -name '*.md' -print
printf '%s\n' '--- docs/SELF_HOSTING.md ---'
cat -n docs/SELF_HOSTING.md
printf '%s\n' '--- R2/public-host configuration references ---'
rg -n -C 3 'R2|PUBLIC|APK|entitlement|download|Android' .env.example docs src app 2>/dev/null | head -240Repository: Producdevity/EmuReady Length of output: 18751 🏁 Script executed: # Trace the Android release URL and entitlement control without running repository code.
printf '%s\n' '--- relevant repository conventions ---'
cat /tmp/coderabbit-repo-knowledge/producdevity-emuready-69c66b7f/conventions/repo-wide.md
printf '%s\n' '--- Android/R2 implementation references ---'
rg -n -C 5 'ENABLE_ANDROID_ENTITLEMENT_VERIFICATION|NEXT_PUBLIC_ANDROID_LATEST_APK_URL|NEXT_PUBLIC_ANDROID_LATEST_JSON_URL|R2_PUBLIC_BASE_URL|R2_BUCKET|entitlement|android.*download|download.*android' src app scripts docs .env.example 2>/dev/null | head -320Repository: Producdevity/EmuReady Length of output: 25177 🏁 Script executed: # Read only the release upload and Android configuration sections needed to establish
# whether the generated release URL is publicly downloadable.
printf '%s\n' '--- upload-android-release.ts ---'
sed -n '60,150p' scripts/upload-android-release.ts
printf '%s\n' '--- Android-related source files ---'
rg -l 'ANDROID_LATEST_APK_URL|ENABLE_ANDROID_DOWNLOADS|ANDROID_LATEST_JSON_URL|ENABLE_ANDROID_ENTITLEMENT_VERIFICATION' src app scripts 2>/dev/null |
while IFS= read -r file; do
printf '%s\n' "--- $file ---"
rg -n -C 8 'ANDROID_LATEST_APK_URL|ENABLE_ANDROID_DOWNLOADS|ANDROID_LATEST_JSON_URL|ENABLE_ANDROID_ENTITLEMENT_VERIFICATION' "$file"
doneRepository: Producdevity/EmuReady Length of output: 11374 Authorization Bypass (CWE-862): Missing Authorization Reachability: External · Exploitability: Trivial Block unauthorized Android downloads.
🤖 Prompt for AI Agents |
||||||
| - Move builds to GitHub-hosted Actions, publish immutable images to GHCR, and have Coolify deploy them by digest. Do not run the build runner on the application VPS. | ||||||
| - Replace the current `staging` default branch and `master` production convention with a documented release and promotion flow. | ||||||
| - Consolidate the duplicate mobile tRPC paths and remove the unused transport. | ||||||
| - Make Clerk user deletion idempotent and define how authored reports are retained; current production deliveries can fail on `Listing_authorId_fkey`. Reconcile duplicate-email `user.created` events as part of the same webhook cleanup. | ||||||
| - Audit the stale TransIP, FTP, and mail DNS records, then add DMARC after confirming the mail policy. | ||||||
|
|
||||||
| ## Verification and cutover | ||||||
|
|
||||||
| 1. Deploy with staging Clerk and Supabase credentials under a temporary hostname. | ||||||
| 2. Verify `/api/health/live`, `/api/health/ready`, public pages, authentication, API routes, and image optimization. | ||||||
| 3. Measure baseline and burst performance against staging, including p95 latency, errors, CPU, memory, image processing, disk use, and Supabase pool usage. | ||||||
| 4. Deploy the production configuration while the production domain still points to Vercel. Verify web and mobile Clerk flows through the temporary hostname. | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: printf '%s\n' '--- repository knowledge files ---'
find /tmp/coderabbit-repo-knowledge/producdevity-emuready-69c66b7f -type f -maxdepth 3 -print
printf '%s\n' '--- applicable conventions and learnings ---'
for f in /tmp/coderabbit-repo-knowledge/producdevity-emuready-69c66b7f/*/*.md; do
printf '\n### %s\n' "$f"
cat "$f"
done
printf '%s\n' '--- documentation change ---'
cat -n docs/SELF_HOSTING.md | sed -n '1,60p'
printf '%s\n' '--- relevant URL and Clerk references ---'
rg -n --glob '!node_modules' --glob '!dist' --glob '!build' \
'NEXT_PUBLIC_APP_URL|Clerk|clerk|redirect|callback|temporary hostname|Vercel' .
printf '%s\n' '--- deployment/build manifests ---'
fd -HI -t f '(Dockerfile|docker-compose|compose|vercel|next\.config|package\.json|env)' . | sortRepository: Producdevity/EmuReady Length of output: 50379 🏁 Script executed: printf '%s\n' '--- Docker build arguments and environment ---'
cat -n Dockerfile | sed -n '1,75p'
printf '%s\n' '--- all application URL consumers ---'
rg -n --glob '*.ts' --glob '*.tsx' --glob '*.js' --glob '*.jsx' \
'NEXT_PUBLIC_APP_URL|APP_URL' src app 2>/dev/null || true
printf '%s\n' '--- Clerk provider and redirect configuration ---'
rg -n --glob '*.ts' --glob '*.tsx' \
'ClerkProvider|SignInButton|SignUpButton|redirectUrl|forceRedirectUrl|fallbackRedirectUrl|afterSignInUrl|afterSignUpUrl|auth\(' \
src app 2>/dev/null || true
printf '%s\n' '--- relevant app/layout and auth files ---'
fd -HI -t f . src app 2>/dev/null | rg '(^|/)(layout|provider|auth|sign-in|sign-up|callback|middleware|proxy)'Repository: Producdevity/EmuReady Length of output: 11929 🏁 Script executed: printf '%s\n' '--- root layout and mobile authentication path ---'
cat -n src/app/layout.tsx | sed -n '1,105p'
cat -n src/app/api/mobile/auth/route.ts | sed -n '1,120p'
cat -n src/server/api/mobileContext.ts | sed -n '140,270p'
printf '%s\n' '--- URL-dependent runtime behavior ---'
cat -n src/lib/env.ts | sed -n '55,85p'
cat -n src/lib/cors.ts | sed -n '70,115p'
cat -n src/features/human-verification/server/providers/turnstile.ts | sed -n '90,130p'
printf '%s\n' '--- mobile API authentication documentation ---'
cat -n docs/MOBILE_API.md | sed -n '1,45p'
printf '%s\n' '--- current diff summary and documentation diff ---'
git diff --stat
git diff -- docs/SELF_HOSTING.mdRepository: Producdevity/EmuReady Length of output: 16763 🏁 Script executed: printf '%s\n' '--- CORS origin policy ---'
cat -n src/lib/cors.ts | sed -n '1,95p'
printf '%s\n' '--- final image environment and runtime stages ---'
cat -n Dockerfile | sed -n '66,145p'
printf '%s\n' '--- Turnstile validation callers ---'
rg -n -A8 -B5 --glob '*.ts' --glob '*.tsx' \
'validateSuccessfulResponse|verifyTurnstile|TURNSTILE_ALLOWED_HOSTNAMES|human verification' \
src
printf '%s\n' '--- Clerk-facing client flow definitions ---'
cat -n src/components/navbar/Navbar.tsx | sed -n '160,195p'
cat -n src/app/home/components/HomeJoinTheCommunity.tsx | sed -n '25,55p'Repository: Producdevity/EmuReady Length of output: 45148 Allow the temporary hostname in production validation. If Step 4 keeps production 🤖 Prompt for AI Agents |
||||||
| 5. Point both the apex and `www` Cloudflare records at the VPS, preserve the current apex-to-`www` canonical redirect, and keep the previous Vercel deployment available for rollback. | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- repository knowledge files ---'
find /tmp/coderabbit-repo-knowledge/producdevity-emuready-69c66b7f -maxdepth 2 -type f -print
printf '%s\n' '--- applicable knowledge previews ---'
for f in /tmp/coderabbit-repo-knowledge/producdevity-emuready-69c66b7f/*/*.md; do
printf '\n### %s\n' "$f"
head -80 "$f"
done
printf '%s\n' '--- self-hosting document ---'
cat -n docs/SELF_HOSTING.md
printf '%s\n' '--- repository files related to migrations and deployment ---'
git ls-files | rg '(^|/)(migrations?|prisma|drizzle|supabase|vercel|Dockerfile|docker-compose|package\.json|README|.*\.ya?ml$)' | head -200Repository: Producdevity/EmuReady Length of output: 21815 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- diff metadata and documentation diff ---'
git diff --stat
git diff -- docs/SELF_HOSTING.md
printf '%s\n' '--- migration/deployment definitions ---'
rg -n -C 4 'migrat|prisma migrate|DATABASE_DIRECT_URL|target app|target migrator|Vercel|rollback' Dockerfile package.json docker-compose.yml README.md .github 2>/dev/null | head -240
printf '%s\n' '--- potentially destructive migration statements ---'
rg -n -i -C 2 'DROP\s+(COLUMN|TABLE|INDEX|CONSTRAINT)|ALTER\s+TABLE|RENAME\s+(COLUMN|TABLE)|SET\s+NOT NULL|CREATE\s+TYPE|DROP\s+TYPE|DELETE\s+FROM|UPDATE\s+' prisma/migrations --glob 'migration.sql' | head -320Repository: Producdevity/EmuReady Length of output: 43094 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- repository state ---'
git status --short
git rev-parse --show-toplevel
git show -s --format='%H%n%s' HEAD
printf '%s\n' '--- files in current commit affecting release/schema ---'
git show --format= --name-only HEAD | rg '(^docs/SELF_HOSTING\.md$|^Dockerfile$|^package\.json$|^prisma/|^scripts/|vercel|coolify)' || true
printf '%s\n' '--- exact destructive migrations ---'
cat -n prisma/migrations/20250607164805_add_approval_status_enum/migration.sql
cat -n prisma/migrations/20250628225912_remove_test_field/migration.sql
printf '%s\n' '--- current schema fields corresponding to those migrations ---'
rg -n -C 3 'status|testField|GameApprovalStatus|ListingApprovalStatus|ApprovalStatus' prisma/schema.prisma src package.jsonRepository: Producdevity/EmuReady Length of output: 50378 🏁 Script executed: #!/bin/bash
set -e
log=/tmp/coderabbit-shell-logs/shell-output-x1XmBA
printf '%s\n' '--- repository state from prior run ---'
sed -n '1,80p' "$log"
printf '%s\n' '--- exact migration/schema references ---'
rg -n -C 2 '^(model (Game|Listing|User)|enum (ApprovalStatus|GameApprovalStatus|ListingApprovalStatus))|testField|status_new|DROP COLUMN|DROP TYPE' prisma/schema.prisma prisma/migrations/20250607164805_add_approval_status_enum/migration.sql prisma/migrations/20250628225912_remove_test_field/migration.sqlRepository: Producdevity/EmuReady Length of output: 13632 Make the Vercel rollback schema-safe.
🤖 Prompt for AI Agents |
||||||
|
|
||||||
| Do not run an upload backfill unless a read-only production database inventory confirms that `/uploads/...` references still exist and the matching source files have been recovered. | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,7 @@ type Header = Awaited<ReturnType<NonNullable<NextConfig['headers']>>>[number] | |
|
|
||
| const isVercelBuild = process.env.VERCEL === '1' | ||
| const isSentryEnabled = process.env.NEXT_PUBLIC_ENABLE_SENTRY === 'true' | ||
| const nextBuildId = process.env.NEXT_BUILD_ID | ||
|
|
||
| const contentSecurityPolicyDirectives = [ | ||
| { | ||
|
|
@@ -147,6 +148,12 @@ function createContentSecurityPolicy(): string { | |
| } | ||
|
|
||
| const nextConfig: NextConfig = { | ||
| output: 'standalone', | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: When Coolify overlaps old and new standalone containers, Server Actions can fail because each build uses a different encryption key. Pass one stable Prompt for AI agents |
||
|
|
||
| // Keep build identity stable and protect clients from version skew while | ||
| // Coolify briefly overlaps the old and new containers during deployment. | ||
| ...(nextBuildId ? { deploymentId: nextBuildId, generateBuildId: () => nextBuildId } : {}), | ||
|
|
||
| images: { | ||
| unoptimized: process.env.NEXT_IMAGE_UNOPTIMIZED === 'true', | ||
| qualities: [50, 75, 85, 100], | ||
|
|
@@ -225,7 +232,7 @@ const nextConfig: NextConfig = { | |
| serverExternalPackages: ['@prisma/client', 'jsdom', 'markdown-it', 'dompurify'], | ||
|
|
||
| outputFileTracingIncludes: { | ||
| '/*': ['docs/**/*.md'], | ||
| '/*': ['docs/**/*.md', 'prisma/generated/client/**'], | ||
| }, | ||
|
|
||
| outputFileTracingExcludes: { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
Repository: Producdevity/EmuReady
Length of output: 1033
🏁 Script executed:
Repository: Producdevity/EmuReady
Length of output: 22984
🏁 Script executed:
Repository: Producdevity/EmuReady
Length of output: 432
🌐 Web query:
Official Dockerfile ARG environment scope and Next.js NEXT_PUBLIC environment variable build-time inlining documentation💡 Result:
Docker ARG scope and Next.js NEXT_PUBLIC build-time inlining function differently, often causing confusion when containerizing applications. Docker ARG Scope The ARG instruction defines a variable that users can pass at build-time to the builder with the docker build command using the --build-arg = flag [1][2]. - Global vs. Stage Scope: An ARG declared before the first FROM instruction is in the global scope and cannot be accessed inside any build stage [1][2]. To use it within a stage, you must redeclare the ARG without a value inside that stage [1][2]. - Inheritance: Once an ARG is declared or consumed within a build stage, it is automatically inherited by child stages [2]. - Persistence: ARG variables are not persisted in the final image as environment variables [3]. If a variable is needed at runtime (e.g., inside the container), it must be explicitly assigned to an ENV instruction within the Dockerfile [3]. Next.js NEXT_PUBLIC Build-Time Inlining Next.js replaces variables prefixed with NEXT_PUBLIC_ with their hard-coded values at build-time (when running next build) [4][5]. - Mechanism: Next.js performs a static substitution, replacing all references to process.env.NEXT_PUBLIC_VARIABLE with the actual value available during the build process [4][6]. - Limitations: Because this happens at build-time, these values are frozen into the JavaScript bundles [4]. Changing these variables at runtime (e.g., via docker run -e) will have no effect, as the client-side code already contains the baked-in build-time value [4][6]. - Docker Integration: Because docker build does not inherently have access to runtime environment variables, you must pass these values as build arguments [7]. The common pattern is: 1. Define ARG NEXT_PUBLIC_EXAMPLE in the Dockerfile. 2. Use ENV NEXT_PUBLIC_EXAMPLE=$NEXT_PUBLIC_EXAMPLE to make the build argument available as an environment variable during the build process [7][8]. 3. Run npm run build [7]. 4. Pass the value when building the image: docker build --build-arg NEXT_PUBLIC_EXAMPLE=value. [7][6]. Summary Table Feature | ARG | NEXT_PUBLIC_ Variables --- | --- | --- Primary Purpose | Build-time configuration of the Dockerfile process [1][2]. | Browser-accessible configuration [4]. Processing Time | During docker build [1][2]. | During next build [4][6]. Runtime Access | Not available unless mapped to ENV [3]. | Frozen/baked into JS bundle [4]. Docker Flag | --build-arg [2][7]. | N/A (must be passed via ARG) [7][6].
Citations:
Forward each public variable used by client-side code.
NEXT_PUBLIC_ENABLE_ANDROID_DOWNLOADSis declared but is not available topnpm build. Next.js inlinesNEXT_PUBLIC_*values during the build, soProfileHeadercan compile the Android-download condition as disabled even when Coolify supplies the build argument. Export all client-used public arguments beforepnpm build.🧰 Tools
🪛 Hadolint (2.15.1)
[info] 68-68: Multiple consecutive
RUNinstructions. Consider consolidation.(DL3059)
[warning] 68-68: This expansion will not see the mentioned assignment.
(SC2098)
🤖 Prompt for AI Agents