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%)
This commit is contained in:
@@ -157,3 +157,193 @@ def delete_backup(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Failed to delete backup: {e}"
|
||||
) from e
|
||||
|
||||
|
||||
@router.get("/section/advanced", response_model=Dict[str, object])
|
||||
def get_advanced_config(
|
||||
auth: Optional[dict] = Depends(require_auth)
|
||||
) -> Dict[str, object]:
|
||||
"""Get advanced configuration section.
|
||||
|
||||
Returns:
|
||||
Dictionary with advanced configuration settings
|
||||
"""
|
||||
try:
|
||||
config_service = get_config_service()
|
||||
app_config = config_service.load_config()
|
||||
return app_config.other.get("advanced", {})
|
||||
except ConfigServiceError as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Failed to load advanced config: {e}"
|
||||
) from e
|
||||
|
||||
|
||||
@router.post("/section/advanced", response_model=Dict[str, str])
|
||||
def update_advanced_config(
|
||||
config: Dict[str, object], auth: dict = Depends(require_auth)
|
||||
) -> Dict[str, str]:
|
||||
"""Update advanced configuration section.
|
||||
|
||||
Args:
|
||||
config: Advanced configuration settings
|
||||
auth: Authentication token (required)
|
||||
|
||||
Returns:
|
||||
Success message
|
||||
"""
|
||||
try:
|
||||
config_service = get_config_service()
|
||||
app_config = config_service.load_config()
|
||||
|
||||
# Update advanced section in other
|
||||
if "advanced" not in app_config.other:
|
||||
app_config.other["advanced"] = {}
|
||||
app_config.other["advanced"].update(config)
|
||||
|
||||
config_service.save_config(app_config)
|
||||
return {"message": "Advanced configuration updated successfully"}
|
||||
except ConfigServiceError as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Failed to update advanced config: {e}"
|
||||
) from e
|
||||
|
||||
|
||||
@router.post("/directory", response_model=Dict[str, str])
|
||||
def update_directory(
|
||||
directory_config: Dict[str, str], auth: dict = Depends(require_auth)
|
||||
) -> Dict[str, str]:
|
||||
"""Update anime directory configuration.
|
||||
|
||||
Args:
|
||||
directory_config: Dictionary with 'directory' key
|
||||
auth: Authentication token (required)
|
||||
|
||||
Returns:
|
||||
Success message
|
||||
"""
|
||||
try:
|
||||
directory = directory_config.get("directory")
|
||||
if not directory:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Directory path is required"
|
||||
)
|
||||
|
||||
config_service = get_config_service()
|
||||
app_config = config_service.load_config()
|
||||
|
||||
# Store directory in other section
|
||||
if "anime_directory" not in app_config.other:
|
||||
app_config.other["anime_directory"] = directory
|
||||
else:
|
||||
app_config.other["anime_directory"] = directory
|
||||
|
||||
config_service.save_config(app_config)
|
||||
return {"message": "Anime directory updated successfully"}
|
||||
except ConfigServiceError as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Failed to update directory: {e}"
|
||||
) from e
|
||||
|
||||
|
||||
@router.post("/export")
|
||||
async def export_config(
|
||||
export_options: Dict[str, bool], auth: dict = Depends(require_auth)
|
||||
):
|
||||
"""Export configuration to JSON file.
|
||||
|
||||
Args:
|
||||
export_options: Options for export (include_sensitive, etc.)
|
||||
auth: Authentication token (required)
|
||||
|
||||
Returns:
|
||||
JSON file download response
|
||||
"""
|
||||
try:
|
||||
import json
|
||||
|
||||
from fastapi.responses import Response
|
||||
|
||||
config_service = get_config_service()
|
||||
app_config = config_service.load_config()
|
||||
|
||||
# Convert to dict
|
||||
config_dict = app_config.model_dump()
|
||||
|
||||
# Optionally remove sensitive data
|
||||
if not export_options.get("include_sensitive", False):
|
||||
# Remove sensitive fields if present
|
||||
config_dict.pop("password_salt", None)
|
||||
config_dict.pop("password_hash", None)
|
||||
|
||||
# Create filename with timestamp
|
||||
from datetime import datetime
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
filename = f"aniworld_config_{timestamp}.json"
|
||||
|
||||
# Return as downloadable JSON
|
||||
content = json.dumps(config_dict, indent=2)
|
||||
return Response(
|
||||
content=content,
|
||||
media_type="application/json",
|
||||
headers={
|
||||
"Content-Disposition": f'attachment; filename="{filename}"'
|
||||
}
|
||||
)
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Failed to export config: {e}"
|
||||
) from e
|
||||
|
||||
|
||||
@router.post("/reset", response_model=Dict[str, str])
|
||||
def reset_config(
|
||||
reset_options: Dict[str, bool], auth: dict = Depends(require_auth)
|
||||
) -> Dict[str, str]:
|
||||
"""Reset configuration to defaults.
|
||||
|
||||
Args:
|
||||
reset_options: Options for reset (preserve_security, etc.)
|
||||
auth: Authentication token (required)
|
||||
|
||||
Returns:
|
||||
Success message
|
||||
"""
|
||||
try:
|
||||
config_service = get_config_service()
|
||||
|
||||
# Create backup before resetting
|
||||
config_service.create_backup("pre_reset")
|
||||
|
||||
# Load default config
|
||||
default_config = AppConfig()
|
||||
|
||||
# If preserve_security is True, keep authentication settings
|
||||
if reset_options.get("preserve_security", True):
|
||||
current_config = config_service.load_config()
|
||||
# Preserve security-related fields from other
|
||||
if "password_salt" in current_config.other:
|
||||
default_config.other["password_salt"] = (
|
||||
current_config.other["password_salt"]
|
||||
)
|
||||
if "password_hash" in current_config.other:
|
||||
default_config.other["password_hash"] = (
|
||||
current_config.other["password_hash"]
|
||||
)
|
||||
|
||||
# Save default config
|
||||
config_service.save_config(default_config)
|
||||
|
||||
return {
|
||||
"message": "Configuration reset to defaults successfully"
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Failed to reset config: {e}"
|
||||
) from e
|
||||
|
||||
|
||||
Reference in New Issue
Block a user