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:
1 parent
15368f2779
commit
3dd5731751
137 files changed
+8571
-14151
No files matched your search
+45
-31
@@ -3,44 +3,41 @@ from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from fastapi import APIRouter, File, HTTPException, Query, Response, UploadFile
|
||||
from fastapi import APIRouter, File, HTTPException, Query, Request, Response, UploadFile
|
||||
|
||||
from app.auth import CurrentUser
|
||||
from app.data_platform.csv_service import (
|
||||
MAX_CSV_BYTES,
|
||||
export_csv_records,
|
||||
import_csv_records,
|
||||
preview_csv_import,
|
||||
)
|
||||
from app.data_platform.record_service import (
|
||||
create_record,
|
||||
delete_record,
|
||||
inspect_table,
|
||||
list_databases,
|
||||
list_records,
|
||||
list_tables,
|
||||
update_record,
|
||||
)
|
||||
from app.data_platform.schema import (
|
||||
from app.auth import AdminUser, CurrentUser, DataOperatorUser
|
||||
from app.data_platform.mysql_service import (
|
||||
IDENTIFIER_PATTERN,
|
||||
MAX_CSV_BYTES,
|
||||
create_custom_table,
|
||||
create_custom_table_from_sql,
|
||||
create_record,
|
||||
create_table_column,
|
||||
delete_custom_table,
|
||||
delete_project_database,
|
||||
delete_record,
|
||||
delete_table_column,
|
||||
ensure_project_database,
|
||||
execute_project_sql,
|
||||
export_csv_records,
|
||||
import_csv_records,
|
||||
inspect_table,
|
||||
list_admin_action_logs,
|
||||
list_databases,
|
||||
list_records,
|
||||
list_tables,
|
||||
preview_csv_import,
|
||||
rename_custom_table,
|
||||
rename_project_database,
|
||||
update_record,
|
||||
update_table_column,
|
||||
)
|
||||
from app.data_platform.sql_console import execute_project_sql
|
||||
|
||||
router = APIRouter(prefix="/data-platform")
|
||||
|
||||
|
||||
@router.get("/databases")
|
||||
async def databases(_user: CurrentUser):
|
||||
async def databases(_user: DataOperatorUser):
|
||||
return await list_databases()
|
||||
|
||||
|
||||
@@ -56,7 +53,10 @@ async def add_database(body: dict[str, Any], user: CurrentUser):
|
||||
)
|
||||
display_name = str(body.get("display_name") or database_id).strip()
|
||||
tenant_id = str(body.get("tenant_id") or database_id).strip()
|
||||
return await ensure_project_database(database_id, tenant_id, display_name)
|
||||
try:
|
||||
return await ensure_project_database(database_id, tenant_id, display_name)
|
||||
except ValueError as exc:
|
||||
raise HTTPException(400, str(exc)) from exc
|
||||
|
||||
|
||||
@router.post("/databases/initialize")
|
||||
@@ -93,7 +93,7 @@ async def remove_database(project_id: str, body: dict[str, Any], user: CurrentUs
|
||||
|
||||
|
||||
@router.get("/databases/{project_id}/tables")
|
||||
async def tables(project_id: str, _user: CurrentUser):
|
||||
async def tables(project_id: str, _user: DataOperatorUser):
|
||||
return await list_tables(project_id)
|
||||
|
||||
|
||||
@@ -101,18 +101,32 @@ async def tables(project_id: str, _user: CurrentUser):
|
||||
async def execute_console_sql(
|
||||
project_id: str,
|
||||
body: dict[str, Any],
|
||||
request: Request,
|
||||
user: CurrentUser,
|
||||
):
|
||||
if "admin" not in user.get("roles", []):
|
||||
raise HTTPException(403, "只有系统管理员可以使用 SQL 控制台")
|
||||
try:
|
||||
return await execute_project_sql(project_id, str(body.get("sql") or ""))
|
||||
return await execute_project_sql(
|
||||
project_id,
|
||||
str(body.get("sql") or ""),
|
||||
actor=user["username"],
|
||||
source_ip=request.client.host if request.client else None,
|
||||
)
|
||||
except ValueError as exc:
|
||||
raise HTTPException(400, str(exc)) from exc
|
||||
|
||||
|
||||
@router.get("/security/audit-logs")
|
||||
async def security_audit_logs(
|
||||
_user: AdminUser,
|
||||
limit: int = Query(default=200, ge=1, le=1000),
|
||||
):
|
||||
return await list_admin_action_logs(limit)
|
||||
|
||||
|
||||
@router.get("/databases/{project_id}/tables/{table_code}/inspection")
|
||||
async def table_inspection(project_id: str, table_code: str, _user: CurrentUser):
|
||||
async def table_inspection(project_id: str, table_code: str, _user: DataOperatorUser):
|
||||
return await inspect_table(project_id, table_code)
|
||||
|
||||
|
||||
@@ -245,7 +259,7 @@ async def remove_table(
|
||||
async def records(
|
||||
project_id: str,
|
||||
table_code: str,
|
||||
_user: CurrentUser,
|
||||
_user: DataOperatorUser,
|
||||
page: int = Query(default=1, ge=1),
|
||||
page_size: int = Query(default=50, ge=1, le=5000),
|
||||
search: str | None = None,
|
||||
@@ -279,7 +293,7 @@ async def _read_csv_upload(file: UploadFile) -> tuple[bytes, str]:
|
||||
async def preview_records_import(
|
||||
project_id: str,
|
||||
table_code: str,
|
||||
user: CurrentUser,
|
||||
user: DataOperatorUser,
|
||||
file: UploadFile = File(...),
|
||||
):
|
||||
content, file_name = await _read_csv_upload(file)
|
||||
@@ -290,7 +304,7 @@ async def preview_records_import(
|
||||
async def import_records(
|
||||
project_id: str,
|
||||
table_code: str,
|
||||
user: CurrentUser,
|
||||
user: DataOperatorUser,
|
||||
file: UploadFile = File(...),
|
||||
):
|
||||
content, file_name = await _read_csv_upload(file)
|
||||
@@ -307,7 +321,7 @@ async def import_records(
|
||||
async def export_records(
|
||||
project_id: str,
|
||||
table_code: str,
|
||||
_user: CurrentUser,
|
||||
_user: DataOperatorUser,
|
||||
search: str | None = None,
|
||||
):
|
||||
content, filename, total = await export_csv_records(
|
||||
@@ -330,7 +344,7 @@ async def add_record(
|
||||
project_id: str,
|
||||
table_code: str,
|
||||
body: dict[str, Any],
|
||||
user: CurrentUser,
|
||||
user: DataOperatorUser,
|
||||
):
|
||||
return await create_record(project_id, table_code, body, user["username"])
|
||||
|
||||
@@ -341,7 +355,7 @@ async def edit_record(
|
||||
table_code: str,
|
||||
record_id: str,
|
||||
body: dict[str, Any],
|
||||
user: CurrentUser,
|
||||
user: DataOperatorUser,
|
||||
):
|
||||
return await update_record(project_id, table_code, record_id, body, user["username"])
|
||||
|
||||
@@ -351,6 +365,6 @@ async def remove_record(
|
||||
project_id: str,
|
||||
table_code: str,
|
||||
record_id: str,
|
||||
user: CurrentUser,
|
||||
user: DataOperatorUser,
|
||||
):
|
||||
return await delete_record(project_id, table_code, record_id, user["username"])
|
||||
Reference in new issue
Block a user