From 49871f82f37d84979ea9ec91055e3f046d5854be Mon Sep 17 00:00:00 2001 From: Fringg Date: Mon, 9 Feb 2026 17:39:25 +0300 Subject: [PATCH] fix: prevent sync from overwriting end_date for non-ACTIVE panel users sync_users_to_panel uses _safe_expire_at_for_panel which replaces past end_dates with now+1min for expired subscriptions. When sync_users_from_panel reads these artificial dates back, it treated them as legitimate "newer" dates and overwrote all expired subscriptions' end_date to approximately current time. This caused all subscription end dates to show as "just now" after sync. Fix: only update end_date from panel when the panel user status is ACTIVE. For EXPIRED/DISABLED users, the panel date may be a _safe_expire_at artifact and should not override the real expiry date in the local database. --- app/services/remnawave_service.py | 59 ++++++++++++++++++------------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/app/services/remnawave_service.py b/app/services/remnawave_service.py index 6d0e6d89..59c7d7f0 100644 --- a/app/services/remnawave_service.py +++ b/app/services/remnawave_service.py @@ -1682,38 +1682,47 @@ class RemnaWaveService: # expire_at приходит в UTC (naive) из _parse_remnawave_date expire_at = self._parse_remnawave_date(expire_at_str) - # Конвертируем локальную дату из БД в UTC для корректного сравнения - # subscription.end_date хранится в локальной таймзоне (MSK) - local_end_date_utc = self._local_to_utc(subscription.end_date) + # Обновляем end_date только если пользователь ACTIVE в панели. + # Для EXPIRED/DISABLED панель может содержать искусственную дату + # (установленную _safe_expire_at_for_panel при sync_users_to_panel), + # которая не должна перезаписывать реальную дату окончания подписки. + if panel_status == 'ACTIVE': + # Конвертируем локальную дату из БД в UTC для корректного сравнения + local_end_date_utc = self._local_to_utc(subscription.end_date) - # КРИТИЧНО: НЕ перезаписываем end_date если локальная дата ПОЗЖЕ - # Это защищает от ситуации когда подписка была продлена в боте, - # но RemnaWave ещё не получил обновление или вернул старую дату - time_diff = abs((local_end_date_utc - expire_at).total_seconds()) - if time_diff > 60: - if expire_at > local_end_date_utc: - # RemnaWave имеет более позднюю дату - обновляем - # Конвертируем UTC обратно в локальное время для сохранения в БД - new_end_date_local = ( - expire_at.replace(tzinfo=self._utc_timezone) - .astimezone(self._panel_timezone) - .replace(tzinfo=None) - ) - logger.info( - f'✅ Sync: обновлена end_date для user {getattr(user, "telegram_id", "?")}: ' - f'{subscription.end_date} -> {new_end_date_local} (разница: {time_diff:.0f}с)' - ) - subscription.end_date = new_end_date_local + # КРИТИЧНО: НЕ перезаписываем end_date если локальная дата ПОЗЖЕ + # Это защищает от ситуации когда подписка была продлена в боте, + # но RemnaWave ещё не получил обновление или вернул старую дату + time_diff = abs((local_end_date_utc - expire_at).total_seconds()) + if time_diff > 60: + if expire_at > local_end_date_utc: + # RemnaWave имеет более позднюю дату - обновляем + # Конвертируем UTC обратно в локальное время для сохранения в БД + new_end_date_local = ( + expire_at.replace(tzinfo=self._utc_timezone) + .astimezone(self._panel_timezone) + .replace(tzinfo=None) + ) + logger.info( + f'✅ Sync: обновлена end_date для user {getattr(user, "telegram_id", "?")}: ' + f'{subscription.end_date} -> {new_end_date_local} (разница: {time_diff:.0f}с)' + ) + subscription.end_date = new_end_date_local + else: + # Локальная дата позже - НЕ перезаписываем + logger.debug( + f'⏭️ Sync: end_date для user {getattr(user, "telegram_id", "?")} актуальна: ' + f'локальная ({subscription.end_date} / UTC: {local_end_date_utc}) >= RemnaWave ({expire_at} UTC)' + ) else: - # Локальная дата позже - НЕ перезаписываем logger.debug( - f'⏭️ Sync: end_date для user {getattr(user, "telegram_id", "?")} актуальна: ' - f'локальная ({subscription.end_date} / UTC: {local_end_date_utc}) >= RemnaWave ({expire_at} UTC)' + f'⏭️ Sync: пропускаем обновление end_date для user {getattr(user, "telegram_id", "?")}: ' + f'разница слишком мала ({time_diff:.0f}с < 60с)' ) else: logger.debug( f'⏭️ Sync: пропускаем обновление end_date для user {getattr(user, "telegram_id", "?")}: ' - f'разница слишком мала ({time_diff:.0f}с < 60с)' + f'панель не ACTIVE (статус: {panel_status})' ) current_time = self._now_utc()