2026-05-31 23:45:07 +03:30
|
|
|
name: meezi # Lock project name — prevents runner workspace from overriding it
|
|
|
|
|
|
2026-05-29 17:02:22 +03:30
|
|
|
# Meezi — main stack (Postgres, Redis, API, Dashboard, Website, Koja)
|
2026-05-27 21:33:10 +03:30
|
|
|
#
|
2026-05-31 11:06:24 +03:30
|
|
|
# All images/packages served from Nexus at mirror.soroushasadi.com:
|
|
|
|
|
# Docker images → mirror.soroushasadi.com (docker-group: Docker Hub + MCR)
|
|
|
|
|
# NuGet → https://mirror.soroushasadi.com/repository/nuget-group/
|
|
|
|
|
# npm → https://mirror.soroushasadi.com/repository/npm-group/
|
2026-05-30 00:28:07 +03:30
|
|
|
#
|
2026-05-31 11:06:24 +03:30
|
|
|
# Docker Desktop: merge docker/daemon-registry-mirror.example.json into daemon.json
|
2026-05-30 00:28:07 +03:30
|
|
|
#
|
2026-05-28 18:54:55 +03:30
|
|
|
# Local dev:
|
|
|
|
|
# cp .env.example .env
|
|
|
|
|
# docker compose up -d --build
|
2026-05-27 21:33:10 +03:30
|
|
|
#
|
2026-05-28 18:54:55 +03:30
|
|
|
# Production (IP-based, no domain yet):
|
|
|
|
|
# Set ENV_FILE secret in Gitea — CI writes .env and runs docker compose up -d
|
2026-05-27 21:33:10 +03:30
|
|
|
#
|
2026-05-28 18:54:55 +03:30
|
|
|
# Production (with domain, add Caddy):
|
|
|
|
|
# docker compose -f docker-compose.yml -f docker-compose.admin.yml -f docker-compose.caddy.yml up -d
|
|
|
|
|
#
|
|
|
|
|
# URLs (port-based defaults):
|
|
|
|
|
# Dashboard http://SERVER:3101/fa/login
|
|
|
|
|
# Website http://SERVER:3010/fa
|
2026-05-29 17:02:22 +03:30
|
|
|
# Koja http://SERVER:3103/fa
|
2026-05-28 18:54:55 +03:30
|
|
|
# API http://SERVER:5080/swagger
|
2026-05-27 21:33:10 +03:30
|
|
|
|
|
|
|
|
services:
|
|
|
|
|
postgres:
|
2026-05-31 11:06:24 +03:30
|
|
|
image: ${POSTGRES_IMAGE:-mirror.soroushasadi.com/postgres:16-alpine}
|
2026-05-27 21:33:10 +03:30
|
|
|
container_name: meezi-db
|
|
|
|
|
restart: unless-stopped
|
|
|
|
|
environment:
|
|
|
|
|
POSTGRES_DB: meezi
|
|
|
|
|
POSTGRES_USER: meezi
|
2026-05-28 18:54:55 +03:30
|
|
|
POSTGRES_PASSWORD: "${DB_PASSWORD:-meezi_local_pass}"
|
2026-05-27 21:33:10 +03:30
|
|
|
ports:
|
|
|
|
|
- "${POSTGRES_PORT:-5434}:5432"
|
|
|
|
|
volumes:
|
|
|
|
|
- postgres_data:/var/lib/postgresql/data
|
|
|
|
|
healthcheck:
|
|
|
|
|
test: ["CMD-SHELL", "pg_isready -U meezi -d meezi"]
|
|
|
|
|
interval: 5s
|
|
|
|
|
timeout: 5s
|
|
|
|
|
retries: 10
|
|
|
|
|
|
|
|
|
|
redis:
|
2026-05-31 11:06:24 +03:30
|
|
|
image: ${REDIS_IMAGE:-mirror.soroushasadi.com/redis:7-alpine}
|
2026-05-27 21:33:10 +03:30
|
|
|
container_name: meezi-redis
|
|
|
|
|
restart: unless-stopped
|
|
|
|
|
ports:
|
|
|
|
|
- "${REDIS_PORT:-6381}:6379"
|
|
|
|
|
command: redis-server --appendonly yes
|
|
|
|
|
volumes:
|
|
|
|
|
- redis_data:/data
|
|
|
|
|
healthcheck:
|
|
|
|
|
test: ["CMD", "redis-cli", "ping"]
|
|
|
|
|
interval: 5s
|
|
|
|
|
timeout: 3s
|
|
|
|
|
retries: 10
|
|
|
|
|
|
|
|
|
|
api:
|
|
|
|
|
build:
|
|
|
|
|
context: .
|
|
|
|
|
dockerfile: docker/api/Dockerfile
|
2026-05-28 21:01:43 +03:30
|
|
|
extra_hosts:
|
|
|
|
|
- "mirror:host-gateway"
|
2026-05-27 21:33:10 +03:30
|
|
|
args:
|
2026-05-31 11:06:24 +03:30
|
|
|
DOTNET_SDK_IMAGE: ${DOTNET_SDK_IMAGE:-mirror.soroushasadi.com/dotnet/sdk:10.0}
|
|
|
|
|
DOTNET_ASPNET_IMAGE: ${DOTNET_ASPNET_IMAGE:-mirror.soroushasadi.com/dotnet/aspnet:10.0}
|
2026-05-27 21:33:10 +03:30
|
|
|
container_name: meezi-api
|
|
|
|
|
restart: unless-stopped
|
|
|
|
|
depends_on:
|
|
|
|
|
postgres:
|
|
|
|
|
condition: service_healthy
|
|
|
|
|
redis:
|
|
|
|
|
condition: service_healthy
|
|
|
|
|
environment:
|
2026-06-12 10:16:01 +03:30
|
|
|
ASPNETCORE_ENVIRONMENT: "${ASPNETCORE_ENVIRONMENT:-Production}"
|
2026-05-27 21:33:10 +03:30
|
|
|
ASPNETCORE_URLS: http://+:8080
|
2026-05-28 18:54:55 +03:30
|
|
|
RUN_MIGRATIONS: "${RUN_MIGRATIONS:-true}"
|
|
|
|
|
ConnectionStrings__DefaultConnection: "${DB_CONNECTION_STRING:-Host=postgres;Port=5432;Database=meezi;Username=meezi;Password=meezi_local_pass}"
|
2026-05-27 21:33:10 +03:30
|
|
|
ConnectionStrings__Redis: redis:6379
|
2026-05-28 18:54:55 +03:30
|
|
|
Jwt__Key: "${JWT_KEY:-dev-jwt-key-CHANGE-THIS-IN-PRODUCTION-min32chars}"
|
|
|
|
|
App__PublicBaseUrl: "${NEXT_PUBLIC_API_URL:-http://localhost:5080}"
|
|
|
|
|
App__QrPublicBaseUrl: "${APP_QR_BASE_URL:-http://localhost:3101}"
|
|
|
|
|
Billing__DashboardBaseUrl: "${BILLING_DASHBOARD_URL:-http://localhost:3101}"
|
|
|
|
|
Cors__Origins__0: "${CORS_ORIGIN_0:-http://localhost:3101}"
|
|
|
|
|
Cors__Origins__1: "${CORS_ORIGIN_1:-http://localhost:3010}"
|
|
|
|
|
Cors__Origins__2: "${CORS_ORIGIN_2:-http://localhost:3103}"
|
|
|
|
|
Auth__MaxOtpAttemptsPerHour: "${OTP_RATE_LIMIT:-100}"
|
|
|
|
|
Kavenegar__ApiKey: "${KAVENEGAR_API_KEY:-}"
|
2026-05-29 02:38:06 +03:30
|
|
|
Kavenegar__SenderNumber: "${KAVENEGAR_SENDER:-90005671}"
|
2026-05-28 18:54:55 +03:30
|
|
|
Snappfood__WebhookSecret: "${SNAPPFOOD_WEBHOOK_SECRET:-meezi-dev-snappfood-secret}"
|
2026-05-27 21:33:10 +03:30
|
|
|
ZarinPal__MerchantId: "${ZARINPAL_MERCHANT_ID:-}"
|
|
|
|
|
ZarinPal__Sandbox: "${ZARINPAL_SANDBOX:-true}"
|
2026-06-26 04:20:02 +03:30
|
|
|
FlatPay__ApiKey: "${FLATPAY_API_KEY:-}"
|
|
|
|
|
FlatPay__Secret: "${FLATPAY_SECRET:-}"
|
|
|
|
|
FlatPay__BaseUrl: "${FLATPAY_BASE_URL:-https://pay.flatrender.ir}"
|
|
|
|
|
FlatPay__ReturnUrl: "${FLATPAY_RETURN_URL:-https://meezi.ir/payment/return}"
|
2026-05-31 20:10:56 +03:30
|
|
|
Seed__SystemAdminPhone: "${SEED_ADMIN_PHONE:-}"
|
|
|
|
|
Seed__SystemAdminUsername: "${SEED_ADMIN_USERNAME:-admin}"
|
|
|
|
|
Seed__SystemAdminPassword: "${SEED_ADMIN_PASSWORD:-}"
|
2026-05-27 21:33:10 +03:30
|
|
|
ports:
|
|
|
|
|
- "${API_PORT:-5080}:8080"
|
|
|
|
|
volumes:
|
|
|
|
|
- api_uploads:/app/uploads
|
|
|
|
|
healthcheck:
|
|
|
|
|
test: ["CMD-SHELL", "bash -c 'cat </dev/null >/dev/tcp/127.0.0.1/8080' || exit 1"]
|
|
|
|
|
interval: 10s
|
|
|
|
|
timeout: 5s
|
|
|
|
|
retries: 12
|
|
|
|
|
start_period: 40s
|
|
|
|
|
|
|
|
|
|
web:
|
|
|
|
|
build:
|
|
|
|
|
context: .
|
|
|
|
|
dockerfile: docker/web/Dockerfile
|
2026-05-28 21:01:43 +03:30
|
|
|
extra_hosts:
|
|
|
|
|
- "mirror:host-gateway"
|
2026-05-27 21:33:10 +03:30
|
|
|
args:
|
2026-05-31 11:06:24 +03:30
|
|
|
NODE_IMAGE: ${NODE_IMAGE:-mirror.soroushasadi.com/node:20-alpine}
|
|
|
|
|
NPM_REGISTRY: ${NPM_REGISTRY:-https://mirror.soroushasadi.com/repository/npm-group/}
|
2026-05-27 21:33:10 +03:30
|
|
|
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-http://localhost:5080}
|
|
|
|
|
container_name: meezi-web
|
|
|
|
|
restart: unless-stopped
|
|
|
|
|
depends_on:
|
|
|
|
|
api:
|
|
|
|
|
condition: service_healthy
|
|
|
|
|
environment:
|
|
|
|
|
PORT: "3000"
|
|
|
|
|
HOSTNAME: 0.0.0.0
|
|
|
|
|
ports:
|
|
|
|
|
- "${WEB_PORT:-3101}:3000"
|
|
|
|
|
|
|
|
|
|
website:
|
|
|
|
|
build:
|
|
|
|
|
context: .
|
|
|
|
|
dockerfile: docker/website/Dockerfile
|
2026-05-28 21:01:43 +03:30
|
|
|
extra_hosts:
|
|
|
|
|
- "mirror:host-gateway"
|
2026-05-27 21:33:10 +03:30
|
|
|
args:
|
2026-05-31 11:06:24 +03:30
|
|
|
NODE_IMAGE: ${NODE_IMAGE:-mirror.soroushasadi.com/node:20-alpine}
|
|
|
|
|
NPM_REGISTRY: ${NPM_REGISTRY:-https://mirror.soroushasadi.com/repository/npm-group/}
|
2026-05-27 21:33:10 +03:30
|
|
|
MEEZI_API_URL: http://api:8080
|
2026-06-22 18:58:38 +03:30
|
|
|
NEXT_PUBLIC_SITE_URL: ${NEXT_PUBLIC_SITE_URL:-https://meezi.ir}
|
2026-05-27 21:33:10 +03:30
|
|
|
container_name: meezi-website
|
|
|
|
|
restart: unless-stopped
|
|
|
|
|
depends_on:
|
|
|
|
|
api:
|
|
|
|
|
condition: service_healthy
|
|
|
|
|
environment:
|
|
|
|
|
PORT: "3000"
|
|
|
|
|
HOSTNAME: 0.0.0.0
|
|
|
|
|
MEEZI_API_URL: http://api:8080
|
2026-06-22 18:58:38 +03:30
|
|
|
NEXT_PUBLIC_SITE_URL: "${NEXT_PUBLIC_SITE_URL:-https://meezi.ir}"
|
2026-05-27 21:33:10 +03:30
|
|
|
ports:
|
|
|
|
|
- "${WEBSITE_PORT:-3010}:3000"
|
|
|
|
|
|
2026-05-29 17:02:22 +03:30
|
|
|
koja:
|
2026-05-27 21:33:10 +03:30
|
|
|
build:
|
|
|
|
|
context: .
|
2026-05-29 17:02:22 +03:30
|
|
|
dockerfile: docker/koja/Dockerfile
|
2026-05-28 21:01:43 +03:30
|
|
|
extra_hosts:
|
|
|
|
|
- "mirror:host-gateway"
|
2026-05-27 21:33:10 +03:30
|
|
|
args:
|
2026-05-31 11:06:24 +03:30
|
|
|
NODE_IMAGE: ${NODE_IMAGE:-mirror.soroushasadi.com/node:20-alpine}
|
|
|
|
|
NPM_REGISTRY: ${NPM_REGISTRY:-https://mirror.soroushasadi.com/repository/npm-group/}
|
2026-05-27 21:33:10 +03:30
|
|
|
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-http://localhost:5080}
|
2026-06-22 18:58:38 +03:30
|
|
|
NEXT_PUBLIC_SITE_URL: ${NEXT_PUBLIC_KOJA_URL:-https://koja.meezi.ir}
|
2026-05-29 17:02:22 +03:30
|
|
|
container_name: meezi-koja
|
2026-05-27 21:33:10 +03:30
|
|
|
restart: unless-stopped
|
|
|
|
|
depends_on:
|
|
|
|
|
api:
|
|
|
|
|
condition: service_healthy
|
|
|
|
|
environment:
|
|
|
|
|
PORT: "3000"
|
|
|
|
|
HOSTNAME: 0.0.0.0
|
2026-05-28 18:54:55 +03:30
|
|
|
NEXT_PUBLIC_API_URL: "${NEXT_PUBLIC_API_URL:-http://localhost:5080}"
|
2026-06-22 18:58:38 +03:30
|
|
|
NEXT_PUBLIC_SITE_URL: "${NEXT_PUBLIC_KOJA_URL:-https://koja.meezi.ir}"
|
2026-05-27 21:33:10 +03:30
|
|
|
ports:
|
2026-05-29 17:02:22 +03:30
|
|
|
- "${KOJA_PORT:-3103}:3000"
|
2026-05-27 21:33:10 +03:30
|
|
|
|
2026-06-15 18:45:07 +03:30
|
|
|
# Nightly Postgres backup — dumps the DB every night, keeps the last 14 days.
|
|
|
|
|
# Dumps land in the host ./backups dir (bind mount) so they survive a full
|
|
|
|
|
# container/volume wipe and can be rsync'd off-box. See scripts/backup/RESTORE.md.
|
|
|
|
|
backup:
|
|
|
|
|
image: ${POSTGRES_IMAGE:-mirror.soroushasadi.com/postgres:16-alpine}
|
|
|
|
|
container_name: meezi-backup
|
|
|
|
|
restart: unless-stopped
|
|
|
|
|
depends_on:
|
|
|
|
|
postgres:
|
|
|
|
|
condition: service_healthy
|
|
|
|
|
environment:
|
|
|
|
|
PGHOST: postgres
|
|
|
|
|
PGPORT: "5432"
|
|
|
|
|
PGUSER: meezi
|
|
|
|
|
PGPASSWORD: "${DB_PASSWORD:-meezi_local_pass}"
|
|
|
|
|
PGDATABASE: meezi
|
|
|
|
|
RETAIN_DAYS: "${BACKUP_RETAIN_DAYS:-14}"
|
|
|
|
|
BACKUP_HOUR: "${BACKUP_HOUR:-2}"
|
|
|
|
|
TZ: Asia/Tehran
|
|
|
|
|
entrypoint: ["/bin/sh", "/backup/pg-backup-loop.sh"]
|
|
|
|
|
volumes:
|
|
|
|
|
- ./scripts/backup:/backup:ro
|
|
|
|
|
- ${BACKUP_DIR:-./backups}:/backups
|
|
|
|
|
|
2026-05-27 21:33:10 +03:30
|
|
|
volumes:
|
|
|
|
|
postgres_data:
|
|
|
|
|
redis_data:
|
|
|
|
|
api_uploads:
|