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

+45 -31
View File
@@ -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"])