diff --git a/frontend/src/utils/recoveryCodeDownload.js b/frontend/src/utils/recoveryCodeDownload.js new file mode 100644 index 0000000..c67805d --- /dev/null +++ b/frontend/src/utils/recoveryCodeDownload.js @@ -0,0 +1,23 @@ +export function downloadRecoveryCodes(codes, documentRef = document, urlApi = URL) { + if (!Array.isArray(codes) || codes.length === 0) return false + + const blob = new Blob([`${codes.join('\n')}\n`], { + type: 'text/plain;charset=utf-8', + }) + const objectUrl = urlApi.createObjectURL(blob) + const anchor = documentRef.createElement('a') + + anchor.href = objectUrl + anchor.download = 'smail-recovery-codes.txt' + anchor.style.display = 'none' + documentRef.body.appendChild(anchor) + + try { + anchor.click() + } finally { + anchor.remove() + urlApi.revokeObjectURL(objectUrl) + } + + return true +} diff --git a/frontend/src/views/components/SecuritySettings/SecuritySettings.js b/frontend/src/views/components/SecuritySettings/SecuritySettings.js index 71b388f..655602e 100644 --- a/frontend/src/views/components/SecuritySettings/SecuritySettings.js +++ b/frontend/src/views/components/SecuritySettings/SecuritySettings.js @@ -11,6 +11,7 @@ import { } from '@/utils/recentAuthentication' import { useGlobalStatusStore } from '@/stores/useGlobalStatusStore' import { normalizeCreationOptions, serializeCredential, webAuthnSupported } from '@/utils/webauthn' +import { downloadRecoveryCodes } from '@/utils/recoveryCodeDownload' export function usePage() { const globalStatus = useGlobalStatusStore() @@ -296,6 +297,12 @@ export function usePage() { } } + const saveRecoveryCodes = () => { + if (!generatedCodes.value.length) return + downloadRecoveryCodes(generatedCodes.value) + notify('安全码已保存到本地文件,文件不会上传服务器', 'success') + } + const revokeRecoveryCodes = async () => { if (recoveryLoading.value) return clearRecoveryCodes() @@ -441,7 +448,7 @@ export function usePage() { registering, credentialsLoading, credentials, securityLoading, recoveryLoading, securityQuestion, questionForm, recoveryConfigured, generatedCodes, externalEmails, externalListLoading, externalDialogVisible, ruleForm, rules, submit, registerCredential, renameCredential, revokeCredential, - saveSecurityQuestion, generateRecoveryCodes, revealRecoveryCodes, revokeRecoveryCodes, openExternalEmailDialog, + saveSecurityQuestion, generateRecoveryCodes, revealRecoveryCodes, saveRecoveryCodes, revokeRecoveryCodes, openExternalEmailDialog, handleExternalEmailBound, disableExternalEmail, formatTime, } diff --git a/frontend/src/views/components/SecuritySettings/SecuritySettings.vue b/frontend/src/views/components/SecuritySettings/SecuritySettings.vue index 12672a4..e764d20 100644 --- a/frontend/src/views/components/SecuritySettings/SecuritySettings.vue +++ b/frontend/src/views/components/SecuritySettings/SecuritySettings.vue @@ -92,7 +92,33 @@ >
安全码默认隐藏,完成安全验证后可以重新查看。