Fix architecture issues from todolist
- Add documentation warnings for in-memory rate limiting and failed login attempts - Consolidate duplicate health endpoints into api/health.py - Fix CLI to use correct async rescan method names - Update download.py and anime.py to use custom exception classes - Add WebSocket room validation and rate limiting
This commit is contained in:
@@ -2,12 +2,18 @@ import logging
|
||||
import warnings
|
||||
from typing import Any, List, Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from fastapi import APIRouter, Depends, status
|
||||
from pydantic import BaseModel, Field
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from src.core.entities.series import Serie
|
||||
from src.server.database.service import AnimeSeriesService
|
||||
from src.server.exceptions import (
|
||||
BadRequestError,
|
||||
NotFoundError,
|
||||
ServerError,
|
||||
ValidationError,
|
||||
)
|
||||
from src.server.services.anime_service import AnimeService, AnimeServiceError
|
||||
from src.server.utils.dependencies import (
|
||||
get_anime_service,
|
||||
@@ -55,9 +61,8 @@ async def get_anime_status(
|
||||
"series_count": series_count
|
||||
}
|
||||
except Exception as exc:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Failed to get status: {str(exc)}",
|
||||
raise ServerError(
|
||||
message=f"Failed to get status: {str(exc)}"
|
||||
) from exc
|
||||
|
||||
|
||||
@@ -208,35 +213,30 @@ async def list_anime(
|
||||
try:
|
||||
page_num = int(page)
|
||||
if page_num < 1:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||
detail="Page number must be positive"
|
||||
raise ValidationError(
|
||||
message="Page number must be positive"
|
||||
)
|
||||
page = page_num
|
||||
except (ValueError, TypeError):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||
detail="Page must be a valid number"
|
||||
raise ValidationError(
|
||||
message="Page must be a valid number"
|
||||
)
|
||||
|
||||
if per_page is not None:
|
||||
try:
|
||||
per_page_num = int(per_page)
|
||||
if per_page_num < 1:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||
detail="Per page must be positive"
|
||||
raise ValidationError(
|
||||
message="Per page must be positive"
|
||||
)
|
||||
if per_page_num > 1000:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||
detail="Per page cannot exceed 1000"
|
||||
raise ValidationError(
|
||||
message="Per page cannot exceed 1000"
|
||||
)
|
||||
per_page = per_page_num
|
||||
except (ValueError, TypeError):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||
detail="Per page must be a valid number"
|
||||
raise ValidationError(
|
||||
message="Per page must be a valid number"
|
||||
)
|
||||
|
||||
# Validate sort_by parameter to prevent ORM injection
|
||||
@@ -245,9 +245,8 @@ async def list_anime(
|
||||
allowed_sort_fields = ["title", "id", "missing_episodes", "name"]
|
||||
if sort_by not in allowed_sort_fields:
|
||||
allowed = ", ".join(allowed_sort_fields)
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||
detail=f"Invalid sort_by parameter. Allowed: {allowed}"
|
||||
raise ValidationError(
|
||||
message=f"Invalid sort_by parameter. Allowed: {allowed}"
|
||||
)
|
||||
|
||||
# Validate filter parameter
|
||||
@@ -260,9 +259,8 @@ async def list_anime(
|
||||
lower_filter = filter.lower()
|
||||
for pattern in dangerous_patterns:
|
||||
if pattern in lower_filter:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||
detail="Invalid filter parameter"
|
||||
raise ValidationError(
|
||||
message="Invalid filter parameter"
|
||||
)
|
||||
|
||||
try:
|
||||
@@ -310,12 +308,11 @@ async def list_anime(
|
||||
)
|
||||
|
||||
return summaries
|
||||
except HTTPException:
|
||||
except (ValidationError, BadRequestError, NotFoundError, ServerError):
|
||||
raise
|
||||
except Exception as exc:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Failed to retrieve anime list",
|
||||
raise ServerError(
|
||||
message="Failed to retrieve anime list"
|
||||
) from exc
|
||||
|
||||
|
||||
@@ -346,14 +343,12 @@ async def trigger_rescan(
|
||||
"message": "Rescan started successfully",
|
||||
}
|
||||
except AnimeServiceError as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Rescan failed: {str(e)}",
|
||||
raise ServerError(
|
||||
message=f"Rescan failed: {str(e)}"
|
||||
) from e
|
||||
except Exception as exc:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Failed to start rescan",
|
||||
raise ServerError(
|
||||
message="Failed to start rescan"
|
||||
) from exc
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user