diff --git a/app/webapi/routes/miniapp.py b/app/webapi/routes/miniapp.py index 3dbf5ebb..9ebdec4b 100644 --- a/app/webapi/routes/miniapp.py +++ b/app/webapi/routes/miniapp.py @@ -152,7 +152,6 @@ from ..schemas.miniapp import ( MiniAppSubscriptionRenewalPeriod, MiniAppSubscriptionRenewalRequest, MiniAppSubscriptionRenewalResponse, - MiniAppEmptyState, ) @@ -1740,7 +1739,6 @@ def _status_label(status: str) -> str: "trial": "Trial", "expired": "Expired", "disabled": "Disabled", - "none": "No subscription", } return mapping.get(status, status.title()) @@ -2087,44 +2085,35 @@ async def get_subscription_details( user = await get_user_by_telegram_id(db, telegram_id) purchase_url = (settings.MINIAPP_PURCHASE_URL or "").strip() - if not user: - detail: Dict[str, str] = { - "code": "user_not_found", - "message": "User is not registered in the bot", - } + if not user or not user.subscription: + detail: Union[str, Dict[str, str]] = "Subscription not found" if purchase_url: - detail["purchase_url"] = purchase_url + detail = { + "message": "Subscription not found", + "purchase_url": purchase_url, + } raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=detail, ) - subscription: Optional[Subscription] = getattr(user, "subscription", None) - status_actual = subscription.actual_status if subscription else "none" - has_active_subscription = status_actual in {"active", "trial"} - - traffic_used = _format_gb(subscription.traffic_used_gb) if subscription else 0.0 - traffic_limit_value = subscription.traffic_limit_gb if subscription else None + subscription = user.subscription + traffic_used = _format_gb(subscription.traffic_used_gb) + traffic_limit = subscription.traffic_limit_gb or 0 lifetime_used = _bytes_to_gb(getattr(user, "lifetime_used_traffic_bytes", 0)) - links_payload: Dict[str, Any] = {} - if subscription: - links_payload = await _load_subscription_links(subscription) + status_actual = subscription.actual_status + links_payload = await _load_subscription_links(subscription) - subscription_url = links_payload.get("subscription_url") if subscription else None - if not subscription_url and subscription: - subscription_url = subscription.subscription_url - - subscription_crypto_link = links_payload.get("happ_crypto_link") if subscription else None - if not subscription_crypto_link and subscription: - subscription_crypto_link = subscription.subscription_crypto_link + subscription_url = links_payload.get("subscription_url") or subscription.subscription_url + subscription_crypto_link = ( + links_payload.get("happ_crypto_link") + or subscription.subscription_crypto_link + ) happ_redirect_link = get_happ_cryptolink_redirect_link(subscription_crypto_link) - connected_squads: List[str] = [] - if subscription and subscription.connected_squads: - connected_squads = list(subscription.connected_squads) - + connected_squads: List[str] = list(subscription.connected_squads or []) connected_servers = await _resolve_connected_servers(db, connected_squads) devices_count, devices = await _load_devices_info(user) links: List[str] = links_payload.get("links") or connected_squads @@ -2323,21 +2312,6 @@ async def get_subscription_details( updated_at=getattr(service_rules, "updated_at", None), ) - subscription_status = subscription.status if subscription else "none" - expires_at = subscription.end_date if subscription else None - device_limit = subscription.device_limit if subscription else None - traffic_limit_label = ( - _format_limit_label(traffic_limit_value) - if subscription and traffic_limit_value is not None - else "—" - ) - traffic_limit_gb = traffic_limit_value if subscription else None - traffic_used_label = ( - _format_gb_label(traffic_used) - if subscription - else "0 GB" - ) - response_user = MiniAppSubscriptionUser( telegram_id=user.telegram_id, username=user.username, @@ -2353,17 +2327,17 @@ async def get_subscription_details( ), language=user.language, status=user.status, - subscription_status=subscription_status, + subscription_status=subscription.status, subscription_actual_status=status_actual, status_label=_status_label(status_actual), - expires_at=expires_at, - device_limit=device_limit, + expires_at=subscription.end_date, + device_limit=subscription.device_limit, traffic_used_gb=round(traffic_used, 2), - traffic_used_label=traffic_used_label, - traffic_limit_gb=traffic_limit_gb, - traffic_limit_label=traffic_limit_label, + traffic_used_label=_format_gb_label(traffic_used), + traffic_limit_gb=traffic_limit, + traffic_limit_label=_format_limit_label(traffic_limit), lifetime_used_traffic_gb=lifetime_used, - has_active_subscription=has_active_subscription, + has_active_subscription=status_actual in {"active", "trial"}, promo_offer_discount_percent=active_discount_percent, promo_offer_discount_expires_at=active_discount_expires_at, promo_offer_discount_source=promo_offer_source, @@ -2371,31 +2345,9 @@ async def get_subscription_details( referral_info = await _build_referral_info(db, user) - trial_available = not getattr(user, "has_had_paid_subscription", False) - empty_state_payload: Optional[MiniAppEmptyState] = None - if not has_active_subscription: - empty_state_payload = MiniAppEmptyState( - code="no_subscription", - title="Subscription not found", - message="Purchase a plan or activate a trial in the bot.", - purchase_url=purchase_url or None, - trial_available=trial_available, - ) - - subscription_id_value = subscription.id if subscription else None - remnawave_short_uuid = subscription.remnawave_short_uuid if subscription else None - subscription_type_value = ( - "trial" - if subscription and subscription.is_trial - else ("paid" if has_active_subscription else "none") - ) - autopay_enabled_value = bool(subscription.autopay_enabled) if subscription else False - return MiniAppSubscriptionResponse( - success=has_active_subscription, - state="subscription" if has_active_subscription else "no_subscription", - subscription_id=subscription_id_value, - remnawave_short_uuid=remnawave_short_uuid, + subscription_id=subscription.id, + remnawave_short_uuid=subscription.remnawave_short_uuid, user=response_user, subscription_url=subscription_url, subscription_crypto_link=subscription_crypto_link, @@ -2406,9 +2358,9 @@ async def get_subscription_details( connected_servers=connected_servers, connected_devices_count=devices_count, connected_devices=devices, - happ=links_payload.get("happ") if subscription else None, - happ_link=links_payload.get("happ_link") if subscription else None, - happ_crypto_link=links_payload.get("happ_crypto_link") if subscription else None, + happ=links_payload.get("happ"), + happ_link=links_payload.get("happ_link"), + happ_crypto_link=links_payload.get("happ_crypto_link"), happ_cryptolink_redirect_link=happ_redirect_link, balance_kopeks=user.balance_kopeks, balance_rubles=round(user.balance_rubles, 2), @@ -2428,13 +2380,12 @@ async def get_subscription_details( total_spent_kopeks=total_spent_kopeks, total_spent_rubles=round(total_spent_kopeks / 100, 2), total_spent_label=settings.format_price(total_spent_kopeks), - subscription_type=subscription_type_value, - autopay_enabled=autopay_enabled_value, + subscription_type="trial" if subscription.is_trial else "paid", + autopay_enabled=bool(subscription.autopay_enabled), branding=settings.get_miniapp_branding(), faq=faq_payload, legal_documents=legal_documents_payload, referral=referral_info, - empty_state=empty_state_payload, ) diff --git a/app/webapi/schemas/miniapp.py b/app/webapi/schemas/miniapp.py index 93a472b5..243a2fae 100644 --- a/app/webapi/schemas/miniapp.py +++ b/app/webapi/schemas/miniapp.py @@ -381,18 +381,9 @@ class MiniAppPaymentStatusResponse(BaseModel): results: List[MiniAppPaymentStatusResult] = Field(default_factory=list) -class MiniAppEmptyState(BaseModel): - code: str - title: Optional[str] = None - message: Optional[str] = None - purchase_url: Optional[str] = Field(default=None, alias="purchaseUrl") - trial_available: bool = Field(default=False, alias="trialAvailable") - - class MiniAppSubscriptionResponse(BaseModel): success: bool = True - state: str = "subscription" - subscription_id: Optional[int] = None + subscription_id: int remnawave_short_uuid: Optional[str] = None user: MiniAppSubscriptionUser subscription_url: Optional[str] = None @@ -424,7 +415,6 @@ class MiniAppSubscriptionResponse(BaseModel): faq: Optional[MiniAppFaq] = None legal_documents: Optional[MiniAppLegalDocuments] = None referral: Optional[MiniAppReferralInfo] = None - empty_state: Optional[MiniAppEmptyState] = Field(default=None, alias="emptyState") class MiniAppSubscriptionServerOption(BaseModel): diff --git a/miniapp/index.html b/miniapp/index.html index 108cfb5b..f99c02b6 100644 --- a/miniapp/index.html +++ b/miniapp/index.html @@ -320,50 +320,6 @@ line-height: 1.6; } - .empty-state { - text-align: center; - padding: 32px 24px; - background: var(--bg-secondary); - border-radius: var(--radius-xl); - margin: 20px 0; - box-shadow: var(--shadow-sm); - display: flex; - flex-direction: column; - gap: 16px; - } - - .empty-state .empty-icon { - font-size: 48px; - } - - .empty-state .empty-title { - font-size: 20px; - font-weight: 700; - color: var(--text-primary); - } - - .empty-state .empty-text { - font-size: 15px; - color: var(--text-secondary); - line-height: 1.6; - } - - .empty-state .empty-note { - font-size: 13px; - color: var(--text-secondary); - } - - .empty-state .empty-actions { - display: flex; - justify-content: center; - } - - .empty-state .empty-hint { - font-size: 13px; - color: var(--primary); - font-weight: 600; - } - .error-actions { margin-top: 24px; display: flex; @@ -1926,11 +1882,6 @@ color: #41464b; } - .status-none { - background: linear-gradient(135deg, #f8d7da, #fce4e6); - color: #842029; - } - /* Stats Grid */ .stats-grid { display: grid; @@ -4272,42 +4223,13 @@ - -