Die Einkaufsliste war ueber /shopping-list und /shopping-list/groups schon abrufbar, aber nur mit einem Login-JWT, das nach 7 Tagen ablaeuft - fuer HA unpraktisch. - Neue Tabelle api_tokens (Name, SHA-256-Hash, Ersteller, zuletzt genutzt). Der Klartext wird nur einmal beim Anlegen zurueckgegeben. - get_current_user akzeptiert jetzt JWT ODER API-Token im Authorization-Header; bei Nutzung wird last_used_at fortgeschrieben. - Endpunkte /api-tokens (Admin): auflisten, erzeugen, widerrufen. - Einstellungsseite: Tokens verwalten inkl. einmaliger Anzeige und einem aufklappbaren Konfigurationsbeispiel fuer Home Assistant. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
56 lines
1.6 KiB
Python
56 lines
1.6 KiB
Python
from fastapi import APIRouter, Depends, HTTPException, status
|
|
from sqlalchemy.orm import Session
|
|
|
|
from ..database import get_db
|
|
from ..deps import require_admin
|
|
from ..models import ApiToken, User
|
|
from ..schemas import ApiTokenCreate, ApiTokenCreated, ApiTokenOut
|
|
from ..security import generate_api_token, hash_api_token
|
|
|
|
router = APIRouter(prefix="/api-tokens", tags=["api-tokens"])
|
|
|
|
|
|
@router.get("", response_model=list[ApiTokenOut])
|
|
def list_tokens(
|
|
db: Session = Depends(get_db), _: User = Depends(require_admin)
|
|
) -> list[ApiToken]:
|
|
return db.query(ApiToken).order_by(ApiToken.id).all()
|
|
|
|
|
|
@router.post("", response_model=ApiTokenCreated, status_code=status.HTTP_201_CREATED)
|
|
def create_token(
|
|
payload: ApiTokenCreate,
|
|
db: Session = Depends(get_db),
|
|
admin: User = Depends(require_admin),
|
|
) -> ApiTokenCreated:
|
|
"""Erzeugt ein langlebiges Token. Der Klartext wird nur hier zurückgegeben."""
|
|
plain = generate_api_token()
|
|
entry = ApiToken(
|
|
name=payload.name.strip(),
|
|
token_hash=hash_api_token(plain),
|
|
user_id=admin.id,
|
|
)
|
|
db.add(entry)
|
|
db.commit()
|
|
db.refresh(entry)
|
|
return ApiTokenCreated(
|
|
id=entry.id,
|
|
name=entry.name,
|
|
created_at=entry.created_at,
|
|
last_used_at=entry.last_used_at,
|
|
token=plain,
|
|
)
|
|
|
|
|
|
@router.delete("/{token_id}", status_code=status.HTTP_204_NO_CONTENT)
|
|
def delete_token(
|
|
token_id: int,
|
|
db: Session = Depends(get_db),
|
|
_: User = Depends(require_admin),
|
|
) -> None:
|
|
entry = db.get(ApiToken, token_id)
|
|
if entry is None:
|
|
raise HTTPException(status.HTTP_404_NOT_FOUND, "Token nicht gefunden")
|
|
db.delete(entry)
|
|
db.commit()
|