feat: streamline platform and secure data access

- move the relational data center to MySQL and a standalone workbench\n- add Interface Center API credentials, policies, logs, and DBeaver SSH guidance\n- harden authentication and deployment while retiring unused management surfaces
This commit is contained in:
xuelong committed 2026-08-25 02:06:28 -07:00
1 parent 15368f2779
commit 3dd5731751
137 files changed
+8571 -14151

No files matched your search

+14 -34
View File
@@ -1,13 +1,12 @@
"""RBAC & Accounts (P1) — roles, capability matrix, users CRUD."""
from fastapi import APIRouter, HTTPException
from app.auth import CurrentUser
from app.auth import AdminUser
from app.contracts import (
CapabilityCreate,
RoleCapCell,
RoleCreate,
RoleUpdate,
UserAreasSet,
UserCreate,
UserUpdate,
)
@@ -21,13 +20,10 @@ from app.db import (
get_permission_matrix,
list_capabilities,
list_roles,
list_user_areas,
list_users,
set_role_cap,
set_user_areas,
update_role,
update_user,
upsert_custom_area,
)
from app.security import hash_password
@@ -37,12 +33,12 @@ router = APIRouter()
# ── Roles ────────────────────────────────────────────────────────────────────
@router.get("/roles")
async def _list_roles(_user: CurrentUser = None):
async def _list_roles(_user: AdminUser):
return await list_roles()
@router.post("/roles")
async def _create_role(body: RoleCreate, _user: CurrentUser):
async def _create_role(body: RoleCreate, _user: AdminUser):
try:
return await create_role(body.model_dump())
except Exception as e: # unique violation etc.
@@ -50,7 +46,7 @@ async def _create_role(body: RoleCreate, _user: CurrentUser):
@router.patch("/roles/{role_key}")
async def _update_role(role_key: str, body: RoleUpdate, _user: CurrentUser):
async def _update_role(role_key: str, body: RoleUpdate, _user: AdminUser):
row = await update_role(role_key, body.model_dump(exclude_none=True))
if not row:
raise HTTPException(404, "角色不存在")
@@ -58,7 +54,7 @@ async def _update_role(role_key: str, body: RoleUpdate, _user: CurrentUser):
@router.delete("/roles/{role_key}")
async def _delete_role(role_key: str, _user: CurrentUser):
async def _delete_role(role_key: str, _user: AdminUser):
try:
ok = await delete_role(role_key)
except ValueError as e:
@@ -71,12 +67,12 @@ async def _delete_role(role_key: str, _user: CurrentUser):
# ── Capabilities ─────────────────────────────────────────────────────────────
@router.get("/capabilities")
async def _list_caps(_user: CurrentUser = None):
async def _list_caps(_user: AdminUser):
return await list_capabilities()
@router.post("/capabilities")
async def _create_cap(body: CapabilityCreate, _user: CurrentUser):
async def _create_cap(body: CapabilityCreate, _user: AdminUser):
try:
return await create_capability(body.model_dump())
except Exception as e:
@@ -84,7 +80,7 @@ async def _create_cap(body: CapabilityCreate, _user: CurrentUser):
@router.delete("/capabilities/{cap_key}")
async def _delete_cap(cap_key: str, _user: CurrentUser):
async def _delete_cap(cap_key: str, _user: AdminUser):
await delete_capability(cap_key)
return {"deleted": cap_key}
@@ -92,12 +88,12 @@ async def _delete_cap(cap_key: str, _user: CurrentUser):
# ── Permission matrix ────────────────────────────────────────────────────────
@router.get("/permission-matrix")
async def _matrix(_user: CurrentUser = None):
async def _matrix(_user: AdminUser):
return await get_permission_matrix()
@router.put("/permission-matrix")
async def _set_cell(body: RoleCapCell, _user: CurrentUser):
async def _set_cell(body: RoleCapCell, _user: AdminUser):
await set_role_cap(body.role_key, body.cap_key, body.value)
return {"ok": True, "role_key": body.role_key, "cap_key": body.cap_key, "value": body.value}
@@ -105,12 +101,12 @@ async def _set_cell(body: RoleCapCell, _user: CurrentUser):
# ── Users ────────────────────────────────────────────────────────────────────
@router.get("/users")
async def _list_users(_user: CurrentUser = None):
async def _list_users(_user: AdminUser):
return await list_users()
@router.post("/users")
async def _create_user(body: UserCreate, _user: CurrentUser):
async def _create_user(body: UserCreate, _user: AdminUser):
data = body.model_dump()
roles = data.pop("roles", [])
pw = data.pop("password")
@@ -122,7 +118,7 @@ async def _create_user(body: UserCreate, _user: CurrentUser):
@router.patch("/users/{user_id}")
async def _update_user(user_id: int, body: UserUpdate, _user: CurrentUser):
async def _update_user(user_id: int, body: UserUpdate, _user: AdminUser):
data = body.model_dump(exclude_none=True)
roles = data.pop("roles", None)
if "password" in data:
@@ -134,22 +130,6 @@ async def _update_user(user_id: int, body: UserUpdate, _user: CurrentUser):
@router.delete("/users/{user_id}")
async def _delete_user(user_id: int, _user: CurrentUser):
async def _delete_user(user_id: int, _user: AdminUser):
await delete_user(user_id)
return {"deleted": user_id}
@router.get("/users/{user_id}/areas")
async def _get_user_areas(user_id: int, _user: CurrentUser = None):
return await list_user_areas(user_id)
@router.put("/users/{user_id}/areas")
async def _set_user_areas(user_id: int, body: UserAreasSet, _user: CurrentUser):
"""Set the areas this user is responsible for (existing + free-text)."""
area_ids = list(body.area_ids)
for name in body.custom_areas:
if name and name.strip():
area_ids.append(await upsert_custom_area(name))
await set_user_areas(user_id, area_ids)
return await list_user_areas(user_id)