feat: add install guide share tokens and animations

This commit is contained in:
3252a8
2026-05-22 23:20:25 +03:00
parent d3925cad22
commit d9a4a007e2
14 changed files with 403 additions and 150 deletions
+16 -20
View File
@@ -32,12 +32,14 @@ async def subscription_guides_route(request: web.Request) -> web.Response:
async def public_subscription_guides_route(request: web.Request) -> web.Response:
short_uuid = _normalize_short_uuid(request.match_info.get("short_uuid"))
if not short_uuid:
return web.json_response({"ok": False, "error": "invalid_short_uuid"}, status=404)
share_token = subscription_dal.normalize_install_share_token(
request.match_info.get("share_token")
)
if not share_token:
return web.json_response({"ok": False, "error": "invalid_share_token"}, status=404)
status = await _subscription_guides_status_shared(request.app)
subscription = await _public_subscription_payload(request, short_uuid)
subscription = await _public_subscription_payload(request, share_token)
payload = {
"enabled": bool(status.get("enabled")),
"config": status.get("config") if status.get("enabled") else None,
@@ -160,19 +162,19 @@ async def _default_panel_subscription_page_config_uuid(panel_service: Any) -> st
async def _public_subscription_payload(
request: web.Request,
short_uuid: str,
share_token: str,
) -> Dict[str, Any]:
settings: Settings = request.app["settings"]
panel_service = _panel_service_from_app(request.app)
raw_link = ""
username = ""
resolved_short_uuid = short_uuid
resolved_short_uuid = ""
async_session_factory: sessionmaker = request.app["async_session_factory"]
async with async_session_factory() as session:
local_sub = await subscription_dal.get_subscription_by_panel_subscription_uuid(
local_sub = await subscription_dal.get_subscription_by_install_share_token(
session,
short_uuid,
share_token,
)
if (
@@ -185,16 +187,17 @@ async def _public_subscription_payload(
if panel_user:
raw_link = str(panel_user.get("subscriptionUrl") or "").strip()
username = str(panel_user.get("username") or "").strip()
resolved_short_uuid = str(panel_user.get("shortUuid") or short_uuid).strip()
resolved_short_uuid = str(panel_user.get("shortUuid") or "").strip()
display_link, connect_url = await prepare_config_links(settings, raw_link)
return {
"active": bool(display_link),
"config_link": display_link,
"connect_url": connect_url or display_link,
"panel_short_uuid": resolved_short_uuid,
"panel_short_uuid": resolved_short_uuid or None,
"install_share_token": share_token,
"username": username,
"share_url": _public_install_url(request, resolved_short_uuid),
"share_url": _public_install_url(request, share_token),
}
@@ -231,14 +234,7 @@ def _local_subscription_is_publicly_active(subscription: Any) -> bool:
)
def _normalize_short_uuid(value: Any) -> str:
short_uuid = str(value or "").strip()
if not re.fullmatch(r"[A-Za-z0-9_-]{8,128}", short_uuid):
return ""
return short_uuid
def _public_install_url(request: web.Request, short_uuid: str) -> str:
def _public_install_url(request: web.Request, share_token: str) -> str:
settings: Settings = request.app["settings"]
configured_base = str(getattr(settings, "SUBSCRIPTION_MINI_APP_URL", "") or "").strip()
if configured_base:
@@ -255,7 +251,7 @@ def _public_install_url(request: web.Request, short_uuid: str) -> str:
)
proto = request.headers.get("X-Forwarded-Proto") or request.scheme or "https"
base = f"{proto}://{host}"
return f"{base.rstrip('/')}/install/share/{quote(short_uuid)}"
return f"{base.rstrip('/')}/s/{quote(share_token)}"
def _subscription_page_request_headers(request: web.Request) -> Dict[str, str]:
+2 -2
View File
@@ -7,7 +7,7 @@ def setup_subscription_webapp_routes(app: web.Application) -> None:
app.router.add_get("/login/password", index_route)
app.router.add_get("/home", index_route)
app.router.add_get("/install", index_route)
app.router.add_get(r"/install/share/{short_uuid:[A-Za-z0-9_-]{8,128}}", index_route)
app.router.add_get(r"/s/{share_token:[a-f0-9]{32}}", index_route)
app.router.add_get("/invite", index_route)
app.router.add_get("/devices", index_route)
app.router.add_get("/settings", index_route)
@@ -64,7 +64,7 @@ def setup_subscription_webapp_routes(app: web.Application) -> None:
app.router.add_get("/api/me", me_route)
app.router.add_get("/api/subscription-guides", subscription_guides_route)
app.router.add_get(
r"/api/subscription-guides/public/{short_uuid:[A-Za-z0-9_-]{8,128}}",
r"/api/subscription-guides/public/{share_token:[a-f0-9]{32}}",
public_subscription_guides_route,
)
app.router.add_get("/api/account/avatar", account_avatar_route)
+43 -12
View File
@@ -53,6 +53,11 @@ async def _build_user_payload(request: web.Request, user_id: int) -> Dict[str, A
if db_user.panel_user_uuid
else None
)
install_share_token = (
await subscription_dal.ensure_install_share_token(session, local_sub)
if active and local_sub
else None
)
trial_available = bool(
settings.TRIAL_ENABLED
and settings.TRIAL_DURATION_DAYS > 0
@@ -83,7 +88,14 @@ async def _build_user_payload(request: web.Request, user_id: int) -> Dict[str, A
"language_code": lang,
"is_admin": is_admin,
},
"subscription": _serialize_subscription(request, settings, active, local_sub, lang),
"subscription": _serialize_subscription(
request,
settings,
active,
local_sub,
lang,
install_share_token=install_share_token,
),
"referral": {
"code": referral_code,
"bot_link": referral_link,
@@ -181,12 +193,26 @@ def _build_webapp_referral_link(
def _serialize_subscription(
request: web.Request,
settings: Settings,
active: Optional[Dict[str, Any]],
local_sub: Optional[Any],
lang: str,
request_or_settings: Any,
settings_or_active: Any,
active_or_local_sub: Optional[Any] = None,
local_sub_or_lang: Optional[Any] = None,
lang: Optional[str] = None,
*,
install_share_token: Optional[str] = None,
) -> Dict[str, Any]:
if lang is None:
request = None
settings = request_or_settings
active = settings_or_active
local_sub = active_or_local_sub
lang = str(local_sub_or_lang or "ru")
else:
request = request_or_settings
settings = settings_or_active
active = active_or_local_sub
local_sub = local_sub_or_lang
if not active:
return {
"active": False,
@@ -196,6 +222,7 @@ def _serialize_subscription(
"config_link": None,
"connect_url": None,
"panel_short_uuid": None,
"install_share_token": None,
"install_share_url": None,
}
@@ -237,6 +264,9 @@ def _serialize_subscription(
can_topup_devices = False
panel_short_uuid = str(active.get("panel_short_uuid") or "").strip()
share_token = str(
install_share_token or getattr(local_sub, "install_share_token", "") or ""
).strip()
return {
"active": seconds_left > 0,
"status": active.get("status_from_panel") or "UNKNOWN",
@@ -247,7 +277,8 @@ def _serialize_subscription(
"config_link": active.get("config_link"),
"connect_url": active.get("connect_button_url") or active.get("config_link"),
"panel_short_uuid": panel_short_uuid or None,
"install_share_url": _build_install_share_link(request, settings, panel_short_uuid),
"install_share_token": subscription_dal.normalize_install_share_token(share_token) or None,
"install_share_url": _build_install_share_link(request, settings, share_token),
"traffic_limit": _format_bytes(active.get("traffic_limit_bytes"), zero_as_unlimited=True),
"traffic_used": _format_bytes(active.get("traffic_used_bytes")),
"traffic_limit_bytes": _coerce_int_or_none(active.get("traffic_limit_bytes")),
@@ -293,12 +324,12 @@ def _serialize_subscription(
def _build_install_share_link(
request: web.Request,
request: Optional[web.Request],
settings: Settings,
short_uuid: str,
share_token: str,
) -> Optional[str]:
short_uuid = str(short_uuid or "").strip()
if not short_uuid:
share_token = subscription_dal.normalize_install_share_token(share_token)
if not share_token or request is None:
return None
configured_base = str(getattr(settings, "SUBSCRIPTION_MINI_APP_URL", "") or "").strip()
if configured_base:
@@ -315,7 +346,7 @@ def _build_install_share_link(
)
proto = request.headers.get("X-Forwarded-Proto") or request.scheme or "https"
base = f"{proto}://{host}"
return f"{base.rstrip('/')}/install/share/{quote(short_uuid)}"
return f"{base.rstrip('/')}/s/{quote(share_token)}"
def _serialize_plans(