Add config API endpoints and tests; update docs

This commit is contained in:
Lukas 2025-10-14 21:45:30 +02:00
parent 52b96da8dc
commit 6b979eb57a
3 changed files with 114 additions and 0 deletions

View File

@ -122,6 +122,16 @@ conda activate AniWorld
- `PUT /api/config` - Update configuration - `PUT /api/config` - Update configuration
- `POST /api/setup` - Initial setup - `POST /api/setup` - Initial setup
### Configuration API Notes
- The configuration endpoints are exposed under `/api/config` and
operate primarily on a JSON-serializable `AppConfig` model. They are
designed to be lightweight and avoid performing IO during validation
(the `/api/config/validate` endpoint runs in-memory checks only).
- Persistence of configuration changes is intentionally "best-effort"
for now and mirrors fields into the runtime settings object. A
follow-up task should add durable storage (file or DB) for configs.
### Anime Management ### Anime Management
- `GET /api/anime` - List anime with missing episodes - `GET /api/anime` - List anime with missing episodes

68
src/server/api/config.py Normal file
View File

@ -0,0 +1,68 @@
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException, status
from src.config.settings import settings
from src.server.models.config import AppConfig, ConfigUpdate, ValidationResult
from src.server.utils.dependencies import require_auth
router = APIRouter(prefix="/api/config", tags=["config"])
@router.get("", response_model=AppConfig)
def get_config(auth: Optional[dict] = Depends(require_auth)) -> AppConfig:
"""Return current application configuration (read-only)."""
# Construct AppConfig from pydantic-settings where possible
cfg_data = {
"name": getattr(settings, "app_name", "Aniworld"),
"data_dir": getattr(settings, "data_dir", "data"),
"scheduler": getattr(settings, "scheduler", {}),
"logging": getattr(settings, "logging", {}),
"backup": getattr(settings, "backup", {}),
"other": getattr(settings, "other", {}),
}
try:
return AppConfig(**cfg_data)
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to read config: {e}")
@router.put("", response_model=AppConfig)
def update_config(update: ConfigUpdate, auth: dict = Depends(require_auth)) -> AppConfig:
"""Apply an update to the configuration and return the new config.
Note: persistence strategy for settings is out-of-scope for this task.
This endpoint updates the in-memory Settings where possible and returns
the merged result as an AppConfig.
"""
# Build current AppConfig from settings then apply update
current = get_config(auth)
new_cfg = update.apply_to(current)
# Mirror some fields back into pydantic-settings 'settings' where safe.
# Avoid writing secrets or unsupported fields.
try:
if new_cfg.data_dir:
setattr(settings, "data_dir", new_cfg.data_dir)
# scheduler/logging/backup/other kept in memory only for now
setattr(settings, "scheduler", new_cfg.scheduler.model_dump())
setattr(settings, "logging", new_cfg.logging.model_dump())
setattr(settings, "backup", new_cfg.backup.model_dump())
setattr(settings, "other", new_cfg.other)
except Exception:
# Best-effort; do not fail the request if persistence is not available
pass
return new_cfg
@router.post("/validate", response_model=ValidationResult)
def validate_config(cfg: AppConfig, auth: dict = Depends(require_auth)) -> ValidationResult:
"""Validate a provided AppConfig without applying it.
Returns ValidationResult with any validation errors.
"""
try:
return cfg.validate()
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))

View File

@ -0,0 +1,36 @@
from fastapi.testclient import TestClient
from src.server.fastapi_app import app
from src.server.models.config import AppConfig, SchedulerConfig
client = TestClient(app)
def test_get_config_public():
resp = client.get("/api/config")
assert resp.status_code == 200
data = resp.json()
assert "name" in data
assert "data_dir" in data
def test_validate_config():
cfg = {
"name": "Aniworld",
"data_dir": "data",
"scheduler": {"enabled": True, "interval_minutes": 30},
"logging": {"level": "INFO"},
"backup": {"enabled": False},
"other": {},
}
resp = client.post("/api/config/validate", json=cfg)
assert resp.status_code == 200
body = resp.json()
assert body.get("valid") is True
def test_update_config_unauthorized():
# update requires auth; without auth should be 401
update = {"scheduler": {"enabled": False}}
resp = client.put("/api/config", json=update)
assert resp.status_code in (401, 422)