Commit Graph

164 Commits

Author SHA1 Message Date
c0mrade 862352139e fix: use 'is not None' for telegram_id in create_user API (BUG-9) 2026-04-10 10:33:25 +03:00
c0mrade 78f963bf5e fix: batch bug fixes from user complaints
- 100% discount: daily tariff fallback to smallest configured period discount
- 100% discount: purchase blocked by safety guard (base_price → original_total in 6 guards)
- Gift subscription reset existing days (replace → extend for active/trial subs)
- Cabinet broadcast: target alias active_subscribers not mapped to active
- Promo code: error always "expired" — split into inactive/not_yet_valid/expired
- Multi-tariff: add delete subscription button in admin bot
- Multi-tariff → single: select subscription with most remaining time (end_date DESC)
- Gift purchases not counted in total spent (added GIFT_PAYMENT type)
- Remnawave API: retry on 502/503/504 (was only 429)
- Heleket: add from_referral_code to invoice payload
- Whitespace fix in blacklist_service
2026-04-08 18:33:17 +03:00
Fringg 972614511f fix: address remaining review issues in device limit patch
- Migrate get_device_reduction_info to get_user_devices_all (was raw _make_request)
- Migrate admin_users and miniapp callers to get_user_devices_all
- Migrate reset_user_devices internal call to paginated version
- Fix tariff_max_devices falsy-zero in handlers (use explicit is not None and > 0)
- Fix device deletion sort: dateless devices now sort last (candidates for removal)
2026-03-29 07:26:35 +03:00
Fringg 931eeb3568 fix: device limit decrease, HWID pagination, tariff max enforcement
1. Device decrease minimum is now always 1 (was incorrectly using
   tariff.device_limit as floor, blocking decrease e.g. 3/3)
2. Cabinet "Already at minimum device limit" fixed — same root cause
3. Added get_user_devices_all() with pagination for HWID cleanup
4. add_subscription_devices now caps by tariff.max_device_limit
5. Keyboard range expanded to 100 when no global limit set (was 20)
2026-03-29 07:18:55 +03:00
Fringg b59c581e91 feat: include countryEmoji and providerName in realtime metrics 2026-03-28 23:55:29 +03:00
Fringg 81505c8c1d chore: remove debug logging from get_nodes_realtime_usage 2026-03-28 23:47:50 +03:00
Fringg d6a49e8331 debug: log raw metrics structure to find correct key 2026-03-28 23:45:13 +03:00
Fringg 7ecd95aec0 debug: log raw metrics response structure 2026-03-28 23:40:35 +03:00
Fringg 1471320606 fix: parse_bytes now handles IEC units (GiB, MiB, KiB) from API 2026-03-28 23:37:59 +03:00
Fringg 3d0b874cb4 style: format remnawave_api.py per ruff 2026-03-28 23:28:49 +03:00
Fringg 5d173c806a feat: expose per-inbound traffic breakdown in nodes realtime API
get_nodes_realtime_usage() now preserves the per-inbound and
per-outbound traffic stats from /api/system/nodes/metrics instead
of summing them into node-level totals. Each node in the response
includes inbounds[] and outbounds[] arrays with tag, download,
upload, and total bytes.
2026-03-28 22:47:16 +03:00
Fringg 6d167d2922 fix: harden node info display against injection and type errors
- HTML-escape all external strings (cpuModel, node name, address,
  last_status_message, provider_uuid, versions) in Telegram HTML messages
- Add _safe_int() helper for defensive xrayUptime parsing
- Remove redundant int() casts in route serializers
2026-03-28 22:34:54 +03:00
Fringg 173cc374bb refactor: update remnawave API integration for v2.7.0
- Node: replace flat fields (cpuCount, cpuModel, totalRam, xrayVersion,
  nodeVersion) with nested versions and system dicts, add activePluginUuid
- Node: xrayUptime changed from string to int (seconds), usersOnline
  now non-nullable
- User: remove subLastOpenedAt and subLastUserAgent (dropped in v2.7.0)
- System stats: remove cpu.physicalCores, memory.available/active
- Update all layers: dataclass, service, Pydantic schemas, route
  serializers, Telegram admin handlers
