refactor: tighten backend, flows, frontend, tests; push flows to Langflow

This commit is contained in:
Jonas Miederer 2026-09-28 15:58:00 +02:00
parent 6688fb08c6
commit 06ab25fb47
31 changed files with 783 additions and 358 deletions

View file

@ -6,16 +6,11 @@ from uuid import UUID
from fastapi import APIRouter, Depends, HTTPException, Query, Request, Response
from sqlmodel import Session
from app.db import get_db
from app.models import User
from . import mail, service
from .deps import (
SESSION_COOKIE,
SESSION_MAX_AGE,
get_current_user,
get_db,
require_admin,
)
from .deps import SESSION_COOKIE, SESSION_MAX_AGE, get_current_user, require_admin
from .schemas import (
AccessDecisionRead,
AccessRequestCreate,
@ -33,26 +28,11 @@ auth_router = APIRouter(prefix="/api/auth", tags=["auth"])
def _read_request(r) -> AccessRequestRead:
return AccessRequestRead(
id=r.id,
name=r.name,
email=r.email,
reason=r.reason,
status=r.status,
created_at=r.created_at,
decided_at=r.decided_at,
decided_by=r.decided_by,
)
return AccessRequestRead.model_validate(r, from_attributes=True)
def _read_me(user: User) -> MeRead:
return MeRead(
id=user.id,
email=user.email,
name=user.name,
role=user.role,
tenant_id=user.tenant_id,
)
return MeRead.model_validate(user, from_attributes=True)
def _set_session_cookie(response: Response, session_id: str) -> None:

View file

@ -1,14 +1,13 @@
"""Session-cookie dependencies. Handlers stay thin; DB via get_session."""
"""Session-cookie dependencies. Handlers stay thin; DB via app.db.get_db."""
from __future__ import annotations
from collections.abc import Iterator
from typing import Annotated
from fastapi import Depends, HTTPException, Request
from sqlmodel import Session
from app.db import get_session
from app.db import get_db
from app.models import User
from . import service
@ -17,10 +16,6 @@ SESSION_COOKIE = "kirche_session"
SESSION_MAX_AGE = int(service.SESSION_TTL.total_seconds())
def get_db() -> Iterator[Session]:
yield from get_session()
def get_optional_user(
request: Request, session: Annotated[Session, Depends(get_db)]
) -> User | None:

View file

@ -22,11 +22,6 @@ BLUE_LIGHT = "#eef4fb"
INK = "#162f4c"
MUTED = "#5b6b7c"
BLUE = "#1d4a79"
BLUE_LIGHT = "#eef4fb"
INK = "#162f4c"
MUTED = "#5b6b7c"
def magic_link(token: str) -> str:
return f"{settings.frontend_url.rstrip('/')}/login?token={token}"

View file

@ -15,6 +15,7 @@ from uuid import UUID
from sqlmodel import Session, func, select
from app.models import AccessRequest, MagicLinkToken, User, UserSession
from app.models.base import utcnow
from app.settings import settings
logger = logging.getLogger(__name__)
@ -60,10 +61,6 @@ class RateLimitedError(AuthError):
pass
def utcnow() -> datetime:
return datetime.now(UTC)
def normalize_email(email: str) -> str:
return email.strip().lower()
@ -92,18 +89,22 @@ def user_count(session: Session) -> int:
return session.exec(select(func.count()).select_from(User)).one()
def recent_request_count(session: Session, email: str, hours: int = 1) -> int:
def _recent_count(session: Session, model, *filters, hours: int = 1) -> int:
since = utcnow() - timedelta(hours=hours)
return session.exec(
select(func.count())
.select_from(AccessRequest)
.where(
AccessRequest.email == normalize_email(email),
AccessRequest.created_at >= since,
)
select(func.count()).select_from(model).where(*filters, model.created_at >= since)
).one()
def recent_request_count(session: Session, email: str, hours: int = 1) -> int:
return _recent_count(
session,
AccessRequest,
AccessRequest.email == normalize_email(email),
hours=hours,
)
def create_access_request(
session: Session, name: str, email: str, reason: str = ""
) -> AccessRequest:
@ -165,15 +166,7 @@ def issue_magic_token(session: Session, user: User) -> str:
def recent_token_count(session: Session, user: User, hours: int = 1) -> int:
since = utcnow() - timedelta(hours=hours)
return session.exec(
select(func.count())
.select_from(MagicLinkToken)
.where(
MagicLinkToken.user_id == user.id,
MagicLinkToken.created_at >= since,
)
).one()
return _recent_count(session, MagicLinkToken, MagicLinkToken.user_id == user.id, hours=hours)
def request_magic_link(session: Session, email: str) -> tuple[User, str]: