fix: Prisma binary download for Iran servers

This commit is contained in:
2026-05-06 18:04:19 +03:30
parent ea6e67b4e8
commit b0a345c6b7
23 changed files with 285 additions and 116 deletions

2
.env
View File

@@ -15,4 +15,4 @@ IPPANEL_ORIGINATOR="+983000505"
IPPANEL_BASE_URL="https://edge.ippanel.com/v1" IPPANEL_BASE_URL="https://edge.ippanel.com/v1"
# Node Environment # Node Environment
NODE_ENV="development" NODE_ENV="production"

60
.gitignore vendored
View File

@@ -1,46 +1,42 @@
# Dependencies # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
node_modules/
.pnp # dependencies
/node_modules
/.pnp
.pnp.js .pnp.js
.yarn/install-state.gz
# Next.js # testing
.next/ /coverage
out/
build/
# Environment variables # next.js
.env.local /.next/
.env.development.local /out/
.env.test.local
.env.production.local
.env2
# Prisma # production
prisma/migrations/ /build
# Logs # misc
.DS_Store
*.pem
# debug
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
pnpm-debug.log*
*.log
# OS # local env files
.DS_Store .env*.local
Thumbs.db .env
desktop.ini
# Editor # vercel
.vscode/ .vercel
.idea/
*.swp
*.swo
# TypeScript # typescript
*.tsbuildinfo *.tsbuildinfo
next-env.d.ts next-env.d.ts
# Misc # Prisma
.vercel # Note: We keep generated Prisma client for deployment in Iran servers
.turbo # /node_modules/.prisma
coverage/ # /node_modules/@prisma/client

3
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"kiroAgent.configureMCP": "Disabled"
}

View File