- Fix variable scoping in show_node_statistics error fallback
2026-03-28 22:32:15 +03:00
c0mrade 565c08366b feat: add Remnawave panel 2.7.0 API support
- Add MONTH_ROLLING traffic reset strategy (enum, UI, mapping)
- Fix squad remove-users HTTP method POST → DELETE
- Add forceRestart param to restart_all_nodes (API, service, routes)
- Replace removed /bandwidth-stats/nodes/realtime with /system/nodes/metrics
- Fix parse_bytes suffix matching bug (B was catching GB/MB/KB/TB)
- Add torrent_blocker.report webhook event with admin notifications
- Add subpage_config_changed → auto-invalidate app config cache
- Pass webhook meta field to handlers (notConnectedAfterHours)
- Improve CRM/login webhook formatting (providerName, loginAttempt)
- Update bandwidth display: remove /s suffix, show inbound traffic totals
2026-03-28 12:34:15 +03:00
c0mrade 82958801b5 fix: harden webhook signature verification across all payment providers
- Replace fail-open with fail-closed in CryptoBot, Heleket, Tribute webhooks
  (missing API key now rejects instead of accepting)
- Use hmac.compare_digest for timing-safe comparison in Freekassa, KassaAI,
  Pal24, and Platega webhook verification
- CloudPayments: reject webhooks when signature header is missing but
  API_SECRET is configured (check, pay, fail, universal endpoints)
- CloudPayments: return code 13 (reject) instead of code 0 on parse errors
  and exception handlers to prevent fail-open
