aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/static/group-settings.js
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-08-31 17:19:17 +0200
committerChristophe Besson <cbesson@gmail.com>2026-08-31 17:19:17 +0200
commitc6fd7ea89b6e0a96eb1d81989de891b4768b1044 (patch)
tree12e869044c80c889f83b588210cc0ac500cd7a6a /packages/meshbay-hub/src/meshbay_hub/static/group-settings.js
parentf4c6628c8e85513d9fd110ead95682368a15a0fd (diff)
downloadmeshbay-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.js20
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>
`}