All tweaks added
Build and push images / build-backend (push) Successful in 33s
Build and push images / build-worker (push) Successful in 32s
Build and push images / build-frontend (push) Failing after 6s

This commit is contained in:
2026-06-06 18:10:35 +01:00
parent 043b3b7269
commit ec5a01d12a
92 changed files with 7517 additions and 784 deletions
+31 -43
View File
@@ -7,13 +7,23 @@ from typing import Optional
import httpx
from app.core.database import get_db
from app.core.security import verify_password, create_access_token, hash_password, get_current_user
from app.core.security import verify_password, create_access_token, get_current_user
from app.core.config import settings
from app.models.user import User
router = APIRouter()
async def _get_pocketid_config(db: AsyncSession):
"""Get PocketID config from DB (admin user) falling back to env vars."""
result = await db.execute(select(User).where(User.is_admin == True).limit(1))
admin = result.scalar_one_or_none()
issuer = (admin and admin.pocketid_issuer) or settings.pocketid_issuer
client_id = (admin and admin.pocketid_client_id) or settings.pocketid_client_id
client_secret = (admin and admin.pocketid_client_secret) or settings.pocketid_client_secret
return issuer, client_id, client_secret
class Token(BaseModel):
access_token: str
token_type: str
@@ -37,24 +47,15 @@ async def login(
form_data: OAuth2PasswordRequestForm = Depends(),
db: AsyncSession = Depends(get_db),
):
result = await db.execute(
select(User).where(User.username == form_data.username)
)
result = await db.execute(select(User).where(User.username == form_data.username))
user = result.scalar_one_or_none()
if not user or not user.hashed_password:
raise HTTPException(status_code=400, detail="Invalid credentials")
if not verify_password(form_data.password, user.hashed_password):
raise HTTPException(status_code=400, detail="Invalid credentials")
token = create_access_token({"sub": str(user.id)})
return Token(
access_token=token,
token_type="bearer",
user_id=user.id,
username=user.username,
is_admin=user.is_admin,
)
return Token(access_token=token, token_type="bearer",
user_id=user.id, username=user.username, is_admin=user.is_admin)
@router.get("/me", response_model=UserOut)
@@ -63,51 +64,44 @@ async def get_me(current_user: User = Depends(get_current_user)):
@router.get("/pocketid/available")
async def pocketid_available():
return {"available": bool(settings.pocketid_issuer and settings.pocketid_client_id)}
async def pocketid_available(db: AsyncSession = Depends(get_db)):
issuer, client_id, _ = await _get_pocketid_config(db)
return {"available": bool(issuer and client_id)}
@router.get("/pocketid/login-url")
async def pocketid_login_url():
"""Return the OIDC authorization URL for PocketID."""
if not settings.pocketid_issuer:
async def pocketid_login_url(db: AsyncSession = Depends(get_db)):
issuer, client_id, _ = await _get_pocketid_config(db)
if not issuer or not client_id:
raise HTTPException(status_code=404, detail="PocketID not configured")
from urllib.parse import urlencode
params = {
"client_id": settings.pocketid_client_id,
"client_id": client_id,
"redirect_uri": "/api/auth/pocketid/callback",
"response_type": "code",
"scope": "openid profile email",
}
from urllib.parse import urlencode
url = f"{settings.pocketid_issuer}/authorize?{urlencode(params)}"
return {"url": url}
return {"url": f"{issuer}/authorize?{urlencode(params)}"}
@router.get("/pocketid/callback")
async def pocketid_callback(code: str, db: AsyncSession = Depends(get_db)):
"""Exchange OIDC code for tokens and create/login user."""
if not settings.pocketid_issuer:
issuer, client_id, client_secret = await _get_pocketid_config(db)
if not issuer:
raise HTTPException(status_code=404, detail="PocketID not configured")
# Exchange code for tokens
async with httpx.AsyncClient() as client:
resp = await client.post(
f"{settings.pocketid_issuer}/token",
data={
"grant_type": "authorization_code",
"code": code,
"redirect_uri": "/api/auth/pocketid/callback",
"client_id": settings.pocketid_client_id,
"client_secret": settings.pocketid_client_secret,
},
f"{issuer}/token",
data={"grant_type": "authorization_code", "code": code,
"redirect_uri": "/api/auth/pocketid/callback",
"client_id": client_id, "client_secret": client_secret},
)
if resp.status_code != 200:
raise HTTPException(status_code=400, detail="Token exchange failed")
tokens = resp.json()
userinfo_resp = await client.get(
f"{settings.pocketid_issuer}/userinfo",
f"{issuer}/userinfo",
headers={"Authorization": f"Bearer {tokens['access_token']}"},
)
userinfo = userinfo_resp.json()
@@ -118,17 +112,11 @@ async def pocketid_callback(code: str, db: AsyncSession = Depends(get_db)):
result = await db.execute(select(User).where(User.pocketid_sub == sub))
user = result.scalar_one_or_none()
if not user:
user = User(
username=preferred_username,
email=email,
pocketid_sub=sub,
)
user = User(username=preferred_username, email=email, pocketid_sub=sub)
db.add(user)
await db.flush()
token = create_access_token({"sub": str(user.id)})
# Redirect to frontend with token
from fastapi.responses import RedirectResponse
return RedirectResponse(url=f"/?token={token}")