"""Public endpoint for resolving an account invite code into display info. Mounted as a public route (no tenant context, no auth) — used by the /accept-invite page on the frontend so an invitee can see what account they are about to join before they sign up. Uses the BYPASSRLS admin session factory because account_invites is account-scoped under Phase 4 RLS but the caller has no tenant identity yet. """ from typing import Annotated from fastapi import APIRouter, Depends, HTTPException from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.orm import joinedload from app.core.admin_database import get_admin_db from app.models.account_invite import AccountInvite from app.schemas.oauth import InviteLookupResponse router = APIRouter(prefix="/accounts", tags=["account-invite-lookup"]) @router.get("/invites/{code}/lookup", response_model=InviteLookupResponse) async def lookup_invite( code: str, db: Annotated[AsyncSession, Depends(get_admin_db)], ) -> InviteLookupResponse: """Return minimal display data for a valid (unused, unexpired, not revoked) invite. Returns 404 with `invite_invalid_or_expired_or_revoked` for any invalid state — the AcceptInvitePage shows a single "ask the inviter to resend" message regardless of which condition failed (anti-enumeration).""" result = await db.execute( select(AccountInvite) .where(AccountInvite.code == code) .options( joinedload(AccountInvite.account), joinedload(AccountInvite.invited_by), ) ) invite = result.scalar_one_or_none() if invite is None or not invite.is_valid: raise HTTPException( status_code=404, detail={"error": "invite_invalid_or_expired_or_revoked"}, ) return InviteLookupResponse( account_name=invite.account.name, inviter_name=invite.invited_by.name, invited_email=invite.email, role=invite.role, )