139 lines
5.0 KiB
Python
139 lines
5.0 KiB
Python
import logging
|
|
from typing import Optional
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, WebSocket, WebSocketDisconnect
|
|
from sqlmodel import Session
|
|
|
|
from core.database import engine, get_session
|
|
from core.docker_instance import docker_manager
|
|
from core.websocket_manager import manager
|
|
from services.bot_runtime_service import (
|
|
delete_cron_job as delete_cron_job_service,
|
|
ensure_monitor_websocket_access,
|
|
get_bot_logs as get_bot_logs_service,
|
|
list_cron_jobs as list_cron_jobs_service,
|
|
relogin_weixin as relogin_weixin_service,
|
|
start_cron_job as start_cron_job_service,
|
|
stop_cron_job as stop_cron_job_service,
|
|
)
|
|
from services.runtime_service import docker_callback
|
|
|
|
router = APIRouter()
|
|
logger = logging.getLogger("dashboard.backend")
|
|
|
|
|
|
@router.get("/api/bots/{bot_id}/logs")
|
|
def get_bot_logs(
|
|
bot_id: str,
|
|
tail: Optional[int] = 300,
|
|
offset: int = 0,
|
|
limit: Optional[int] = None,
|
|
reverse: bool = False,
|
|
session: Session = Depends(get_session),
|
|
):
|
|
try:
|
|
return get_bot_logs_service(
|
|
session,
|
|
bot_id=bot_id,
|
|
tail=tail,
|
|
offset=offset,
|
|
limit=limit,
|
|
reverse=reverse,
|
|
)
|
|
except LookupError as exc:
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
|
|
|
|
@router.post("/api/bots/{bot_id}/weixin/relogin")
|
|
async def relogin_weixin(bot_id: str, session: Session = Depends(get_session)):
|
|
try:
|
|
return await relogin_weixin_service(session, bot_id=bot_id)
|
|
except LookupError as exc:
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
except ValueError as exc:
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
except RuntimeError as exc:
|
|
raise HTTPException(status_code=500, detail=str(exc)) from exc
|
|
|
|
|
|
@router.get("/api/bots/{bot_id}/cron/jobs")
|
|
def list_cron_jobs(bot_id: str, include_disabled: bool = True, session: Session = Depends(get_session)):
|
|
try:
|
|
return list_cron_jobs_service(session, bot_id=bot_id, include_disabled=include_disabled)
|
|
except LookupError as exc:
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
|
|
|
|
@router.post("/api/bots/{bot_id}/cron/jobs/{job_id}/stop")
|
|
def stop_cron_job(bot_id: str, job_id: str, session: Session = Depends(get_session)):
|
|
try:
|
|
return stop_cron_job_service(session, bot_id=bot_id, job_id=job_id)
|
|
except LookupError as exc:
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
|
|
|
|
@router.post("/api/bots/{bot_id}/cron/jobs/{job_id}/start")
|
|
def start_cron_job(bot_id: str, job_id: str, session: Session = Depends(get_session)):
|
|
try:
|
|
return start_cron_job_service(session, bot_id=bot_id, job_id=job_id)
|
|
except LookupError as exc:
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
|
|
|
|
@router.delete("/api/bots/{bot_id}/cron/jobs/{job_id}")
|
|
def delete_cron_job(bot_id: str, job_id: str, session: Session = Depends(get_session)):
|
|
try:
|
|
return delete_cron_job_service(session, bot_id=bot_id, job_id=job_id)
|
|
except LookupError as exc:
|
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
|
|
|
|
@router.websocket("/ws/monitor/{bot_id}")
|
|
async def websocket_endpoint(websocket: WebSocket, bot_id: str):
|
|
with Session(engine) as session:
|
|
try:
|
|
ensure_monitor_websocket_access(session, websocket, bot_id)
|
|
except PermissionError:
|
|
await websocket.close(code=4401, reason="Bot or panel authentication required")
|
|
return
|
|
except LookupError:
|
|
await websocket.close(code=4404, reason="Bot not found")
|
|
return
|
|
|
|
connected = False
|
|
try:
|
|
await manager.connect(bot_id, websocket)
|
|
connected = True
|
|
except Exception as exc:
|
|
logger.warning("websocket connect failed bot_id=%s detail=%s", bot_id, exc)
|
|
try:
|
|
await websocket.close(code=1011, reason="WebSocket accept failed")
|
|
except Exception:
|
|
pass
|
|
return
|
|
|
|
docker_manager.ensure_monitor(bot_id, docker_callback)
|
|
try:
|
|
while True:
|
|
await websocket.receive_text()
|
|
with Session(engine) as session:
|
|
try:
|
|
ensure_monitor_websocket_access(session, websocket, bot_id)
|
|
except PermissionError:
|
|
await websocket.close(code=4401, reason="Authentication expired")
|
|
return
|
|
except LookupError:
|
|
await websocket.close(code=4404, reason="Bot not found")
|
|
return
|
|
except WebSocketDisconnect:
|
|
pass
|
|
except RuntimeError as exc:
|
|
msg = str(exc or "").lower()
|
|
if "need to call \"accept\" first" not in msg and "not connected" not in msg:
|
|
logger.exception("websocket runtime error bot_id=%s", bot_id)
|
|
except Exception:
|
|
logger.exception("websocket unexpected error bot_id=%s", bot_id)
|
|
finally:
|
|
if connected:
|
|
manager.disconnect(bot_id, websocket)
|