@@ -3,89 +3,105 @@
## مشکل ## مشکل
سرورهای ایران به دلیل تحریم‌ها نمی‌توانند به `binaries.prisma.sh` دسترسی داشته باشند و Prisma نمی‌تواند باینری‌های لازم را دانلود کند. سرورهای ایران به دلیل تحریم‌ها نمی‌توانند به `binaries.prisma.sh` دسترسی داشته باشند و Prisma نمی‌تواند باینری‌های لازم را دانلود کند.
## راه‌حل‌های پیاده‌سازی شده ## راه‌حل پیاده‌سازی شده
### ۱. تغییرات Dockerfile ### دانلود Manual باینری‌های Prisma
- باینری‌های Prisma در مرحله `deps` دانلود و generate می‌شوند
- از `PRISMA_ENGINES_MIRROR` برای استفاده از CDN استفاده شده
- باینری‌ها به مرحله `builder` منتقل می‌شوند
### ۲. تغییرات package.json روی سیستمی با دسترسی به اینترنت (مثل لپ‌تاپ شخصی با VPN):
- اضافه شدن `postinstall` script برای اجرای خودکار `prisma generate`
- تغییر `build` script برای اطمینان از generate شدن Prisma قبل از build
## راه‌حل‌های جایگزین (در صورت عدم موفقیت)
### الف) استفاده از Proxy یا VPN
اگر سرور شما به proxy یا VPN دسترسی دارد:
```bash ```bash
# در Dockerfile قبل از RUN npm ci # اجرای اسکریپت دانلود
bash scripts/download-prisma-engines.sh
# Commit کردن فایل‌ها
git add prisma/engines
git commit -m "Add Prisma engines for offline deployment"
git push
```
این کار باینری‌های Prisma را در پوشه `prisma/engines` قرار می‌دهد و Dockerfile از آن‌ها استفاده می‌کند.
## راه‌حل‌های جایگزین
### الف) Build روی سیستم محلی و Push کردن Image
اگر دانلود engines کار نکرد:
```bash
# روی سیستم با اینترنت
docker build -t football-next:latest \
--build-arg DATABASE_URL="your-db-url" \
--build-arg NEXTAUTH_URL="your-url" \
--build-arg NEXTAUTH_SECRET="your-secret" \
.
# Save کردن image
docker save football-next:latest | gzip > football-next.tar.gz
# انتقال به سرور و load کردن
gunzip -c football-next.tar.gz | docker load
```
### ب) استفاده از Registry خصوصی
1. Push کردن image به Docker Hub یا registry خصوصی
2. Pull کردن از سرور ایران
```bash
# روی سیستم با اینترنت
docker build -t your-username/football-next:latest .
docker push your-username/football-next:latest
# روی سرور ایران
docker pull your-username/football-next:latest
```
### ج) استفاده از Proxy
اگر سرور به proxy دسترسی دارد:
```dockerfile
# در Dockerfile قبل از RUN commands
ENV HTTP_PROXY=http://your-proxy:port ENV HTTP_PROXY=http://your-proxy:port
ENV HTTPS_PROXY=http://your-proxy:port ENV HTTPS_PROXY=http://your-proxy:port
``` ```
### ب) Pre-build کردن و Push کردن Image
روی سیستم محلی (با دسترسی به اینترنت):
```bash
# Build کردن image
docker build -t football-next:latest .
# Save کردن image
docker save football-next:latest > football-next.tar
# انتقال فایل به سرور و load کردن
docker load < football-next.tar
```
### ج) استفاده از Prisma Binary از قبل دانلود شده
۱. روی سیستم با اینترنت:
```bash
npx prisma generate
```
۲. فایل‌های generate شده در `node_modules/.prisma` و `node_modules/@prisma` را کپی کنید
۳. در Dockerfile از COPY استفاده کنید:
```dockerfile
COPY node_modules/.prisma ./node_modules/.prisma
COPY node_modules/@prisma ./node_modules/@prisma
```
### د) استفاده از Mirror داخلی
اگر یک mirror داخلی دارید:
```dockerfile
ENV PRISMA_ENGINES_MIRROR=https://your-internal-mirror.ir
```
## تست محلی ## تست محلی
برای تست Dockerfile قبل از دیپلوی:
```bash ```bash
# Build # Build
docker build -t football-next:test . docker build -t football-next:test \
--build-arg DATABASE_URL="postgresql://user:pass@host:5432/db" \
--build-arg NEXTAUTH_URL="http://localhost:3000" \
--build-arg NEXTAUTH_SECRET="test-secret" \
.
# Run # Run
docker run -p 3000:3000 \ docker run -p 3000:3000 football-next:test
-e DATABASE_URL="your-db-url" \
-e NEXTAUTH_URL="http://localhost:3000" \
-e NEXTAUTH_SECRET="your-secret" \
football-next:test
``` ```
## نکات مهم ## نکات مهم
1. مطمئن شوید `prisma` folder در `.dockerignore` نیست 1. فایل‌های `prisma/engines/*` باید در git commit شوند
2. متغیرهای محیطی را در Coolify یا platform دیپلوی خود تنظیم کنید 2. این فایل‌ها binary هستند و حجم دارند (~50MB)
3. اگر همچنان مشکل دارید، لاگ‌های کامل build را بررسی کنید 3. هر بار که Prisma version تغییر کرد، باید engines را دوباره دانلود کنید
4. متغیرهای محیطی را در Coolify تنظیم کنید
## متغیرهای محیطی مورد نیاز ## متغیرهای محیطی مورد نیاز
```env ```env
DATABASE_URL=postgresql://user:password@host:5432/dbname DATABASE_URL=postgresql://user:password@host:5432/dbname
NEXTAUTH_URL=https://your-domain.com NEXTAUTH_URL=https://your-domain.com
NEXTAUTH_SECRET=your-secret-key NEXTAUTH_SECRET=your-secret-key-min-32-chars
IPPANEL_TOKEN=your-ippanel-token (optional)
``` ```
## عیب‌یابی
اگر build با خطا مواجه شد:
1. بررسی کنید که `prisma/engines` وجود دارد
2. بررسی کنید که فایل‌ها executable هستند
3. لاگ‌های کامل Docker را بررسی کنید
4. از روش "Build روی سیستم محلی" استفاده کنید

53
DEPLOY_STEPS.md Normal file
View File

