Initial Release

This commit is contained in:
2026-05-24 21:58:23 -07:00
commit 0a782e0096
209 changed files with 11657 additions and 0 deletions
@@ -0,0 +1,281 @@
(() => {
const
elemPreviewEmail = $('#preview-email'),
actionEmailReveal = $('#email-reveal'),
actionEmailEdit = $('#email-edit'),
actionEmailResend = $('#email-resend'),
actionPassChange = $('#password-change'),
actionMFASetup = $('#mfa-setup-start'),
actionMFASetupRemove = $('#mfa-setup-remove'),
actionMFARecoveryView = $('#mfa-recovery-view'),
actionMFARecoveryRegen = $('#mfa-recovery-regen'),
actionDangerDeleteAccount = $('#danger-deleteaccount'),
errorMFA = $('#error-mfa'),
errorEmail = $('#error-email'),
errorDanger = $('#error-danger')
SendRequest('GET', '/api/v1/users/@me', null, 1)
.then(([_, body]) => {
// Email
let emailHidden = false
let emailTemplate = elemPreviewEmail.textContent
actionEmailReveal.onclick = () => {
let newPreview
if (emailHidden) {
newPreview = body.email
actionEmailReveal.textContent = 'Hide Email Address'
} else {
const usernameLength = body.email.indexOf('@')
const censoredSegment = ''.padStart(usernameLength > 12 ? 12 : usernameLength, '*')
const hostSegment = body.email.slice(usernameLength)
newPreview = censoredSegment + hostSegment
actionEmailReveal.textContent = 'Reveal Email Address'
}
elemPreviewEmail.textContent = (emailTemplate + newPreview)
emailHidden = !emailHidden
}
actionEmailReveal.onclick()
if (!body.verified) {
actionEmailResend.removeAttribute('hidden')
actionEmailResend.onclick = () => {
const unlock = LockInteraction()
SendRequest('POST', '/api/v1/users/@me/security/email', null, 1)
.then(() => CreateModal({
behaviour: {
cancelText: '',
submitText: 'Close',
title: 'Verification Email Sent',
tooltip: 'An email to verify this email address has been sent. Please check your spam or inbox.',
}
}))
.catch(e => errorEmail.textContent = e)
.finally(unlock)
}
}
actionEmailEdit.onclick = () => {
CreateModal({
behaviour: {
title: 'Update Email',
tooltip: 'Changing your email address will mark it as unverified.',
onSubmit: ({ email }) => new Promise(ok => {
SendRequest('PATCH', '/api/v1/users/@me/security/email', { email }, 2)
.then(() => window.location.reload())
.catch(ok)
})
},
'_divider': { type: 'divider-line' },
'email': {
type: 'text',
required: true,
title: 'Email Address',
validator: TestEmailSuite,
}
})
}
// Password
actionPassChange.onclick = () => {
CreateModal({
behaviour: {
title: 'Change Password',
tooltip: 'Enter your current password and new password.',
onSubmit: ({ old_password, new_password }) => new Promise(ok => {
SendRequest(
'PATCH', '/api/v1/users/@me/security/password',
{ old_password, new_password }, 1
)
.then(() => ok())
.catch(ok)
})
},
'_link': {
type: 'link',
title: 'Forgot Password?',
href: '/password-reset',
},
'_divider': { type: 'divider-line' },
'old_password': {
type: 'password',
required: true,
title: 'Old Password',
},
'new_password': {
type: 'password',
required: true,
title: 'New Password',
validator: (v, { new_password_again }) => {
if (v !== new_password_again) return 'Passwords do not Match'
return TestPasswordSuite(v)
},
},
'new_password_again': {
type: 'password',
required: true,
title: 'New Password Again',
validator: (v, { new_password }) => {
if (v !== new_password) return 'Passwords do not Match'
return TestPasswordSuite(v)
},
},
})
}
// Multi-Factor
function RecoveryModal(codes) {
return CreateModal({
behaviour: {
title: 'Recovery Codes',
tooltip:
'Keep these recovery codes safe! ' +
'You\'ll lose access to your account if both recovery codes and your authenticator app are lost.',
submitText: 'Download',
cancelText: 'Close',
onSubmit: () => {
// Generate Text
const filename = `Recovery Codes for ${body.username}.txt`
const filedata =
`Recovery Codes for ${body.username} (${body.email})\n` +
`Generated on ${new Date().toLocaleString()}\n` +
`------------\n` +
codes.map(c => `> ${c}\n`).join('');
// Fake Download Element
const a = document.createElement('a')
a.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(filedata))
a.setAttribute('download', filename)
a.style.display = 'none'
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
return ' '
}
},
'_codes': {
type: 'recovery-codes',
codes: codes
}
})
}
if (body.mfa_enabled) {
actionMFASetupRemove.removeAttribute('hidden')
actionMFASetupRemove.onclick = async () => {
await CreateModal({
behaviour: {
title: 'Remove Authenticator App?',
tooltip: 'This will disable MFA on your account.',
}
})
const unlock = LockInteraction()
SendRequest('DELETE', '/api/v1/users/@me/security/mfa/setup', null, 2)
.then(() => window.location.reload())
.catch(e => errorMFA.textContent = e)
.finally(unlock)
}
actionMFARecoveryView.removeAttribute('hidden')
actionMFARecoveryView.onclick = () => {
const unlock = LockInteraction()
SendRequest('GET', '/api/v1/users/@me/security/mfa/codes', null, 2)
.then(([_, codes]) => RecoveryModal(codes))
.catch(e => errorMFA.textContent = e)
.finally(unlock)
}
actionMFARecoveryRegen.removeAttribute('hidden')
actionMFARecoveryRegen.onclick = async () => {
await CreateModal({
behaviour: {
title: 'Reset Recovery Codes?',
tooltip: 'This will void the previously generated codes.',
}
})
const unlock = LockInteraction()
SendRequest('DELETE', '/api/v1/users/@me/security/mfa/codes', null, 2)
.then(([_, codes]) => RecoveryModal(codes))
.catch(e => errorMFA.textContent = e)
.finally(unlock)
}
} else {
actionMFASetup.removeAttribute('hidden')
actionMFASetup.onclick = () => {
const unlock = LockInteraction()
SendRequest('GET', '/api/v1/users/@me/security/mfa/setup', null, 1)
.then(([_, mfa]) => {
CreateModal({
behaviour: {
title: 'Setup Authenticator App',
tooltip: 'Scan the QR code using your Authenticator App and enter the generated passcode.',
onSubmit: ({ passcode }) => new Promise(ok => {
const unlock = LockInteraction()
SendRequest('POST', '/api/v1/users/@me/security/mfa/setup', { passcode }, 1)
.then((/* 204 No Content */) => {
RecoveryModal(mfa.recovery_codes)
.finally(() => window.location.reload())
ok()
})
.catch(ok)
.finally(unlock)
})
},
'_spacer1': { type: 'divider-line' },
'_qrcode': {
type: 'qr-code',
src: mfa.image,
},
'_spacer2': { type: 'divider-line' },
'_secret': {
type: 'text',
title: 'Can\'t Scan the QR Code? Use this secret key:',
value: mfa.secret,
readonly: true,
},
passcode: {
required: true,
type: 'password',
title: 'Passcode',
autocomplete: 'one-time-code',
placeholder: '123456',
validator: v => {
if (!/[0-9]{6}/.test(v)) return 'Invalid Passcode'
return true
}
},
})
})
.catch(e => errorMFA.textContent = e)
.finally(unlock)
}
}
// Danger Zone
actionDangerDeleteAccount.onclick = async () => {
// Prompt Modal
await CreateModal({
behaviour: {
title: 'Delete Account?',
tooltip:
'This will start a countdown where afterwards your account and all of its data will be deleted. ' +
'More details will be sent to your email address.',
}
})
const unlock = LockInteraction()
SendRequest('DELETE', '/api/v1/users/@me', null, 2)
.then(() => SignOut())
.catch(e => errorDanger.textContent = e)
.finally(unlock)
}
// Display Menu
$('#security-wrapper').removeAttribute('hidden')
})
.catch(e => {
// Display Error Message
$('#alert-error').removeAttribute('hidden')
$('#error-message').textContent = e
console.log(e)
})
.finally(() => {
// Hide Loading Symbol
$('#alert-load').setAttribute('hidden', true)
})
})()