Files
remnawave-bedolaga-telegram…/app/cabinet/schemas/subscription.py
T
c0mrade 9a2aea038a chore: add uv package manager and ruff linter configuration
- Add pyproject.toml with uv and ruff configuration
- Pin Python version to 3.13 via .python-version
- Add Makefile commands: lint, format, fix
- Apply ruff formatting to entire codebase
- Remove unused imports (base64 in yookassa/simple_subscription)
- Update .gitignore for new config files
2026-01-24 17:45:27 +03:00

149 lines
4.0 KiB
Python

"""Subscription schemas for cabinet."""
from datetime import datetime
from pydantic import BaseModel, Field
class ServerInfo(BaseModel):
"""Server info for display."""
uuid: str
name: str
country_code: str | None = None
class TrafficPurchaseInfo(BaseModel):
"""Purchased traffic package info."""
id: int
traffic_gb: int
expires_at: datetime
created_at: datetime
days_remaining: int
progress_percent: float
class SubscriptionResponse(BaseModel):
"""User subscription data."""
id: int
status: str
is_trial: bool
start_date: datetime
end_date: datetime
days_left: int
hours_left: int = 0
minutes_left: int = 0
time_left_display: str = '' # Human readable format like "2д 5ч" or "5ч 30м"
traffic_limit_gb: int
traffic_used_gb: float
traffic_used_percent: float
device_limit: int
connected_squads: list[str] = []
servers: list[ServerInfo] = [] # Server display info
autopay_enabled: bool
autopay_days_before: int
subscription_url: str | None = None
hide_subscription_link: bool = False # Скрывать ли отображение ссылки (но кнопки работают)
is_active: bool
is_expired: bool
traffic_purchases: list[TrafficPurchaseInfo] = []
# Daily tariff fields
is_daily: bool = False
is_daily_paused: bool = False
daily_price_kopeks: int | None = None
next_daily_charge_at: datetime | None = None # When next daily charge will happen
tariff_id: int | None = None
tariff_name: str | None = None
class Config:
from_attributes = True
class RenewalOptionResponse(BaseModel):
"""Available subscription renewal option."""
period_days: int
price_kopeks: int
price_rubles: float
discount_percent: int = 0
original_price_kopeks: int | None = None
class RenewalRequest(BaseModel):
"""Request to renew subscription."""
period_days: int = Field(..., description='Renewal period in days')
class TrafficPackageResponse(BaseModel):
"""Available traffic package."""
gb: int
price_kopeks: int
price_rubles: float
is_unlimited: bool = False
class TrafficPurchaseRequest(BaseModel):
"""Request to purchase additional traffic."""
gb: int = Field(..., ge=0, description='GB to purchase (0 = unlimited)')
class DevicePurchaseRequest(BaseModel):
"""Request to purchase additional device slots."""
devices: int = Field(..., ge=1, description='Number of additional devices')
class AutopayUpdateRequest(BaseModel):
"""Request to update autopay settings."""
enabled: bool
days_before: int | None = Field(None, ge=1, le=30, description='Days before expiration to charge')
class TrialInfoResponse(BaseModel):
"""Trial subscription info."""
is_available: bool
duration_days: int
traffic_limit_gb: int
device_limit: int
requires_payment: bool = False
price_kopeks: int = 0
price_rubles: float = 0.0
reason_unavailable: str | None = None
# ============ Purchase Options Schemas ============
class PurchaseSelectionRequest(BaseModel):
"""User's selection for subscription purchase."""
period_id: str | None = Field(None, description="Period ID like 'days:30'")
period_days: int | None = Field(None, description='Period in days')
traffic_value: int | None = Field(None, description='Traffic in GB (0 = unlimited)')
servers: list[str] | None = Field(default_factory=list, description='Server UUIDs')
devices: int | None = Field(None, description='Device limit')
class PurchasePreviewRequest(BaseModel):
"""Request to preview purchase pricing."""
selection: PurchaseSelectionRequest
# ============ Tariff Purchase Schemas ============
class TariffPurchaseRequest(BaseModel):
"""Request to purchase a tariff."""
tariff_id: int = Field(..., description='Tariff ID to purchase')
period_days: int = Field(..., description='Period in days')
traffic_gb: int | None = Field(None, ge=0, description='Custom traffic in GB (for custom_traffic_enabled tariffs)')