Skip to content

Commit b7bc617

Browse files
committed
feat: Add support for user deletion based on target type (expired or limited)
1 parent 81bbe6b commit b7bc617

File tree

9 files changed

+181
-63
lines changed

9 files changed

+181
-63
lines changed

app/db/crud/user.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from copy import deepcopy
22
from datetime import UTC, datetime, timedelta, timezone
33
from enum import Enum
4-
from typing import List, Optional, Sequence
4+
from typing import List, Literal, Optional, Sequence
55

66
from sqlalchemy import and_, case, delete, desc, func, literal, not_, or_, select, update
77
from sqlalchemy.ext.asyncio import AsyncSession
@@ -376,12 +376,20 @@ async def get_expired_users(
376376
expired_after: datetime | None = None,
377377
expired_before: datetime | None = None,
378378
admin_id: int | None = None,
379+
target: Literal["expired", "limited"] = "expired",
379380
):
380-
query = select(User).where(User.status.in_([UserStatus.limited, UserStatus.expired])).where(User.is_expired)
381-
if expired_after:
382-
query = query.where(User.expire >= expired_after)
383-
if expired_before:
384-
query = query.where(User.expire <= expired_before)
381+
query = select(User)
382+
383+
if target == "limited":
384+
query = query.where(User.status == UserStatus.limited).where(User.is_limited)
385+
else:
386+
# Time-expired users support expiration date range filtering.
387+
query = query.where(User.status == UserStatus.expired).where(User.is_expired)
388+
if expired_after:
389+
query = query.where(User.expire >= expired_after)
390+
if expired_before:
391+
query = query.where(User.expire <= expired_before)
392+
385393
if admin_id:
386394
query = query.where(User.admin_id == admin_id)
387395

app/operation/user.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import secrets
44
from collections import Counter
55
from datetime import datetime as dt, timedelta as td, timezone as tz
6+
from typing import Literal
67

78
from fastapi import HTTPException
89
from pydantic import ValidationError
@@ -559,22 +560,24 @@ async def get_expired_users(
559560
expired_after: dt = None,
560561
expired_before: dt = None,
561562
admin_username: str = None,
563+
target: Literal["expired", "limited"] = "expired",
562564
) -> list[str]:
563565
"""
564566
Get users who have expired within the specified date range.
565567
568+
- **target**: `expired` (time-based) or `limited` (usage-based).
566569
- **expired_after** UTC datetime (optional)
567570
- **expired_before** UTC datetime (optional)
568-
- At least one of expired_after or expired_before must be provided for filtering
569-
- If both are omitted, returns all expired users
571+
- Date range filters are applied only when target is `expired`.
572+
- If both dates are omitted, returns all users matching target.
570573
"""
571574

572575
expired_after, expired_before = await self.validate_dates(expired_after, expired_before, False)
573576
if admin_username:
574577
admin_id = (await self.get_validated_admin(db, admin_username)).id
575578
else:
576579
admin_id = None
577-
users = await get_expired_users(db, expired_after, expired_before, admin_id)
580+
users = await get_expired_users(db, expired_after, expired_before, admin_id, target=target)
578581
return [row.username for row in users]
579582

