feat: migrate to Pydantic V2 and implement rate limiting middleware

- Migrate settings.py to Pydantic V2 (SettingsConfigDict, validation_alias)
- Update config models to use @field_validator with @classmethod
- Replace deprecated datetime.utcnow() with datetime.now(timezone.utc)
- Migrate FastAPI app from @app.on_event to lifespan context manager
- Implement comprehensive rate limiting middleware with:
  * Endpoint-specific rate limits (login: 5/min, register: 3/min)
  * IP-based and user-based tracking
  * Authenticated user multiplier (2x limits)
  * Bypass paths for health, docs, static, websocket endpoints
  * Rate limit headers in responses
- Add 13 comprehensive tests for rate limiting (all passing)
- Update instructions.md to mark completed tasks
- Fix asyncio.create_task usage in anime_service.py

All 714 tests passing. No deprecation warnings.
This commit is contained in:
2025-10-23 22:03:15 +02:00
parent 6a6ae7e059
commit 17e5a551e1
23 changed files with 949 additions and 269 deletions

View File

@@ -174,7 +174,7 @@ class TestEpisode:
file_path="/anime/test/S01E05.mp4",
file_size=524288000, # 500 MB
is_downloaded=True,
download_date=datetime.utcnow(),
download_date=datetime.now(timezone.utc),
)
db_session.add(episode)
@@ -310,7 +310,7 @@ class TestUserSession:
def test_create_user_session(self, db_session: Session):
"""Test creating a user session."""
expires = datetime.utcnow() + timedelta(hours=24)
expires = datetime.now(timezone.utc) + timedelta(hours=24)
session = UserSession(
session_id="test-session-123",
@@ -333,7 +333,7 @@ class TestUserSession:
def test_session_unique_session_id(self, db_session: Session):
"""Test that session_id must be unique."""
expires = datetime.utcnow() + timedelta(hours=24)
expires = datetime.now(timezone.utc) + timedelta(hours=24)
session1 = UserSession(
session_id="duplicate-id",
@@ -371,7 +371,7 @@ class TestUserSession:
def test_session_revoke(self, db_session: Session):
"""Test session revocation."""
expires = datetime.utcnow() + timedelta(hours=24)
expires = datetime.now(timezone.utc) + timedelta(hours=24)
session = UserSession(
session_id="revoke-test",
token_hash="hash",
@@ -531,7 +531,7 @@ class TestDatabaseQueries:
def test_query_active_sessions(self, db_session: Session):
"""Test querying active user sessions."""
expires = datetime.utcnow() + timedelta(hours=24)
expires = datetime.now(timezone.utc) + timedelta(hours=24)
# Create active and inactive sessions
active = UserSession(