openvibers/openvibe.network Commits

Registration (WS-B task 10): /register and /signup open the create-account view; the form is username, optional email and password — the claim code for a reserved name appears only when the chosen name is reserved (it was an always-visible 'Verification Key' field); the username hint is plain words

OP OpenVibers committed 25 Sept 2026 · 186b7fc766 · 2 files, +17 −4


Changes

ddiff --git a/public/login.html b/public/login.html
iindex 12d22825..c66f4a95 100644
---- a/public/login.html
++++ b/public/login.html
@@@ -305,15 +305,16 @@ body{min-height:100dvh;background:var(--bg);color:var(--text);font-family:'Inter
<div class="field">
<label>Username</label>
<input type="text" name="username" placeholder="Choose a username" required minlength="3" maxlength="24" pattern="^[a-zA-Z0-9_]+$" title="Use letters, numbers, and underscores only" autocomplete="username">
-- <small class="panel-note">Allowed: letters, numbers, and underscores only; 3–24 characters; cannot start with "anon".</small>
++ <small class="panel-note">Letters, numbers and underscores, 3 to 24 characters. You can pick a display name later.</small>
</div>
<div class="field">
<label>Email <span style="color:var(--text-dim)">(optional)</span></label>
<input type="email" name="email" placeholder="[email protected]" autocomplete="email">
</div>
-- <div class="field">
-- <label>Verification Key <span style="color:var(--text-dim)">(optional)</span></label>
-- <input type="text" name="verification_key" placeholder="OPENVIBE-XXXX-XXXX-XXXX" style="font-family:monospace">
++ <div class="field" id="claim-field" hidden>
++ <label for="claim-code">Claim code</label>
++ <input type="text" id="claim-code" name="verification_key" placeholder="OPENVIBE-XXXX-XXXX-XXXX" style="font-family:monospace" autocomplete="off">
++ <small class="panel-note">This name is reserved for someone. If it is yours, enter the code you were given to claim it; otherwise choose another name.</small>
</div>
<div class="field">
<label>Password</label>
@@@ -893,6 +894,12 @@ async function handleRegister(e) {
}),
});
const data = await res.json();
++ // A reserved name: show the claim-code field only now (most people never need it).
++ if (!res.ok && data.reserved) {
++ const claim = document.getElementById('claim-field');
++ if (claim) { claim.hidden = false; document.getElementById('claim-code').focus(); }
++ throw new Error('This username is reserved. Enter its claim code, or choose another name.');
++ }
if (!res.ok) throw new Error(data.error || 'Registration failed');
localStorage.setItem('ov_token', data.token);
ddiff --git a/server/index.js b/server/index.js
iindex af082de4..5b1fd448 100644
---- a/server/index.js
++++ b/server/index.js
@@@ -734,6 +734,12 @@ if (!fs.existsSync(avatarDir)) fs.mkdirSync(avatarDir, { recursive: true });
app.use('/data/avatars', express.static(avatarDir, { maxAge: '7d' }));
// Serve clean auth + recovery routes
++// People type /register and /signup: the sign-in page's create-account view (WS-B task 10), query kept.
++app.get(['/register', '/signup', '/sign-up'], (req, res) => {
++ const q = new URLSearchParams(req.query);
++ q.set('tab', 'register');
++ res.redirect(302, `/login?${q}`);
++});
app.get(['/login', '/forgot-password', '/reset-password'], (req, res) => {
res.sendFile(path.join(__dirname, '..', 'public', 'login.html'));
});