feat(frontend): Phase 5d-iii — settings Security tab + 2FA modal

Ports the panel/security partial: change-credentials form and 2FA
toggle. The 2FA modal is a new shared component since enabling 2FA,
disabling 2FA, and changing credentials all funnel through it with
slightly different copy.

- TwoFactorModal.vue: 'set' flow renders a QR code + manual key + a
  6-digit verifier; 'confirm' flow renders just the verifier. The
  parent passes a confirm(success) callback that fires only when the
  entered code matches the live TOTP value (otpauth lib).
- SecurityTab.vue: holds the local user form (oldUsername/oldPassword/
  new*), POSTs /panel/setting/updateUser, and on success force-redirects
  to logout. When 2FA is on, the credentials change goes through the
  confirm-modal first.
- toggleTwoFactor leaves the switch read-only (the v-bound :checked
  matches AllSetting) and only flips after the modal succeeds, so
  cancelling out leaves state unchanged.
- Adds otpauth ^9.5.1 dep (qrious was already present).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
MHSanaei
2026-05-08 13:08:39 +02:00
parent 56cdf05909
commit bd20b8fd7f
5 changed files with 348 additions and 2 deletions
+2 -1
View File
@@ -15,6 +15,7 @@ import { useMediaQuery } from '@/composables/useMediaQuery.js';
import AppSidebar from '@/components/AppSidebar.vue';
import { useAllSetting } from './useAllSetting.js';
import GeneralTab from './GeneralTab.vue';
import SecurityTab from './SecurityTab.vue';
const antdThemeConfig = computed(() => ({
algorithm: themeState.isDark ? antdTheme.darkAlgorithm : antdTheme.defaultAlgorithm,
@@ -214,7 +215,7 @@ const alertVisible = ref(true);
<SafetyOutlined />
<span>Security</span>
</template>
<a-empty description="Security — coming in 5d-iii" />
<SecurityTab :all-setting="allSetting" />
</a-tab-pane>
<a-tab-pane key="3" class="tab-pane">
<template #tab>