tts-voice-creator-clone-and.../routes/settings.py
mARTin-B78 c7a1e35539 Security audit, modular refactor, and container-name field
Security fixes:
- Block /proc /sys /dev /run /boot in /api/browse-dirs (path traversal)
- Verify yt-dlp output stays inside TEMP_DIR before registration
- Remove Access-Control-Allow-Origin: * from /api/proxy-audio
- TTL-based temp file registry (default 2h) to prevent disk fill

Performance:
- Cache settings + routing rules in memory (mtime-checked); eliminates
  per-request disk reads on every TTS call

UI:
- Add container name (optional) field to Docker stack TTS/STT engine
  cards (Qwen3 Voice Clone, Voice Design, Custom Voice, Streaming,
  NVIDIA Magpie, Parakeet) — enables Stop/Start/Restart buttons on
  all engine cards, matching the existing Other Local TTS/STT cards

Refactor — backend:
- server.py: 5560 lines → 43-line entry point
- core/ package: constants, registry, validation, docker_client,
  config, routing, audio, voice, presets, tts_helpers
- routes/ package: admin, settings, library, stt, sources, docker,
  tts, conversation (FastAPI APIRouter modules)
- Dockerfile + docker-compose.yml updated to include core/ and routes/

Refactor — frontend:
- static/app.js: 8744 lines → 16 modules in static/js/
  utils, voice-inspector, voice-sources, integrations, routing,
  settings, voice-clone, voice-library, tts-preview, benchmark,
  stt, init, engines, ai-backends, generation, conversation
- static/loader.js updated to load modules sequentially

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 12:13:07 +02:00

80 lines
2.4 KiB
Python

"""Settings, TTS routes, routing log, server logs, and voice design presets endpoints."""
from __future__ import annotations
from fastapi import APIRouter, HTTPException, Request
from core.config import _load_settings, _save_settings, _normalize_settings, _SETTINGS_KEYS
from core.routing import _load_tts_routes, _save_tts_routes
from core.constants import _log_buffer, _LOG_BUFFER_MAX, _routing_log, _ROUTING_LOG_MAX
from core.presets import _load_design_presets, _save_design_presets
router = APIRouter()
@router.get("/api/settings")
async def get_settings():
return _load_settings()
@router.post("/api/settings")
async def post_settings(request: Request):
data = await request.json()
s = _load_settings()
s.update({k: v for k, v in data.items() if k in _SETTINGS_KEYS})
s = _normalize_settings(s)
_save_settings(s)
return {"ok": True}
@router.get("/api/tts-routes")
async def get_tts_routes():
return {"routes": _load_tts_routes()}
@router.post("/api/tts-routes")
async def post_tts_routes(request: Request):
data = await request.json()
routes = data.get("routes", data) if isinstance(data, dict) else data
if not isinstance(routes, list):
raise HTTPException(400, "routes must be a list")
_save_tts_routes(routes)
return {"ok": True, "routes": _load_tts_routes()}
@router.get("/api/tts-routing-log")
async def get_tts_routing_log(limit: int = 60):
limit = max(1, min(int(limit or 60), _ROUTING_LOG_MAX))
return {"items": _routing_log[:limit], "max": _ROUTING_LOG_MAX}
@router.delete("/api/tts-routing-log")
async def clear_tts_routing_log():
_routing_log.clear()
return {"ok": True, "items": []}
@router.get("/api/logs")
async def get_server_logs(limit: int = 200):
limit = max(1, min(int(limit or 200), _LOG_BUFFER_MAX))
return {"items": _log_buffer[:limit], "max": _LOG_BUFFER_MAX}
@router.delete("/api/logs")
async def clear_server_logs():
_log_buffer.clear()
return {"ok": True}
@router.get("/api/voice-design-presets")
async def get_voice_design_presets():
return _load_design_presets()
@router.post("/api/voice-design-presets")
async def post_voice_design_presets(request: Request):
data = await request.json()
if not isinstance(data, dict):
raise HTTPException(400, "Expected a preset object")
_save_design_presets(data)
return {"ok": True, "presets": _load_design_presets()}