# نمونه‌ی تنظیماتِ بک‌اندِ تدریسینو — برای لپ‌تاپ و سرور. # # cp .env.example .env # # هیچ‌کدام برای بالا آمدنِ سرویس اجباری نیست: دامنه‌ها، CORS، کلیدِ امضا و کش در # خودِ config/settings.py پیش‌فرضِ کارا دارند. مقدارهای این فایل با آن پیش‌فرض‌ها # ادغام می‌شوند، جایگزینشان نمی‌شوند. شرحِ کامل: docs/operations.md. # # مقدارِ واقعیِ کلیدها فقط در .env (که در git نیست)، هرگز در این فایل یا در چت. # ═══════════════════════════════════════════════════════════════════════ # ۱) پایه # ═══════════════════════════════════════════════════════════════════════ # روی لپ‌تاپ 1، روی سرور همیشه 0. # DEBUG=1 روی سرور یعنی کدِ ورودِ 0000 برای هر شماره‌ای و صفحه‌ی خطا با تنظیمات. DJANGO_DEBUG=0 # خالی بماند: بارِ اول یک کلیدِ تصادفی ساخته و در .django_secret_key (0600) نگه # داشته می‌شود. هرگز مقدارِ django-insecure-… داخلِ settings.py را این‌جا نگذارید. DJANGO_SECRET_KEY= # Redis برای محدودیتِ نرخِ مشترک بینِ workerها؛ خالی = کشِ حافظه‌ی هر worker # (/api/health هشدار می‌دهد). مثال: redis://127.0.0.1:6379/1 CACHE_URL= # Postgres: با ست‌بودنِ DB_NAME برنامه روی Postgres بالا می‌آید (DB_USER و DB_PASSWORD الزامی)؛ خالی = SQLite. DB_NAME= DB_USER= DB_PASSWORD= DB_HOST=127.0.0.1 DB_PORT=5432 # pool اتصال‌ها (config/settings.py): هر worker حداکثر DB_POOL_MAX_SIZE اتصال؛ بعد از DB_POOL_TIMEOUT ثانیه انتظار ۵۰۳. # workers × DB_POOL_MAX_SIZE + 6 باید زیرِ max_connectionsِ Postgres بماند (/api/health → dbPool). # DB_POOL_MAX_SIZE=8 # DB_POOL_TIMEOUT=10 # سقفِ هر کوئری (میلی‌ثانیه)؛ 0 برای فرمانِ مدیریتیِ طولانیِ دستی. # DB_STATEMENT_TIMEOUT_MS=30000 # اسلاتِ تکثیری که سرورِ بکاپ از آن کپیِ زنده می‌گیرد (ops/pgbackup/). روی سرور: zareian_247. # خالی روی Postgres = کپیِ بیرونی نداریم و /api/health هشدار می‌دهد. روی لپ‌تاپ خالی. OFFSITE_WAL_SLOT= # ═══════════════════════════════════════════════════════════════════════ # ۲) دامنه‌ها — دو آدرس، بقیه از آن‌ها ساخته می‌شوند # ═══════════════════════════════════════════════════════════════════════ # ALLOWED_HOSTS، CSRF، CORS، callbackِ زرین‌پال، برگشت از پرداخت و redirectِ بازار # همه از این دو ساخته می‌شوند. پیش‌فرضشان همین دامنه‌های فعلی است. # جابه‌جاییِ دامنه: docs/operations.md، بخشِ «عوض کردنِ دامنه». # # PUBLIC_BASE_URL=https://mathapi.tadrisino2.ir # FRONTEND_URL=https://math.tadrisino2.ir # # فقط برای دامنه‌ی *اضافه* (مثلاً دامنه‌ی قبلی در دورانِ جابه‌جایی): # DJANGO_ALLOWED_HOSTS=old.example.ir # CSRF_TRUSTED_ORIGINS=https://old.example.ir # CORS_ALLOWED_ORIGINS=https://old-app.example.ir # تعدادِ پروکسی‌های جلوی اپ. سرورِ فعلی: ArvanCloud → nginx، یعنی 2. # بیشتر از واقعیت = جعلِ IP و دور زدنِ محدودیتِ نرخ؛ کمتر = همه‌ی مهمان‌ها یک IP. NUM_PROXIES=2 # ═══════════════════════════════════════════════════════════════════════ # ۳) پیامکِ کدِ ورود (کاوه‌نگار) # ═══════════════════════════════════════════════════════════════════════ # بدونِ کلید، درخواستِ کد خطای صریحِ ۵۰۳ می‌دهد (نه موفقیتِ ساختگی). KAVENEGAR_API_KEY= # نامِ الگوی تأییدشده در پنلِ کاوه‌نگار (verify/lookup). روی سرورِ فعلی SMSVerify. # خطِ این حساب پیامکِ متنِ آزاد را لغو می‌کند، پس بدونِ الگو کدی نمی‌رسد. KAVENEGAR_OTP_TEMPLATE= # خطِ اختصاصی؛ خالی = خطِ پیش‌فرضِ حساب. KAVENEGAR_SENDER= # شماره‌های تستیِ تیم با کدِ ثابت، بدونِ پیامک: «شماره:کد» با کاما. # کد تصادفی باشد؛ پیش از کاربرِ واقعی خالی شود (/api/health تا آن‌وقت هشدار می‌دهد). # TEST_LOGIN_PHONES=09120000001:834512,09120000002:110947 TEST_LOGIN_PHONES= # ═══════════════════════════════════════════════════════════════════════ # ۴) دستیارِ هوش مصنوعی (RAG) # ═══════════════════════════════════════════════════════════════════════ # بدونِ این دو کلید، POST /api/ai/ask پاسخِ آزمایشیِ ثابت (stub) می‌دهد. # GapGPT پروکسیِ OpenAI است و فقط از داخلِ ایران جواب می‌دهد. OPENAI_API_KEY= OPENAI_BASE_URL=https://api.gapgpt.app/v1 QDRANT_URL=https://qdrant.tadrisino2.ir:443 QDRANT_API_KEY= # مدل‌ها؛ پیش‌فرض‌ها در settings.py همین‌اند و سنجیده شده‌اند (docs/architecture.md). # OPENAI_CHAT_MODEL=gpt-4.1-mini # OPENAI_VISION_MODEL=gpt-4.1-mini # مدلِ جدا فقط برای پایه‌های ۱۰ تا ۱۲؛ خالی = همان مدلِ بالا. # OPENAI_CHAT_MODEL_UPPER= # embedding را عوض نکنید مگر collectionهای Qdrant هم با همان مدل بازساخته شوند. # OPENAI_EMBEDDING_MODEL=text-embedding-3-small # سقفِ سوالِ روزانه از دستیار برای کاربرِ بدونِ اشتراک (پیش‌فرض ۳). سقفِ مشترکان روی # هر پلن در ادمین است (ai_daily_quota). حسابِ هزینه: docs/ai-cost-and-guardrails.md # AI_DAILY_QUOTA_FREE=2 # ═══════════════════════════════════════════════════════════════════════ # ۵) پرداخت # ═══════════════════════════════════════════════════════════════════════ # زرین‌پال. بدونِ مرچنت، پرداخت ۵۰۳ می‌دهد. مرچنت باید برای دامنه‌ی PUBLIC_BASE_URL # ثبت شده باشد، وگرنه زرین‌پال خطای -14 می‌دهد. ZARINPAL_MERCHANT_ID= # 1 = محیطِ آزمایشیِ زرین‌پال (بی‌پولِ واقعی). روی سرورِ فعلی خالی است: درگاهِ واقعی. ZARINPAL_SANDBOX= # کافه‌بازار (فقط خریدِ درون‌برنامه‌ایِ اپِ اندروید). روشِ ترجیحی توکنِ ثابتِ پیشخان # است؛ سه متغیرِ OAuth فقط وقتی آن نیست. بدونِ هیچ‌کدام، مسیرِ بازار بسته می‌ماند. BAZAAR_PISHKHAN_API_SECRET= BAZAAR_CLIENT_ID= BAZAAR_CLIENT_SECRET= BAZAAR_REFRESH_TOKEN= # BAZAAR_PACKAGE_NAME=com.tadrisino.math.app # کلیدِ «تخفیف پویا» از پیشخان (سمتِ سرور؛ قیمتِ ادمین و کدِ تخفیف را به بازار می‌گوید). BAZAAR_DYNAMIC_PRICE_KEY= # ═══════════════════════════════════════════════════════════════════════ # ۶) اختیاری — پیش‌فرض‌ها معمولاً کافی‌اند # ═══════════════════════════════════════════════════════════════════════ # کلیدِ مسیرهای داخلیِ سرور-به-سرور؛ خالی = آن مسیرها بسته‌اند (در DEBUG هم پیش‌فرضی ندارد). # INTERNAL_SERVICE_KEY= # مهمان‌های نمایشی که با «حذفِ حساب» پاک نمی‌شوند (با کاما). پیش‌فرض: frontend-test-device. # DEMO_GUEST_DEVICE_IDS= # با صفر قلب جلوی جواب دادن گرفته شود؟ پیش‌فرض روشن (از ۲۰۲۶-۱۰-۰۲)؛ ۰ خاموشش می‌کند. # HEARTS_ENFORCED=1 # سقف‌های کدِ ورود: هر شماره در روز، و کلِ سامانه در روز (ضدِ SMS pumping). # OTP_REQUEST_PHONE_DAILY_MAX=10 # OTP_SMS_GLOBAL_DAILY_MAX=300 # سقفِ خواندن روی اندپوینت‌های پرترافیک، و سقفِ /api/health. # READ_THROTTLE_RATE=120/min # HEALTH_THROTTLE_RATE=60/min # سقفِ تمدیدِ توکن برای هر IP (همه‌ی کاربرانِ پشتِ یک وای‌فای یا CGNAT با هم). # TOKEN_REFRESH_IP_RATE=600/hour # سقفِ روزانه‌ی کلِ سامانه برای سوال‌هایی که به مدلِ هوش مصنوعی می‌رسند (ضدِ خرجِ بی‌سقف). # AI_GLOBAL_DAILY_MAX=3000 # کشِ پاسخِ دستیار (ثانیه)؛ سوالِ تکراری دوباره به مدل نمی‌رود. # RAG_ANSWER_CACHE_TTL=1800 # 1 = دستیار حتی با کلیدها هم پاسخِ آزمایشی بدهد. # RAG_FORCE_MOCK= # لاگ در فایل علاوه بر stdout (فقط داخلِ Docker). # LOG_DIR= # رمزِ ادمینِ Grafana در docker compose (فقط محلی). # GRAFANA_ADMIN_PASSWORD=admin