- Add node filter (comma-separated UUIDs) and status filter query params
- Add custom date range (start_date/end_date) as alternative to period
- Fetch connected device count per user via HWID API (semaphore=10)
- Cache key changed to (start_str, end_str) tuple for both modes
- CSV export now respects all active filters and date range
- Backend returns available_statuses and filtered nodes list
- Validate future dates, max 31-day range
Cabinet was calling YooKassaService.create_payment() directly, bypassing
PaymentService which saves the payment record to the local database.
When YooKassa webhook arrived, the payment was not found in the DB,
causing payment processing failures.
Now uses PaymentService.create_yookassa_payment() and
create_yookassa_sbp_payment() consistently with all other payment methods.
Also standardizes metadata key from 'type' to 'purpose' to match bot flow.
- Switch from per-user to per-node API strategy in _aggregate_traffic
(O(nodes) calls instead of O(users), ~10 vs ~200 requests)
- Add retry with exponential backoff for 429 in _make_request
- Reduce concurrency limit from 20 to 5 to prevent request bursts
- Switch from get_bandwidth_stats_node_users (broken UUID matching) to
get_bandwidth_stats_user per user (same API as working detail page)
- Add tariff filter with available_tariffs in response
- Add concurrency-limited parallel per-user bandwidth stats fetching
Sort by tariff_name/full_name crashed with TypeError when some values
were None (fallback to 0) mixed with strings. Use empty string fallback
for string fields with case-insensitive comparison.
Add paginated GET /admin/traffic endpoint aggregating per-user traffic
across all nodes with server-side sorting, search, and 5-min in-memory
cache. Add POST /admin/traffic/export-csv to generate CSV and send
to admin via Telegram DM.
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
- Add GitHub Markdown to Telegram HTML converter utility
- Place release description in blockquote expandable
- Auto-truncate description to fit 4096 char message limit
- Clean compact layout with clickable version link
- Convert markdown headers, bold, italic, code, links, strikethrough
- Use Redis key with 6h TTL to prevent notification spam on each monitoring cycle
- Fallback to sending notification if Redis is unavailable
- Key auto-expires when user tops up balance and autopay succeeds
- Skip daily tariff subscriptions in monitoring autopay cycle
- Filter daily subscriptions in get_subscriptions_for_autopay CRUD
- Block autopay menu and toggle for daily tariffs in bot handler
- Reject autopay enable for daily subscriptions in Cabinet API (HTTP 400)
- Reject autopay enable for daily subscriptions in MiniApp API (HTTP 400)
- Add real-time progress bar with updates every 500 msgs / 5 sec
- Fix Telegram rate limiting: batch=25, delay=1.0s (~25 msg/sec)
- Add global flood_wait_until to prevent semaphore slot starvation
- Add parse_mode=HTML for web API broadcasts
- Separate error handling for FloodWait, Forbidden, BadRequest
- Convert ORM objects to scalars before long broadcast operations
- Add email recipients dataclass to prevent detached ORM state
- Add discount calculation for purchase_devices and get_device_price endpoints
- Fix traffic purchase discount to use period-aware calculation
- Apply period discount to tariff switch upgrade_cost
- Return discount info in API responses for frontend display
- Extract scalar values from ORM objects before long operations
- Create fresh DB sessions for persist operations with retry mechanism
- Replace ORM User objects with telegram_id integers in broadcast loops
- Update .gitignore to exclude Python cache, IDE files, and local configs
Fixes: InterfaceError "connection is closed" and MissingGreenlet errors
during mass message broadcasts