Aniworld/src/server/api/scheduler.py
Lukas 85d73b8294 feat: implement missing API endpoints for scheduler, logging, and diagnostics
- Add scheduler API endpoints for configuration and manual rescan triggers
- Add logging API endpoints for config management and log file operations
- Add diagnostics API endpoints for network and system information
- Extend config API with advanced settings, directory updates, export, and reset
- Update FastAPI app to include new routers
- Update API reference documentation with all new endpoints
- Update infrastructure documentation with endpoint listings
- Add comprehensive API implementation summary

All new endpoints follow project coding standards with:
- Type hints and Pydantic validation
- Proper authentication and authorization
- Comprehensive error handling and logging
- Security best practices (path validation, input sanitization)

Test results: 752/802 tests passing (93.8%)
2025-10-24 10:39:29 +02:00

131 lines
3.9 KiB
Python

"""Scheduler API endpoints for Aniworld.
This module provides endpoints for managing scheduled tasks such as
automatic anime library rescans.
"""
import logging
from typing import Dict, Optional
from fastapi import APIRouter, Depends, HTTPException, status
from src.server.models.config import SchedulerConfig
from src.server.services.config_service import ConfigServiceError, get_config_service
from src.server.utils.dependencies import require_auth
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/scheduler", tags=["scheduler"])
@router.get("/config", response_model=SchedulerConfig)
def get_scheduler_config(
auth: Optional[dict] = Depends(require_auth)
) -> SchedulerConfig:
"""Get current scheduler configuration.
Args:
auth: Authentication token (optional for read operations)
Returns:
SchedulerConfig: Current scheduler configuration
Raises:
HTTPException: If configuration cannot be loaded
"""
try:
config_service = get_config_service()
app_config = config_service.load_config()
return app_config.scheduler
except ConfigServiceError as e:
logger.error(f"Failed to load scheduler config: {e}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to load scheduler configuration: {e}",
) from e
@router.post("/config", response_model=SchedulerConfig)
def update_scheduler_config(
scheduler_config: SchedulerConfig,
auth: dict = Depends(require_auth),
) -> SchedulerConfig:
"""Update scheduler configuration.
Args:
scheduler_config: New scheduler configuration
auth: Authentication token (required)
Returns:
SchedulerConfig: Updated scheduler configuration
Raises:
HTTPException: If configuration update fails
"""
try:
config_service = get_config_service()
app_config = config_service.load_config()
# Update scheduler section
app_config.scheduler = scheduler_config
# Save and return
config_service.save_config(app_config)
logger.info(
f"Scheduler config updated by {auth.get('username', 'unknown')}"
)
return scheduler_config
except ConfigServiceError as e:
logger.error(f"Failed to update scheduler config: {e}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to update scheduler configuration: {e}",
) from e
@router.post("/trigger-rescan", response_model=Dict[str, str])
async def trigger_rescan(auth: dict = Depends(require_auth)) -> Dict[str, str]:
"""Manually trigger a library rescan.
This endpoint triggers an immediate anime library rescan, bypassing
the scheduler interval.
Args:
auth: Authentication token (required)
Returns:
Dict with success message
Raises:
HTTPException: If rescan cannot be triggered
"""
try:
# Import here to avoid circular dependency
from src.server.fastapi_app import get_series_app
series_app = get_series_app()
if not series_app:
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail="SeriesApp not initialized",
)
# Trigger the rescan
logger.info(
f"Manual rescan triggered by {auth.get('username', 'unknown')}"
)
# Use existing rescan logic from anime API
from src.server.api.anime import trigger_rescan as do_rescan
return await do_rescan()
except HTTPException:
raise
except Exception as e:
logger.exception("Failed to trigger manual rescan")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to trigger rescan: {str(e)}",
) from e