580583
async def delete_expired_users(
@@ -584,13 +587,15 @@ async def delete_expired_users(
584587
expired_after: dt | None = None,
585588
expired_before: dt | None = None,
586589
admin_username: str = None,
590+
target: Literal["expired", "limited"] = "expired",
587591
) -> RemoveUsersResponse:
588592
"""
589593
Delete users who have expired within the specified date range.
590594
595+
- **target**: `expired` (time-based) or `limited` (usage-based).
591596
- **expired_after** UTC datetime (optional)
592597
- **expired_before** UTC datetime (optional)
593-
- At least one of expired_after or expired_before must be provided
598+
- Date range filters are applied only when target is `expired`.
594599
"""
595600

596601
expired_after, expired_before = await self.validate_dates(expired_after, expired_before, False)
@@ -599,7 +604,7 @@ async def delete_expired_users(
599604
admin_id = (await self.get_validated_admin(db, admin_username)).id
600605
else:
601606
admin_id = None
602-
users = await get_expired_users(db, expired_after, expired_before, admin_id)
607+
users = await get_expired_users(db, expired_after, expired_before, admin_id, target=target)
603608
await remove_users(db, users)
604609

605610
username_list = [row.username for row in users]

app/routers/user.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from datetime import datetime as dt
2+
from typing import Literal
23

34
from fastapi import APIRouter, Depends, Query, status
45

@@ -302,38 +303,47 @@ async def get_expired_users(
302303
db: AsyncSession = Depends(get_db),
303304
_: AdminDetails = Depends(check_sudo_admin),
304305
admin_username: str | None = None,
306+
target: Literal["expired", "limited"] = Query("expired"),
305307
expired_after: dt | None = Query(None, examples=["2024-01-01T00:00:00+03:30"]),
306308
expired_before: dt | None = Query(None, examples=["2024-01-31T23:59:59+03:30"]),
307309
):
308310
"""
309-
Get users who have expired within the specified date range.
311+
Get cleanup-target users in the specified scope.
310312
313+
- **target**: `expired` (time-based) or `limited` (usage-based)
311314
- **expired_after** UTC datetime (optional)
312315
- **expired_before** UTC datetime (optional)
313-
- At least one of expired_after or expired_before must be provided for filtering
314-
- If both are omitted, returns all expired users
316+
- Date range filters are applied only when target is `expired`
315317
"""
316318

317-
return await user_operator.get_expired_users(db, expired_after, expired_before, admin_username)
319+
return await user_operator.get_expired_users(
320+
db,
321+
expired_after,
322+
expired_before,
323+
admin_username,
324+
target=target,
325+
)
318326

319327

320328
@router.delete("s/expired", response_model=RemoveUsersResponse)
321329
async def delete_expired_users(
322330
db: AsyncSession = Depends(get_db),
323331
admin: AdminDetails = Depends(check_sudo_admin),
324332
admin_username: str | None = None,
333+
target: Literal["expired", "limited"] = Query("expired"),
325334
expired_after: dt | None = Query(None, examples=["2024-01-01T00:00:00+03:30"]),
326335
expired_before: dt | None = Query(None, examples=["2024-01-31T23:59:59+03:30"]),
327336
):
328337
"""
329-
Delete users who have expired within the specified date range.
338+
Delete cleanup-target users in the specified scope.
330339
340+
- **target**: `expired` (time-based) or `limited` (usage-based)
331341
- **expired_after** UTC datetime (optional)
332342
- **expired_before** UTC datetime (optional)
333-
- At least one of expired_after or expired_before must be provided
343+
- Date range filters are applied only when target is `expired`
334344
"""
335345
return await user_operator.delete_expired_users(
336-
db, admin, expired_after, expired_before, admin_username=admin_username
346+
db, admin, expired_after, expired_before, admin_username=admin_username, target=target
337347
)
338348

339349

dashboard/public/statics/locales/en.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,19 +420,31 @@
420420
"expiredUsers": {
421421
"title": "Delete Expired Accounts",
422422
"description": "Remove accounts that have expired within a specific date range",
423+
"target": "Delete Target",
424+
"targetPlaceholder": "Select target",
425+
"targets": {
426+
"expired": "Time Expired",
427+
"limited": "Usage Limited"
428+
},
423429
"expiredAfter": "Expired After",
424430
"expiredBefore": "Expired Before",
425431
"expiredAfterPlaceholder": "Select start date",
426432
"expiredBeforePlaceholder": "Select end date",
427433
"dateRange": "Date Range",
428434
"deleteExpired": "Delete Expired",
435+
"deleteLimited": "Delete Limited",
429436
"deleting": "Deleting...",
430437
"deleteSuccess": "{{count}} expired accounts deleted successfully",
438+
"deleteLimitedSuccess": "{{count}} limited accounts deleted successfully",
431439
"deleteFailed": "Failed to delete expired accounts",
440+
"deleteLimitedFailed": "Failed to delete limited accounts",
432441
"confirmDelete": "Delete Expired Accounts",
442+
"confirmDeleteLimited": "Delete Limited Accounts",
433443
"confirmDeleteMessage": "Are you sure you want to delete all expired accounts in the selected date range? This action cannot be undone.",
444+
"confirmDeleteLimitedMessage": "Are you sure you want to delete all usage-limited accounts? This action cannot be undone.",
434445
"noDateSelected": "Please select at least one date",
435-
"selectDateRange": "Select the date range for expired accounts to delete (only past dates are selectable)"
446+
"selectDateRange": "Select the date range for expired accounts to delete (only past dates are selectable)",
447+
"selectLimitedInfo": "Delete all usage-limited accounts for the selected admin scope."
436448
},
437449
"resetUsage": {
438450
"title": "Reset All Data Usage",

dashboard/public/statics/locales/fa.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,19 +326,31 @@
326326
"expiredUsers": {
327327
"title": "حذف حساب‌های منقضی شده",
328328
"description": "حذف حساب‌هایی که در بازه زمانی مشخصی منقضی شده‌اند",
329+
"target": "هدف حذف",
330+
"targetPlaceholder": "هدف را انتخاب کنید",
331+
"targets": {
332+
"expired": "انقضای زمانی",
333+
"limited": "محدودیت حجم"
334+
},
329335
"expiredAfter": "منقضی شده بعد از",
330336
"expiredBefore": "منقضی شده قبل از",
331337
"expiredAfterPlaceholder": "تاریخ شروع را انتخاب کنید",
332338
"expiredBeforePlaceholder": "تاریخ پایان را انتخاب کنید",
333339
"dateRange": "بازه زمانی",
334340
"deleteExpired": "حذف منقضی‌ها",
341+
"deleteLimited": "حذف محدودها",
335342
"deleting": "در حال حذف...",
336343
"deleteSuccess": "{{count}} حساب منقضی شده با موفقیت حذف شد",
344+
"deleteLimitedSuccess": "{{count}} حساب محدود با موفقیت حذف شد",
337345
"deleteFailed": "حذف حساب‌های منقضی شده ناموفق بود",
346+
"deleteLimitedFailed": "حذف حساب‌های محدود ناموفق بود",
338347
"confirmDelete": "حذف حساب‌های منقضی شده",
348+
"confirmDeleteLimited": "حذف حساب‌های محدود",
339349
"confirmDeleteMessage": "آیا مطمئن هستید که می‌خواهید همه حساب‌های منقضی شده در بازه زمانی انتخاب شده را حذف کنید؟ این عمل قابل بازگشت نیست.",
350+
"confirmDeleteLimitedMessage": "آیا مطمئن هستید که می‌خواهید همه حساب‌های محدود از نظر حجم را حذف کنید؟ این عمل قابل بازگشت نیست.",
340351
"noDateSelected": "لطفاً حداقل یک تاریخ انتخاب کنید",
341-
"selectDateRange": "بازه زمانی حساب‌های منقضی شده برای حذف را انتخاب کنید (فقط تاریخ‌های گذشته قابل انتخاب هستند)"
352+
"selectDateRange": "بازه زمانی حساب‌های منقضی شده برای حذف را انتخاب کنید (فقط تاریخ‌های گذشته قابل انتخاب هستند)",
353+
"selectLimitedInfo": "همه حساب‌های محدود از نظر حجم در محدوده ادمین انتخاب‌شده حذف می‌شوند."
342354
},
343355
"resetUsage": {
344356
"title": "بازنشانی کل مصرف داده‌ها",

dashboard/public/statics/locales/ru.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,19 +433,31 @@
433433
"expiredUsers": {
434434
"title": "Удаление просроченных аккаунтов",
435435
"description": "Удаление аккаунтов, которые просрочены в определенном диапазоне дат",
436+
"target": "Цель удаления",
437+
"targetPlaceholder": "Выберите цель",
438+
"targets": {
439+
"expired": "По сроку",
440+
"limited": "По трафику"
441+
},
436442
"expiredAfter": "Просрочено после",
437443
"expiredBefore": "Просрочено до",
438444
"expiredAfterPlaceholder": "Выберите дату начала",
439445
"expiredBeforePlaceholder": "Выберите дату окончания",
440446
"dateRange": "Диапазон дат",
441447
"deleteExpired": "Удалить просроченные",
448+
"deleteLimited": "Удалить ограниченные",
442449
"deleting": "Удаление...",
443450
"deleteSuccess": "{{count}} просроченных аккаунтов успешно удалено",
451+
"deleteLimitedSuccess": "Успешно удалено {{count}} ограниченных аккаунтов",
444452
"deleteFailed": "Не удалось удалить просроченные аккаунты",
453+
"deleteLimitedFailed": "Не удалось удалить ограниченные аккаунты",
445454
"confirmDelete": "Удалить просроченные аккаунты",
455+
"confirmDeleteLimited": "Удалить ограниченные аккаунты",
446456
"confirmDeleteMessage": "Вы уверены, что хотите удалить все просроченные аккаунты в выбранном диапазоне дат? Это действие нельзя отменить.",
457+
"confirmDeleteLimitedMessage": "Вы уверены, что хотите удалить все аккаунты с ограничением по трафику? Это действие нельзя отменить.",
447458
"noDateSelected": "Пожалуйста, выберите хотя бы одну дату",
448-
"selectDateRange": "Выберите диапазон дат для просроченных аккаунтов для удаления (доступны только прошедшие даты)"
459+
"selectDateRange": "Выберите диапазон дат для просроченных аккаунтов для удаления (доступны только прошедшие даты)",
460+
"selectLimitedInfo": "Удаляются все аккаунты с ограничением по трафику в выбранной области администратора."
449461
},
450462
"resetUsage": {
451463
"title": "Сброс всего использования данных",

dashboard/public/statics/locales/zh.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,19 +401,31 @@
401401
"expiredUsers": {
402402
"title": "删除过期账户",
403403
"description": "删除在特定日期范围内过期的账户",
404+
"target": "删除目标",
405+
"targetPlaceholder": "选择目标",
406+
"targets": {
407+
"expired": "按时间过期",
408+
"limited": "按流量受限"
409+
},
404410
"expiredAfter": "过期时间晚于",
405411
"expiredBefore": "过期时间早于",
406412
"expiredAfterPlaceholder": "选择开始日期",
407413
"expiredBeforePlaceholder": "选择结束日期",
408414
"dateRange": "日期范围",
409415
"deleteExpired": "删除过期账户",
416+
"deleteLimited": "删除受限账户",
410417
"deleting": "删除中...",
411418
"deleteSuccess": "成功删除 {{count}} 个过期账户",
419+
"deleteLimitedSuccess": "成功删除 {{count}} 个受限账户",
412420
"deleteFailed": "删除过期账户失败",
421+
"deleteLimitedFailed": "删除受限账户失败",
413422
"confirmDelete": "删除过期账户",
423+
"confirmDeleteLimited": "删除受限账户",
414424
"confirmDeleteMessage": "您确定要删除所选日期范围内的所有过期账户吗?此操作无法撤销。",
425+
"confirmDeleteLimitedMessage": "您确定要删除所有按流量受限的账户吗?此操作无法撤销。",
415426
"noDateSelected": "请至少选择一个日期",
416-
"selectDateRange": "选择要删除过期账户的日期范围(只能选择过去的日期)"
427+
"selectDateRange": "选择要删除过期账户的日期范围(只能选择过去的日期)",
428+
"selectLimitedInfo": "将删除所选管理员范围内的所有按流量受限账户。"
417429
},
418430
"resetUsage": {
419431
"title": "重置所有数据使用量",

0 commit comments

Comments
 (0)