chore: make sure that there is only one app
This commit is contained in:
@@ -4,11 +4,7 @@ from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from src.core.entities.series import Serie
|
||||
from src.server.utils.dependencies import (
|
||||
get_optional_series_app,
|
||||
get_series_app,
|
||||
require_auth,
|
||||
)
|
||||
from src.server.utils.dependencies import get_series_app, require_auth
|
||||
|
||||
router = APIRouter(prefix="/api/anime", tags=["anime"])
|
||||
|
||||
@@ -50,51 +46,6 @@ async def get_anime_status(
|
||||
) from exc
|
||||
|
||||
|
||||
@router.get("/process/locks")
|
||||
async def get_process_locks(
|
||||
_auth: dict = Depends(require_auth),
|
||||
series_app: Any = Depends(get_series_app),
|
||||
) -> dict:
|
||||
"""Get process lock status for rescan and download operations.
|
||||
|
||||
Args:
|
||||
_auth: Ensures the caller is authenticated (value unused)
|
||||
series_app: Core `SeriesApp` instance provided via dependency
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: Lock status information
|
||||
|
||||
Raises:
|
||||
HTTPException: If lock status retrieval fails
|
||||
"""
|
||||
try:
|
||||
locks = {
|
||||
"rescan": {"is_locked": False},
|
||||
"download": {"is_locked": False}
|
||||
}
|
||||
|
||||
# Check if SeriesApp has lock status methods
|
||||
if series_app:
|
||||
if hasattr(series_app, "isRescanning"):
|
||||
locks["rescan"]["is_locked"] = series_app.isRescanning()
|
||||
if hasattr(series_app, "isDownloading"):
|
||||
locks["download"]["is_locked"] = series_app.isDownloading()
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"locks": locks
|
||||
}
|
||||
except Exception as exc:
|
||||
return {
|
||||
"success": False,
|
||||
"error": str(exc),
|
||||
"locks": {
|
||||
"rescan": {"is_locked": False},
|
||||
"download": {"is_locked": False}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class AnimeSummary(BaseModel):
|
||||
"""Summary of an anime series with missing episodes."""
|
||||
key: str # Unique identifier (used as id in frontend)
|
||||
@@ -402,7 +353,7 @@ class SearchAnimeRequest(BaseModel):
|
||||
@router.get("/search", response_model=List[AnimeSummary])
|
||||
async def search_anime_get(
|
||||
query: str,
|
||||
series_app: Optional[Any] = Depends(get_optional_series_app),
|
||||
series_app: Optional[Any] = Depends(get_series_app),
|
||||
) -> List[AnimeSummary]:
|
||||
"""Search the provider for additional series matching a query (GET).
|
||||
|
||||
@@ -425,7 +376,7 @@ async def search_anime_get(
|
||||
)
|
||||
async def search_anime_post(
|
||||
request: SearchAnimeRequest,
|
||||
series_app: Optional[Any] = Depends(get_optional_series_app),
|
||||
series_app: Optional[Any] = Depends(get_series_app),
|
||||
) -> List[AnimeSummary]:
|
||||
"""Search the provider for additional series matching a query (POST).
|
||||
|
||||
@@ -591,7 +542,7 @@ async def add_series(
|
||||
@router.get("/{anime_id}", response_model=AnimeDetail)
|
||||
async def get_anime(
|
||||
anime_id: str,
|
||||
series_app: Optional[Any] = Depends(get_optional_series_app)
|
||||
series_app: Optional[Any] = Depends(get_series_app)
|
||||
) -> AnimeDetail:
|
||||
"""Return detailed information about a specific series.
|
||||
|
||||
@@ -649,46 +600,6 @@ async def get_anime(
|
||||
) from exc
|
||||
|
||||
|
||||
# Test endpoint for input validation
|
||||
class AnimeCreateRequest(BaseModel):
|
||||
"""Request model for creating anime (test endpoint)."""
|
||||
|
||||
title: str
|
||||
description: Optional[str] = None
|
||||
|
||||
|
||||
# Maximum allowed input size for security
|
||||
MAX_INPUT_LENGTH = 100000 # 100KB
|
||||
|
||||
|
||||
@router.post("", include_in_schema=False, status_code=status.HTTP_201_CREATED)
|
||||
async def create_anime_test(request: AnimeCreateRequest):
|
||||
"""Test endpoint for input validation testing.
|
||||
|
||||
This endpoint validates input sizes and content for security testing.
|
||||
Not used in production - only for validation tests.
|
||||
"""
|
||||
# Validate input size
|
||||
if len(request.title) > MAX_INPUT_LENGTH:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
|
||||
detail="Title exceeds maximum allowed length",
|
||||
)
|
||||
|
||||
if request.description and len(request.description) > MAX_INPUT_LENGTH:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
|
||||
detail="Description exceeds maximum allowed length",
|
||||
)
|
||||
|
||||
# Return success for valid input
|
||||
return {
|
||||
"status": "success",
|
||||
"message": "Anime created (test mode)",
|
||||
"data": {
|
||||
"title": request.title[:100], # Truncated for response
|
||||
"description": (
|
||||
request.description[:100] if request.description else None
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -8,14 +8,14 @@ from typing import Optional
|
||||
from fastapi import APIRouter, Depends
|
||||
|
||||
from src.core.SeriesApp import SeriesApp
|
||||
from src.server.utils.dependencies import get_optional_series_app
|
||||
from src.server.utils.dependencies import get_series_app
|
||||
|
||||
router = APIRouter(prefix="/health", tags=["health"])
|
||||
|
||||
|
||||
@router.get("")
|
||||
async def health_check(
|
||||
series_app: Optional[SeriesApp] = Depends(get_optional_series_app)
|
||||
series_app: Optional[SeriesApp] = Depends(get_series_app)
|
||||
):
|
||||
"""Health check endpoint for monitoring."""
|
||||
return {
|
||||
|
||||
@@ -7,7 +7,6 @@ integration.
|
||||
"""
|
||||
from contextlib import asynccontextmanager
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
import uvicorn
|
||||
from fastapi import FastAPI, HTTPException, Request
|
||||
@@ -17,7 +16,6 @@ from fastapi.staticfiles import StaticFiles
|
||||
from src.config.settings import settings
|
||||
|
||||
# Import core functionality
|
||||
from src.core.SeriesApp import SeriesApp
|
||||
from src.infrastructure.logging import setup_logging
|
||||
from src.server.api.anime import router as anime_router
|
||||
from src.server.api.auth import router as auth_router
|
||||
@@ -69,20 +67,6 @@ async def lifespan(app: FastAPI):
|
||||
except Exception as e:
|
||||
logger.warning("Failed to load config from config.json: %s", e)
|
||||
|
||||
# Initialize SeriesApp with configured directory and store it on
|
||||
# application state so it can be injected via dependencies.
|
||||
if settings.anime_directory:
|
||||
app.state.series_app = SeriesApp(settings.anime_directory)
|
||||
logger.info(
|
||||
"SeriesApp initialized with directory: %s",
|
||||
settings.anime_directory
|
||||
)
|
||||
else:
|
||||
# Log warning when anime directory is not configured
|
||||
logger.warning(
|
||||
"ANIME_DIRECTORY not configured. "
|
||||
"Some features may be unavailable."
|
||||
)
|
||||
|
||||
# Initialize progress service with websocket callback
|
||||
progress_service = get_progress_service()
|
||||
@@ -116,15 +100,6 @@ async def lifespan(app: FastAPI):
|
||||
logger.info("FastAPI application shutting down")
|
||||
|
||||
|
||||
def get_series_app() -> Optional[SeriesApp]:
|
||||
"""Dependency to retrieve the SeriesApp instance from application state.
|
||||
|
||||
Returns None when the application wasn't configured with an anime
|
||||
directory (for example during certain test runs).
|
||||
"""
|
||||
return getattr(app.state, "series_app", None)
|
||||
|
||||
|
||||
# Initialize FastAPI app with lifespan
|
||||
app = FastAPI(
|
||||
title="Aniworld Download Manager",
|
||||
|
||||
@@ -5,13 +5,14 @@ from functools import lru_cache
|
||||
from typing import List, Optional
|
||||
|
||||
import structlog
|
||||
from fastapi import Depends
|
||||
|
||||
from src.core.SeriesApp import SeriesApp
|
||||
from src.server.services.progress_service import (
|
||||
ProgressService,
|
||||
ProgressType,
|
||||
get_progress_service,
|
||||
)
|
||||
from src.server.utils.dependencies import get_series_app
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
@@ -38,7 +39,7 @@ class AnimeService:
|
||||
self._progress_service = progress_service or get_progress_service()
|
||||
# Initialize SeriesApp with async methods
|
||||
try:
|
||||
self._app = SeriesApp(directory)
|
||||
self._app = Depends(get_series_app)
|
||||
# Subscribe to SeriesApp events
|
||||
self._app.download_status += self._on_download_status
|
||||
self._app.scan_status += self._on_scan_status
|
||||
|
||||
@@ -103,40 +103,6 @@ def reset_series_app() -> None:
|
||||
_series_app = None
|
||||
|
||||
|
||||
def get_optional_series_app() -> Optional[SeriesApp]:
|
||||
"""
|
||||
Dependency to optionally get SeriesApp instance.
|
||||
|
||||
Returns None if not configured instead of raising an exception.
|
||||
Useful for endpoints that can validate input before needing the service.
|
||||
|
||||
Returns:
|
||||
Optional[SeriesApp]: The main application instance or None
|
||||
"""
|
||||
global _series_app
|
||||
|
||||
# Try to load anime_directory from config.json if not in settings
|
||||
if not settings.anime_directory:
|
||||
try:
|
||||
from src.server.services.config_service import get_config_service
|
||||
config_service = get_config_service()
|
||||
config = config_service.load_config()
|
||||
if config.other and config.other.get("anime_directory"):
|
||||
settings.anime_directory = str(config.other["anime_directory"])
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
if not settings.anime_directory:
|
||||
return None
|
||||
|
||||
if _series_app is None:
|
||||
try:
|
||||
_series_app = SeriesApp(settings.anime_directory)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
return _series_app
|
||||
|
||||
|
||||
async def get_database_session() -> AsyncGenerator:
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user