TenantInsurance reads ran through the tenant filter, which pins every query to the *requesting* user's environment. A clinic owner managing a doctor's contracts therefore read an empty set, recomputed version 1, and hit `uniq_tenant_insurance_version` on insert. The reads now bypass the filter — authorization is already established by resolveTargetEntity(), and the uuid-based paths re-assert ownership after loading. UserActiveContext::upsert() raced with itself: the panel fires several /oauth/userinfo requests at once, all saw no row, all inserted, and the losers died on a duplicate PRIMARY (closing the EntityManager with them). Replaced with INSERT ... ON DUPLICATE KEY UPDATE. A service that carries a treatment protocol but no catalog category is bad catalog data, not a system failure; it was logged at error level on every confirm and buried the real errors. Now a warning carrying the service id. Kavenegar's HTTP 431 says only "malformed request". The provider's own message and the token slot names are now logged so the template can actually be fixed in the panel; token values stay out of the log. Redis DSNs gained timeout/retry_interval/tcp_keepalive so a brief connection loss reconnects quietly instead of logging a warning each time. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
93 lines
5.6 KiB
Bash
93 lines
5.6 KiB
Bash
# ============================================================
|
|
# Coolify Environment Variables — ClinicPro (Docker Compose)
|
|
# ------------------------------------------------------------
|
|
# Copy these into the Coolify resource's "Environment Variables" tab.
|
|
# Only the variables referenced as ${...} in docker-compose.yml need to be set.
|
|
#
|
|
# MariaDB and Redis are SEPARATE Coolify Database Resources (not in the compose).
|
|
# So DATABASE_URL / REDIS_URL / MESSENGER_TRANSPORT_DSN must be set HERE, pointing
|
|
# at those resources by their internal hostname (mariadb-<uuid> / redis-<uuid>).
|
|
# Steps:
|
|
# 1. Create a standalone MariaDB 11.8 resource + a standalone Redis resource.
|
|
# 2. Enable "Connect to Predefined Network" on this app stack.
|
|
# 3. Copy each resource's internal hostname/credentials into the URLs below.
|
|
# ============================================================
|
|
|
|
# ── Database / Redis connections (point at the SEPARATE Coolify resources) ──
|
|
# Replace mariadb-XXXXXXXX / redis-XXXXXXXX with the real hostname shown on each
|
|
# resource's page (Internal URL). serverVersion MUST match the MariaDB resource (11.8).
|
|
DATABASE_URL="mysql://clinic:DB_PASSWORD@mariadb-XXXXXXXX:3306/clinic_pro?serverVersion=mariadb-11.8.0&charset=utf8mb4"
|
|
# retry_interval/tcp_keepalive: قطع کوتاه اتصال به redis بیسروصدا دوباره برقرار میشود
|
|
# و «Connection lost» بهصورت warning در app_log نمینشیند.
|
|
REDIS_URL="redis://redis-XXXXXXXX:6379?timeout=5&read_timeout=5&retry_interval=100&tcp_keepalive=60"
|
|
# stream_max_entries caps the Redis stream so the queue cannot grow without bound
|
|
MESSENGER_TRANSPORT_DSN="redis://redis-XXXXXXXX:6379/messages?stream_max_entries=20000"
|
|
# If the Redis resource has a password: redis://:PASSWORD@redis-XXXXXXXX:6379
|
|
|
|
# ── Backend's own domain (single URL — used for payment callbacks & absolute URLs) ──
|
|
# This is the API host, NOT a frontend domain.
|
|
APP_BASE_URL=https://api.nobat724.com
|
|
|
|
# ── Frontend domains (MANY) — CORS ──
|
|
# فقط همین یک متغیرِ سادهٔ کامادار لازم است؛ رجکسِ CORS در PHP از روی همین ساخته میشود
|
|
# (CorsRegexEnvProcessor → nelmio). چون این مقدار هیچ کاراکتر خاصی (`$`,`\`) ندارد، Coolify
|
|
# آن را خراب نمیکند — برخلاف CORS_ALLOW_ORIGIN که حذف شده است.
|
|
# افزودن/حذف دامنهٔ شهر: docker/frontend-domains.json را ویرایش کن، سپس
|
|
# ddev exec php docker/gen-cors-env.php (یا روی سرور: php docker/gen-cors-env.php)
|
|
# و خروجی ALLOWED_FRONTEND_HOSTS را اینجا/در Coolify جایگزین کن.
|
|
# ⚠️ دیگر CORS_ALLOW_ORIGIN را ست نکن (استفاده نمیشود).
|
|
ALLOWED_FRONTEND_HOSTS=ahvaz-nobat.ir,arak-nobat.ir,ardabil-nobat.ir,bandar-nobat.ir,behbahan-nobat.ir,birjand-nobat.ir,bojnord-nobat.ir,bushehr-nobat.ir,clinic-pro.ir,dehdasht-nobat.ir,esf-nobat.ir,golestan-nobat.ir,hamadan-nobat.ir,ilam-nobat.ir,karaj-nobat.ir,kerman-nobat.ir,kermanshah-nobat.ir,lorestan-nobat.ir,mashhad-nobat.ir,nobat724.com,qazvin-nobat.ir,qom-nobat.ir,rasht-nobat.ir,sanandaj-nobat.ir,sari-nobat.ir,semnan-nobat.ir,shiraz-nobat.ir,shkord-nobat.ir,tabriz-nobat.ir,tehran-nobat.ir,urmia-nobat.ir,yasuj-nobat.ir,yazd-nobat.ir,zahedan-nobat.ir,zanjan-nobat.ir
|
|
|
|
# ── Secrets (REQUIRED — set before the first deploy) ──
|
|
APP_SECRET= # php -r "echo bin2hex(random_bytes(32));"
|
|
JWT_PASSPHRASE= # openssl rand -hex 32 (must exist before first start: JWT keypair is generated with it)
|
|
|
|
# NOTE: DB_PASSWORD / DB_ROOT_PASSWORD are NO LONGER set here. The DB credentials
|
|
# now belong to the standalone MariaDB resource — set them when you create that
|
|
# resource, then embed the user password inside DATABASE_URL above.
|
|
|
|
# Tip: in Coolify you may use magic vars instead of hardcoding, e.g.
|
|
# APP_SECRET=${SERVICE_HEX_APPSECRET}
|
|
|
|
# ── Reverse proxy (Coolify/Traefik) ──
|
|
# Docker internal network ranges so Symfony trusts X-Forwarded-* headers.
|
|
TRUSTED_PROXIES=10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.1
|
|
|
|
# ── api.ir identity inquiry (Shahkar + IbanMatch) ──
|
|
# Empty token => fail-closed (representative verification is rejected).
|
|
API_IR_BASE_URL=https://s.api.ir
|
|
API_IR_TOKEN=
|
|
|
|
# ── SMS ──
|
|
# Kavenegar API key is read ONLY from this env (not the DB).
|
|
KAVENEGAR_API_KEY=
|
|
|
|
# ── Crawler service login (optional) ──
|
|
# Shared secret that lets the crawler / content pipeline bypass the LOGIN captcha
|
|
# only (password + rate-limit still apply). Must equal CLINICPRO_SERVICE_TOKEN on
|
|
# the crawler side. Passed to the container via docker-compose (x-app-env).
|
|
# Leave EMPTY unless ALTCHA is enabled — the compose ships ALTCHA_ENABLED=false, so
|
|
# there is no captcha to bypass. Generate with: openssl rand -hex 32
|
|
CRAWLER_SERVICE_TOKEN=
|
|
|
|
# ── Notes ──
|
|
# • Payment gateway keys (mellat/sep) are read from the DB ("Site Settings"), NOT from env.
|
|
# • REFRESH_TOKEN_TTL / OTP_TTL / MAX_FILE_SIZE_BYTES are fixed in the compose file.
|
|
# • MariaDB & Redis are separate Coolify resources — see DATABASE_URL/REDIS_URL above.
|
|
|
|
|
|
|
|
|
|
|
|
APP_ENV=prod
|
|
APP_DEBUG=0
|
|
APP_SECRET=...
|
|
JWT_PASSPHRASE=...
|
|
JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
|
|
JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
|
|
TRUSTED_PROXIES=10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.1
|
|
ALLOWED_FRONTEND_HOSTS=... # همان خروجی gen-cors-env.php (رجکس CORS از همین ساخته میشود)
|
|
API_IR_BASE_URL=https://s.api.ir
|
|
API_IR_TOKEN=...
|
|
REFRESH_TOKEN_TTL / OTP_TTL / MAX_FILE_SIZE_BYTES / UPLOAD_DIR
|