Add config API endpoints and tests; update docs
This commit is contained in:
parent
52b96da8dc
commit
6b979eb57a
@ -122,6 +122,16 @@ conda activate AniWorld
|
||||
- `PUT /api/config` - Update configuration
|
||||
- `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
|
||||
|
||||
- `GET /api/anime` - List anime with missing episodes
|
||||
|
||||
68
src/server/api/config.py
Normal file
68
src/server/api/config.py
Normal 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))
|
||||
36
tests/api/test_config_endpoints.py
Normal file
36
tests/api/test_config_endpoints.py
Normal 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)
|
||||
Loading…
x
Reference in New Issue
Block a user