diff --git a/app/main.py b/app/main.py index a056c9a..4c0a6af 100644 --- a/app/main.py +++ b/app/main.py @@ -1097,9 +1097,12 @@ async def admin_overview(token: str): ).fetchall() conn.close() - # Fetch Cloudron users once to map user_id → username (most provisioned - # members have username=None, so we surface it only when it exists). + # Fetch Cloudron users once to map user_id → username + inviteAccepted. + # username is set only after the member completes account setup (chooses a + # username + password via the setup link); inviteAccepted tracks that same + # step, so "activated" = inviteAccepted (username present). usernames = {} + invite_accepted = {} try: async with httpx.AsyncClient(timeout=15.0) as client: r = await client.get( @@ -1108,8 +1111,10 @@ async def admin_overview(token: str): ) r.raise_for_status() for u in r.json().get("users", []): + uid = u.get("id") if u.get("username"): - usernames[u.get("id")] = u["username"] + usernames[uid] = u["username"] + invite_accepted[uid] = bool(u.get("inviteAccepted")) except Exception as e: logger.warning("admin overview: cloudron users fetch failed: %s", e) @@ -1137,6 +1142,7 @@ async def admin_overview(token: str): { "email": email, "username": usernames.get(user_id), + "activated": invite_accepted.get(user_id, False), "active": bool(active), "balance": float(balance) if balance is not None else MEMBER_BUDGET, "spend": spend,