@@ -0,0 +1,53 @@
# مراحل دیپلوی برای سرورهای ایران
## گام ۱: دانلود Prisma Engines (فقط یک بار)
روی سیستم با دسترسی به اینترنت (لپ‌تاپ با VPN):
```bash
chmod +x scripts/download-prisma-engines.sh
bash scripts/download-prisma-engines.sh
```
## گام ۲: Commit و Push
```bash
git add prisma/engines Dockerfile package.json DEPLOY_IRAN.md
git commit -m "feat: Add Prisma engines for offline deployment"
git push
```
## گام ۳: Deploy در Coolify
Coolify به صورت خودکار تغییرات را تشخیص داده و deploy می‌کند.
## نکته مهم
اگر اسکریپت دانلود کار نکرد، از روش manual استفاده کنید:
```bash
# دریافت Prisma version
PRISMA_VERSION=$(node -p "require('./package.json').dependencies['@prisma/client']")
echo "Prisma version: $PRISMA_VERSION"
# دانلود manual engines
mkdir -p prisma/engines
cd prisma/engines
# Query Engine
curl -L "https://binaries.prisma.sh/all_commits/c2990dca591cba766e3b7ef5d9e8a84796e47ab7/linux-musl-openssl-3.0.x/query-engine.gz" | gunzip > query-engine
chmod +x query-engine
# Schema Engine
curl -L "https://binaries.prisma.sh/all_commits/c2990dca591cba766e3b7ef5d9e8a84796e47ab7/linux-musl-openssl-3.0.x/schema-engine.gz" | gunzip > schema-engine
chmod +x schema-engine
cd ../..
```
## عیب‌یابی
اگر build شکست خورد:
1. بررسی کنید فایل‌های `prisma/engines/*` وجود دارند
2. بررسی کنید executable هستند: `ls -la prisma/engines`
3. از روش "Build Local" در `DEPLOY_IRAN.md` استفاده کنید

View File

@@ -1,30 +1,40 @@
FROM node:20-alpine AS deps FROM node:20-alpine AS deps
WORKDIR /app WORKDIR /app
ENV NODE_ENV=development ENV PRISMA_SKIP_POSTINSTALL_GENERATE=1
ENV PRISMA_ENGINES_MIRROR=https://binaries.prismacdn.com
ENV PRISMA_ENGINES_CHECKSUM_IGNORE_MISSING=1
COPY package.json package-lock.json ./ COPY package.json package-lock.json ./
COPY prisma ./prisma RUN npm ci --ignore-scripts
RUN npm ci
RUN npx prisma generate
FROM node:20-alpine AS builder FROM node:20-alpine AS builder
WORKDIR /app WORKDIR /app
ARG DATABASE_URL ARG DATABASE_URL
ARG NEXTAUTH_URL ARG NEXTAUTH_URL
ARG NEXTAUTH_SECRET
ARG IPPANEL_TOKEN
ENV DATABASE_URL=$DATABASE_URL ENV DATABASE_URL=$DATABASE_URL
ENV NEXTAUTH_URL=$NEXTAUTH_URL ENV NEXTAUTH_URL=$NEXTAUTH_URL
ENV NEXTAUTH_SECRET=$NEXTAUTH_SECRET
ENV IPPANEL_TOKEN=$IPPANEL_TOKEN
ENV NEXT_TELEMETRY_DISABLED=1 ENV NEXT_TELEMETRY_DISABLED=1
ENV PRISMA_ENGINES_MIRROR=https://binaries.prismacdn.com ENV PRISMA_SKIP_POSTINSTALL_GENERATE=1
ENV PRISMA_ENGINES_CHECKSUM_IGNORE_MISSING=1
COPY --from=deps /app/node_modules ./node_modules COPY --from=deps /app/node_modules ./node_modules
COPY --from=deps /app/prisma ./prisma
COPY . . COPY . .
# Set Prisma to use local engines if they exist
ENV PRISMA_QUERY_ENGINE_BINARY=/app/prisma/engines/query-engine
ENV PRISMA_SCHEMA_ENGINE_BINARY=/app/prisma/engines/schema-engine
ENV PRISMA_QUERY_ENGINE_LIBRARY=/app/prisma/engines/query-engine
# Generate Prisma client using local engines
RUN if [ -f prisma/engines/query-engine ]; then \
npx prisma generate; \
else \
echo "Warning: Prisma engines not found, build may fail"; \
fi
ENV NODE_ENV=production ENV NODE_ENV=production
RUN npm run build RUN npm run build
RUN npm prune --omit=dev RUN npm prune --omit=dev

View File

