- 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:
+34
-23
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user