feat: add referral network graph visualization admin API
4 endpoints for referral network analysis: full graph with batched aggregation queries, user detail with recursive CTE branch counting, campaign detail with conversion metrics, and search with LIKE escaping. All endpoints rate-limited, scoped queries to prevent full-table scans, depth-limited recursive CTE, fail_closed on expensive graph endpoint.
This commit is contained in:
@@ -20,6 +20,7 @@ from .admin_pinned_messages import router as admin_pinned_messages_router
|
||||
from .admin_policies import router as admin_policies_router
|
||||
from .admin_promo_offers import router as admin_promo_offers_router
|
||||
from .admin_promocodes import promo_groups_router as admin_promo_groups_router, router as admin_promocodes_router
|
||||
from .admin_referral_network import router as admin_referral_network_router
|
||||
from .admin_remnawave import router as admin_remnawave_router
|
||||
from .admin_roles import router as admin_roles_router
|
||||
from .admin_sales_stats import router as admin_sales_stats_router
|
||||
@@ -99,6 +100,7 @@ router.include_router(admin_wheel_router)
|
||||
router.include_router(admin_tariffs_router)
|
||||
router.include_router(admin_servers_router)
|
||||
router.include_router(admin_stats_router)
|
||||
router.include_router(admin_referral_network_router)
|
||||
router.include_router(admin_sales_stats_router)
|
||||
router.include_router(admin_ban_system_router)
|
||||
router.include_router(admin_broadcasts_router)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+4
-2
@@ -400,9 +400,11 @@ return c
|
||||
return fail_closed
|
||||
|
||||
@staticmethod
|
||||
async def is_rate_limited(user_id: int, action: str, limit: int, window: int) -> bool:
|
||||
async def is_rate_limited(
|
||||
user_id: int, action: str, limit: int, window: int, *, fail_closed: bool = False,
|
||||
) -> bool:
|
||||
key = cache_key('rate_limit', user_id, action)
|
||||
return await RateLimitCache._atomic_rate_check(key, limit, window)
|
||||
return await RateLimitCache._atomic_rate_check(key, limit, window, fail_closed=fail_closed)
|
||||
|
||||
@staticmethod
|
||||
async def reset_rate_limit(user_id: int, action: str) -> bool:
|
||||
|
||||
Reference in New Issue
Block a user