From e4535210982351413cb82483000fe441e7b7300a Mon Sep 17 00:00:00 2001 From: Fringg Date: Mon, 16 Mar 2026 03:25:24 +0300 Subject: [PATCH] =?UTF-8?q?fix:=20=D1=83=D1=81=D1=82=D1=80=D0=B0=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D0=BE=D1=82=D0=BF=D1=80=D0=B0=D0=B2=D0=BA?= =?UTF-8?q?=D0=B0=20externalSquadUuid=3Dnull=20=D0=B2=20RemnaWave=20API=20?= =?UTF-8?q?(A039)=20=D0=B8=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20reduce=5Fdevices?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - reduce_devices: убрано молчаливое проглатывание ошибки RemnaWave, теперь при неудаче делается rollback и возвращается HTTP 502 - Убрана отправка external_squad_uuid=None в 8 местах: subscription_service, monitoring_service, remnawave_service, admin/users, cabinet/admin_users --- app/cabinet/routes/admin_users.py | 10 ++++------ app/cabinet/routes/subscription.py | 26 ++++++++++++++++++-------- app/handlers/admin/users.py | 5 ++--- app/services/monitoring_service.py | 5 ++--- app/services/remnawave_service.py | 10 ++++------ app/services/subscription_service.py | 8 +++----- 6 files changed, 33 insertions(+), 31 deletions(-) diff --git a/app/cabinet/routes/admin_users.py b/app/cabinet/routes/admin_users.py index 86d76033..3d049884 100644 --- a/app/cabinet/routes/admin_users.py +++ b/app/cabinet/routes/admin_users.py @@ -318,11 +318,10 @@ async def _sync_subscription_to_panel( if hwid_limit is not None: update_kwargs['hwid_device_limit'] = hwid_limit - # Внешний сквад: синхронизируем из тарифа или сбрасываем + # Внешний сквад: синхронизируем из тарифа (если задан) + # Не отправляем null — RemnaWave API не принимает null для externalSquadUuid (A039) if ext_squad_uuid is not None: update_kwargs['external_squad_uuid'] = ext_squad_uuid - else: - update_kwargs['external_squad_uuid'] = None try: updated_panel_user = await api.update_user(**update_kwargs) @@ -2777,11 +2776,10 @@ async def sync_user_to_panel( update_kwargs['hwid_device_limit'] = hwid_limit changes['device_limit'] = hwid_limit - # Внешний сквад: синхронизируем из тарифа или сбрасываем + # Внешний сквад: синхронизируем из тарифа (если задан) + # Не отправляем null — RemnaWave API не принимает null для externalSquadUuid (A039) if ext_squad_uuid is not None: update_kwargs['external_squad_uuid'] = ext_squad_uuid - else: - update_kwargs['external_squad_uuid'] = None try: await api.update_user(**update_kwargs) diff --git a/app/cabinet/routes/subscription.py b/app/cabinet/routes/subscription.py index c04238ea..951a0d97 100644 --- a/app/cabinet/routes/subscription.py +++ b/app/cabinet/routes/subscription.py @@ -3762,17 +3762,27 @@ async def reduce_devices( old_device_limit = current_device_limit - # Update subscription + # Update subscription in memory (will be committed by update_remnawave_user on success) subscription.device_limit = new_device_limit subscription.updated_at = datetime.now(UTC) - await db.commit() - # Update RemnaWave - try: - subscription_service = SubscriptionService() - await subscription_service.update_remnawave_user(db, subscription) - except Exception as e: - logger.error('Error updating RemnaWave user', error=e) + # Update RemnaWave — commits on success, returns None on failure + subscription_service = SubscriptionService() + result = await subscription_service.update_remnawave_user(db, subscription) + + if result is None: + # RemnaWave update failed — rollback local changes + await db.rollback() + logger.error( + 'Failed to update RemnaWave after device limit reduction', + user_id=user.id, + old_device_limit=old_device_limit, + new_device_limit=new_device_limit, + ) + raise HTTPException( + status_code=status.HTTP_502_BAD_GATEWAY, + detail='Не удалось обновить VPN-панель. Попробуйте позже.', + ) logger.info( f'User {user.id} reduced device limit from {old_device_limit} to {new_device_limit}' diff --git a/app/handlers/admin/users.py b/app/handlers/admin/users.py index d8ea5e22..5c8a8552 100644 --- a/app/handlers/admin/users.py +++ b/app/handlers/admin/users.py @@ -4590,11 +4590,10 @@ async def admin_buy_subscription_execute(callback: types.CallbackQuery, db_user: if hwid_limit is not None: update_kwargs['hwid_device_limit'] = hwid_limit - # Внешний сквад: синхронизируем из тарифа или сбрасываем + # Внешний сквад: синхронизируем из тарифа (если задан) + # Не отправляем null — RemnaWave API не принимает null для externalSquadUuid (A039) if ext_squad_uuid is not None: update_kwargs['external_squad_uuid'] = ext_squad_uuid - else: - update_kwargs['external_squad_uuid'] = None remnawave_user = await api.update_user(**update_kwargs) else: diff --git a/app/services/monitoring_service.py b/app/services/monitoring_service.py index 0ba7b0c8..444ea13a 100644 --- a/app/services/monitoring_service.py +++ b/app/services/monitoring_service.py @@ -390,11 +390,10 @@ class MonitoringService: if hwid_limit is not None: update_kwargs['hwid_device_limit'] = hwid_limit - # Внешний сквад: синхронизируем из тарифа или сбрасываем + # Внешний сквад: синхронизируем из тарифа (если задан) + # Не отправляем null — RemnaWave API не принимает null для externalSquadUuid (A039) if subscription.tariff and subscription.tariff.external_squad_uuid: update_kwargs['external_squad_uuid'] = subscription.tariff.external_squad_uuid - else: - update_kwargs['external_squad_uuid'] = None updated_user = await api.update_user(**update_kwargs) diff --git a/app/services/remnawave_service.py b/app/services/remnawave_service.py index 73106a4d..a13a3918 100644 --- a/app/services/remnawave_service.py +++ b/app/services/remnawave_service.py @@ -1961,11 +1961,10 @@ class RemnaWaveService: if hwid_limit is not None: create_kwargs['hwid_device_limit'] = hwid_limit - # Внешний сквад: синхронизируем из тарифа или сбрасываем + # Внешний сквад: синхронизируем из тарифа (если задан) + # Не отправляем null — RemnaWave API не принимает null для externalSquadUuid (A039) if sub.tariff and sub.tariff.external_squad_uuid: create_kwargs['external_squad_uuid'] = sub.tariff.external_squad_uuid - else: - create_kwargs['external_squad_uuid'] = None # Определяем UUID для обновления panel_uuid = user.remnawave_uuid @@ -2007,11 +2006,10 @@ class RemnaWaveService: if hwid_limit is not None: update_kwargs['hwid_device_limit'] = hwid_limit - # Внешний сквад: синхронизируем из тарифа или сбрасываем + # Внешний сквад: синхронизируем из тарифа (если задан) + # Не отправляем null — RemnaWave API не принимает null для externalSquadUuid (A039) if sub.tariff and sub.tariff.external_squad_uuid: update_kwargs['external_squad_uuid'] = sub.tariff.external_squad_uuid - else: - update_kwargs['external_squad_uuid'] = None try: await api.update_user(**update_kwargs) diff --git a/app/services/subscription_service.py b/app/services/subscription_service.py index 40709174..340aa40b 100644 --- a/app/services/subscription_service.py +++ b/app/services/subscription_service.py @@ -238,11 +238,10 @@ class SubscriptionService: if hwid_limit is not None: update_kwargs['hwid_device_limit'] = hwid_limit - # Внешний сквад: назначаем из тарифа или сбрасываем + # Внешний сквад: назначаем из тарифа (если задан) + # Не отправляем null — RemnaWave API не принимает null для externalSquadUuid (A039) if ext_squad_uuid is not None: update_kwargs['external_squad_uuid'] = ext_squad_uuid - else: - update_kwargs['external_squad_uuid'] = None updated_user = await api.update_user(**update_kwargs) @@ -872,10 +871,9 @@ class SubscriptionService: if hwid_limit is not None: update_kwargs['hwid_device_limit'] = hwid_limit + # Не отправляем null — RemnaWave API не принимает null для externalSquadUuid (A039) if ext_squad_uuid is not None: update_kwargs['external_squad_uuid'] = ext_squad_uuid - else: - update_kwargs['external_squad_uuid'] = None updated_user = await api.update_user(**update_kwargs)