# Database configuration POSTGRES_USER=postgres POSTGRES_PASSWORD=postgres POSTGRES_DB=goldbank # Host port exposed for PostgreSQL (used by docker-compose db service) EXTERNAL_DB_PORT=5432 # Prisma connection string (inside Docker network use hostname "db") DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}?schema=public # --------------------------------------------------------------------------- # Dev server ports — SINGLE SOURCE OF TRUTH for this monorepo. # Every app's dev script/config reads its port from here. Do not hardcode a # port anywhere else. strictPort is enabled everywhere: if a port below is # already taken, the dev server fails with a clear error instead of silently # picking another port. # --------------------------------------------------------------------------- API_PORT=3000 ADMIN_PANEL_PORT=5180 USER_APP_PORT=5181 USER_APP_V2_PORT=5182 # API Configuration (kept for any tooling that still reads bare PORT) PORT=3000 # --------------------------------------------------------------------------- # Authentication # --------------------------------------------------------------------------- # Admin JWT signing secret. REQUIRED in any real deployment — without it the # API falls back to a well-known development string. JWT_SECRET= # Admin token lifetime (default 12h). JWT_EXPIRES_IN= # Customer JWT signing secret. Deliberately SEPARATE from JWT_SECRET so an # admin token can never validate as a customer token (T1). JWT_CUSTOMER_SECRET= # Customer token lifetime (default 30d). JWT_CUSTOMER_EXPIRES_IN= # --------------------------------------------------------------------------- # Central IAM — delegated admin login (docs/iam-sso.md) # --------------------------------------------------------------------------- # OFF by default. When off, nothing changes: admin login stays a local bcrypt # check and no request ever leaves this server. # # When on, the admin's phone + password are verified against the central # identity service instead. There is NO UI change: no SSO button, no redirect. # If the identity service is unreachable or does not know the account, the # local password check still runs (see IAM_LOGIN_DELEGATION). IAM_ENABLED=false IAM_ISSUER= # The client must be registered there as confidential AND trusted. IAM_CLIENT_ID= IAM_CLIENT_SECRET= # One of the two; tenant_id wins when both are set. IAM_TENANT_ID= IAM_TENANT_SLUG= # off | fallback (default, safest) | exclusive (local password no longer accepted) IAM_LOGIN_DELEGATION=fallback IAM_HTTP_TIMEOUT_MS=8000 # صفحه callback اپ کاربر. باید در کلاینت IAM دقیقاً ثبت شود. IAM_REDIRECT_URI= # callbackهای اضافه (دولوپ و دامنه دیگر) با کاما. آدرس ناشناس جایگزین نمی‌شود. IAM_REDIRECT_URIS= # بعد از ورود محلی، مرورگر برای کوکی نشست مشترک به اینجا برمی‌گردد. IAM_BROWSER_RETURN_URL= # خروج سراسری فقط اگر این آدرس در post_logout کلاینت ثبت شده باشد برمی‌گردد. IAM_POST_LOGOUT_REDIRECT_URI= # مقصدهای مجاز خروج اپ کاربر، با کاما. باید عیناً در post_logout کلاینت باشند. IAM_ALLOWED_RETURN_URLS= # پنل ادمین. خالی یعنی بررسی بی‌صدای ورود در پنل انجام نمی‌شود. IAM_ADMIN_REDIRECT_URI= IAM_ADMIN_REDIRECT_URIS= IAM_ADMIN_BROWSER_RETURN_URL= IAM_ADMIN_POST_LOGOUT_REDIRECT_URI= IAM_ADMIN_ALLOWED_RETURN_URLS= # Revocable admin sessions (AdminSession table). # # An admin token is self-contained and lives for JWT_EXPIRES_IN (12h), so until # this existed nothing could end a session early — not disabling the account, not # a central sign-out. Every issued token now carries a `jti` recorded in the # database, and a revoked row is refused on the next request. # # Unset = follows IAM_ENABLED (on with the IAM, so central revocation reaches # here). `true` turns it on without the IAM; `false` opts out of the per-request # lookup and central revocation then cannot end a session here. AUTH_SESSION_TRACKING= # To let the IAM end sessions here, register this path on the client there as its # backchannel_logout_uri: # https:///api/iam/backchannel-logout # --------------------------------------------------------------------------- # ⚠ DELIVERY ONLY — REMOVE BEFORE THE REAL RELEASE (D20 / ISSUES REL-01) # --------------------------------------------------------------------------- # A fixed OTP that signs in ANY active customer, alongside the real code. # MUST BE EXACTLY 4 DIGITS: the apps' login box has four single-character # inputs and only submits at four (LoginView.vue:43). A longer code still works # over the API but cannot be typed into either app. # Leave it EMPTY or unset to disable it completely — that is the correct state # for production. While it is set, the API prints a warning banner at boot. OTP_MASTER_CODE= # --------------------------------------------------------------------------- # SMS gateway (Trez). Leave blank to disable sending: the server still boots, # messages are logged instead of sent, and customers cannot receive a login # code (D20). Never commit real values. # --------------------------------------------------------------------------- # "trez" (default) or "none" to disable explicitly. SMS_PROVIDER= # Override only for staging; defaults to the provider's live endpoint. SMS_TREZ_URL= SMS_TREZ_USERNAME= SMS_TREZ_PASSWORD= SMS_TREZ_SENDER_LINE= # --------------------------------------------------------------------------- # Uploads # --------------------------------------------------------------------------- # Absolute path for uploaded files; defaults to /storage/uploads. UPLOAD_DIR= # --------------------------------------------------------------------------- # Profile completion ("تکمیل پروفایل") # --------------------------------------------------------------------------- # Comma-separated admin mobiles told when a customer uploads their documents. # The reference hardcoded four numbers in its source # (henza_server Major/CustomerController::sendSmsToAdmin); they live here # instead so no personal number is committed. Blank means nobody is notified — # the upload and the customer's own SMS still go through (D20). PROFILE_ADMIN_MOBILES=