fix: close remaining daily subscription expire paths

- get_all_subscriptions: add selectinload(tariff) so validate_and_fix guard works
- get_subscriptions_batch: add selectinload(tariff) for sync flows
- get_expiring_subscriptions: exclude active daily subs (prevents spurious notifications)
- update_remnawave_user: add daily guard to prevent expire during panel sync
- _handle_user_disabled webhook: add daily guard to prevent deactivation
This commit is contained in:
Fringg
2026-03-04 05:46:37 +03:00
parent 0ed6397fa9
commit 618c936ac9
3 changed files with 46 additions and 8 deletions
+12 -4
View File
@@ -763,18 +763,26 @@ async def reactivate_subscription(db: AsyncSession, subscription: Subscription)
async def get_expiring_subscriptions(db: AsyncSession, days_before: int = 3) -> list[Subscription]:
from app.database.models import Tariff
threshold_date = datetime.now(UTC) + timedelta(days=days_before)
result = await db.execute(
select(Subscription)
.join(User, Subscription.user_id == User.id)
.options(selectinload(Subscription.user))
.outerjoin(Tariff, Subscription.tariff_id == Tariff.id)
.options(selectinload(Subscription.user), selectinload(Subscription.tariff))
.where(
and_(
Subscription.status == SubscriptionStatus.ACTIVE.value,
User.status == UserStatus.ACTIVE.value,
Subscription.end_date <= threshold_date,
Subscription.end_date > datetime.now(UTC),
# Не включаем активные суточные подписки — у них end_date всегда +24ч
~and_(
Tariff.is_daily.is_(True),
Subscription.is_daily_paused.is_(False),
),
)
)
)
@@ -1048,7 +1056,7 @@ async def get_all_subscriptions(db: AsyncSession, page: int = 1, limit: int = 10
result = await db.execute(
select(Subscription)
.options(selectinload(Subscription.user))
.options(selectinload(Subscription.user), selectinload(Subscription.tariff))
.order_by(Subscription.created_at.desc())
.offset(offset)
.limit(limit)
@@ -1064,10 +1072,10 @@ async def get_subscriptions_batch(
offset: int = 0,
limit: int = 500,
) -> list[Subscription]:
"""Получает подписки пачками для синхронизации. Загружает связанных пользователей."""
"""Получает подписки пачками для синхронизации. Загружает связанных пользователей и тарифы."""
result = await db.execute(
select(Subscription)
.options(selectinload(Subscription.user))
.options(selectinload(Subscription.user), selectinload(Subscription.tariff))
.order_by(Subscription.id)
.offset(offset)
.limit(limit)
+17 -4
View File
@@ -330,10 +330,23 @@ class MonitoringService:
is_active = subscription.status == SubscriptionStatus.ACTIVE.value and subscription.end_date > current_time
if subscription.status == SubscriptionStatus.ACTIVE.value and subscription.end_date <= current_time:
subscription.status = SubscriptionStatus.EXPIRED.value
await db.commit()
is_active = False
logger.info("📝 Статус подписки обновлен на 'expired'", subscription_id=subscription.id)
# Суточные подписки управляются DailySubscriptionService — не экспайрим
tariff = getattr(subscription, 'tariff', None)
is_active_daily = (
tariff is not None
and getattr(tariff, 'is_daily', False)
and not getattr(subscription, 'is_daily_paused', False)
)
if is_active_daily:
logger.debug(
'update_remnawave_user: пропуск expire для суточной подписки',
subscription_id=subscription.id,
)
else:
subscription.status = SubscriptionStatus.EXPIRED.value
await db.commit()
is_active = False
logger.info("📝 Статус подписки обновлен на 'expired'", subscription_id=subscription.id)
if not self.subscription_service.is_configured:
logger.warning(
+17
View File
@@ -499,6 +499,23 @@ class RemnaWaveWebhookService:
self, db: AsyncSession, user: User, subscription: Subscription | None, data: dict
) -> None:
if subscription:
# Суточные подписки управляются DailySubscriptionService — не деактивируем
tariff = getattr(subscription, 'tariff', None)
is_active_daily = (
tariff is not None
and getattr(tariff, 'is_daily', False)
and not getattr(subscription, 'is_daily_paused', False)
)
if is_active_daily:
logger.info(
'Webhook: пропуск disabled для суточной подписки',
subscription_id=subscription.id,
user_id=user.id,
)
self._stamp_webhook_update(subscription)
await db.commit()
return
self._stamp_webhook_update(subscription)
if subscription.status != SubscriptionStatus.DISABLED.value:
await deactivate_subscription(db, subscription)