2026-03-23 18:45:46 +03:00
Fringg 8175bc8bfe fix: comprehensive security hardening across payment and API layers
- CloudPayments: require webhook signature when secret configured (all 4 handlers)
- Platega: timing-safe HMAC comparison via hmac.compare_digest
- CryptoBot/Heleket: return False when API token unconfigured
- Tribute: return 503 when API key not configured
- Freekassa: use request.client.host instead of X-Forwarded-For
- Pal24: verify webhook amount matches stored payment amount
- YooKassa: reject test-mode payments in production; add YOOKASSA_TEST_MODE config
- CloudPayments: reject test-mode payments in production
- WebAPI: add upper bounds to duration_days, traffic_limit_gb, device_limit schemas
- WebAPI: bound balance update amount to ±100M kopeks
- WebAPI: sign-dispatch for balance updates (negative → subtract_user_balance)
- WebAPI miniapp: add blocked/deleted user checks, restriction_topup/subscription guards
- Admin handlers: add @admin_required and @error_handler to moderator panel
- add_user_balance: guard against negative amounts (use subtract_user_balance instead)
2026-03-23 16:44:38 +03:00
Fringg 4990ddf9e4 fix: add diagnostic payload logging in create_user error path
Consistent with update_user — log full payload before re-raising
non-A039 errors to aid debugging.
2026-03-21 04:08:18 +03:00
Fringg de00612965 fix: retry Remnawave API calls without externalSquadUuid on A039 FK violation
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.
2026-03-21 03:58:21 +03:00
Fringg b40a812f3a fix: fix Platega and CryptoBot webhook verification
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
2026-03-18 20:11:55 +03:00
Fringg d7ad9d7033 fix: correct CryptoBot webhook signature verification and auto-fill topup amount from cart
- 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
2026-03-18 17:44:52 +03:00
Fringg 038c34e52a fix: swap Caddy auth headers — api_key to Authorization, caddy_token to X-Api-Key
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.
2026-03-17 23:28:44 +03:00
c0mrade 94199413c2 fix: миграция Tribute webhook с deprecated user_id на trb_user_id
- Убран fallback на deprecated поле user_id (удаляется 14 апреля 2026)
- Добавлен парсинг trb_user_id во всех ветках обработки webhook
- trb_user_id прокинут в результат и логи всех хендлеров
2026-03-17 12:43:04 +03:00
Fringg 8d7f0eea0f fix: лог полного payload при ошибке PATCH /api/users для диагностики A039 2026-03-16 03:44:45 +03:00
Fringg db2f0c93f2 fix: расширен лог PATCH /api/users payload для диагностики A039 2026-03-16 03:35:06 +03:00
Fringg f59b215645 style: fix import sorting and formatting after lint
ruff auto-fix for import ordering in cabinet/subscription.py and
formatting adjustments across changed files.
2026-03-12 22:58:35 +03:00
Fringg 585baaf63c fix: harden remnawave API error handling and YooKassa user cross-validation
- 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
2026-03-12 20:17:51 +03:00
Fringg 04197817fe fix: downgrade known-harmless RemnaWave 400s to warning level
"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.
2026-03-12 20:08:53 +03:00
Fringg c10d6780ba feat: add external squad support for tariffs
- 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
2026-03-07 05:44:19 +03:00
Fringg 4984f20e8f fix: устранение race conditions и атомарность платёжной системы
- 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
2026-03-06 04:20:41 +03:00
Fringg 1ce91749aa fix: resolve sync 404 errors, user deletion FK constraint, and device limit not sent to RemnaWave
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.
2026-02-25 11:53:49 +03:00
Fringg 97b3f899d1 fix: add diagnostic logging for device_limit sync to RemnaWave
Users report tariff change doesn't update device count and device
purchase doesn't sync to panel. Added structured logging to trace:
- resolve_hwid_device_limit: forced limit vs subscription limit
- PATCH /api/users: payload hwidDeviceLimit vs response value
2026-02-23 19:45:00 +03:00
Fringg eb18994b7d fix: complete datetime.utcnow() → datetime.now(UTC) migration
- Migrate 660+ datetime.utcnow() across 153 files to datetime.now(UTC)
- Migrate 30+ datetime.now() without UTC to datetime.now(UTC)
- Convert all 170 DateTime columns to DateTime(timezone=True)
- Add migrate_datetime_to_timestamptz() in universal_migration with SET LOCAL timezone='UTC' safety
- Remove 70+ .replace(tzinfo=None) workarounds
- Fix utcfromtimestamp → fromtimestamp(..., tz=UTC)
- Fix fromtimestamp() without tz= (system_logs, backup_service, referral_diagnostics)
- Fix fromisoformat/isoparse to ensure aware output (platega, yookassa, wata, miniapp, nalogo)
- Fix strptime() to add .replace(tzinfo=UTC) (backup_service, referral_diagnostics)
- Fix datetime.combine() to include tzinfo=UTC (remnawave_sync, traffic_monitoring)
- Fix datetime.max/datetime.min sentinels with .replace(tzinfo=UTC)
- Rename panel_datetime_to_naive_utc → panel_datetime_to_utc
- Remove DTZ003 from ruff ignore list
2026-02-17 04:45:40 +03:00
Fringg 1f0fef114b refactor: complete structlog migration with contextvars, kwargs, and logging hardening
- 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
2026-02-16 09:18:12 +03:00
Fringg ec8eaf52bf fix: downgrade transient API errors (502/503/504) to warning level
502/503/504 are transient errors that don't need ERROR reports in chat.
Also downgrade API connection test failure to warning.
2026-02-10 07:27:20 +03:00
Fringg 119f463c36 refactor: remove Flask, use FastAPI exclusively for all webhooks
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
2026-02-09 21:54:15 +03:00
Fringg 17af51ce0b fix: use correct pagination params (start/size) for bulk HWID devices
Remnawave API uses start/size (not take/skip) with default size=25.
Now fetches all devices with size=1000 per page. Remove debug logging.
2026-02-08 22:32:20 +03:00
Fringg 8f7fa76e6a fix: revert device pagination, add raw user data field discovery
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.
2026-02-08 22:26:06 +03:00
Fringg 4648a82da9 fix: paginate bulk device endpoint to fetch all HWID devices
The GET /api/hwid/devices endpoint returns only 25 devices by default.
Add take/skip pagination to fetch all devices across all pages.
2026-02-08 22:21:55 +03:00
Fringg 5cf3f2f76e feat: add traffic usage enrichment endpoint with devices, spending, dates, last node
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.
2026-02-08 21:49:42 +03:00
Fringg 38018514dc style: apply ruff formatting 2026-02-07 09:48:54 +03:00
Fringg 924d6bc09c fix: resolve 429 rate limiting on traffic page
- 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
2026-02-07 09:46:59 +03:00
Fringg c4da591731 fix: use accessible nodes API and fix date format for node usage
- 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
2026-02-07 06:22:07 +03:00
Egor 779cccffe6 Update tribute.py 2026-02-01 00:53:50 +03:00
c0mrade 1b212cc8d6 ci: add ruff lint workflow and fix formatting 2026-01-25 19:31:00 +03:00
Egor 99c60793b8 Update remnawave_api.py 2026-01-25 13:29:52 +03:00
Egor 5d66cc21bb Update remnawave_api.py 2026-01-25 10:52:13 +03:00
Egor b547bea807 Update remnawave_api.py 2026-01-25 10:34:51 +03:00
c0mrade 9a2aea038a chore: add uv package manager and ruff linter configuration
- 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
2026-01-24 17:45:27 +03:00
Egor d78f85735b Update remnawave_api.py 2026-01-20 00:52:59 +03:00
Egor 5fcc202542 Add files via upload 2026-01-17 01:18:02 +03:00