thay dỏi cong

This commit is contained in:
2026-04-27 22:13:43 +07:00
parent 0de7d67511
commit f6c5dce452
6 changed files with 66 additions and 15 deletions
+32 -4
View File
@@ -2,9 +2,11 @@ from __future__ import annotations
import asyncio
import os
import logging
from pathlib import Path
from typing import Optional
import httpx
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
@@ -26,6 +28,8 @@ def _cors_origins() -> list[str]:
raw = os.getenv("CORS_ORIGINS", "http://localhost:5173")
return [x.strip() for x in raw.split(",") if x.strip()]
logger = logging.getLogger("ipcam_dashboard")
app = FastAPI(title="IPCam Dashboard API")
app.add_middleware(
@@ -61,9 +65,21 @@ async def _scheduler_loop() -> None:
try:
cfg = await store.load()
await scheduler.tick(cfg.schedule)
except Exception:
logger.exception("scheduler_tick_failed")
finally:
await asyncio.sleep(60)
def _raise_mediamtx_http_error(err: httpx.HTTPError) -> None:
if isinstance(err, httpx.HTTPStatusError):
code = err.response.status_code
if code == 401:
raise HTTPException(status_code=502, detail="mediamtx_unauthorized")
if code == 403:
raise HTTPException(status_code=502, detail="mediamtx_forbidden")
raise HTTPException(status_code=502, detail=f"mediamtx_http_{code}")
raise HTTPException(status_code=502, detail="mediamtx_unreachable")
@app.on_event("startup")
async def _startup() -> None:
@@ -98,7 +114,10 @@ async def add_camera(camera: Camera) -> AppConfig:
username=os.getenv("MEDIAMTX_API_USER"),
password=os.getenv("MEDIAMTX_API_PASS"),
)
await client.upsert_paths_sources_bulk({camera.name: camera.rtsp_url})
try:
await client.upsert_paths_sources_bulk({camera.name: camera.rtsp_url})
except httpx.HTTPError as e:
_raise_mediamtx_http_error(e)
return cfg
@@ -116,7 +135,10 @@ async def delete_camera(name: str) -> AppConfig:
username=os.getenv("MEDIAMTX_API_USER"),
password=os.getenv("MEDIAMTX_API_PASS"),
)
await client.delete_path(name)
try:
await client.delete_path(name)
except httpx.HTTPError as e:
_raise_mediamtx_http_error(e)
return cfg
@@ -128,12 +150,18 @@ async def list_paths() -> dict:
username=os.getenv("MEDIAMTX_API_USER"),
password=os.getenv("MEDIAMTX_API_PASS"),
)
return await client.list_paths_status()
try:
return await client.list_paths_status()
except httpx.HTTPError as e:
_raise_mediamtx_http_error(e)
@app.post("/api/recording")
async def toggle_recording(data: RecordingToggle) -> dict:
await _apply_recording(data.enabled)
try:
await _apply_recording(data.enabled)
except httpx.HTTPError as e:
_raise_mediamtx_http_error(e)
return {"enabled": data.enabled}