Logical replication carries rows but not DDL, so a migration that adds a column stalls every subscriber with "missing replicated columns" until the column exists there too. _prisma_migrations is replicated, so migrate deploy on a subscriber reports nothing pending while its tables lag behind. sync-replica-schema.sh applies pending migrations to each subscriber first, tracking them locally in _replica_ddl_applied, then migrates the publisher. --check reports subscription state and columns missing on any replica. Co-authored-by: Cursor <cursoragent@cursor.com>
3.8 KiB
Deploy — SuperAdmin on shared montime host
Host: 185.50.38.53 (SSH port 3031)
Domain: https://adm.montzar.ir
DB: Postgres superadmin (owner montime) — already created
API port: 3010 (3000=montime, 3001=forisanad)
Layout on server
/var/www/superadmin/
api/ # clone of controlPanel/api.git
deploy/ # nginx + pm2 + this guide (in api repo)
web/ # clone of controlPanel/web.git (Vue dist)
1) Clone / pull
sudo mkdir -p /var/www/superadmin
sudo chown montime:montime /var/www/superadmin
cd /var/www/superadmin
git clone https://get.feep.ir/controlPanel/api.git api
git clone https://get.feep.ir/controlPanel/web.git web
Later updates: git pull in each folder.
2) API .env
cd /var/www/superadmin/api
cp .env.example .env
nano .env
Required:
NODE_ENV=production
PORT=3010
DATABASE_URL="postgresql://montime:YOUR_PASSWORD@127.0.0.1:5432/superadmin?schema=public"
JWT_SECRET="long-random-production-secret"
JWT_EXPIRES_IN="7d"
CORS_ORIGIN="https://adm.montzar.ir"
PUBLIC_API_URL="https://adm.montzar.ir"
SEED_ADMIN_MOBILE="09128081392"
SEED_ADMIN_PASSWORD="change-me"
# CENTRAL_SMS_* as needed
Password = same Postgres password that works for psql -h 127.0.0.1 -U montime -d superadmin.
3) Install, migrate, build, seed
cd /var/www/superadmin/api
npm ci
npx prisma generate
npx prisma migrate deploy
npm run build
npm run prisma:seed
# optional first import:
# npm run prisma:import-users
4) PM2
pm2 start /var/www/superadmin/api/deploy/ecosystem.config.cjs
pm2 save
pm2 status
curl -s http://127.0.0.1:3010/api/health || true
5) Web build
Same-origin: leave VITE_API_BASE unset (defaults to /api).
cd /var/www/superadmin/web
npm ci
npm run build
# dist → /var/www/superadmin/web/dist
6) Nginx
sudo cp /var/www/superadmin/api/deploy/nginx-adm.montzar.ir.conf /etc/nginx/sites-available/adm.montzar.ir
sudo ln -sf /etc/nginx/sites-available/adm.montzar.ir /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
SSL: if Certbot already issued for adm.montzar.ir, paths in the conf should match. Otherwise:
sudo certbot --nginx -d adm.montzar.ir
7) Smoke
- Open
https://adm.montzar.ir→ login https://adm.montzar.ir/get_user?domain=montpar.ir(or any known domain)- Confirm uploads/logo URLs use
PUBLIC_API_URL
8) Migrations with logical replication (failover replicas)
superadmin_pub streams rows to the failover hosts (adm.montech.ir, adm.montest.ir).
Logical replication does not carry DDL: a migration that adds a column on the
publisher makes every subscriber fail with missing replicated columns and the
subscription stalls until that column exists there too. Because _prisma_migrations
is replicated, prisma migrate deploy on a subscriber wrongly reports
"No pending migrations".
So never run migrate deploy alone. On the publisher host:
cp deploy/replicas.env.example deploy/replicas.env # once; add real DSNs
bash deploy/sync-replica-schema.sh --init # once, when schemas already match
For every release:
npm run replicas:sync # DDL to each replica first, then migrate deploy here
npm run replicas:check # subscription state + columns missing on any replica
Keep migrations schema-only. Data backfills would run once per replica and then again through replication, so apply those on the publisher by hand.
Local development note
Prisma provider is PostgreSQL only. Local options:
- Docker:
docker run -d -p 5432:5432 -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=superadmin postgres:16 - Or point
DATABASE_URLat the server DB only for short tests (prefer local DB)
SQLite history is kept under prisma/migrations_sqlite_archive/ (reference only; not applied by migrate deploy).