diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-08-31 17:19:17 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-08-31 17:19:17 +0200 |
| commit | c6fd7ea89b6e0a96eb1d81989de891b4768b1044 (patch) | |
| tree | 12e869044c80c889f83b588210cc0ac500cd7a6a /packages/meshbay-hub/src/meshbay_hub/static/group-settings.js | |
| parent | f4c6628c8e85513d9fd110ead95682368a15a0fd (diff) | |
| download | meshbay-c6fd7ea89b6e0a96eb1d81989de891b4768b1044.tar.gz | |
feat: email verification for registration, email change, and invitations
Registration now creates a pending account and sends a 6-digit code via
email; the account activates only after verification. Email changes on
the profile page follow the same flow. Group invitations send a
notification email to the invitee (without revealing their address to
the inviter) containing the invite code and hub link.
Backend: blind HMAC-SHA256 email index for uniqueness without decryption,
mail.py for localhost Postfix delivery, verification endpoints, cleanup
of expired codes and stale pending accounts, startup backfill of
email_hash for existing users.
Frontend: 3-phase register page, inline email change verification on
profile, invite-notify call with status display. All 10 locales updated.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/group-settings.js')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/static/group-settings.js | 20 |
1 files changed, 19 insertions, 1 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/group-settings.js b/packages/meshbay-hub/src/meshbay_hub/static/group-settings.js index 4f55dfb..1c6ca71 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/group-settings.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/group-settings.js @@ -793,7 +793,21 @@ function GroupSettingsPanel({ groupId, group, token, transportRef, gekRef, method: 'POST', token, body: {}, }); - setInviteCode({ username, code: result.code, expires: result.expires_at }); + // Send an email notification to the invitee with the code. + // The hub decrypts their email server-side — the inviter never sees it. + let emailStatus = 'no_email'; + try { + const notif = await hubFetch(`/v1/groups/${groupId}/invite-notify`, { + method: 'POST', token, + body: { username, code: result.code, group_name: group?.name || '' }, + }); + emailStatus = notif.status; + } catch { /* best effort */ } + + setInviteCode({ + username, code: result.code, expires: result.expires_at, + emailSent: emailStatus === 'sent', + }); setInviteUser(''); loadMembers(); } catch (err) { @@ -831,6 +845,10 @@ function GroupSettingsPanel({ groupId, group, token, transportRef, gekRef, <div class="success-msg" style="margin-bottom:8px"> <p>${t('members.invite_code_ready', { user: inviteCode.username })}</p> <p class="code-display">${inviteCode.code}</p> + ${inviteCode.emailSent + ? html`<p style="color:var(--success)">${t('members.invite_email_sent')}</p>` + : html`<p style="color:var(--text-dim)">${t('members.invite_email_failed')}</p>` + } <p>${t('members.invite_code_hint')}</p> </div> `} |