Files
Audiolib/backend/app/routers/auth.py
Audiolib 14ffee3051 Initial commit: Phase 1 – Projektstruktur, DB-Schema, Core-API
- FastAPI-Backend mit vollständiger ABS v2.x API-Kompatibilität
- SQLAlchemy-Models: User, Library, LibraryItem, BookFile, Chapter,
  Podcast, PodcastEpisode, MediaProgress, Bookmark, PlaybackSession
- Auth: JWT-Login (/login, /logout, /api/authorize)
- Library + Items Endpoints inkl. camelCase ABS-Response-Format
- HLS-Streaming via FFmpeg (POST /api/items/:id/play, Session-Sync)
- Me/Progress Endpoints + Lesezeichen
- User-Management + Server-Settings (Admin)
- Library-Scanner (MP3/WAV Discovery, Hintergrund-Task)
- File Watcher (watchdog, 30s Debounce)
- Matching-Skelett (MusicBrainz, OpenLibrary, Google Books – Phase 5)
- Docker-Setup: backend (Python 3.12+FFmpeg), frontend (React/Vite),
  nginx Reverse-Proxy auf Port 3000
- setup.sh: Installiert Docker auf Debian/Ubuntu, richtet .env ein

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 11:43:35 +02:00

103 lines
3.3 KiB
Python

from datetime import datetime
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from ..database import AsyncSessionLocal
from ..dependencies import get_db, get_current_user
from ..models.user import User
from ..models.library import Library
from ..models.session import ServerSetting
from ..services.auth import verify_password, create_token
from ..schemas.auth import LoginRequest, LoginResponse, AuthorizeResponse
from ..schemas.user import UserOut, UserSettings, ServerSettingsOut
router = APIRouter(tags=["auth"])
def _build_user_out(user: User) -> UserOut:
raw_settings = user.settings or {}
settings = UserSettings(**{k: v for k, v in raw_settings.items() if k in UserSettings.model_fields})
return UserOut(
id=user.id,
username=user.username,
email=user.email,
is_admin=user.is_admin,
is_active=user.is_active,
last_seen=user.last_seen,
created_at=user.created_at,
token=user.token,
settings=settings,
type="root" if user.is_admin else "user",
permissions={
"download": True,
"update": user.is_admin,
"delete": user.is_admin,
"upload": user.is_admin,
"access_all_libraries": True,
"access_explicit_content": True,
},
)
@router.post("/login", response_model=LoginResponse)
async def login(body: LoginRequest, db: AsyncSession = Depends(get_db)):
result = await db.execute(
select(User).where(User.username == body.username, User.is_active == True)
)
user = result.scalar_one_or_none()
if not user or not verify_password(body.password, user.password_hash):
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials")
token = create_token(user.id)
user.token = token
user.last_seen = datetime.utcnow()
await db.commit()
# Erste Library als Default zurückgeben
lib_result = await db.execute(select(Library).limit(1))
first_lib = lib_result.scalar_one_or_none()
return LoginResponse(
user=_build_user_out(user),
user_default_library_id=first_lib.id if first_lib else None,
server_settings=ServerSettingsOut(),
)
@router.post("/logout")
async def logout(current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db)):
current_user.token = None
await db.commit()
return {"success": True}
@router.get("/api/authorize", response_model=AuthorizeResponse)
async def authorize(current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db)):
current_user.last_seen = datetime.utcnow()
await db.commit()
lib_result = await db.execute(select(Library))
libraries = lib_result.scalars().all()
from ..routers.libraries import _library_to_out
libs_out = [_library_to_out(lib) for lib in libraries]
first_lib_id = libraries[0].id if libraries else None
return AuthorizeResponse(
user=_build_user_out(current_user),
libraries=libs_out,
user_default_library_id=first_lib_id,
server_settings=ServerSettingsOut(),
)
@router.get("/ping")
async def ping():
return {"success": True}
@router.get("/health")
async def health():
return {"status": "ok"}