@@ -2,7 +2,7 @@ import { NextRequest, NextResponse } from "next/server";
import { db } from "@/lib/db"; import { db } from "@/lib/db";
import { getServerSession } from "next-auth"; import { getServerSession } from "next-auth";
import { authOptions } from "@/lib/auth"; import { authOptions } from "@/lib/auth";
import { Prisma } from "@prisma/client"; import { Prisma } from "@/lib/generated/prisma";
async function requireAdmin() { async function requireAdmin() {
const session = await getServerSession(authOptions); const session = await getServerSession(authOptions);

View File

@@ -2,7 +2,7 @@ import { NextRequest, NextResponse } from "next/server";
import { db } from "@/lib/db"; import { db } from "@/lib/db";
import { getServerSession } from "next-auth"; import { getServerSession } from "next-auth";
import { authOptions } from "@/lib/auth"; import { authOptions } from "@/lib/auth";
import { Prisma } from "@prisma/client"; import { Prisma } from "@/lib/generated/prisma";
async function adminOnly(req: NextRequest) { async function adminOnly(req: NextRequest) {
const session = await getServerSession(authOptions); const session = await getServerSession(authOptions);

View File

@@ -1,4 +1,4 @@
import type { CardTier, DailyQuiz } from "@prisma/client"; import type { CardTier, DailyQuiz } from "@/lib/generated/prisma";
export const CARD_TIER_LABELS: Record<CardTier, string> = { export const CARD_TIER_LABELS: Record<CardTier, string> = {
GOLD: "طلایی", GOLD: "طلایی",

View File

@@ -1,4 +1,4 @@
import { PrismaClient } from "@prisma/client"; import { PrismaClient } from "@/lib/generated/prisma";
import { PrismaPg } from "@prisma/adapter-pg"; import { PrismaPg } from "@prisma/adapter-pg";
const globalForPrisma = globalThis as unknown as { const globalForPrisma = globalThis as unknown as {

View File

@@ -1,4 +1,4 @@
import type { Position, EventType } from "@prisma/client"; import type { Position, EventType } from "@/lib/generated/prisma";
export const DEFAULT_RULES: Record<string, Partial<Record<string, number>>> = { export const DEFAULT_RULES: Record<string, Partial<Record<string, number>>> = {
GK: { GK: {

View File

@@ -1,4 +1,4 @@
import type { Position, TeamPlayer } from "@prisma/client"; import type { Position, TeamPlayer } from "@/lib/generated/prisma";
import { FORMATIONS } from "@/lib/teamValidation"; import { FORMATIONS } from "@/lib/teamValidation";
export const SPECIAL_CARD_TEAM_LIMIT = 3; export const SPECIAL_CARD_TEAM_LIMIT = 3;

View File

@@ -3,7 +3,6 @@
"dev": "next dev", "dev": "next dev",
"build": "prisma generate && next build", "build": "prisma generate && next build",
"start": "next start", "start": "next start",
"postinstall": "prisma generate",
"db:push": "prisma db push", "db:push": "prisma db push",
"db:generate": "prisma generate", "db:generate": "prisma generate",
"db:studio": "prisma studio", "db:studio": "prisma studio",

View File

@@ -1,6 +1,7 @@
generator client { generator client {
provider = "prisma-client-js" provider = "prisma-client-js"
engineType = "client" engineType = "client"
output = "../lib/generated/prisma"
} }
datasource db { datasource db {

View File

@@ -1,4 +1,4 @@
import { PrismaClient } from "@prisma/client"; import { PrismaClient } from "../lib/generated/prisma";
import bcrypt from "bcryptjs"; import bcrypt from "bcryptjs";
// DEFAULT_RULES رو مستقیم اینجا تعریف می‌کنیم تا از @/ alias استفاده نکنیم // DEFAULT_RULES رو مستقیم اینجا تعریف می‌کنیم تا از @/ alias استفاده نکنیم

View File

@@ -1,4 +1,4 @@
import { PrismaClient } from "@prisma/client"; import { PrismaClient } from "../lib/generated/prisma";
const prisma = new PrismaClient(); const prisma = new PrismaClient();

View File

@@ -1,4 +1,4 @@
import { PrismaClient } from "@prisma/client"; import { PrismaClient } from "../lib/generated/prisma";
import bcrypt from "bcryptjs"; import bcrypt from "bcryptjs";
const prisma = new PrismaClient(); const prisma = new PrismaClient();

View File

@@ -1,4 +1,4 @@
import { PrismaClient } from "@prisma/client"; import { PrismaClient } from "../lib/generated/prisma";
import bcrypt from "bcryptjs"; import bcrypt from "bcryptjs";
const prisma = new PrismaClient(); const prisma = new PrismaClient();

View File

@@ -0,0 +1,51 @@
# این اسکریپت باید روی سیستمی با دسترسی به اینترنت اجرا شود
$COMMIT_HASH = "c2990dca591cba766e3b7ef5d9e8a84796e47ab7"
$PLATFORM = "linux-musl-openssl-3.0.x"
# Create engines directory
New-Item -ItemType Directory -Force -Path "prisma/engines" | Out-Null
Write-Host "Downloading Prisma engines..." -ForegroundColor Green
# Download query engine
Write-Host "Downloading query-engine..."
$queryEngineUrl = "https://binaries.prisma.sh/all_commits/$COMMIT_HASH/$PLATFORM/query-engine.gz"
$queryEnginePath = "prisma/engines/query-engine"
try {
Invoke-WebRequest -Uri $queryEngineUrl -OutFile "prisma/engines/query-engine.gz"
# Decompress using 7zip or built-in if available
if (Get-Command 7z -ErrorAction SilentlyContinue) {
7z e "prisma/engines/query-engine.gz" -o"prisma/engines" -y
Remove-Item "prisma/engines/query-engine.gz"
} else {
Write-Host "Please install 7-Zip or manually decompress prisma/engines/query-engine.gz" -ForegroundColor Yellow
}
} catch {
Write-Host "Error downloading query-engine: $_" -ForegroundColor Red
}
# Download schema engine
Write-Host "Downloading schema-engine..."
$schemaEngineUrl = "https://binaries.prisma.sh/all_commits/$COMMIT_HASH/$PLATFORM/schema-engine.gz"
try {
Invoke-WebRequest -Uri $schemaEngineUrl -OutFile "prisma/engines/schema-engine.gz"
if (Get-Command 7z -ErrorAction SilentlyContinue) {
7z e "prisma/engines/schema-engine.gz" -o"prisma/engines" -y
Remove-Item "prisma/engines/schema-engine.gz"
} else {
Write-Host "Please install 7-Zip or manually decompress prisma/engines/schema-engine.gz" -ForegroundColor Yellow
}
} catch {
Write-Host "Error downloading schema-engine: $_" -ForegroundColor Red
}
Write-Host "`nEngines downloaded successfully!" -ForegroundColor Green
Write-Host "Now commit these files:" -ForegroundColor Cyan
Write-Host "git add prisma/engines"
Write-Host "git commit -m 'Add Prisma engines for offline deployment'"
Write-Host "git push"

View File

@@ -0,0 +1,27 @@
#!/bin/bash
# این اسکریپت باید روی سیستمی با دسترسی به اینترنت اجرا شود
PRISMA_VERSION=$(node -p "require('./package.json').dependencies['@prisma/client']" | tr -d '^~')
COMMIT_HASH="c2990dca591cba766e3b7ef5d9e8a84796e47ab7"
PLATFORM="linux-musl-openssl-3.0.x"
mkdir -p prisma/engines
echo "Downloading Prisma engines for version $PRISMA_VERSION..."
# Download query engine
curl -L "https://binaries.prisma.sh/all_commits/${COMMIT_HASH}/${PLATFORM}/query-engine.gz" \
| gunzip > prisma/engines/query-engine
chmod +x prisma/engines/query-engine
# Download schema engine
curl -L "https://binaries.prisma.sh/all_commits/${COMMIT_HASH}/${PLATFORM}/schema-engine.gz" \
| gunzip > prisma/engines/schema-engine
chmod +x prisma/engines/schema-engine
echo "Engines downloaded successfully!"
echo "Now commit these files:"
echo "git add prisma/engines"
echo "git commit -m 'Add Prisma engines for offline deployment'"
echo "git push"

View File

@@ -0,0 +1,13 @@
#!/bin/bash
# این اسکریپت باید روی سیستمی با دسترسی به اینترنت اجرا شود
# و فایل‌های generate شده را commit کنید
echo "Generating Prisma Client..."
npx prisma generate
echo "Prisma Client generated successfully!"
echo "Now commit the generated files:"
echo "git add node_modules/.prisma node_modules/@prisma"
echo "git commit -m 'Add pre-generated Prisma client for deployment'"
echo "git push"

View File

@@ -1,4 +1,4 @@
import { CardTier, PrismaClient } from "@prisma/client"; import { CardTier, PrismaClient } from "../lib/generated/prisma";
const db = new PrismaClient(); const db = new PrismaClient();

View File

@@ -1,4 +1,4 @@
import type { DailyQuiz, QuizQuestion, QuizSubmission, GoldenCard, Player, Country, User } from "@prisma/client"; import type { DailyQuiz, QuizQuestion, QuizSubmission, GoldenCard, Player, Country, User } from "@/lib/generated/prisma";
export type QuizWithQuestions = DailyQuiz & { export type QuizWithQuestions = DailyQuiz & {
questions: QuizQuestion[]; questions: QuizQuestion[];