From dad4a5afd2e1546c9451daebaf5bd247c5f143ea Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 21 Jan 2026 21:02:39 -0300 Subject: [PATCH] portal: surface verified status --- .../atlas_portal/routes/access_requests.py | 21 +++++++++++++++++-- backend/tests/test_access_requests.py | 20 ++++++++++++++++++ frontend/src/views/RequestAccessView.vue | 4 ++++ 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/backend/atlas_portal/routes/access_requests.py b/backend/atlas_portal/routes/access_requests.py index e9d331f..4e0b5f5 100644 --- a/backend/atlas_portal/routes/access_requests.py +++ b/backend/atlas_portal/routes/access_requests.py @@ -745,7 +745,15 @@ def register(app) -> None: try: with connect() as conn: row = conn.execute( - "SELECT status, username, initial_password, initial_password_revealed_at FROM access_requests WHERE request_code = %s", + """ + SELECT status, + username, + initial_password, + initial_password_revealed_at, + email_verified_at + FROM access_requests + WHERE request_code = %s + """, (code,), ).fetchone() if not row: @@ -757,7 +765,15 @@ def register(app) -> None: except Exception: pass row = conn.execute( - "SELECT status, username, initial_password, initial_password_revealed_at FROM access_requests WHERE request_code = %s", + """ + SELECT status, + username, + initial_password, + initial_password_revealed_at, + email_verified_at + FROM access_requests + WHERE request_code = %s + """, (code,), ).fetchone() if not row: @@ -768,6 +784,7 @@ def register(app) -> None: "ok": True, "status": status, "username": row.get("username") or "", + "email_verified": bool(row.get("email_verified_at")), } task_rows = conn.execute( """ diff --git a/backend/tests/test_access_requests.py b/backend/tests/test_access_requests.py index defcbbe..10d77cf 100644 --- a/backend/tests/test_access_requests.py +++ b/backend/tests/test_access_requests.py @@ -206,3 +206,23 @@ class AccessRequestTests(TestCase): resp = self.client.get(f"/api/access/request/verify-link?code=alice~CODE123&token={token}") self.assertEqual(resp.status_code, 302) self.assertIn("verified=1", resp.headers.get("Location", "")) + + def test_status_includes_email_verified(self): + rows = { + "SELECT status": { + "status": "pending", + "username": "alice", + "initial_password": None, + "initial_password_revealed_at": None, + "email_verified_at": ar.datetime.now(ar.timezone.utc), + } + } + with mock.patch.object(ar, "connect", lambda: dummy_connect(rows)): + resp = self.client.post( + "/api/access/request/status", + data=json.dumps({"request_code": "alice~CODE123"}), + content_type="application/json", + ) + data = resp.get_json() + self.assertEqual(resp.status_code, 200) + self.assertTrue(data.get("email_verified")) diff --git a/frontend/src/views/RequestAccessView.vue b/frontend/src/views/RequestAccessView.vue index 48add7d..d218ddb 100644 --- a/frontend/src/views/RequestAccessView.vue +++ b/frontend/src/views/RequestAccessView.vue @@ -442,6 +442,7 @@ async function copyRequestCode() { async function checkStatus() { if (checking.value) return; error.value = ""; + verifyMessage.value = ""; const trimmed = statusForm.request_code.trim(); if (!trimmed) return; if (!trimmed.includes("~")) { @@ -466,6 +467,9 @@ async function checkStatus() { onboardingUrl.value = data.onboarding_url || ""; tasks.value = Array.isArray(data.tasks) ? data.tasks : []; blocked.value = Boolean(data.blocked); + if (data.email_verified) { + verifyMessage.value = "Email confirmed."; + } } catch (err) { error.value = err.message || "Failed to check status"; status.value = "unknown"; -- 2.47.2