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>
77 lines
4.6 KiB
Bash
77 lines
4.6 KiB
Bash
# ============================================================
|
|
# Liara Environment Variables — ClinicPro (Docker, single app)
|
|
# ------------------------------------------------------------
|
|
# Liara does NOT support docker-compose. The whole app (web + both message
|
|
# consumers) runs as ONE Docker app via Supervisor (docker/supervisord.conf).
|
|
# MariaDB and Redis are SEPARATE Liara managed services on the PRIVATE NETWORK.
|
|
#
|
|
# Set these in the Liara console (Environment tab) or via the CLI:
|
|
# liara env set KEY=VALUE --app clinicpro-api
|
|
# Build/deploy config (platform, port, disks, healthcheck) lives in liara.json.
|
|
# ============================================================
|
|
|
|
APP_ENV=prod
|
|
APP_DEBUG=0
|
|
|
|
# ── Secrets (REQUIRED — set before the first deploy) ──
|
|
APP_SECRET= # php -r "echo bin2hex(random_bytes(32));"
|
|
JWT_PASSPHRASE= # openssl rand -hex 32 (JWT keypair is generated with it on first boot)
|
|
|
|
# ── Database / Redis (point at the Liara MANAGED services via PRIVATE hostnames) ──
|
|
# Create a MariaDB 11.8 service + a Redis service in Liara, enable private network,
|
|
# put both + this app on the SAME private network, then copy their private hosts here.
|
|
# serverVersion MUST match the MariaDB service (11.8).
|
|
DATABASE_URL="mysql://<user>:<pass>@<db-private-host>:3306/<db>?serverVersion=mariadb-11.8.0&charset=utf8mb4"
|
|
# retry_interval/tcp_keepalive: قطع کوتاه اتصال به redis بیسروصدا دوباره برقرار میشود
|
|
# و «Connection lost» بهصورت warning در app_log نمینشیند.
|
|
REDIS_URL="redis://<redis-private-host>:6379?timeout=5&read_timeout=5&retry_interval=100&tcp_keepalive=60"
|
|
MESSENGER_TRANSPORT_DSN="redis://<redis-private-host>:6379/messages"
|
|
# If the Redis service has a password: redis://:<pass>@<redis-private-host>:6379
|
|
|
|
# ── JWT key paths (keys live on the persistent 'jwt' disk, see liara.json) ──
|
|
JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
|
|
JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
|
|
|
|
# ── Backend's own domain (API host — used for payment callbacks & absolute URLs) ──
|
|
APP_BASE_URL=https://<your-liara-domain>
|
|
DEFAULT_URI=https://<your-liara-domain>
|
|
|
|
# ── Reverse proxy (Liara router) — trust X-Forwarded-* from the private ranges ──
|
|
TRUSTED_PROXIES=10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.1
|
|
|
|
# ── Frontend domains (MANY) — CORS ──
|
|
# فقط همین متغیرِ سادهٔ کامادار لازم است؛ رجکسِ CORS در PHP از روی همین ساخته میشود
|
|
# (CorsRegexEnvProcessor → nelmio). CORS_ALLOW_ORIGIN حذف شده — دیگر ست نکن.
|
|
# افزودن/حذف دامنه: docker/frontend-domains.json را ویرایش کن، سپس گرفتن خروجی جدید:
|
|
# ddev exec php docker/gen-cors-env.php (یا روی سرور: php docker/gen-cors-env.php)
|
|
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
|
|
|
|
# ── 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. Must equal CLINICPRO_SERVICE_TOKEN on the crawler side. Leave EMPTY unless
|
|
# ALTCHA is enabled (no captcha to bypass otherwise). Generate: openssl rand -hex 32
|
|
CRAWLER_SERVICE_TOKEN=
|
|
|
|
# ── Fixed app params (same values as the compose stack) ──
|
|
REFRESH_TOKEN_TTL=2592000
|
|
OTP_TTL=1200
|
|
MAX_FILE_SIZE_BYTES=5242880
|
|
UPLOAD_DIR=var/uploads
|
|
|
|
# ── Notes ──
|
|
# • Do NOT set RUN_INIT — entrypoint.sh defaults it to 1, and this is a single app,
|
|
# so JWT keygen + doctrine migrations run once on boot (correct).
|
|
# • Kavenegar API key is read ONLY from env — set KAVENEGAR_API_KEY below.
|
|
# • Payment gateway keys (mellat/sep) are read from the DB ("Site Settings"), NOT from env.
|
|
# • Liara exposes only ONE external HTTP port (8080, from liara.json). MariaDB/Redis
|
|
# are reachable on the private network only.
|