Skip to content

Commit 3e714ae

Browse files
committed
fix: enhance bulk removal functionality across various CRUD operations and add tests for it
1 parent 1838c68 commit 3e714ae

File tree

8 files changed

+576
-16
lines changed

8 files changed

+576
-16
lines changed

app/db/crud/admin.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from datetime import datetime, timezone
22
from enum import Enum
33

4-
from sqlalchemy import and_, case, delete, func, select
4+
from sqlalchemy import and_, case, delete, func, select, update
55
from sqlalchemy.ext.asyncio import AsyncSession
66

77
from app.db.crud.general import (
@@ -560,5 +560,7 @@ async def remove_admins(db: AsyncSession, admin_ids: list[int]) -> None:
560560
if not admin_ids:
561561
return
562562

563+
await db.execute(update(User).where(User.admin_id.in_(admin_ids)).values(admin_id=None))
564+
await db.execute(delete(AdminUsageLogs).where(AdminUsageLogs.admin_id.in_(admin_ids)))
563565
await db.execute(delete(Admin).where(Admin.id.in_(admin_ids)))
564566
await db.commit()

app/db/crud/client_template.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ async def get_first_template_by_type(
159159
db: AsyncSession,
160160
template_type: ClientTemplateType,
161161
exclude_id: int | None = None,
162+
exclude_ids: list[int] | set[int] | None = None,
162163
) -> ClientTemplate | None:
163164
stmt = (
164165
select(ClientTemplate)
@@ -167,6 +168,8 @@ async def get_first_template_by_type(
167168
)
168169
if exclude_id is not None:
169170
stmt = stmt.where(ClientTemplate.id != exclude_id)
171+
if exclude_ids:
172+
stmt = stmt.where(ClientTemplate.id.not_in(list(exclude_ids)))
170173
return (await db.execute(stmt)).scalars().first()
171174

172175

app/db/crud/core.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
from enum import Enum
22

3-
from sqlalchemy import delete, func, select
3+
from sqlalchemy import delete, func, select, update
44
from sqlalchemy.ext.asyncio import AsyncSession
55

6-
from app.db.models import CoreConfig
6+
from app.db.models import CoreConfig, Node
77
from app.models.core import CoreCreate
88

99
CoreSortingOptionsSimple = Enum(
@@ -186,5 +186,6 @@ async def remove_cores(db: AsyncSession, core_ids: list[int]) -> None:
186186
if not core_ids:
187187
return
188188

189+
await db.execute(update(Node).where(Node.core_config_id.in_(core_ids)).values(core_config_id=None))
189190
await db.execute(delete(CoreConfig).where(CoreConfig.id.in_(core_ids)))
190191
await db.commit()

app/db/crud/group.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@
33
from sqlalchemy.ext.asyncio import AsyncSession
44
from sqlalchemy.orm import selectinload
55

6-
from app.db.models import ProxyInbound, Group, users_groups_association
6+
from app.db.models import (
7+
Group,
8+
ProxyInbound,
9+
inbounds_groups_association,
10+
template_group_association,
11+
users_groups_association,
12+
)
713
from app.models.group import GroupCreate, GroupModify
814

915
from .host import upsert_inbounds
@@ -257,8 +263,8 @@ async def remove_groups(db: AsyncSession, group_ids: list[int]) -> None:
257263
if not group_ids:
258264
return
259265

260-
# Delete user-group associations first
261266
await db.execute(delete(users_groups_association).where(users_groups_association.c.groups_id.in_(group_ids)))
262-
# Then delete groups
267+
await db.execute(delete(template_group_association).where(template_group_association.c.group_id.in_(group_ids)))
268+
await db.execute(delete(inbounds_groups_association).where(inbounds_groups_association.c.group_id.in_(group_ids)))
263269
await db.execute(delete(Group).where(Group.id.in_(group_ids)))
264270
await db.commit()

app/db/crud/node.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -830,5 +830,9 @@ async def remove_nodes(db: AsyncSession, node_ids: list[int]) -> None:
830830
if not node_ids:
831831
return
832832

833+
await db.execute(delete(NodeUserUsage).where(NodeUserUsage.node_id.in_(node_ids)))
834+
await db.execute(delete(NodeUsage).where(NodeUsage.node_id.in_(node_ids)))
835+
await db.execute(delete(NodeUsageResetLogs).where(NodeUsageResetLogs.node_id.in_(node_ids)))
836+
await db.execute(delete(NodeStat).where(NodeStat.node_id.in_(node_ids)))
833837
await db.execute(delete(Node).where(Node.id.in_(node_ids)))
834838
await db.commit()

app/db/crud/user_template.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
from typing import Union, List
22
from enum import Enum
33

4-
from sqlalchemy import delete, func, select
4+
from sqlalchemy import delete, func, select, update
55
from sqlalchemy.ext.asyncio import AsyncSession
66

7-
from app.db.models import UserTemplate
7+
from app.db.models import NextPlan, UserTemplate, template_group_association
88
from app.models.user_template import UserTemplateCreate, UserTemplateModify
99

1010
from .group import get_groups_by_ids
@@ -234,5 +234,9 @@ async def remove_user_templates(db: AsyncSession, template_ids: list[int]) -> No
234234
if not template_ids:
235235
return
236236

237+
await db.execute(
238+
delete(template_group_association).where(template_group_association.c.user_template_id.in_(template_ids))
239+
)
240+
await db.execute(update(NextPlan).where(NextPlan.user_template_id.in_(template_ids)).values(user_template_id=None))
237241
await db.execute(delete(UserTemplate).where(UserTemplate.id.in_(template_ids)))
238242
await db.commit()

app/operation/client_template.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -236,15 +236,8 @@ async def bulk_remove_client_templates(
236236
for template_type, templates_of_type in templates_by_type.items():
237237
defaults_to_replace = [t for t in templates_of_type if t.is_default]
238238
if defaults_to_replace:
239-
# Get replacement for this type (exclude all templates being deleted)
240239
exclude_ids = {t.id for t in templates_of_type}
241-
replacement = await get_first_template_by_type(db, template_type, exclude_id=None)
242-
# Find first non-deleted template
243-
for candidate_id in [t.id for t in templates_of_type if not t.is_default]:
244-
if candidate_id not in exclude_ids:
245-
replacement = await self.get_validated_client_template(db, candidate_id)
246-
break
247-
240+
replacement = await get_first_template_by_type(db, template_type, exclude_ids=exclude_ids)
248241
if replacement:
249242
await set_default_template(db, replacement)
250243

0 commit comments

Comments
 (0)