11
This commit is contained in:
61
app/api/v1/python_scripts.py
Normal file
61
app/api/v1/python_scripts.py
Normal file
@@ -0,0 +1,61 @@
|
||||
"""封面等 Python 脚本:按相对路径下发源码,供桌面端拉取后在 bundled python.exe 中执行。"""
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import APIRouter
|
||||
|
||||
from app.schemas.common import ApiResponse
|
||||
|
||||
router = APIRouter(prefix="/python-scripts", tags=["python"])
|
||||
|
||||
# pythonbackend/scripts/cover/
|
||||
_SCRIPTS_DIR = Path(__file__).resolve().parents[3] / "scripts" / "cover"
|
||||
|
||||
_SAFE_NAME = re.compile(r"^[a-zA-Z0-9][a-zA-Z0-9._/-]*\.py$")
|
||||
|
||||
|
||||
def _validate_name(name: str) -> str | None:
|
||||
raw = name.strip().replace("\\", "/")
|
||||
if not raw or not _SAFE_NAME.fullmatch(raw):
|
||||
return None
|
||||
if ".." in raw.split("/"):
|
||||
return None
|
||||
return raw
|
||||
|
||||
|
||||
def _resolve_script_path(safe: str) -> Path | None:
|
||||
path = _SCRIPTS_DIR / safe
|
||||
try:
|
||||
base_resolved = _SCRIPTS_DIR.resolve()
|
||||
resolved = path.resolve()
|
||||
resolved.relative_to(base_resolved)
|
||||
except (OSError, ValueError):
|
||||
return None
|
||||
if not path.is_file():
|
||||
return None
|
||||
return path
|
||||
|
||||
|
||||
@router.get("", response_model=ApiResponse[dict])
|
||||
async def get_python_script(name: str) -> ApiResponse[dict]:
|
||||
safe = _validate_name(name)
|
||||
if not safe:
|
||||
return ApiResponse(ok=False, message="无效的脚本名称", data=None)
|
||||
path = _resolve_script_path(safe)
|
||||
if path is None:
|
||||
return ApiResponse(ok=False, message=f"脚本不存在: {name}", data=None)
|
||||
source = path.read_text(encoding="utf-8")
|
||||
return ApiResponse(ok=True, message="", data={"source": source})
|
||||
|
||||
|
||||
@router.get("/list", response_model=ApiResponse[dict])
|
||||
async def list_python_scripts() -> ApiResponse[dict]:
|
||||
if not _SCRIPTS_DIR.is_dir():
|
||||
return ApiResponse(ok=True, message="", data={"names": []})
|
||||
names: list[str] = []
|
||||
for p in sorted(_SCRIPTS_DIR.rglob("*.py")):
|
||||
if p.is_file():
|
||||
rel = p.relative_to(_SCRIPTS_DIR).as_posix()
|
||||
names.append(rel)
|
||||
return ApiResponse(ok=True, message="", data={"names": names})
|
||||
Reference in New Issue
Block a user