Files
remnawave-bedolaga-telegram…/app/cabinet/routes/subscription.py
T
Fringg 1ab1ff90bf feat: add subscription reissue with 15-min cooldown
- Add revoke handler for classic and multi-tariff modes with 2-step
  confirmation dialog and TOCTOU-safe cooldown enforcement
- Add cabinet API endpoint POST /subscription/revoke with 429 + Retry-After
  for cooldown, IDOR protection via resolve_subscription
- Add last_revoke_at column to subscriptions (Alembic migration 0071)
- Add SUBSCRIPTION_REVOKE_ENABLED and COOLDOWN_SECONDS config settings
- Add revoke button to classic subscription settings keyboard and
  multi-tariff detail keyboard (gated by feature toggle)
- Add locale keys for revoke UI in all 5 languages (ru, en, ua, zh, fa)
2026-05-04 08:08:56 +03:00

55 lines
1.8 KiB
Python

"""Subscription management routes for cabinet.
This module is a thin aggregator that includes all subscription sub-routers
from subscription_modules/. Each domain (status, purchase, traffic, devices, etc.)
lives in its own module for maintainability.
The router exported here preserves the original prefix='/subscription' and tags,
so all existing API paths remain unchanged.
"""
from fastapi import APIRouter, Depends, Query
from sqlalchemy.ext.asyncio import AsyncSession
from ..dependencies import get_cabinet_db, get_current_cabinet_user
from ..schemas.subscription import SubscriptionStatusResponse
from .subscription_modules import (
autopay_router,
daily_router,
devices_router,
purchase_router,
renewal_router,
revoke_router,
servers_router,
status_router,
tariff_switch_router,
traffic_router,
)
from .subscription_modules.status import get_subscription as _get_subscription_handler
router = APIRouter(prefix='/subscription', tags=['Cabinet Subscription'])
# Root endpoint: GET /subscription (empty path — must be on this router directly)
@router.get('', response_model=SubscriptionStatusResponse)
async def get_subscription(
user=Depends(get_current_cabinet_user),
db: AsyncSession = Depends(get_cabinet_db),
subscription_id: int | None = Query(None, description='Subscription ID for multi-tariff'),
):
return await _get_subscription_handler(user=user, db=db, subscription_id=subscription_id)
# Include all sub-routers
router.include_router(status_router)
router.include_router(renewal_router)
router.include_router(purchase_router)
router.include_router(traffic_router)
router.include_router(devices_router)
router.include_router(servers_router)
router.include_router(autopay_router)
router.include_router(daily_router)
router.include_router(tariff_switch_router)
router.include_router(revoke_router)