Merge branch 'xuhd' into 'master'
feat: admin可删除内置专家 See merge request knowledge-base-bj/deerflow-server!7
This commit is contained in:
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
|
||||
+7
@@ -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()
|
||||
|
||||
Reference in new issue
Block a user