Merge branch 'xuhd' into 'master'

feat: admin可删除内置专家

See merge request knowledge-base-bj/deerflow-server!7
This commit is contained in:
xu hd committed 2026-05-14 02:18:33 +00:00
commit 7417a1c950
6 files changed
+59 -7

No files matched your search

@@ -66,10 +66,11 @@ export function AgentCard({ agent }: AgentCardProps) {
const isOwned =
!isBuiltin && agent.user_id !== null && agent.user_id === currentUserId;
const isFromOthers = !isBuiltin && !isOwned;
// System admins are allowed to edit built-in agents in place. Delete and
// publish-toggle stay owner-only — built-ins shouldn't be removable, and
// they are inherently visible to everyone so toggling published is moot.
// System admins are allowed to edit and delete built-in agents.
// Publish-toggle stays owner-only — built-ins are inherently visible to
// everyone so toggling published is moot.
const canEdit = isOwned || (isBuiltin && isAdmin);
const canDelete = isOwned || (isBuiltin && isAdmin);
useEffect(() => {
if (!editOpen) {
@@ -241,7 +242,7 @@ export function AgentCard({ agent }: AgentCardProps) {
<MessageSquareIcon className="mr-1.5 h-3.5 w-3.5" />
{t.agents.chat}
</Button>
{(canEdit || isOwned) && (
{(canEdit || canDelete || isOwned) && (
<div className="flex gap-1">
{isOwned && (
<Button
@@ -270,7 +271,7 @@ export function AgentCard({ agent }: AgentCardProps) {
<SquarePenIcon className="h-3.5 w-3.5" />
</Button>
)}
{isOwned && (
{canDelete && (
<Button
size="icon"
variant="ghost"
@@ -470,8 +470,15 @@ async def delete_agent(agent_id: str, request: Request) -> None:
try:
store = get_agent_store(request)
user_id = _current_user_id(request)
await _get_owned_or_raise(store, agent_id, user_id)
deleted = await store.delete(agent_id, user_id)
is_admin = _is_admin_user(request)
_, edit_mode = await _get_editable_or_raise(
store, agent_id, user_id, is_admin=is_admin
)
if edit_mode == "admin":
deleted = await store.delete_builtin(agent_id)
else:
deleted = await store.delete(agent_id, user_id)
if not deleted:
raise HTTPException(status_code=404, detail=f"Agent '{agent_id}' not found")
@@ -47,3 +47,12 @@ class AgentStore(ABC):
@abstractmethod
async def delete(self, agent_id: str, user_id: str) -> bool:
raise NotImplementedError
@abstractmethod
async def delete_builtin(self, agent_id: str) -> bool:
"""Delete a built-in agent (user_id IS NULL).
Caller is responsible for authorization — this method does NOT check
any user identity and returns ``False`` if the target is not a built-in.
"""
raise NotImplementedError
@@ -107,3 +107,10 @@ class MemoryAgentStore(AgentStore):
return False
self._agents.pop(agent_id, None)
return True
async def delete_builtin(self, agent_id: str) -> bool:
row = self._agents.get(agent_id)
if row is None or row.get("user_id") is not None:
return False
self._agents.pop(agent_id, None)
return True
@@ -128,3 +128,12 @@ class AgentRepository(AgentStore):
await session.delete(row)
await session.commit()
return True
async def delete_builtin(self, agent_id: str) -> bool:
async with self._sf() as session:
row = await session.get(AgentRow, agent_id)
if row is None or row.user_id is not None:
return False
await session.delete(row)
await session.commit()
return True
@@ -52,6 +52,25 @@ async def test_agent_store_only_owner_can_update_or_delete() -> None:
assert await store.delete("private-a", "user-a") is True
@pytest.mark.asyncio
async def test_delete_builtin_only_targets_builtins() -> None:
store = MemoryAgentStore()
await store.ensure_builtin({"id": "builtin-agent", "name": "Built in"})
await store.create({"id": "owned-agent", "name": "Owned", "user_id": "user-a"})
# delete_builtin refuses to touch a user-owned agent
assert await store.delete_builtin("owned-agent") is False
assert await store.get_any("owned-agent") is not None
# delete_builtin succeeds on a built-in
assert await store.delete_builtin("builtin-agent") is True
assert await store.get_any("builtin-agent") is None
# And no-ops on a missing id
assert await store.delete_builtin("does-not-exist") is False
@pytest.mark.asyncio
async def test_update_builtin_only_targets_builtins() -> None:
store = MemoryAgentStore()