()
| 6795 | if (input) { input.value = ''; input.focus(); } |
| 6796 | if (otpInput) otpInput.value = ''; |
| 6797 | } |
| 6798 | } |
| 6799 | |
| 6800 | async function submitRecovery() { |
| 6801 | try { |
| 6802 | await apiPost('/api/auth/recover', { |
| 6803 | username: document.getElementById('loginUsernameInput').value.trim(), |
| 6804 | answers: [ |
| 6805 | document.getElementById('recoveryAnswer1Input').value, |
| 6806 | document.getElementById('recoveryAnswer2Input').value, |
| 6807 | document.getElementById('recoveryAnswer3Input').value |
| 6808 | ], |
| 6809 | new_password: document.getElementById('recoveryNewPasswordInput').value |
| 6810 | }); |
| 6811 | state.authenticated = true; |
| 6812 | state.authRequired = false; |
| 6813 | hideLoginOverlay(); |
| 6814 | await bootApp(); |
| 6815 | } catch (err) { |
| 6816 | showToast(err.message || t('Recovery answers do not match.'), true); |
| 6817 | } |
| 6818 | } |
| 6819 | |
| 6820 | function bindLoginUi() { |
| 6821 | var btn = document.getElementById('loginSubmitBtn'); |
| 6822 | if (btn && !btn.dataset.bound) { |
| 6823 | btn.dataset.bound = '1'; |
| 6824 | btn.addEventListener('click', function(){ submitLogin().catch(handleError); }); |
| 6825 | } |
| 6826 | ['loginUsernameInput','loginPasswordInput','loginOtpInput'].forEach(function(id){ |
| 6827 | var input = document.getElementById(id); |
| 6828 | if (input && !input.dataset.bound) { |
| 6829 | input.dataset.bound = '1'; |
| 6830 | input.addEventListener('keydown', function(event){ if (event.key === 'Enter') { event.preventDefault(); submitLogin().catch(handleError); } }); |
| 6831 | } |
| 6832 | }); |
| 6833 | var forgotBtn = document.getElementById('forgotPasswordBtn'); |
| 6834 | if (forgotBtn && !forgotBtn.dataset.bound) { |
| 6835 | forgotBtn.dataset.bound = '1'; |
| 6836 | forgotBtn.addEventListener('click', function(){ toggleRecoveryPanel(true); }); |
| 6837 | } |
| 6838 | var recoveryBtn = document.getElementById('recoverySubmitBtn'); |
| 6839 | if (recoveryBtn && !recoveryBtn.dataset.bound) { |
| 6840 | recoveryBtn.dataset.bound = '1'; |
| 6841 | recoveryBtn.addEventListener('click', function(){ submitRecovery().catch(handleError); }); |
| 6842 | } |
| 6843 | } |
| 6844 | |
| 6845 | async function bootApp() { |
| 6846 | try { |
| 6847 | var results = await Promise.all([ |
| 6848 | apiGet('/api/info'), |
| 6849 | apiGet('/api/config'), |
| 6850 | apiGet('/api/jobs') |
| 6851 | ]); |
| 6852 | state.info = results[0]; |
| 6853 | state.config = results[1].config; |
| 6854 | state.jobs = results[2].jobs || []; |
no test coverage detected