2026-03-31 04:31:47 +00:00
|
|
|
from typing import Any, Dict
|
|
|
|
|
|
2026-04-03 15:00:08 +00:00
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Request, Response
|
2026-03-31 04:31:47 +00:00
|
|
|
from sqlmodel import Session, select
|
|
|
|
|
|
|
|
|
|
from core.database import engine, get_session
|
|
|
|
|
from core.settings import DATABASE_ENGINE, PANEL_ACCESS_PASSWORD, REDIS_ENABLED, REDIS_PREFIX, REDIS_URL
|
|
|
|
|
from core.utils import _get_default_system_timezone
|
|
|
|
|
from models.bot import BotInstance
|
|
|
|
|
from schemas.system import PanelLoginRequest, SystemTemplatesUpdateRequest
|
|
|
|
|
from core.cache import cache
|
2026-04-03 15:00:08 +00:00
|
|
|
from services.platform_auth_service import (
|
|
|
|
|
clear_panel_token_cookie,
|
|
|
|
|
create_panel_token,
|
|
|
|
|
resolve_panel_request_auth,
|
|
|
|
|
revoke_panel_token,
|
|
|
|
|
set_panel_token_cookie,
|
|
|
|
|
)
|
2026-03-31 04:31:47 +00:00
|
|
|
from services.platform_service import get_platform_settings_snapshot, get_speech_runtime_settings
|
|
|
|
|
from services.template_service import (
|
|
|
|
|
get_agent_md_templates,
|
|
|
|
|
get_topic_presets,
|
|
|
|
|
update_agent_md_templates,
|
|
|
|
|
update_topic_presets,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/api/panel/auth/status")
|
2026-04-03 15:00:08 +00:00
|
|
|
def get_panel_auth_status(request: Request, session: Session = Depends(get_session)):
|
2026-03-31 04:31:47 +00:00
|
|
|
configured = str(PANEL_ACCESS_PASSWORD or "").strip()
|
2026-04-03 15:00:08 +00:00
|
|
|
principal = resolve_panel_request_auth(session, request)
|
|
|
|
|
return {
|
|
|
|
|
"enabled": bool(configured),
|
|
|
|
|
"authenticated": bool(principal.authenticated),
|
|
|
|
|
"auth_source": principal.auth_source if principal.authenticated else None,
|
|
|
|
|
}
|
2026-03-31 04:31:47 +00:00
|
|
|
|
|
|
|
|
@router.post("/api/panel/auth/login")
|
2026-04-03 15:00:08 +00:00
|
|
|
def panel_login(payload: PanelLoginRequest, request: Request, response: Response, session: Session = Depends(get_session)):
|
2026-03-31 04:31:47 +00:00
|
|
|
configured = str(PANEL_ACCESS_PASSWORD or "").strip()
|
|
|
|
|
if not configured:
|
2026-04-03 15:00:08 +00:00
|
|
|
clear_panel_token_cookie(response)
|
2026-03-31 04:31:47 +00:00
|
|
|
return {"success": True, "enabled": False}
|
|
|
|
|
supplied = str(payload.password or "").strip()
|
|
|
|
|
if supplied != configured:
|
|
|
|
|
raise HTTPException(status_code=401, detail="Invalid panel access password")
|
2026-04-03 15:00:08 +00:00
|
|
|
try:
|
|
|
|
|
raw_token = create_panel_token(session, request)
|
|
|
|
|
except RuntimeError as exc:
|
|
|
|
|
raise HTTPException(status_code=503, detail=str(exc)) from exc
|
|
|
|
|
set_panel_token_cookie(response, request, raw_token, session)
|
|
|
|
|
return {"success": True, "enabled": True, "authenticated": True}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/api/panel/auth/logout")
|
|
|
|
|
def panel_logout(request: Request, response: Response, session: Session = Depends(get_session)):
|
|
|
|
|
revoke_panel_token(session, request)
|
|
|
|
|
clear_panel_token_cookie(response)
|
|
|
|
|
return {"success": True}
|
2026-03-31 04:31:47 +00:00
|
|
|
|
|
|
|
|
@router.get("/api/system/defaults")
|
|
|
|
|
def get_system_defaults():
|
|
|
|
|
md_templates = get_agent_md_templates()
|
|
|
|
|
platform_settings = get_platform_settings_snapshot()
|
|
|
|
|
speech_settings = get_speech_runtime_settings()
|
|
|
|
|
return {
|
|
|
|
|
"templates": md_templates,
|
|
|
|
|
"limits": {
|
|
|
|
|
"upload_max_mb": platform_settings.upload_max_mb,
|
|
|
|
|
},
|
|
|
|
|
"workspace": {
|
|
|
|
|
"download_extensions": list(platform_settings.workspace_download_extensions),
|
|
|
|
|
"allowed_attachment_extensions": list(platform_settings.allowed_attachment_extensions),
|
|
|
|
|
},
|
|
|
|
|
"bot": {
|
|
|
|
|
"system_timezone": _get_default_system_timezone(),
|
|
|
|
|
},
|
|
|
|
|
"chat": {
|
|
|
|
|
"pull_page_size": platform_settings.chat_pull_page_size,
|
|
|
|
|
"page_size": platform_settings.page_size,
|
|
|
|
|
"command_auto_unlock_seconds": platform_settings.command_auto_unlock_seconds,
|
|
|
|
|
},
|
|
|
|
|
"topic_presets": get_topic_presets().get("presets", []),
|
|
|
|
|
"speech": {
|
|
|
|
|
"enabled": speech_settings["enabled"],
|
|
|
|
|
"model": speech_settings["model"],
|
|
|
|
|
"device": speech_settings["device"],
|
|
|
|
|
"max_audio_seconds": speech_settings["max_audio_seconds"],
|
|
|
|
|
"default_language": speech_settings["default_language"],
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@router.get("/api/system/templates")
|
|
|
|
|
def get_system_templates():
|
|
|
|
|
return {
|
|
|
|
|
"agent_md_templates": get_agent_md_templates(),
|
|
|
|
|
"topic_presets": get_topic_presets(),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@router.put("/api/system/templates")
|
|
|
|
|
def update_system_templates(payload: SystemTemplatesUpdateRequest):
|
|
|
|
|
if payload.agent_md_templates is not None:
|
|
|
|
|
update_agent_md_templates(payload.agent_md_templates.model_dump())
|
|
|
|
|
|
|
|
|
|
if payload.topic_presets is not None:
|
|
|
|
|
try:
|
|
|
|
|
update_topic_presets(payload.topic_presets)
|
|
|
|
|
except ValueError as exc:
|
|
|
|
|
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
"status": "ok",
|
|
|
|
|
"agent_md_templates": get_agent_md_templates(),
|
|
|
|
|
"topic_presets": get_topic_presets(),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@router.get("/api/health")
|
|
|
|
|
def get_health():
|
|
|
|
|
try:
|
|
|
|
|
with Session(engine) as session:
|
|
|
|
|
session.exec(select(BotInstance).limit(1)).first()
|
|
|
|
|
return {"status": "ok", "database": DATABASE_ENGINE}
|
|
|
|
|
except Exception as e:
|
|
|
|
|
raise HTTPException(status_code=503, detail=f"database check failed: {e}")
|
|
|
|
|
|
|
|
|
|
@router.get("/api/health/cache")
|
|
|
|
|
def get_cache_health():
|
|
|
|
|
redis_url = str(REDIS_URL or "").strip()
|
|
|
|
|
configured = bool(REDIS_ENABLED and redis_url)
|
|
|
|
|
client_enabled = bool(getattr(cache, "enabled", False))
|
|
|
|
|
reachable = bool(cache.ping()) if client_enabled else False
|
|
|
|
|
status = "ok"
|
|
|
|
|
if configured and not reachable:
|
|
|
|
|
status = "degraded"
|
|
|
|
|
return {
|
|
|
|
|
"status": status,
|
|
|
|
|
"cache": {
|
|
|
|
|
"configured": configured,
|
|
|
|
|
"enabled": client_enabled,
|
|
|
|
|
"reachable": reachable,
|
|
|
|
|
"prefix": REDIS_PREFIX,
|
|
|
|
|
},
|
|
|
|
|
}
|