list a user's own support tickets, Persian digits in completion notifications, rewrite .env.example to match the code
GET /api/support/tickets returns the caller's last 20 tickets with their support-reply count, on the read throttle so listing never spends the 30/hour create quota. Before it the app kept ticket ids only in one browser's localStorage, so a ticket and its answer vanished with the device. Completion notifications now use Persian digits and drop the 'with 0 stars ... 0 points' sentence when nothing was earned. .env.example: NUM_PROXIES=2 (ArvanCloud + nginx), KAVENEGAR_OTP_TEMPLATE, BAZAAR_PISHKHAN_API_SECRET, sandbox off by default; removed variables the code no longer reads. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
234
.env.example
234
.env.example
@@ -1,189 +1,117 @@
|
||||
# نمونهی فایلِ تنظیمات — هم برای ماشینِ خودتان، هم برای سرور.
|
||||
# نمونهی تنظیماتِ بکاندِ تدریسینو — برای لپتاپ و سرور.
|
||||
#
|
||||
# cp .env.example .env
|
||||
#
|
||||
# تقریباً هیچکدام اجباری نیستند. هر چیزی که از قبل قابلِ تصمیم بود در خودِ
|
||||
# کد پیشفرض شده: دامنهها، origin های مجاز، و کلیدِ امضا (اولین اجرا خودش
|
||||
# یک کلیدِ تصادفی میسازد و در .django_secret_key نگه میدارد). حتی با یک
|
||||
# فایلِ خالی هم سرویس بالا میآید.
|
||||
# هیچکدام برای بالا آمدنِ سرویس اجباری نیست: دامنهها، CORS، کلیدِ امضا و کش در
|
||||
# خودِ config/settings.py پیشفرضِ کارا دارند. مقدارهای این فایل با آن پیشفرضها
|
||||
# ادغام میشوند، جایگزینشان نمیشوند. شرحِ کامل: docs/operations.md.
|
||||
#
|
||||
# روی سرور، بعد از یکبار ساختنِ این فایل، هر آپدیت فقط:
|
||||
# git pull && ./deploy.sh
|
||||
# مقدارِ واقعیِ کلیدها فقط در .env (که در git نیست)، هرگز در این فایل یا در چت.
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# پایه
|
||||
# -----------------------------------------------------------------------
|
||||
# DEBUG اینجا عمداً 0 است.
|
||||
#
|
||||
# این فایل هم روی لپتاپِ توسعهدهنده کپی میشود هم روی سرور، و حالتِ پیشفرض
|
||||
# باید آنی باشد که اگر کسی فراموشش کند، بیخطر بماند. یک بار همین فایل با
|
||||
# مقدارِ 1 روی سرور نشست و ماهها آنجا ماند.
|
||||
#
|
||||
# ⚠️ DEBUG=1 روی سرورِ عمومی دو ریسک دارد:
|
||||
# • صفحههای خطا کلِ تنظیمات و traceback را نشان میدهند
|
||||
# • کدِ ورودِ آزمایشیِ 0000 برای هر شمارهای پذیرفته میشود، یعنی هر
|
||||
# کسی میتواند بهجای هر کاربری وارد شود
|
||||
#
|
||||
# روی ماشینِ خودتان (و فقط آنجا) این را 1 کنید تا خطاها کامل دیده شوند.
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
# ۱) پایه
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
# روی لپتاپ 1، روی سرور همیشه 0.
|
||||
# DEBUG=1 روی سرور یعنی کدِ ورودِ 0000 برای هر شمارهای و صفحهی خطا با تنظیمات.
|
||||
DJANGO_DEBUG=0
|
||||
|
||||
# خالی بگذارید تا خودش ساخته و در .django_secret_key ذخیره شود.
|
||||
# خالی بماند: بارِ اول یک کلیدِ تصادفی ساخته و در .django_secret_key (0600) نگه
|
||||
# داشته میشود. هرگز مقدارِ django-insecure-… داخلِ settings.py را اینجا نگذارید.
|
||||
DJANGO_SECRET_KEY=
|
||||
|
||||
# بدونِ Redis سرویس بالا میآید ولی محدودیتِ نرخ بینِ workerها مشترک نیست؛
|
||||
# /api/health همین را هشدار میدهد. برای پروداکشنِ واقعی پرش کنید.
|
||||
# Redis برای محدودیتِ نرخِ مشترک بینِ workerها؛ خالی = کشِ حافظهی هر worker
|
||||
# (/api/health هشدار میدهد). مثال: redis://127.0.0.1:6379/1
|
||||
CACHE_URL=
|
||||
|
||||
# نامِ سرویسِ systemd تا deploy.sh خودش ریاستارت کند (فقط روی سرور).
|
||||
# TADRISINO_SERVICE=
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# آدرسِ سرویس — دو آدرس، بقیه از آنها ساخته میشوند
|
||||
# -----------------------------------------------------------------------
|
||||
# ALLOWED_HOSTS، CSRF_TRUSTED_ORIGINS، CORS_ALLOWED_ORIGINS، callbackِ زرینپال،
|
||||
# برگشت از پرداخت و redirectِ بازار همه از این دو ساخته میشوند. پیشفرضشان
|
||||
# دامنهی فعلی است و لازم نیست ست شوند. برای جابهجایی دامنه فقط همین دو را
|
||||
# باز کنید (ترتیبِ کار در docs/operations.md، «عوض کردنِ دامنه»):
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
# ۲) دامنهها — دو آدرس، بقیه از آنها ساخته میشوند
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
# 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
|
||||
|
||||
# اختیاری — بدونش OTP واقعی ارسال نمیشه (فقط خطای صریح ۵۰۳ برمیگرده،
|
||||
# طبق رفتارِ موجودِ accounts/services.py)
|
||||
KAVENEGAR_API_KEY=
|
||||
# تعدادِ پروکسیهای جلوی اپ. سرورِ فعلی: ArvanCloud → nginx، یعنی 2.
|
||||
# بیشتر از واقعیت = جعلِ IP و دور زدنِ محدودیتِ نرخ؛ کمتر = همهی مهمانها یک IP.
|
||||
NUM_PROXIES=2
|
||||
|
||||
# شمارههای تستِ تیم — «شماره:کد» با کامای جداکننده.
|
||||
#
|
||||
# بدونِ این، روی سروری که DEBUG=0 است و کاوهنگار ندارد، هیچ توسعهدهندهای
|
||||
# نمیتواند وارد API شود مگر اینکه کسی دستی از پنلِ ادمین یک OTPCode بسازد.
|
||||
# با این، هر عضوِ تیم شماره و کدِ خودش را دارد و به کسی وابسته نیست.
|
||||
#
|
||||
# برای این شمارهها پیامکی ارسال نمیشود و کدشان هیچوقت در پاسخِ API
|
||||
# برنمیگردد. هر شمارهی دیگری مسیرِ کاملاً عادیِ OTP را میرود.
|
||||
#
|
||||
# کد را تصادفی بگذارید (نه 1234)، شماره را دقیقاً همانطور که کلاینت
|
||||
# میفرستد بنویسید، و پیش از باز کردنِ سرویس روی کاربرانِ واقعی خالیاش کنید —
|
||||
# /api/health تا وقتی پر باشد هشدار میدهد.
|
||||
#
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
# ۳) پیامکِ کدِ ورود (کاوهنگار)
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
# بدونِ کلید، درخواستِ کد خطای صریحِ ۵۰۳ میدهد (نه موفقیتِ ساختگی).
|
||||
KAVENEGAR_API_KEY=
|
||||
# نامِ الگوی تأییدشده در پنلِ کاوهنگار (verify/lookup). روی سرورِ فعلی SMSVerify.
|
||||
# خطِ این حساب پیامکِ متنِ آزاد را لغو میکند، پس بدونِ الگو کدی نمیرسد.
|
||||
KAVENEGAR_OTP_TEMPLATE=
|
||||
# خطِ اختصاصی؛ خالی = خطِ پیشفرضِ حساب.
|
||||
KAVENEGAR_SENDER=
|
||||
|
||||
# شمارههای تستیِ تیم با کدِ ثابت، بدونِ پیامک: «شماره:کد» با کاما.
|
||||
# کد تصادفی باشد؛ پیش از کاربرِ واقعی خالی شود (/api/health تا آنوقت هشدار میدهد).
|
||||
# TEST_LOGIN_PHONES=09120000001:834512,09120000002:110947
|
||||
TEST_LOGIN_PHONES=
|
||||
|
||||
# رمزِ ادمینِ Grafana (کاربرِ admin) — فقط برای دسترسیِ محلی
|
||||
GRAFANA_ADMIN_PASSWORD=admin
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# RAG / AI tutor (اپ rag) — موقعِ تست پر کنید
|
||||
# -----------------------------------------------------------------------
|
||||
# بدونِ OPENAI_API_KEY و QDRANT_API_KEY، مسیرِ RAG غیرفعاله و
|
||||
# POST /api/ai/ask همون پاسخِ stubِ قبلی رو میده (سازگاریِ عقبرو).
|
||||
# فعلاً از GapGPT (پروکسیِ OpenAI برای ایران) استفاده میکنیم.
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
# ۴) دستیارِ هوش مصنوعی (RAG)
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
# بدونِ این دو کلید، POST /api/ai/ask پاسخِ آزمایشیِ ثابت (stub) میدهد.
|
||||
# GapGPT پروکسیِ OpenAI است و فقط از داخلِ ایران جواب میدهد.
|
||||
OPENAI_API_KEY=
|
||||
OPENAI_BASE_URL=https://api.gapgpt.app/v1
|
||||
# اگه بعداً خواستی مستقیم OpenAI واقعی رو تست کنی: کلیدِ واقعی رو این پایین
|
||||
# پارک کن، بعد فقط دو خطِ بالا رو باهاش عوض کن. هیچ منطقِ سوییچی در کد نیست
|
||||
# تا ساده و بیاشتباه بمونه.
|
||||
# OPENAI_BASE_URL=https://api.openai.com/v1
|
||||
OPENAI_API_KEY_OPENAI_DIRECT=
|
||||
OPENAI_CHAT_MODEL=gpt-4.1-mini
|
||||
OPENAI_VISION_MODEL=gpt-4.1-mini
|
||||
# این رو تغییر نده؛ باید با مدلی که تیمِ دیگه باهاش collectionها رو ساخته یکی باشه.
|
||||
OPENAI_EMBEDDING_MODEL=text-embedding-3-small
|
||||
|
||||
QDRANT_URL=https://qdrant.tadrisino2.ir:443
|
||||
QDRANT_API_KEY=
|
||||
|
||||
# نامِ collection = f"{subject} {grade}".lower() . collectionهای موجود روی
|
||||
# سرورِ تیمِ دیگه (بررسیشده): «علوم تجربی سوم» ، «مطالعات اجتماعی چهارم» ،
|
||||
# «مطالعات اجتماعی ششم» ، «ریاضی سوم» ، «ریاضی ششم» ، «ریاضی هفتم».
|
||||
# یعنی جزءِ گرید فقط «سوم/چهارم/ششم/هفتم» است (بدونِ «دبستان»).
|
||||
#
|
||||
# کلیدِ فیلترِ درس دستی تنظیم نمیشه — rag/services/known_collections.py خودش
|
||||
# تشخیص میده: ریاضی → `chapter` (و شمارهی فصل رو به رشتهی دقیق تبدیل میکنه)،
|
||||
# بقیه → `lesson_number`.
|
||||
RAG_COLLECTION_NAME_TEMPLATE={subject} {grade}
|
||||
RAG_RETRIEVAL_TOP_K=4
|
||||
# RAG_FORCE_MOCK=1 یعنی حتی با وجودِ کلیدها هم مسیرِ RAG خاموش بمونه.
|
||||
RAG_FORCE_MOCK=
|
||||
# مدلها؛ پیشفرضها در 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
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# زرینپال (اپ payment) — درگاهِ اصلیِ پرداختِ ما
|
||||
# -----------------------------------------------------------------------
|
||||
# بدونِ ZARINPAL_MERCHANT_ID، مسیرِ پرداخت fail-closed میمونه (خطای صریحِ
|
||||
# ۵۰۳ بهجایِ پرداختِ ساختگیِ موفق).
|
||||
# callbackِ زرینپال از PUBLIC_BASE_URL (بالاتر) ساخته میشود.
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
# ۵) پرداخت
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
# زرینپال. بدونِ مرچنت، پرداخت ۵۰۳ میدهد. مرچنت باید برای دامنهی PUBLIC_BASE_URL
|
||||
# ثبت شده باشد، وگرنه زرینپال خطای -14 میدهد.
|
||||
ZARINPAL_MERCHANT_ID=
|
||||
# 1 = محیطِ sandbox زرینپال (برای تست بدونِ پرداختِ واقعی)
|
||||
ZARINPAL_SANDBOX=1
|
||||
# اختیاری — بعدِ تاییدِ پرداخت، کاربر به این URL (مثلاً یک deep link اپ) با
|
||||
# ?orderId=...&status=... ریدایرکت میشه. خالی = پاسخِ JSON ساده.
|
||||
ZARINPAL_REDIRECT_URL=
|
||||
# 1 = محیطِ آزمایشیِ زرینپال (بیپولِ واقعی). روی سرورِ فعلی خالی است: درگاهِ واقعی.
|
||||
ZARINPAL_SANDBOX=
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# تنظیماتِ مقیاسپذیری (همه دیفالتِ امن دارن)
|
||||
# -----------------------------------------------------------------------
|
||||
# persistent DB connection (ثانیه). با SQLite بیاثر؛ بعد از Postgres مهم.
|
||||
DB_CONN_MAX_AGE=60
|
||||
# کشِ کوتاهِ ردیفِ کاربر در auth تا هر request یک SELECT نزنه. 0 = خاموش
|
||||
# (پیشفرض). توصیهی پروداکشن با Redis: 30. با هر save/delete روی User باطل میشه.
|
||||
AUTH_USER_CACHE_TTL=0
|
||||
# سقفِ ارزانِ خواندن روی endpointهای پرترافیکِ GET (home/leaderboard/summary/...).
|
||||
READ_THROTTLE_RATE=120/min
|
||||
|
||||
# ⚠️ تعدادِ پروکسیهایی که جلوی اپ نشستهاند (nginx = ۱). محدودیتِ نرخِ
|
||||
# کاربرِ مهمان بر اساسِ IP است و IP از X-Forwarded-For خوانده میشود؛ اگر این
|
||||
# عدد از تعدادِ واقعیِ پروکسیها بیشتر باشد، کلاینت میتواند هدر را جعل کند و
|
||||
# محدودیت را دور بزند. کمتر باشد، همه پشتِ IP پروکسی یکی شمرده میشوند.
|
||||
NUM_PROXIES=1
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# بقیه — همه پیشفرضِ کارا دارند و معمولاً لازم نیست دست بخورند
|
||||
# -----------------------------------------------------------------------
|
||||
# پیامک: بدونِ SENDER هم کاوهنگار با خطِ پیشفرضِ حساب میفرستد، ولی اگر خطِ
|
||||
# اختصاصی دارید اینجا بگذارید. قالبِ متنِ پیامک هم قابلِ تغییر است ({code} لازم).
|
||||
KAVENEGAR_SENDER=
|
||||
# OTP_SMS_TEMPLATE=کد ورود شما به تدریسینو: {code}
|
||||
|
||||
# کلیدِ endpointهای داخلی (ingest در اپ rag، ساختِ سفارشِ سرور-به-سرور). با
|
||||
# DEBUG=1 مقدارِ توسعه دارد؛ با DEBUG=0 خالی است و آن مسیرها بسته میمانند
|
||||
# تا وقتی مقدارِ واقعی بگذارید.
|
||||
INTERNAL_SERVICE_KEY=
|
||||
|
||||
# با صفر قلب جلوی جواب دادن گرفته شود؟ (۱ = بله). فقط وقتی فرانت برای ۴۰۳ِ
|
||||
# no_hearts صفحهی «قلبها تمام شد» دارد.
|
||||
# HEARTS_ENFORCED=0
|
||||
|
||||
# پرداختِ درونبرنامهایِ کافهبازار. بدونِ این چهار تا، مسیرِ بازار
|
||||
# fail-closed میماند (payment/bazaar.py). REFRESH_TOKEN یکبار و دستی از
|
||||
# پنلِ devapi گرفته میشود.
|
||||
# کافهبازار (فقط خریدِ درونبرنامهایِ اپِ اندروید). روشِ ترجیحی توکنِ ثابتِ پیشخان
|
||||
# است؛ سه متغیرِ OAuth فقط وقتی آن نیست. بدونِ هیچکدام، مسیرِ بازار بسته میماند.
|
||||
BAZAAR_PISHKHAN_API_SECRET=
|
||||
BAZAAR_CLIENT_ID=
|
||||
BAZAAR_CLIENT_SECRET=
|
||||
BAZAAR_REFRESH_TOKEN=
|
||||
# BAZAAR_PACKAGE_NAME=ir.tadrisino.app
|
||||
# BAZAAR_API_BASE=https://pardakht.cafebazaar.ir/devapi/v2
|
||||
|
||||
# آدرسهای زرینپال از روی ZARINPAL_SANDBOX بالا انتخاب میشوند؛ فقط اگر
|
||||
# زرینپال دامنهاش را عوض کرد این سه را دستی بدهید.
|
||||
# ZARINPAL_API_BASE=
|
||||
# ZARINPAL_STARTPAY_BASE=
|
||||
# ZARINPAL_CALLBACK_URL=
|
||||
|
||||
# نوشتنِ لاگ در فایل علاوه بر stdout (داخلِ Docker ست میشود). خالی = فقط stdout.
|
||||
LOG_DIR=
|
||||
# سقفِ گیرندهی اکشنِ «ارسالِ پیامک به انتخابشدهها» در پنلِ ادمین. این اکشن
|
||||
# داخلِ همان request اجرا میشود، پس سقف جلوی تایماوت را میگیرد.
|
||||
ADMIN_SMS_MAX_RECIPIENTS=200
|
||||
# سقفِ نرخِ /api/health برای پایشِ بیرونی.
|
||||
HEALTH_THROTTLE_RATE=60/min
|
||||
# تایماوت و پارامترهای RAG. EMBEDDING_DIM باید با مدلِ embedding بالا بخواند.
|
||||
OPENAI_TIMEOUT_SECONDS=60
|
||||
QDRANT_TIMEOUT_SECONDS=30
|
||||
OPENAI_EMBEDDING_DIM=1536
|
||||
# کشِ پاسخِ دستیار (ثانیه) — سوالِ تکراری دوباره به مدل نمیرود.
|
||||
RAG_ANSWER_CACHE_TTL=1800
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
# ۶) اختیاری — پیشفرضها معمولاً کافیاند
|
||||
# ═══════════════════════════════════════════════════════════════════════
|
||||
# کلیدِ مسیرهای داخلیِ سرور-به-سرور؛ خالی روی سرور = آن مسیرها بستهاند.
|
||||
# INTERNAL_SERVICE_KEY=
|
||||
# با صفر قلب جلوی جواب دادن گرفته شود؟ فقط وقتی فرانت صفحهی «قلب تمام شد» دارد.
|
||||
# HEARTS_ENFORCED=0
|
||||
# سقفهای کدِ ورود: هر شماره در روز، و کلِ سامانه در روز (ضدِ 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
|
||||
# کشِ پاسخِ دستیار (ثانیه)؛ سوالِ تکراری دوباره به مدل نمیرود.
|
||||
# RAG_ANSWER_CACHE_TTL=1800
|
||||
# 1 = دستیار حتی با کلیدها هم پاسخِ آزمایشی بدهد.
|
||||
# RAG_FORCE_MOCK=
|
||||
# لاگ در فایل علاوه بر stdout (فقط داخلِ Docker).
|
||||
# LOG_DIR=
|
||||
# رمزِ ادمینِ Grafana در docker compose (فقط محلی).
|
||||
# GRAFANA_ADMIN_PASSWORD=admin
|
||||
|
||||
Reference in New Issue
Block a user