When a tariff has a stale external_squad_uuid that no longer exists in
the Remnawave panel, PATCH/POST /api/users fails with A039 (P2003 FK
constraint violation). This caused subscriptions to not sync with the
panel even though balance was already charged.
Now both update_user() and create_user() catch A039 errors and
automatically retry without externalSquadUuid, logging a warning about
the stale UUID. The subscription sync succeeds without the external
squad assignment rather than failing entirely.
Platega: handle verification ping POST without auth headers (empty body → 200 OK)
CryptoBot: always use API token for signature verification per docs, not WEBHOOK_SECRET
CryptoBot: reject requests without signature in both FastAPI and aiohttp handlers
Remove dead self.webhook_secret from CryptoBotService
Update tests to match new behavior
- Use API token as fallback for webhook signature verification per CryptoBot docs
- Try raw body, re-serialized compact JSON, and ASCII-escaped JSON for signature matching
- Auto-fill payment amount from saved cart in show_payment_methods instead of hardcoded 0
Caddy Security expects the caddy token in X-Api-Key and the Remnawave
API key in Authorization: Bearer. The headers were swapped, causing
401 errors for users with Caddy auth type.
- Убран fallback на deprecated поле user_id (удаляется 14 апреля 2026)
- Добавлен парсинг trb_user_id во всех ветках обработки webhook
- trb_user_id прокинут в результат и логи всех хендлеров
- remnawave_api: use str() before .lower() to handle non-string API messages
- yookassa recovery: cross-validate user_telegram_id metadata against resolved
user to prevent misattribution when legacy telegram_id fits in int32 range
"User already enabled" and "User already disabled" are expected
responses when reactivating subscriptions (e.g., traffic top-up on
active subscription with exhausted traffic). These should not
trigger error notifications in the admin chat.
- Add external_squad_uuid column to Tariff model with Alembic migration
- Add external_squad_uuid parameter to RemnaWave API create_user/update_user
- Pass external squad from tariff to RemnaWave on subscription creation/update
- Sync external squad in monitoring service, sync service, admin user management
- Clear external squad when tariff has none (consistent across all call sites)
- Add GET /available-external-squads endpoint with UUID validation and response model
- Update tariff schemas with UUID pattern validation
- Fix db.refresh to include tariff relationship for async safety
- SELECT FOR UPDATE блокировка во всех 9 провайдерах (кроме YooKassa — свой паттерн)
- create_transaction(commit=False) + единый db.commit() для атомарности
- emit_transaction_side_effects() для отложенных событий после коммита
- Все link_*_payment_to_transaction используют db.flush() вместо db.commit()
- Freekassa/KassaAI: прямое присвоение transaction_id + flush вместо update_status
- MulenPay: прямая мутация balance_kopeks вместо add_user_balance
- Platega: блокировка перед чтением metadata, инлайн обновления полей
- CloudPayments: int(round(amount * 100)) для корректного округления
- Heleket добавлен в SUPPORTED_AUTO_CHECK_METHODS
- Удалены PII из логов yookassa webhook (заголовки, IP)
- UniqueConstraint(external_id, payment_method) на транзакциях + миграция 0017
- Cabinet: PaymentService(bot=bot) внутри try блока
- verify_payment_amount утилита для проверки суммы webhook
1. Remove pointless HWID reset during auto-sync deactivation — user
doesn't exist in panel, API returns 404, UUID is cleaned up below.
2. Clean up RESTRICT FK references (AdminAuditLog, WithdrawalRequest,
AdminRole, UserRole, AccessPolicy) before deleting user to prevent
IntegrityError on admin_audit_log_user_id_fkey.
3. Fix device limit not being sent to RemnaWave when
DEVICES_SELECTION_DISABLED_AMOUNT=0: treat 0 as "no forced override"
instead of sending hwidDeviceLimit:0 (which Remnawave interprets as
unlimited). Now falls through to subscription.device_limit from tariff.
4. Add info-level logging to POST /api/users (was debug) to match
existing PATCH logging for device limit diagnostics.
- Add ContextVarsMiddleware for automatic user_id/chat_id/username binding
via structlog contextvars (aiogram) and http_method/http_path (FastAPI)
- Use bound_contextvars() context manager instead of clear_contextvars()
to safely restore previous state instead of wiping all context
- Register ContextVarsMiddleware as outermost middleware (before GlobalError)
so all error logs include user context
- Replace structlog.get_logger() with structlog.get_logger(__name__) across
270 calls in 265 files for meaningful logger names
- Switch wrapper_class from BoundLogger to make_filtering_bound_logger()
for pre-processor level filtering (performance optimization)
- Migrate 1411 %-style positional arg logger calls to structlog kwargs
style across 161 files via AST script
- Migrate log_rotation_service.py from stdlib logging to structlog
- Add payment module prefixes to TelegramNotifierProcessor.IGNORED_LOGGER_PREFIXES
and ExcludePaymentFilter.PAYMENT_MODULES to prevent payment data leaking
to Telegram notifications and general log files
- Fix LoggingMiddleware: add from_user null-safety for channel posts,
switch time.time() to time.monotonic() for duration measurement
- Remove duplicate logger assignments in purchase.py, config.py,
inline.py, and admin/payments.py
Delete dead Flask-based PAL24 webhook server (app/external/pal24_webhook.py).
PAL24 webhooks already handled by unified FastAPI server on port 8080.
- Remove flask dependency from pyproject.toml and requirements.txt
- Remove PAL24_WEBHOOK_PORT config (unused, FastAPI uses shared port)
- Remove pal24_webhook module reference from log filter
- Update docs: webhook example rewritten from Flask to FastAPI
- Uninstall flask, werkzeug, blinker, itsdangerous
Bulk device endpoint ignores take/skip params, causing duplicates.
Revert to single call. Add logging to discover extra fields in
panel user response that might include device count.
Add GET /admin/traffic/enrichment that returns per-user enrichment data
(connected devices, total spending, subscription dates, last connected node)
via bulk panel API calls with 5-min server-side cache.
- 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
- 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 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
1. Traffic (Трафик) - статистика трафика, топ пользователей по трафику, последние нарушения
2. Reports (Отчёты) - отчёты за период (6h, 12h, 24h, 48h, 72h), статистика активных пользователей и IP, топ нарушителей
3. Settings (Настройки) - управление настройками системы банов, группировка по категориям, переключатели для bool, ввод для int
4. Health (Здоровье) - статус системы (healthy/degraded/unhealthy), аптайм, статус компонентов