From 4b3733bc19821622cd93cdb9d557f30a0da3908c Mon Sep 17 00:00:00 2001 From: jxxghp Date: Thu, 16 Apr 2026 19:21:17 +0800 Subject: [PATCH] feat: add site auth step to setup wizard --- src/composables/useSetupWizard.ts | 186 ++++++++++++++++++++--- src/locales/en-US.ts | 12 ++ src/locales/zh-CN.ts | 11 ++ src/locales/zh-TW.ts | 11 ++ src/pages/setup.vue | 28 ++-- src/views/setup/SiteAuthSettingsStep.vue | 103 +++++++++++++ 6 files changed, 323 insertions(+), 28 deletions(-) create mode 100644 src/views/setup/SiteAuthSettingsStep.vue diff --git a/src/composables/useSetupWizard.ts b/src/composables/useSetupWizard.ts index 842d86d4..61b257fe 100644 --- a/src/composables/useSetupWizard.ts +++ b/src/composables/useSetupWizard.ts @@ -18,6 +18,11 @@ export interface WizardData { proxyHost: string githubToken: string } + siteAuth: { + auxiliaryAuthEnable: boolean + site: string + params: Record + } storage: { downloadPath: string libraryPath: string @@ -85,6 +90,10 @@ export interface ConnectivityTestState { } export interface ValidationErrorState { + siteAuth: { + site: boolean + [key: string]: boolean + } downloader: { name: boolean host: boolean @@ -114,7 +123,7 @@ export interface ValidationErrorState { // 全局状态,所有组件共享 const currentStep = ref(1) -const totalSteps = 7 +const totalSteps = 8 // 加载状态 const isLoading = ref(false) @@ -122,6 +131,22 @@ const isLoading = ref(false) // 选中的预设规则 const selectedPreset = ref('') +// 可认证站点列表 +const authSites = ref<{ + [key: string]: { + name: string + icon: string + params: { + [key: string]: { + name: string + type: string + placeholder?: string + tooltip?: string + } + } + } +}>({}) + // 向导数据 const wizardData = ref({ basic: { @@ -135,6 +160,11 @@ const wizardData = ref({ proxyHost: '', githubToken: '', }, + siteAuth: { + auxiliaryAuthEnable: false, + site: '', + params: {}, + }, storage: { downloadPath: '', libraryPath: '', @@ -194,6 +224,9 @@ const connectivityTest = ref({ // 验证错误状态 const validationErrors = ref({ + siteAuth: { + site: false, + }, downloader: { name: false, host: false, @@ -256,6 +289,7 @@ export function useSetupWizard() { // 步骤标题 const stepTitles = computed(() => [ t('setupWizard.basic.title'), + t('setupWizard.siteAuth.title'), t('setupWizard.storage.title'), t('setupWizard.downloader.title'), t('setupWizard.mediaServer.title'), @@ -267,6 +301,7 @@ export function useSetupWizard() { // 步骤描述 const stepDescriptions = computed(() => [ t('setupWizard.basic.description'), + t('setupWizard.siteAuth.description'), t('setupWizard.storage.description'), t('setupWizard.downloader.description'), t('setupWizard.mediaServer.description'), @@ -378,6 +413,9 @@ export function useSetupWizard() { // 清除验证错误状态 function clearValidationErrors() { + validationErrors.value.siteAuth = { + site: false, + } validationErrors.value.downloader = { name: false, host: false, @@ -404,6 +442,47 @@ export function useSetupWizard() { } } + // 验证用户站点认证字段 + function validateSiteAuthFields(): { isValid: boolean; errors: string[] } { + const errors: string[] = [] + clearValidationErrors() + + if (!wizardData.value.siteAuth.site) { + return { + isValid: true, + errors, + } + } + + const selectedSite = authSites.value[wizardData.value.siteAuth.site] + if (!selectedSite) { + errors.push(t('setupWizard.siteAuth.siteConfigNotExist')) + validationErrors.value.siteAuth.site = true + return { + isValid: false, + errors, + } + } + + const fields = Object.keys(selectedSite.params || {}).filter(key => { + return selectedSite.params[key]?.name && selectedSite.params[key]?.type + }) + + fields.forEach(key => { + const fieldKey = `${wizardData.value.siteAuth.site.toUpperCase()}_${key.toUpperCase()}` + const value = wizardData.value.siteAuth.params[fieldKey] + if (value === undefined || value === null || value === '') { + errors.push(t('setupWizard.siteAuth.fieldRequired', { name: selectedSite.params[key].name })) + validationErrors.value.siteAuth[fieldKey] = true + } + }) + + return { + isValid: errors.length === 0, + errors, + } + } + // 验证下载器字段 function validateDownloaderFields(): { isValid: boolean; errors: string[] } { const errors: string[] = [] @@ -645,6 +724,13 @@ export function useSetupWizard() { break case 2: // 存储设置 + if (wizardData.value.siteAuth.site) { + const validation = validateSiteAuthFields() + errors.push(...validation.errors) + } + break + + case 3: // 存储设置 if (!wizardData.value.storage.downloadPath) { errors.push(t('setupWizard.storage.downloadPathRequired')) } @@ -653,7 +739,7 @@ export function useSetupWizard() { } break - case 3: // 下载器设置 + case 4: // 下载器设置 if (wizardData.value.downloader.type) { // 如果选择了下载器,则验证必输项 const validation = validateDownloaderFields() @@ -661,7 +747,7 @@ export function useSetupWizard() { } break - case 4: // 媒体服务器设置 + case 5: // 媒体服务器设置 if (wizardData.value.mediaServer.type) { // 如果选择了媒体服务器,则验证必输项 const validation = validateMediaServerFields() @@ -669,7 +755,7 @@ export function useSetupWizard() { } break - case 5: // 通知设置 + case 6: // 通知设置 if (wizardData.value.notification.type) { // 如果选择了通知,则验证必输项 const validation = validateNotificationFields() @@ -677,14 +763,14 @@ export function useSetupWizard() { } break - case 6: // 智能助手设置 + case 7: // 智能助手设置 if (wizardData.value.agent.enabled) { const validation = validateAgentFields() errors.push(...validation.errors) } break - case 7: // 偏好设置 + case 8: // 偏好设置 // 偏好设置有默认值,不需要验证 break } @@ -699,12 +785,14 @@ export function useSetupWizard() { function shouldPerformTest(step: number): boolean { switch (step) { case 2: // 存储目录测试 - 总是需要测试 + return false + case 3: // 存储目录测试 - 总是需要测试 return true - case 3: // 下载器测试 - 只有选择了下载器才测试 + case 4: // 下载器测试 - 只有选择了下载器才测试 return !!wizardData.value.downloader.type - case 4: // 媒体服务器测试 - 只有选择了媒体服务器才测试 + case 5: // 媒体服务器测试 - 只有选择了媒体服务器才测试 return !!wizardData.value.mediaServer.type - case 5: // 消息通知测试 - 只有选择了通知才测试 + case 6: // 消息通知测试 - 只有选择了通知才测试 return !!wizardData.value.notification.type default: return false @@ -724,15 +812,17 @@ export function useSetupWizard() { switch (step) { case 2: // 存储目录测试 + break + case 3: // 存储目录测试 testResult = await testStorageConnectivity() break - case 3: // 下载器测试 + case 4: // 下载器测试 testResult = await testDownloaderConnectivity() break - case 4: // 媒体服务器测试 + case 5: // 媒体服务器测试 testResult = await testMediaServerConnectivity() break - case 5: // 消息通知测试 + case 6: // 消息通知测试 testResult = await testNotificationConnectivity() break } @@ -957,16 +1047,18 @@ export function useSetupWizard() { case 1: return await saveBasicSettings() case 2: - return await saveStorageSettings() + return await saveSiteAuthSettings() case 3: - return await saveDownloaderSettings() + return await saveStorageSettings() case 4: - return await saveMediaServerSettings() + return await saveDownloaderSettings() case 5: - return await saveNotificationSettings() + return await saveMediaServerSettings() case 6: - return await saveAgentSettings() + return await saveNotificationSettings() case 7: + return await saveAgentSettings() + case 8: return await savePreferenceSettings() } } catch (error) { @@ -1109,6 +1201,39 @@ export function useSetupWizard() { } } + // 保存用户站点认证设置 + async function saveSiteAuthSettings() { + try { + const envResponse: { [key: string]: any } = await api.post('system/env', { + AUXILIARY_AUTH_ENABLE: wizardData.value.siteAuth.auxiliaryAuthEnable, + }) + + if (!envResponse.success) { + return false + } + + if (!wizardData.value.siteAuth.site) { + return true + } + + const response: { [key: string]: any } = await api.post('site/auth', { + site: wizardData.value.siteAuth.site, + params: wizardData.value.siteAuth.params, + }) + + if (!response.success) { + $toast.error(t('setupWizard.saveSiteAuthSettingsFailed', { message: response.message })) + return false + } + + return true + } catch (error) { + console.error('Save site auth settings failed:', error) + $toast.error(t('setupWizard.saveSiteAuthSettingsFailed', { message: (error as Error).message || '' })) + return false + } + } + // 保存下载器配置 async function saveDownloaderSettings() { if (wizardData.value.downloader.type) { @@ -1298,6 +1423,7 @@ export function useSetupWizard() { if (result.data.GITHUB_TOKEN) { wizardData.value.basic.githubToken = result.data.GITHUB_TOKEN } + wizardData.value.siteAuth.auxiliaryAuthEnable = Boolean(result.data.AUXILIARY_AUTH_ENABLE) if (result.data.SUPERUSER) { wizardData.value.basic.username = result.data.SUPERUSER } @@ -1326,6 +1452,28 @@ export function useSetupWizard() { } } + // 加载用户站点认证列表 + async function loadAuthSites() { + try { + authSites.value = (await api.get('site/auth')) || {} + } catch (error) { + console.log('Load auth sites failed:', error) + } + } + + // 加载用户站点认证设置 + async function loadSiteAuthSettings() { + try { + const result: { [key: string]: any } = await api.get('system/setting/UserSiteAuthParams') + if (result.success && result.data?.value) { + wizardData.value.siteAuth.site = result.data.value.site || '' + wizardData.value.siteAuth.params = result.data.value.params || {} + } + } catch (error) { + console.log('Load site auth settings failed:', error) + } + } + // 加载存储设置 async function loadStorageSettings() { try { @@ -1395,6 +1543,8 @@ export function useSetupWizard() { isLoading.value = true try { await loadSystemSettings() + await loadAuthSites() + await loadSiteAuthSettings() await loadStorageSettings() await loadDownloaderSettings() await loadMediaServerSettings() @@ -1411,6 +1561,7 @@ export function useSetupWizard() { stepTitles, stepDescriptions, wizardData, + authSites, selectedPreset, connectivityTest, validationErrors, @@ -1425,6 +1576,7 @@ export function useSetupWizard() { selectPreset, updatePreferences, validateCurrentStep, + validateSiteAuthFields, validateDownloaderFields, validateMediaServerFields, validateNotificationFields, diff --git a/src/locales/en-US.ts b/src/locales/en-US.ts index d7ddb558..20d74004 100644 --- a/src/locales/en-US.ts +++ b/src/locales/en-US.ts @@ -3183,6 +3183,7 @@ export default { saveMediaServerSettingsFailed: 'Failed to save media server settings', notificationSettingsSaved: 'Notification settings saved successfully', saveNotificationSettingsFailed: 'Failed to save notification settings', + saveSiteAuthSettingsFailed: 'Failed to save user site authentication settings: {message}', saveAgentSettingsFailed: 'Failed to save AI assistant settings', preferenceSettingsSaved: 'Preference settings saved successfully', savePreferenceSettingsFailed: 'Failed to save preference settings', @@ -3205,6 +3206,17 @@ export default { confirmPasswordHint: 'Confirm new password', apiTokenRequired: 'API Token is required', }, + siteAuth: { + title: 'User Authentication', + description: 'Configure site authentication and auxiliary authentication', + info: 'User Site Authentication', + infoDesc: + 'Completing site authentication unlocks site capabilities and some plugin permissions. This step is optional and can also be configured later from the user menu.', + selectSiteHint: 'Choose a supported auth site and fill in the required credentials for that site', + submitHint: 'When you click Next, the wizard will immediately validate against the selected auth site and save the current parameters on success.', + siteConfigNotExist: 'Authentication site configuration does not exist', + fieldRequired: 'Please enter {name}', + }, storage: { title: 'Storage', description: 'Configure download directory and media library directory', diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index bdc512d5..1ef4546c 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -3147,6 +3147,7 @@ export default { saveMediaServerSettingsFailed: '保存媒体服务器设置失败', notificationSettingsSaved: '通知设置保存成功', saveNotificationSettingsFailed: '保存通知设置失败', + saveSiteAuthSettingsFailed: '保存用户站点认证设置失败:{message}', saveAgentSettingsFailed: '保存智能助手设置失败', preferenceSettingsSaved: '偏好设置保存成功', savePreferenceSettingsFailed: '保存偏好设置失败', @@ -3169,6 +3170,16 @@ export default { confirmPasswordHint: '确认新密码', apiTokenRequired: 'API Token不能为空', }, + siteAuth: { + title: '用户认证', + description: '配置用户站点认证与辅助认证', + info: '用户站点认证说明', + infoDesc: '完成站点认证后可解锁站点能力与部分插件权限。此步骤可选,后续也可在个人菜单中继续配置。', + selectSiteHint: '选择一个支持认证的站点,并填写该站点要求的认证参数', + submitHint: '点击下一步时将立即向认证站点发起校验,认证成功后会保存当前参数。', + siteConfigNotExist: '认证站点配置不存在', + fieldRequired: '请输入{name}', + }, storage: { title: '存储', description: '配置下载目录和媒体库目录', diff --git a/src/locales/zh-TW.ts b/src/locales/zh-TW.ts index 1691461c..998d76cf 100644 --- a/src/locales/zh-TW.ts +++ b/src/locales/zh-TW.ts @@ -3148,6 +3148,7 @@ export default { saveMediaServerSettingsFailed: '保存媒體服務器設置失敗', notificationSettingsSaved: '通知設置保存成功', saveNotificationSettingsFailed: '保存通知設置失敗', + saveSiteAuthSettingsFailed: '保存用戶站點認證設置失敗:{message}', saveAgentSettingsFailed: '保存智能助手設置失敗', preferenceSettingsSaved: '偏好設置保存成功', savePreferenceSettingsFailed: '保存偏好設置失敗', @@ -3170,6 +3171,16 @@ export default { confirmPasswordHint: '確認新密碼', apiTokenRequired: 'API Token 不能為空', }, + siteAuth: { + title: '用戶認證', + description: '配置用戶站點認證與輔助認證', + info: '用戶站點認證說明', + infoDesc: '完成站點認證後可解鎖站點能力與部分插件權限。此步驟可選,後續也可在個人選單中繼續配置。', + selectSiteHint: '選擇一個支援認證的站點,並填寫該站點要求的認證參數', + submitHint: '點擊下一步時將立即向認證站點發起校驗,認證成功後會保存當前參數。', + siteConfigNotExist: '認證站點配置不存在', + fieldRequired: '請輸入{name}', + }, storage: { title: '儲存', description: '設定下載目錄和媒體庫目錄', diff --git a/src/pages/setup.vue b/src/pages/setup.vue index 3c9f7e65..02078dec 100644 --- a/src/pages/setup.vue +++ b/src/pages/setup.vue @@ -4,6 +4,7 @@ import { useI18n } from 'vue-i18n' import { useRouter } from 'vue-router' import { useSetupWizard } from '@/composables/useSetupWizard' import BasicSettingsStep from '@/views/setup/BasicSettingsStep.vue' +import SiteAuthSettingsStep from '@/views/setup/SiteAuthSettingsStep.vue' import StorageSettingsStep from '@/views/setup/StorageSettingsStep.vue' import DownloaderSettingsStep from '@/views/setup/DownloaderSettingsStep.vue' import MediaServerSettingsStep from '@/views/setup/MediaServerSettingsStep.vue' @@ -102,33 +103,38 @@ onMounted(async () => { - + + + + + + - - + + - - + + - - + + - - + + - - + + diff --git a/src/views/setup/SiteAuthSettingsStep.vue b/src/views/setup/SiteAuthSettingsStep.vue new file mode 100644 index 00000000..a2bdea75 --- /dev/null +++ b/src/views/setup/SiteAuthSettingsStep.vue @@ -0,0 +1,103 @@ + + +