Fix-Runde nach Review: alle Befunde behoben + echte Tests
Ampel / ampel (push) Failing after 12m31s

- Metadaten-Preview WIEDERHERGESTELLT (Stub ueberschattete echte
  prescan-Implementierung), tote Altmodule geloescht
- Celery update_state statt Phantom-Task/erfundener API
- abcde-Kommando korrigiert (CD-Ripping war nie funktionsfaehig)
- JWT: fester Schluessel Pflicht, echtes Logout, Cleanup nur Abgelaufene
- main.py: crashende Endpoints (Path/secrets/api_keys), year-Bug,
  Admin-Login aus .env
- Ruff gruen (29 Funde), Tests: auth/cache_keys/ripping_helpers,
  Placebo-test_health raus
- SAVEPOINT: offene MakeMKV-Entscheidung SICHTBAR gemacht (Regel B)
This commit is contained in:
Hitonabi
2026-07-22 19:31:48 +02:00
parent 728f650bf8
commit 0ea5f10b31
22 changed files with 722 additions and 719 deletions
+34 -23
View File
@@ -1,23 +1,33 @@
from fastapi import FastAPI, HTTPException, Request, Response
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import StreamingResponse
from fastapi import WebSocket, WebSocketDisconnect
from pydantic import BaseModel
from typing import List, Optional, Dict
from datetime import datetime
import os
from pathlib import Path
import subprocess
import asyncio
import json
import time
from fastapi.security import OAuth2PasswordBearer
from config import settings
from config_validation import validate_config, ConfigValidationError
from cache import init_cache, set
from auth import create_access_token, create_refresh_token, decode_token, is_blacklisted
from ratelimit import check_rate_limit, get_rate_limit_remaining, validate_api_key
from cache import init_cache, set as cache_set
from auth import (
create_access_token,
create_refresh_token,
decode_token,
is_blacklisted,
add_to_blacklist,
)
from ratelimit import (
check_rate_limit,
get_rate_limit_remaining,
validate_api_key,
api_keys,
create_api_key as ratelimit_create_api_key,
delete_api_key as ratelimit_delete_api_key,
)
from prescan import PreScan
from nfo_generator import NFOGenerator
from image_downloader import ImageDownloader
@@ -198,7 +208,7 @@ async def confirm_metadata(title: str, year: Optional[int] = None, metadata: Dic
from cache.keys import generate_confirmed_key
# In Cache speichern
cache_key = generate_confirmed_key(title, year)
set(cache_key, {"title": title, "year": year, "metadata": metadata or {}})
cache_set(cache_key, {"title": title, "year": year, "metadata": metadata or {}})
return {"status": "confirmed", "key": cache_key}
@@ -271,8 +281,10 @@ async def jellyfin_format(request: JellyfinFormatRequest):
else:
# Audio-Formatierung
artist = request.metadata.get("artist", "Unknown Artist")
album = title
album = request.metadata.get("title", request.title)
# Review-Fix 22.07.: `year` war hier undefiniert (existierte nur im Film-Zweig)
year = request.year or request.metadata.get("year")
# album.nfo
album_nfo = nfo_gen.generate_album_nfo(
title=album,
@@ -305,8 +317,8 @@ class LoginRequest(BaseModel):
@app.post("/token")
async def login(request: LoginRequest):
"""Login und Token generieren."""
# Einfache Auth für MVP (in Produktion mit Datenbank)
if request.username == "admin" and request.password == "rippy123":
# Einfache Auth für MVP (in Produktion mit Datenbank); Zugangsdaten aus .env
if request.username == settings.admin_username and request.password == settings.admin_password:
access_token = create_access_token(
data={"sub": request.username, "scopes": ["admin"]}
)
@@ -339,8 +351,11 @@ async def invalidate_token(token: str):
"""Invalidate Token (Logout)."""
if is_blacklisted(token):
raise HTTPException(status_code=400, detail="Token bereits invalidiert")
# In Produktion mit Redis implementieren
# Review-Fix 22.07.: vorher wurde hier NICHTS geblacklistet (Placebo-Logout)
add_to_blacklist(token)
if not is_blacklisted(token):
raise HTTPException(status_code=400, detail="Ungültiger Token")
return {"status": "invalidated"}
@@ -349,17 +364,14 @@ class APIKeyCreateRequest(BaseModel):
name: str
# Review-Fix 22.07.: diese Endpoints nutzten `secrets` und `api_keys`, die in
# diesem Modul NIE existierten (Crash bei jedem Aufruf) — der echte Key-Store
# lebt in ratelimit.py und wird jetzt benutzt.
@app.post("/api-keys")
async def create_api_key(request: APIKeyCreateRequest):
"""Erstelle API Key."""
# In Produktion mit Auth prüfen
key_info = {
"key": secrets.token_urlsafe(32),
"name": request.name,
"created_at": time.time(),
"rate_limit": 100
}
return key_info
return ratelimit_create_api_key(request.name)
@app.get("/api-keys")
@@ -372,7 +384,6 @@ async def list_api_keys():
async def delete_api_key(key: str):
"""Lösche API Key."""
# In Produktion mit Auth prüfen
if key in api_keys:
del api_keys[key]
if ratelimit_delete_api_key(key):
return {"status": "deleted"}
raise HTTPException(status_code=404, detail="API Key nicht gefunden")