Profile recovery: Discord webhook as a reset-code channel

This commit is contained in:
Jonathan Sykes
2026-10-15 17:46:21 +08:00
parent ca47528581
commit 69704d3e1a
4 changed files with 52 additions and 26 deletions

View File

@@ -1557,19 +1557,22 @@ async function recoveryContactsFlow() {
<label style="display:block;font-size:12px;color:var(--text-2);margin:0 0 4px">Email${off('email')}</label>
<input id="recEmail" type="email" autocomplete="email" placeholder="you@example.com" value="${escapeHtml(cur.email || '')}" style="margin-bottom:12px" />
<label style="display:block;font-size:12px;color:var(--text-2);margin:0 0 4px">Discord user id${off('discord')}</label>
<input id="recDiscord" inputmode="numeric" autocomplete="off" placeholder="e.g. 123456789012345678" value="${escapeHtml(cur.discord || '')}" />
<input id="recDiscord" inputmode="numeric" autocomplete="off" placeholder="e.g. 123456789012345678" value="${escapeHtml(cur.discord || '')}" style="margin-bottom:12px" />
<label style="display:block;font-size:12px;color:var(--text-2);margin:0 0 4px">Discord webhook</label>
<input id="recWebhook" type="url" autocomplete="off" spellcheck="false" placeholder="https://discord.com/api/webhooks/…" value="${escapeHtml(cur.webhook || '')}" />
<p style="margin:8px 0 0;color:var(--text-dim);font-size:12px;line-height:1.5">
Discord: Settings → Advanced → Developer Mode, then tap your name → Copy User ID. You must share a server with the YT Player bot for its message to reach you.
Discord user id: Settings → Advanced → Developer Mode, then tap your name → Copy User ID; you must share a server with the YT Player bot.
Webhook: in a private channel of your own server, Edit Channel → Integrations → Webhooks → New Webhook → Copy Webhook URL. Anyone who can read that channel sees the code, so keep it private.
</p>`;
showModal('🛟 Recovery contacts', body, [
{ label: 'Cancel', onClick: closeModal },
{ label: 'Save', primary: true, onClick: async () => {
try {
const res = await fetch('/api/profile/recovery', { method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, current: ProfileSecret.getFor(name), email: $('recEmail').value.trim(), discord: $('recDiscord').value.trim() }) });
body: JSON.stringify({ name, current: ProfileSecret.getFor(name), email: $('recEmail').value.trim(), discord: $('recDiscord').value.trim(), webhook: $('recWebhook').value.trim() }) });
const j = await res.json().catch(() => ({}));
if (!res.ok || !j.ok) throw new Error(j.error || 'could not save');
closeModal(); toast(j.email || j.discord ? 'Recovery contacts saved ✓' : 'Recovery contacts removed');
closeModal(); toast(j.email || j.discord || j.webhook ? 'Recovery contacts saved ✓' : 'Recovery contacts removed');
} catch (e) { toast('⚠ ' + e.message); }
} },
]);
@@ -1581,7 +1584,7 @@ async function resetProfileSecretFlow(name) {
let info = null;
try { info = await (await fetch(`/api/profile/info?name=${encodeURIComponent(name)}`)).json(); } catch { /* offline */ }
const rec = info && info.recovery;
if (!rec || (!rec.email && !rec.discord)) {
if (!rec || (!rec.email && !rec.discord && !rec.webhook)) {
toast(`⚠ ${name} has no recovery email or Discord set — it opens only with its password, key or credentials file`, { duration: 6000 });
return null;
}
@@ -1591,6 +1594,7 @@ async function resetProfileSecretFlow(name) {
Send a reset code for <strong>${escapeHtml(name)}</strong>. It is valid for 15 minutes.</p>`;
showModal('🛟 Reset password', body, [
{ label: 'Cancel', onClick: () => { closeModal(); resolve(null); } },
...(rec.webhook ? [{ label: 'Discord channel', primary: !rec.email && !rec.discord, onClick: () => { closeModal(); resolve('webhook'); } }] : []),
...(rec.discord ? [{ label: 'Discord DM', primary: !rec.email, onClick: () => { closeModal(); resolve('discord'); } }] : []),
...(rec.email ? [{ label: `Email ${rec.emailHint || ''}`.trim(), primary: true, onClick: () => { closeModal(); resolve('email'); } }] : []),
]);