Telegram API rejects messages with mismatched HTML tags. When
truncate_for_blockquote cuts the description mid-way, it can leave
tags like <i>, <b> unclosed inside the blockquote. Telegram then
fails with "Unmatched end tag" error.
Add _close_open_tags helper that scans for unclosed tags and appends
closing tags in reverse order. Also ensure the total length with
closing tags still fits within the message budget.
Always fetch 30 days with daily_bytes per node and categories.
Frontend computes period totals locally without extra API calls.
Removes days query param.
Per-node queries (8+ calls) hit Remnawave rate limit. Switch back to
single get_bandwidth_stats_user call with %Y-%m-%d date format (same
as traffic_monitoring_service). Add response logging to debug format.
Also optimize panel-info to use accessible-nodes instead of all-nodes.
The /api/bandwidth-stats/users/{uuid} endpoint rejects date params.
Switch to querying each accessible node via the working legacy
endpoint /api/bandwidth-stats/nodes/{uuid}/users/legacy and finding
the user in the per-node results.
- Add get_user_accessible_nodes() to fetch user's available nodes
- Fix date format from ISO datetime to date-only (Y-m-d) for bandwidth stats
- Show all accessible nodes (with zero traffic if no stats)
- Add country_code to node usage response
- Add OAuth provider config vars and helpers to config.py
- Add google_id, yandex_id, discord_id, vk_id columns to User model
- Create OAuth provider service with state management and 4 providers
- Add CRUD functions for OAuth user lookup, linking, and creation
- Add 3 API endpoints: providers list, authorize URL, callback
- Add alembic migration and universal_migration support
- Fix trial disable logic to cover OAuth auth_types
Add DisposableEmailService that fetches ~72k disposable email domains
from github.com/disposable/disposable-email-domains into an in-memory
frozenset with 24h auto-refresh via asyncio background task.
Integrated into three email entry points in cabinet auth routes:
- POST /email/register (link email to Telegram account)
- POST /email/register/standalone (standalone email registration)
- POST /email/change (change existing email)
Controlled by DISPOSABLE_EMAIL_CHECK_ENABLED setting (default: true).
Falls back to allowing all emails if domain list fetch fails.
New setting allows granular control over trial availability:
- none: trial available for all (default)
- email: trial disabled for email users
- telegram: trial disabled for telegram users
- all: trial disabled for everyone
Enforced in bot handlers, cabinet API, and miniapp routes.
Automatically appears in admin panel as dropdown via CHOICES.
Telegram Bot API 8.0+ adds a `signature` field to WebApp initData.
Per the official spec, both `hash` and `signature` must be excluded
from the data-check-string before HMAC verification. Without this,
users with newer Telegram clients get a hash mismatch and 401.
Also remove redundant `unquote()` in telegram_auth.py — `parse_qsl`
already URL-decodes values, so the extra decode could corrupt user
data containing percent-like sequences.
Add PUT /cabinet/admin/tariffs/order endpoint for drag-and-drop
tariff sorting in admin cabinet. Move db.commit() from CRUD to
route level for consistency.
Add BlacklistMiddleware for aiogram that blocks all message/callback/pre_checkout
from blacklisted users globally. Add blacklist check to cabinet API dependency.
Fix case-insensitive username matching. Remove 10 redundant manual checks from handlers.
- Add {{HAPP_CRYPT3_LINK}} template support in _resolve_button_url
- Only resolve templates for subscriptionLink and copyButton, not external
- Always send subscriptionUrl and subscriptionCryptoLink (hideLink is display-only flag)
- Pass uiConfig from RemnaWave config for block renderer selection
Preserve svgIconKey, displayName and other platform-level fields
instead of only forwarding apps array. Build platformNames from
RemnaWave displayName with English-only fallback.
- Return original blocks/svgLibrary instead of converting to steps
- Enrich apps with deepLink and buttons with resolvedUrl
- Add _resolve_button_url helper for template substitution
- Keep legacy file-based format as fallback