feat(api): standardize REST contracts and auth

This commit is contained in:
2026-07-30 20:38:51 +08:00
parent ae1a657554
commit ba947b616b
86 changed files with 54193 additions and 990 deletions
+18 -28
View File
@@ -45,8 +45,7 @@ inpDir = "data/" # Assuming data directory exists or is defined somewhere.
router = APIRouter()
lockedPrjs: Dict[str, str] = {}
@router.get("/project-info", summary="获取项目信息", description="从数据库获取项目的详细信息,包括地图范围等。", response_model=ProjectMetaResponse)
@router.get("/project_info/", summary="获取项目信息(旧路径)", description="从数据库获取项目的详细信息,包括地图范围等。", response_model=ProjectMetaResponse, deprecated=True)
@router.get("/projects/current", summary="获取项目信息", description="从数据库获取项目的详细信息,包括地图范围等。", response_model=ProjectMetaResponse)
async def get_project_info_endpoint(
network: str = Query(..., description="管网名称(或项目代码)"),
metadata_repo: MetadataRepository = Depends(get_metadata_repository),
@@ -70,7 +69,7 @@ async def get_project_info_endpoint(
project_role="viewer", # Default role for public access
)
@router.get("/listprojects/", summary="获取项目列表", description="获取服务器上所有可用的供水管网项目名称列表。")
@router.get("/project-codes", summary="获取项目列表", description="获取服务器上所有可用的供水管网项目名称列表。")
async def list_projects_endpoint() -> list[str]:
"""
获取项目列表
@@ -79,7 +78,7 @@ async def list_projects_endpoint() -> list[str]:
"""
return list_project()
@router.get("/haveproject/", summary="检查项目是否存在", description="检查指定名称的项目是否存在。")
@router.get("/projects/existence", summary="检查项目是否存在", description="检查指定名称的项目是否存在。")
async def have_project_endpoint(
network: str = Query(..., description="管网名称(或数据库名称)")
):
@@ -90,7 +89,7 @@ async def have_project_endpoint(
"""
return have_project(network)
@router.post("/createproject/", summary="创建新项目", description="创建一个新的供水管网项目。如果项目已存在,可能会覆盖或报错(取决于底层实现)。")
@router.post("/projects", summary="创建新项目", description="创建一个新的供水管网项目。如果项目已存在,可能会覆盖或报错(取决于底层实现)。")
async def create_project_endpoint(
network: str = Query(..., description="管网名称(或数据库名称)"),
_=Depends(require_permission(ENVIRONMENT_MANAGE)),
@@ -103,7 +102,7 @@ async def create_project_endpoint(
create_project(network)
return network
@router.post("/deleteproject/", summary="删除项目", description="永久删除指定的供水管网项目。此操作不可恢复。")
@router.delete("/projects", summary="删除项目", description="永久删除指定的供水管网项目。此操作不可恢复。")
async def delete_project_endpoint(
network: str = Query(..., description="管网名称(或数据库名称)"),
_=Depends(require_permission(ENVIRONMENT_MANAGE)),
@@ -116,7 +115,7 @@ async def delete_project_endpoint(
delete_project(network)
return True
@router.get("/isprojectopen/", summary="检查项目是否已打开", description="检查指定项目是否已被加载到内存中。")
@router.get("/projects/current/status", summary="检查项目是否已打开", description="检查指定项目是否已被加载到内存中。")
async def is_project_open_endpoint(
network: str = Query(..., description="管网名称(或数据库名称)")
):
@@ -127,8 +126,7 @@ async def is_project_open_endpoint(
"""
return is_project_open(network)
@router.post("/projects/open", summary="打开项目", description="将指定项目加载到内存中,并初始化数据库连接池。")
@router.post("/openproject/", summary="打开项目(旧路径)", description="将指定项目加载到内存中,并初始化数据库连接池。", deprecated=True)
@router.post("/projects/current", summary="打开项目", description="将指定项目加载到内存中,并初始化数据库连接池。")
async def open_project_endpoint(
network: str = Query(..., description="管网名称(或数据库名称)")
):
@@ -162,7 +160,7 @@ async def open_project_endpoint(
return network
@router.post("/closeproject/", summary="关闭项目", description="将指定项目从内存中卸载,释放资源。")
@router.delete("/projects/current", summary="关闭项目", description="将指定项目从内存中卸载,释放资源。")
async def close_project_endpoint(
network: str = Query(..., description="管网名称(或数据库名称)")
):
@@ -174,7 +172,7 @@ async def close_project_endpoint(
close_project(network)
return True
@router.post("/copyproject/", summary="复制项目", description="将现有项目复制为新项目。")
@router.post("/project-copies", summary="复制项目", description="将现有项目复制为新项目。")
async def copy_project_endpoint(
source: str = Query(..., description="管网名称(或数据库名称)"),
target: str = Query(..., description="管网名称(或数据库名称)"),
@@ -189,7 +187,7 @@ async def copy_project_endpoint(
copy_project(source, target)
return True
@router.get("/exportinp/", response_model=None, summary="导出项目为 ChangeSet", description="导出项目的变更集 (ChangeSet),包含顶点、SCADA 元素、DMA、SA、VD 等信息。")
@router.get("/projects/current/exports/change-set", response_model=None, summary="导出项目为 ChangeSet", description="导出项目的变更集 (ChangeSet),包含顶点、SCADA 元素、DMA、SA、VD 等信息。")
async def export_inp_endpoint(
network: str = Query(..., description="管网名称(或数据库名称)"),
version: str = Query(..., description="版本号 (通常用于增量更新)")
@@ -222,7 +220,7 @@ async def export_inp_endpoint(
return cs
@router.post("/readinp/", summary="读取 INP 文件到项目", description="从服务器文件系统中读取指定的 INP 文件并加载到项目中。")
@router.post("/projects/current/imports", summary="读取 INP 文件到项目", description="从服务器文件系统中读取指定的 INP 文件并加载到项目中。")
async def read_inp_endpoint(
network: str = Query(..., description="管网名称(或数据库名称)"),
inp: str = Query(..., description="INP 文件名 (不包含路径)")
@@ -236,7 +234,7 @@ async def read_inp_endpoint(
read_inp(network, inp)
return True
@router.get("/dumpinp/", summary="导出项目到 INP 文件", description="将项目当前状态保存为 INP 文件到服务器文件系统。")
@router.post("/projects/current/exports/inp", summary="导出项目到 INP 文件", description="将项目当前状态保存为 INP 文件到服务器文件系统。")
async def dump_inp_endpoint(
network: str = Query(..., description="管网名称(或数据库名称)"),
inp: str = Query(..., description="目标文件名")
@@ -250,7 +248,7 @@ async def dump_inp_endpoint(
dump_inp(network, inp)
return True
@router.get("/isprojectlocked/", summary="检查项目是否被锁定", description="检查指定项目是否处于锁定状态。")
@router.get("/projects/current/lock", summary="检查项目是否被锁定", description="检查指定项目是否处于锁定状态。")
async def is_project_locked_endpoint(
network: str = Query(..., description="管网名称(或数据库名称)"),
req: Request = None
@@ -262,7 +260,7 @@ async def is_project_locked_endpoint(
"""
return network in lockedPrjs.keys()
@router.get("/isprojectlockedbyme/", summary="检查项目是否被当前用户锁定", description="检查指定项目是否被当前客户端 (IP) 锁定。")
@router.get("/projects/current/lock/ownership", summary="检查项目是否被当前用户锁定", description="检查指定项目是否被当前客户端 (IP) 锁定。")
async def is_project_locked_by_me_endpoint(
network: str = Query(..., description="管网名称(或数据库名称)"),
req: Request = None
@@ -278,7 +276,7 @@ async def is_project_locked_by_me_endpoint(
# 0 successfully locked
# 1 already locked by you
# 2 locked by others
@router.post("/lockproject/", summary="锁定项目", description="锁定指定项目以防止并发修改。")
@router.post("/projects/current/lock", summary="锁定项目", description="锁定指定项目以防止并发修改。")
async def lock_project_endpoint(
network: str = Query(..., description="管网名称(或数据库名称)"),
req: Request = None
@@ -301,7 +299,7 @@ async def lock_project_endpoint(
else:
return 2
@router.post("/unlockproject/", summary="解锁项目", description="释放对项目的锁定。")
@router.delete("/projects/current/lock", summary="解锁项目", description="释放对项目的锁定。")
def unlock_project_endpoint(
network: str = Query(..., description="管网名称(或数据库名称)"),
req: Request = None
@@ -319,7 +317,7 @@ def unlock_project_endpoint(
return False
@router.get("/downloadinp/", status_code=status.HTTP_200_OK, summary="下载 INP 文件", description="从服务器数据目录下载指定的 INP 文件。")
@router.get("/projects/current/files/inp", status_code=status.HTTP_200_OK, summary="下载 INP 文件", description="从服务器数据目录下载指定的 INP 文件。")
async def fastapi_download_inp(
name: str = Query(..., description="文件名"),
response: Response = None
@@ -339,7 +337,7 @@ async def fastapi_download_inp(
return True
# DingZQ, 2024-12-28, convert v3 to v2
@router.get("/convertv3tov2/", response_model=None, summary="转换 INP V3 为 V2", description="将 EPANET 3.0 格式的 INP 内容转换为 2.x 格式。")
@router.post("/project-conversions", response_model=None, summary="转换 INP V3 为 V2", description="将 EPANET 3.0 格式的 INP 内容转换为 2.x 格式。")
async def fastapi_convert_v3_to_v2(
req: Request
) -> ChangeSet:
@@ -373,7 +371,6 @@ async def fastapi_convert_v3_to_v2(
return cs
@router.post("/readinp/", summary="读取 INP 文件到项目", description="从服务器文件系统中读取指定的 INP 文件并加载到项目中。")
async def read_inp_endpoint(
network: str = Query(..., description="管网名称(或数据库名称)"),
inp: str = Query(..., description="INP 文件名 (不包含路径)")
@@ -387,7 +384,6 @@ async def read_inp_endpoint(
read_inp(network, inp)
return True
@router.get("/dumpinp/", summary="导出项目到 INP 文件", description="将项目当前状态保存为 INP 文件到服务器文件系统。")
async def dump_inp_endpoint(
network: str = Query(..., description="管网名称(或数据库名称)"),
inp: str = Query(..., description="目标文件名")
@@ -401,7 +397,6 @@ async def dump_inp_endpoint(
dump_inp(network, inp)
return True
@router.get("/isprojectlocked/", summary="检查项目是否被锁定", description="检查指定项目是否处于锁定状态。")
async def is_project_locked_endpoint(
network: str = Query(..., description="管网名称(或数据库名称)"),
req: Request = None
@@ -413,7 +408,6 @@ async def is_project_locked_endpoint(
"""
return network in lockedPrjs.keys()
@router.get("/isprojectlockedbyme/", summary="检查项目是否被当前用户锁定", description="检查指定项目是否被当前客户端 (IP) 锁定。")
async def is_project_locked_by_me_endpoint(
network: str = Query(..., description="管网名称(或数据库名称)"),
req: Request = None
@@ -429,7 +423,6 @@ async def is_project_locked_by_me_endpoint(
# 0 successfully locked
# 1 already locked by you
# 2 locked by others
@router.post("/lockproject/", summary="锁定项目", description="锁定指定项目以防止并发修改。")
async def lock_project_endpoint(
network: str = Query(..., description="管网名称(或数据库名称)"),
req: Request = None
@@ -452,7 +445,6 @@ async def lock_project_endpoint(
else:
return 2
@router.post("/unlockproject/", summary="解锁项目", description="释放对项目的锁定。")
def unlock_project_endpoint(
network: str = Query(..., description="管网名称(或数据库名称)"),
req: Request = None
@@ -470,7 +462,6 @@ def unlock_project_endpoint(
return False
@router.get("/downloadinp/", status_code=status.HTTP_200_OK, summary="下载 INP 文件", description="从服务器数据目录下载指定的 INP 文件。")
async def fastapi_download_inp(
name: str = Query(..., description="文件名"),
response: Response = None
@@ -490,7 +481,6 @@ async def fastapi_download_inp(
return True
# DingZQ, 2024-12-28, convert v3 to v2
@router.get("/convertv3tov2/", response_model=None, summary="转换 INP V3 为 V2", description="将 EPANET 3.0 格式的 INP 内容转换为 2.x 格式。")
async def fastapi_convert_v3_to_v2(
req: Request
) -> ChangeSet: