work: добавлин web интерфейс.
This commit is contained in:
+113
-2
@@ -1,9 +1,12 @@
|
||||
import logging
|
||||
import shutil
|
||||
from contextlib import asynccontextmanager
|
||||
from typing import Literal, Annotated
|
||||
import yaml
|
||||
from fastapi import FastAPI, Request, HTTPException, Path
|
||||
from fastapi.exceptions import RequestValidationError
|
||||
from fastapi.responses import JSONResponse
|
||||
from fastapi.responses import FileResponse, JSONResponse, RedirectResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from pathlib import Path as FilePath
|
||||
from app.qingping.service import QingpingService
|
||||
|
||||
@@ -184,6 +187,34 @@ app = FastAPI(
|
||||
lifespan=lifespan,
|
||||
)
|
||||
|
||||
WEB_ROOT = FilePath(__file__).resolve().parents[1] / "web"
|
||||
|
||||
app.mount(
|
||||
"/ui/static",
|
||||
StaticFiles(directory=WEB_ROOT),
|
||||
name="ui-static",
|
||||
)
|
||||
|
||||
|
||||
@app.get("/", include_in_schema=False)
|
||||
async def root():
|
||||
return RedirectResponse(url="/ui/panel")
|
||||
|
||||
|
||||
@app.get("/ui", include_in_schema=False)
|
||||
async def ui_root():
|
||||
return RedirectResponse(url="/ui/panel")
|
||||
|
||||
|
||||
@app.get("/ui/widget", include_in_schema=False)
|
||||
async def ui_widget():
|
||||
return FileResponse(WEB_ROOT / "widget.html")
|
||||
|
||||
|
||||
@app.get("/ui/panel", include_in_schema=False)
|
||||
async def ui_panel():
|
||||
return FileResponse(WEB_ROOT / "panel.html")
|
||||
|
||||
|
||||
@app.exception_handler(RequestValidationError)
|
||||
async def validation_exception_handler(
|
||||
@@ -724,6 +755,86 @@ async def set_light(
|
||||
# ----------------------------------------------------------------------
|
||||
# Переход в auto
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
@app.get("/api/schedule/config")
|
||||
async def get_schedule_config():
|
||||
try:
|
||||
raw = yaml.safe_load(
|
||||
FilePath(SCHEDULE_FILE).read_text(encoding="utf-8")
|
||||
)
|
||||
except (OSError, yaml.YAMLError) as exc:
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail={
|
||||
"message": "Could not read schedule config",
|
||||
"error": f"{type(exc).__name__}: {exc}",
|
||||
},
|
||||
) from exc
|
||||
|
||||
if not isinstance(raw, dict):
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail="Schedule config root must be an object",
|
||||
)
|
||||
|
||||
return raw
|
||||
|
||||
|
||||
@app.put("/api/schedule/config")
|
||||
async def update_schedule_config(payload: dict):
|
||||
global schedule_load_error
|
||||
|
||||
schedule_path = FilePath(SCHEDULE_FILE)
|
||||
temp_path = schedule_path.with_name(schedule_path.name + ".tmp")
|
||||
backup_path = schedule_path.with_name(schedule_path.name + ".bak")
|
||||
|
||||
try:
|
||||
serialized = yaml.safe_dump(
|
||||
payload,
|
||||
allow_unicode=True,
|
||||
sort_keys=False,
|
||||
default_flow_style=False,
|
||||
)
|
||||
temp_path.write_text(serialized, encoding="utf-8")
|
||||
config = load_schedule(temp_path)
|
||||
except (OSError, ValueError, yaml.YAMLError) as exc:
|
||||
temp_path.unlink(missing_ok=True)
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail={
|
||||
"message": "Invalid schedule config",
|
||||
"error": str(exc),
|
||||
},
|
||||
) from exc
|
||||
|
||||
try:
|
||||
if schedule_path.exists():
|
||||
shutil.copy2(schedule_path, backup_path)
|
||||
temp_path.replace(schedule_path)
|
||||
except OSError as exc:
|
||||
temp_path.unlink(missing_ok=True)
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail={
|
||||
"message": "Could not save schedule config",
|
||||
"error": f"{type(exc).__name__}: {exc}",
|
||||
},
|
||||
) from exc
|
||||
|
||||
schedule_load_error = None
|
||||
restart_required = schedule_service is None
|
||||
|
||||
if schedule_service is not None:
|
||||
await schedule_service.replace_config(config)
|
||||
|
||||
return {
|
||||
"ok": True,
|
||||
"restart_required": restart_required,
|
||||
"config": payload,
|
||||
"schedule": get_schedule_status(),
|
||||
}
|
||||
|
||||
|
||||
@app.post("/api/schedule/override/clear")
|
||||
async def clear_schedule_override():
|
||||
if schedule_service is None:
|
||||
@@ -776,4 +887,4 @@ async def resume_schedule():
|
||||
return {
|
||||
"ok": True,
|
||||
"schedule": get_schedule_status(),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user