Recover from a dead session, and report a node problem once

An expired or orphaned session left the app on a half-rendered page instead of
the login screen: a properly signed token naming a user who no longer exists
answered 404, which the client does not treat as an authentication failure. All
failures in get_current_user are 401 now, and the client stops retrying them,
so a dead session goes straight back to the login screen.

- Clicking an edge names the two nodes it runs between, not just the message.
- A node with a problem shows one badge carrying the whole explanation, rather
  than a corner badge and a status dot saying the same thing twice. The dot is
  back to what it is good at: whether the node ran.
- The sidebar's collapse control sits in the sidebar, where a phone still finds
  one in the chrome because there is no sidebar on screen to hold it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016WzrvW7rjQbynnhF6pxh6i
This commit is contained in:
Melvin Strobl
2026-08-15 19:19:57 +02:00
co-authored by Claude Fable 5
parent 6fb42bb3ef
commit c254d487ba
9 changed files with 128 additions and 24 deletions
+12 -5
View File
@@ -47,6 +47,12 @@ def user_from_token(session: Session, token: str) -> User | None:
def get_current_user(session: SessionDep, token: TokenDep) -> User:
"""Resolve the bearer token to its user.
Every failure here is an authentication failure, so all of them answer 401:
a token naming a user who no longer exists is a session to log in again,
not a missing resource to report.
"""
try:
payload = jwt.decode(
token, settings.SECRET_KEY, algorithms=[security.ALGORITHM]
@@ -54,14 +60,15 @@ def get_current_user(session: SessionDep, token: TokenDep) -> User:
token_data = TokenPayload(**payload)
except (InvalidTokenError, ValidationError):
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
)
user = session.get(User, token_data.sub)
if not user:
raise HTTPException(status_code=404, detail="User not found")
if not user.is_active:
raise HTTPException(status_code=400, detail="Inactive user")
if user is None or not user.is_active:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="This session is no longer valid — please log in again",
)
return user