see the real client IP behind ArvanCloud, and mark all notifications read in one call

Production is ArvanCloud -> nginx -> gunicorn, but NUM_PROXIES was 1, so DRF
took ArvanCloud's edge IP as the client: every guest and every OTP request
behind one edge shared a single throttle bucket (20 guest sessions an hour for
everyone). Default is now 2, and /api/health reports clientIp so the proxy
count can be checked from outside.

PATCH /api/notifications/read-all replaces the frontend's one-PATCH-per-item
loop, which spent the shared "read" budget 75 requests at a time.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-24 05:03:27 +03:30
parent 147af3ac10
commit c874c186b1
8 changed files with 83 additions and 4 deletions

View File

@@ -36,6 +36,16 @@
`X-Forwarded-For` قابلِ اعتماد است و **باید با تعدادِ واقعیِ پروکسی‌ها یکی
باشد**، وگرنه جعلِ IP و دور زدنِ محدودیت ممکن می‌شود.
روی پروداکشن این عدد **۲** است (پیش‌فرضِ کد از ۲۰۲۶-۰۹-۲۴): ArvanCloud جلوی
سرور است و بعد nginxِ خودِ ماشین. تا آن روز ۱ بود و جنگو IPِ آروان را IPِ کاربر
می‌دید؛ یعنی همه‌ی مهمان‌ها یک سهمِ مشترک داشتند. `clientIp` در `/api/health`
نشان می‌دهد سرور صدازننده را با چه IPای می‌شناسد — باید IPِ عمومیِ خودتان باشد.
⚠️ سرور روی پورتِ ۸۰ مستقیم هم (بدونِ آروان) جواب می‌دهد. کسی که IPِ سرور را
بداند می‌تواند از آن راه XFF جعل کند و محدودیتِ IP را دور بزند. ورودِ OTP جدا
به ازای هر شماره هم محدود است، پس این راه به حدسِ کد نمی‌رسد. بستنِ کاملش (پورتِ
۸۰ فقط برای رنج‌های آروان، یا `set_real_ip_from` در nginx) کارِ ادمینِ سرور است.
## پرداخت: هرگز مستقیم به درگاه
`create_order` هیچ‌وقت زرین‌پال را صدا نمی‌زند. `paymentUrl` به اندپوینتِ خودمان