@@ -281,6 +281,7 @@ async def _persist_bulk_users(
281281 admin ,
282282 data_limit = user_to_create .data_limit ,
283283 expire = user_to_create .expire ,
284+ status = user_to_create .status ,
284285 on_hold_expire_duration = user_to_create .on_hold_expire_duration ,
285286 hwid_limit = user_to_create .hwid_limit ,
286287 data_limit_reset_strategy = user_to_create .data_limit_reset_strategy ,
@@ -374,11 +375,15 @@ async def _enforce_user_limits(
374375 * ,
375376 data_limit : int | None = None ,
376377 expire : dt | int | None = None ,
378+ status : UserStatus | None = None ,
377379 on_hold_expire_duration : int | None = None ,
378380 hwid_limit : int | None = None ,
379381 data_limit_reset_strategy = None ,
380382 next_plan = None ,
381383 check_max_users : bool = False ,
384+ require_finite_data_limit : bool = True ,
385+ require_finite_expire : bool = True ,
386+ require_finite_hwid_limit : bool = True ,
382387 ) -> None :
383388 """Enforce role-level limits and feature flags. No-op for owner."""
384389 if admin .is_owner :
@@ -391,6 +396,13 @@ async def _enforce_user_limits(
391396 if current_count >= limits .max_users :
392397 await self .raise_error (message = f"User limit reached ({ limits .max_users } )" , code = 400 , db = db )
393398
399+ if limits .data_limit_max is not None and require_finite_data_limit and (data_limit is None or data_limit <= 0 ):
400+ await self .raise_error (
401+ message = f"Data limit cannot be unlimited; maximum is { readable_size (limits .data_limit_max )} " ,
402+ code = 400 ,
403+ db = db ,
404+ )
405+
394406 if data_limit is not None and data_limit > 0 :
395407 if limits .data_limit_min is not None and data_limit < limits .data_limit_min :
396408 await self .raise_error (
@@ -401,6 +413,26 @@ async def _enforce_user_limits(
401413 message = f"Data limit cannot exceed { readable_size (limits .data_limit_max )} " , code = 400 , db = db
402414 )
403415
416+ status_value = getattr (status , "value" , status )
417+ uses_on_hold_duration = status_value == UserStatus .on_hold .value
418+
419+ if limits .expire_max is not None and require_finite_expire :
420+ if uses_on_hold_duration :
421+ if on_hold_expire_duration is None or on_hold_expire_duration <= 0 :
422+ await self .raise_error (
423+ message = (
424+ f"On-hold duration cannot be unlimited; maximum is { readable_duration (limits .expire_max )} "
425+ ),
426+ code = 400 ,
427+ db = db ,
428+ )
429+ elif expire is None or expire == 0 :
430+ await self .raise_error (
431+ message = f"Expire cannot be unlimited; maximum is { readable_duration (limits .expire_max )} from now" ,
432+ code = 400 ,
433+ db = db ,
434+ )
435+
404436 if expire is not None and expire != 0 :
405437 expire_dt = fix_datetime_timezone (expire )
406438 seconds = (expire_dt - datetime .now (timezone .utc )).total_seconds ()
@@ -431,6 +463,17 @@ async def _enforce_user_limits(
431463 db = db ,
432464 )
433465
466+ if (
467+ limits .max_hwid_per_user is not None
468+ and require_finite_hwid_limit
469+ and (hwid_limit is None or hwid_limit <= 0 )
470+ ):
471+ await self .raise_error (
472+ message = f"HWID limit cannot be unlimited; maximum is { limits .max_hwid_per_user } " ,
473+ code = 400 ,
474+ db = db ,
475+ )
476+
434477 if hwid_limit is not None :
435478 if limits .min_hwid_per_user is not None and hwid_limit < limits .min_hwid_per_user :
436479 await self .raise_error (
@@ -456,25 +499,26 @@ async def create_user(
456499 if new_user .hwid_limit is None :
457500 new_user .hwid_limit = hwid_conf .fallback_limit
458501
459- if new_user .hwid_limit is not None and not admin .is_owner :
460- if new_user .hwid_limit < hwid_conf .min_limit :
461- await self .raise_error (message = f"HWID limit cannot be less than { hwid_conf .min_limit } " , code = 400 , db = db )
462- if hwid_conf .max_limit > 0 and (new_user .hwid_limit > hwid_conf .max_limit or new_user .hwid_limit == 0 ):
463- await self .raise_error (message = f"HWID limit cannot exceed { hwid_conf .max_limit } " , code = 400 , db = db )
464-
465502 if not skip_role_limits :
466503 await self ._enforce_user_limits (
467504 db ,
468505 admin ,
469506 data_limit = new_user .data_limit ,
470507 expire = new_user .expire ,
508+ status = new_user .status ,
471509 on_hold_expire_duration = new_user .on_hold_expire_duration ,
472510 hwid_limit = new_user .hwid_limit ,
473511 data_limit_reset_strategy = new_user .data_limit_reset_strategy ,
474512 next_plan = new_user .next_plan ,
475513 check_max_users = True ,
476514 )
477515
516+ if new_user .hwid_limit is not None and not admin .is_owner :
517+ if new_user .hwid_limit < hwid_conf .min_limit :
518+ await self .raise_error (message = f"HWID limit cannot be less than { hwid_conf .min_limit } " , code = 400 , db = db )
519+ if hwid_conf .max_limit > 0 and (new_user .hwid_limit > hwid_conf .max_limit or new_user .hwid_limit == 0 ):
520+ await self .raise_error (message = f"HWID limit cannot exceed { hwid_conf .max_limit } " , code = 400 , db = db )
521+
478522 if new_user .next_plan is not None and new_user .next_plan .user_template_id is not None :
479523 await self .get_validated_user_template (db , new_user .next_plan .user_template_id )
480524
@@ -513,27 +557,61 @@ async def _prepare_modified_user(
513557 db = db ,
514558 )
515559
516- if modified_user .hwid_limit is not None and not admin .is_owner :
517- hwid_conf = await hwid_settings ()
518- if modified_user .hwid_limit < hwid_conf .min_limit :
519- await self .raise_error (message = f"HWID limit cannot be less than { hwid_conf .min_limit } " , code = 400 , db = db )
520- if hwid_conf .max_limit > 0 and (
521- modified_user .hwid_limit > hwid_conf .max_limit or modified_user .hwid_limit == 0
522- ):
523- await self .raise_error (message = f"HWID limit cannot exceed { hwid_conf .max_limit } " , code = 400 , db = db )
524-
525560 if not skip_role_limits :
561+ modified_fields = modified_user .model_fields_set
562+ data_limit_was_changed = "data_limit" in modified_fields and modified_user .data_limit is not None
563+ expire_was_changed = "expire" in modified_fields and modified_user .expire is not None
564+ status_was_changed = modified_user .status is not None
565+ modified_status_value = getattr (modified_user .status , "value" , modified_user .status )
566+ status_requires_finite_expire = modified_status_value in {
567+ UserStatus .active .value ,
568+ UserStatus .on_hold .value ,
569+ }
570+ on_hold_duration_was_changed = (
571+ "on_hold_expire_duration" in modified_fields and modified_user .on_hold_expire_duration is not None
572+ )
573+ expire_requires_finite_limit = (
574+ expire_was_changed
575+ or (status_was_changed and status_requires_finite_expire )
576+ or on_hold_duration_was_changed
577+ )
578+ hwid_limit_was_changed = "hwid_limit" in modified_fields and modified_user .hwid_limit is not None
579+
580+ effective_status = modified_user .status if modified_user .status is not None else db_user .status
581+ effective_expire = modified_user .expire
582+ effective_on_hold_expire_duration = modified_user .on_hold_expire_duration
583+ if status_was_changed and status_requires_finite_expire :
584+ if modified_status_value == UserStatus .on_hold .value :
585+ effective_expire = None
586+ if effective_on_hold_expire_duration is None :
587+ effective_on_hold_expire_duration = db_user .on_hold_expire_duration
588+ elif effective_expire is None :
589+ effective_expire = db_user .expire
590+
526591 await self ._enforce_user_limits (
527592 db ,
528593 admin ,
529594 data_limit = modified_user .data_limit ,
530- expire = modified_user .expire ,
531- on_hold_expire_duration = modified_user .on_hold_expire_duration ,
595+ expire = effective_expire ,
596+ status = effective_status ,
597+ on_hold_expire_duration = effective_on_hold_expire_duration ,
532598 hwid_limit = modified_user .hwid_limit ,
533599 data_limit_reset_strategy = modified_user .data_limit_reset_strategy ,
534600 next_plan = modified_user .next_plan ,
601+ require_finite_data_limit = data_limit_was_changed ,
602+ require_finite_expire = expire_requires_finite_limit ,
603+ require_finite_hwid_limit = hwid_limit_was_changed ,
535604 )
536605
606+ if modified_user .hwid_limit is not None and not admin .is_owner :
607+ hwid_conf = await hwid_settings ()
608+ if modified_user .hwid_limit < hwid_conf .min_limit :
609+ await self .raise_error (message = f"HWID limit cannot be less than { hwid_conf .min_limit } " , code = 400 , db = db )
610+ if hwid_conf .max_limit > 0 and (
611+ modified_user .hwid_limit > hwid_conf .max_limit or modified_user .hwid_limit == 0
612+ ):
613+ await self .raise_error (message = f"HWID limit cannot exceed { hwid_conf .max_limit } " , code = 400 , db = db )
614+
537615 validated_groups = None
538616 if modified_user .group_ids :
539617 validated_groups = await self .validate_all_groups (db , modified_user )
@@ -1250,7 +1328,14 @@ async def create_user_from_template(
12501328 raise exc
12511329
12521330 # Template defines data_limit/expire/etc — only check max_users
1253- await self ._enforce_user_limits (db , admin , check_max_users = True )
1331+ await self ._enforce_user_limits (
1332+ db ,
1333+ admin ,
1334+ check_max_users = True ,
1335+ require_finite_data_limit = False ,
1336+ require_finite_expire = False ,
1337+ require_finite_hwid_limit = False ,
1338+ )
12541339 return await self .create_user (db , new_user , admin , skip_role_limits = True )
12551340
12561341 async def _modify_user_with_template (
0 commit comments