Refactor subscription synchronization logic for improved handling
- Enhanced the subscription syncing process to prioritize concrete subscription UUIDs for updates and creations, ensuring idempotency. - Implemented atomic updates for existing subscriptions and streamlined the creation of new subscriptions when a UUID is available. - Improved logging for subscription updates and creations to provide clearer feedback on sync actions. - Added handling for cases where no subscription UUID is present, avoiding unnecessary record creation.
This commit is contained in:
@@ -122,48 +122,88 @@ async def perform_sync(panel_service: PanelApiService, session: AsyncSession,
|
|||||||
panel_expire_at_iso.replace("Z", "+00:00")
|
panel_expire_at_iso.replace("Z", "+00:00")
|
||||||
)
|
)
|
||||||
|
|
||||||
# Update or create subscription
|
# Prefer syncing by concrete subscription UUID (shortUuid/subscriptionUuid)
|
||||||
active_sub = await subscription_dal.get_active_subscription_by_user_id(
|
subscription_uuid_from_panel = (
|
||||||
session, actual_user_id, panel_uuid
|
panel_user_dict.get("subscriptionUuid")
|
||||||
|
or panel_user_dict.get("shortUuid")
|
||||||
)
|
)
|
||||||
|
|
||||||
if active_sub:
|
if subscription_uuid_from_panel:
|
||||||
# Check if subscription needs update
|
# Try to find subscription by its panel_subscription_uuid first (idempotent)
|
||||||
if (active_sub.end_date != panel_expire_at or
|
existing_sub_by_uuid = (
|
||||||
active_sub.status_from_panel != panel_status or
|
await subscription_dal.get_subscription_by_panel_subscription_uuid(
|
||||||
active_sub.is_active != (panel_status == "ACTIVE")):
|
session, subscription_uuid_from_panel
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
await subscription_dal.update_subscription_end_date(
|
if existing_sub_by_uuid:
|
||||||
session, active_sub.subscription_id, panel_expire_at
|
# Atomic update of all relevant fields
|
||||||
|
await subscription_dal.update_subscription(
|
||||||
|
session,
|
||||||
|
existing_sub_by_uuid.subscription_id,
|
||||||
|
{
|
||||||
|
"user_id": actual_user_id,
|
||||||
|
"panel_user_uuid": panel_uuid,
|
||||||
|
"end_date": panel_expire_at,
|
||||||
|
"is_active": panel_status == "ACTIVE",
|
||||||
|
"status_from_panel": panel_status,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
# Update status fields
|
|
||||||
active_sub.status_from_panel = panel_status
|
|
||||||
active_sub.is_active = (panel_status == "ACTIVE")
|
|
||||||
subscriptions_synced_count += 1
|
subscriptions_synced_count += 1
|
||||||
subscriptions_updated += 1
|
subscriptions_updated += 1
|
||||||
user_was_updated = True
|
user_was_updated = True
|
||||||
logging.info(f"Updated subscription for user {actual_user_id}: expires {panel_expire_at}, status {panel_status}")
|
logging.info(
|
||||||
|
f"Synced existing subscription {existing_sub_by_uuid.subscription_id} for user {actual_user_id}: expires {panel_expire_at}, status {panel_status}"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
# Create new subscription record
|
# Create a new subscription only when we have a concrete subscription UUID
|
||||||
subscription_uuid_to_use = panel_subscription_uuid or panel_uuid
|
|
||||||
|
|
||||||
logging.info(f"Creating new subscription for user {actual_user_id} with UUID {subscription_uuid_to_use}")
|
|
||||||
|
|
||||||
sub_payload = {
|
sub_payload = {
|
||||||
"user_id": actual_user_id,
|
"user_id": actual_user_id,
|
||||||
"panel_user_uuid": panel_uuid,
|
"panel_user_uuid": panel_uuid,
|
||||||
"panel_subscription_uuid": subscription_uuid_to_use,
|
"panel_subscription_uuid": subscription_uuid_from_panel,
|
||||||
"start_date": datetime.now(timezone.utc),
|
# Do not guess precise start_date from panel; keep nullable
|
||||||
|
"start_date": None,
|
||||||
"end_date": panel_expire_at,
|
"end_date": panel_expire_at,
|
||||||
"duration_months": 1, # Default
|
"duration_months": None,
|
||||||
"is_active": panel_status == "ACTIVE",
|
"is_active": panel_status == "ACTIVE",
|
||||||
"status_from_panel": panel_status,
|
"status_from_panel": panel_status,
|
||||||
"traffic_limit_bytes": settings.user_traffic_limit_bytes,
|
"traffic_limit_bytes": settings.user_traffic_limit_bytes,
|
||||||
}
|
}
|
||||||
await subscription_dal.upsert_subscription(session, sub_payload)
|
created_sub = await subscription_dal.upsert_subscription(
|
||||||
|
session, sub_payload
|
||||||
|
)
|
||||||
subscriptions_synced_count += 1
|
subscriptions_synced_count += 1
|
||||||
subscriptions_created += 1
|
subscriptions_created += 1
|
||||||
user_was_updated = True
|
user_was_updated = True
|
||||||
|
logging.info(
|
||||||
|
f"Created subscription {created_sub.subscription_id} for user {actual_user_id} by panel_sub_uuid {subscription_uuid_from_panel}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# No subscription UUID from panel: only update an already active subscription for this user/panel UUID
|
||||||
|
active_sub = await subscription_dal.get_active_subscription_by_user_id(
|
||||||
|
session, actual_user_id, panel_uuid
|
||||||
|
)
|
||||||
|
if active_sub:
|
||||||
|
await subscription_dal.update_subscription(
|
||||||
|
session,
|
||||||
|
active_sub.subscription_id,
|
||||||
|
{
|
||||||
|
"end_date": panel_expire_at,
|
||||||
|
"is_active": panel_status == "ACTIVE",
|
||||||
|
"status_from_panel": panel_status,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
subscriptions_synced_count += 1
|
||||||
|
subscriptions_updated += 1
|
||||||
|
user_was_updated = True
|
||||||
|
logging.info(
|
||||||
|
f"Updated active subscription {active_sub.subscription_id} for user {actual_user_id}: expires {panel_expire_at}, status {panel_status}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# Without a concrete subscription UUID we avoid creating new records to keep sync idempotent
|
||||||
|
logging.debug(
|
||||||
|
f"No subscriptionUuid for panel user {panel_uuid}; skipped creation for user {actual_user_id}"
|
||||||
|
)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
sync_errors.append(f"Error syncing subscription for user {actual_user_id}: {str(e)}")
|
sync_errors.append(f"Error syncing subscription for user {actual_user_id}: {str(e)}")
|
||||||
|
|||||||
Reference in New Issue
Block a user