feat: category scopes, group-admin role, and permission model
- Three category scopes: personal / group / system (watch) - PascalCase-with-dashes naming convention enforced at backend + frontend - is_group_admin flag on GroupMembership; PATCH endpoint for admins to toggle it - Categories router: scope-based list/create/rename/delete with _check_can_manage_cat - Documents router: delete uses is_admin + can_delete share flag + group-admin check; remove_category requires doc ownership; assign_category accepts group/system categories - Proxy layers inject x-user-is-admin and x-user-admin-groups headers - Frontend: ManageCategoriesDialog grouped by scope with lock icons; SourcePanel scope picker + client-side name validation; AdminGroupsPage group-admin checkbox Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -21,3 +21,22 @@ async def get_user_groups(x_user_groups: str = Header(default="")) -> list[str]:
|
||||
if not x_user_groups:
|
||||
return []
|
||||
return [g.strip() for g in x_user_groups.split(",") if g.strip()]
|
||||
|
||||
|
||||
async def get_user_is_admin(x_user_is_admin: str = Header(default="false")) -> bool:
|
||||
"""
|
||||
Extract the superuser flag injected by the main backend proxy.
|
||||
Returns True only when the header value is exactly "true".
|
||||
"""
|
||||
return x_user_is_admin.lower() == "true"
|
||||
|
||||
|
||||
async def get_user_admin_groups(x_user_admin_groups: str = Header(default="")) -> list[str]:
|
||||
"""
|
||||
Extract the group IDs for which the current user is a group admin.
|
||||
Injected by the main backend proxy from GroupMembership.is_group_admin.
|
||||
Returns an empty list if absent or empty.
|
||||
"""
|
||||
if not x_user_admin_groups:
|
||||
return []
|
||||
return [g.strip() for g in x_user_admin_groups.split(",") if g.strip()]
|
||||
|
||||
Reference in New Issue
Block a user