feat: 添加CAPTCHA验证
This commit is contained in:
@@ -808,7 +808,10 @@
|
||||
"saved": "CAPTCHA settings saved successfully."
|
||||
},
|
||||
"error": {
|
||||
"saveFailed": "Failed to save CAPTCHA settings."
|
||||
"saveFailed": "Failed to save CAPTCHA settings.",
|
||||
"hcaptchaKeysRequired": "hCaptcha Site Key and Secret Key are required for verification.",
|
||||
"recaptchaKeysRequired": "reCAPTCHA Site Key and Secret Key are required for verification.",
|
||||
"verificationFailed": "CAPTCHA credential verification failed. Please check your Site Key and Secret Key."
|
||||
}
|
||||
},
|
||||
"commandInputSync": {
|
||||
|
||||
@@ -691,7 +691,10 @@
|
||||
"description": "自動化された攻撃を防ぐために、ログインページに CAPTCHA 検証を設定します。",
|
||||
"enableLabel": "ログインページで CAPTCHA を有効にする",
|
||||
"error": {
|
||||
"saveFailed": "CAPTCHA 設定の保存に失敗しました。"
|
||||
"saveFailed": "CAPTCHA 設定の保存に失敗しました。",
|
||||
"hcaptchaKeysRequired": "hCaptcha サイトキーとシークレットキーは検証に必要です。",
|
||||
"recaptchaKeysRequired": "reCAPTCHA サイトキーとシークレットキーは検証に必要です。",
|
||||
"verificationFailed": "CAPTCHA 認証情報検証に失敗しました。サイトキーとシークレットキーを確認してください。"
|
||||
},
|
||||
"hcaptchaHint": "から",
|
||||
"providerLabel": "CAPTCHA プロバイダー:",
|
||||
|
||||
@@ -807,7 +807,10 @@
|
||||
"saved": "CAPTCHA 设置已成功保存。"
|
||||
},
|
||||
"error": {
|
||||
"saveFailed": "保存 CAPTCHA 设置失败。"
|
||||
"saveFailed": "保存 CAPTCHA 设置失败。",
|
||||
"hcaptchaKeysRequired": "hCaptcha Site Key 和 Secret Key 是必填项,以便进行验证。",
|
||||
"recaptchaKeysRequired": "reCAPTCHA Site Key 和 Secret Key 是必填项,以便进行验证。",
|
||||
"verificationFailed": "CAPTCHA 凭据验证失败。请检查您输入的 Site Key 和 Secret Key 是否正确。"
|
||||
}
|
||||
},
|
||||
"commandInputSync": {
|
||||
|
||||
@@ -12,7 +12,7 @@ interface UserInfo {
|
||||
}
|
||||
|
||||
// Passkey Information Interface
|
||||
interface PasskeyInfo {
|
||||
export interface PasskeyInfo { // + Export 接口
|
||||
credentialID: string;
|
||||
publicKey: string; // Or a more specific type if available
|
||||
counter: number;
|
||||
|
||||
@@ -1654,32 +1654,125 @@ const handleUpdateCaptchaSettings = async () => {
|
||||
captchaMessage.value = '';
|
||||
captchaSuccess.value = false;
|
||||
try {
|
||||
// Prepare DTO, only sending secret keys if they have been entered
|
||||
const dto: UpdateCaptchaSettingsDto = {
|
||||
let needsVerification = false;
|
||||
let providerForVerification: CaptchaProvider | null = null;
|
||||
let siteKeyForVerification: string | undefined = undefined;
|
||||
let secretKeyForVerification: string | undefined = undefined;
|
||||
|
||||
// 步骤 1: 确定是否需要验证
|
||||
if (captchaForm.enabled && captchaForm.provider && captchaForm.provider !== 'none') {
|
||||
const originalSettings = captchaSettings.value; // 从 store 获取的持久化设置
|
||||
|
||||
if (captchaForm.provider === 'hcaptcha') {
|
||||
const originalSiteKeyValue = originalSettings?.hcaptchaSiteKey || '';
|
||||
const currentSiteKeyValue = captchaForm.hcaptchaSiteKey || '';
|
||||
const currentSecretKeyValue = captchaForm.hcaptchaSecretKey || ''; // 用户在表单中输入的新 secret
|
||||
|
||||
if (currentSiteKeyValue !== originalSiteKeyValue) { // 情况 A: 站点密钥已更改
|
||||
if (!currentSiteKeyValue || !currentSecretKeyValue) {
|
||||
captchaMessage.value = t('settings.captcha.error.hcaptchaKeysRequired');
|
||||
captchaSuccess.value = false;
|
||||
captchaLoading.value = false;
|
||||
return;
|
||||
}
|
||||
needsVerification = true;
|
||||
providerForVerification = 'hcaptcha';
|
||||
siteKeyForVerification = currentSiteKeyValue;
|
||||
secretKeyForVerification = currentSecretKeyValue;
|
||||
} else if (currentSecretKeyValue) { // 情况 B: 站点密钥未更改,但用户输入了新的秘密密钥
|
||||
if (!currentSiteKeyValue) { // 确保站点密钥本身不为空
|
||||
captchaMessage.value = t('settings.captcha.error.hcaptchaKeysRequired');
|
||||
captchaSuccess.value = false;
|
||||
captchaLoading.value = false;
|
||||
return;
|
||||
}
|
||||
needsVerification = true;
|
||||
providerForVerification = 'hcaptcha';
|
||||
siteKeyForVerification = currentSiteKeyValue; // 使用当前的 (也是原始的) 站点密钥
|
||||
secretKeyForVerification = currentSecretKeyValue;
|
||||
}
|
||||
// 情况 C: 站点密钥未更改,且用户未输入新的秘密密钥 (currentSecretKeyValue is empty) -> needsVerification 保持 false
|
||||
} else if (captchaForm.provider === 'recaptcha') {
|
||||
const originalSiteKeyValue = originalSettings?.recaptchaSiteKey || '';
|
||||
const currentSiteKeyValue = captchaForm.recaptchaSiteKey || '';
|
||||
const currentSecretKeyValue = captchaForm.recaptchaSecretKey || '';
|
||||
|
||||
if (currentSiteKeyValue !== originalSiteKeyValue) { // 情况 A: 站点密钥已更改
|
||||
if (!currentSiteKeyValue || !currentSecretKeyValue) {
|
||||
captchaMessage.value = t('settings.captcha.error.recaptchaKeysRequired');
|
||||
captchaSuccess.value = false;
|
||||
captchaLoading.value = false;
|
||||
return;
|
||||
}
|
||||
needsVerification = true;
|
||||
providerForVerification = 'recaptcha';
|
||||
siteKeyForVerification = currentSiteKeyValue;
|
||||
secretKeyForVerification = currentSecretKeyValue;
|
||||
} else if (currentSecretKeyValue) { // 情况 B: 站点密钥未更改,但用户输入了新的秘密密钥
|
||||
if (!currentSiteKeyValue) { // 确保站点密钥本身不为空
|
||||
captchaMessage.value = t('settings.captcha.error.recaptchaKeysRequired');
|
||||
captchaSuccess.value = false;
|
||||
captchaLoading.value = false;
|
||||
return;
|
||||
}
|
||||
needsVerification = true;
|
||||
providerForVerification = 'recaptcha';
|
||||
siteKeyForVerification = currentSiteKeyValue;
|
||||
secretKeyForVerification = currentSecretKeyValue;
|
||||
}
|
||||
// 情况 C: 站点密钥未更改,且用户未输入新的秘密密钥 -> needsVerification 保持 false
|
||||
}
|
||||
}
|
||||
|
||||
// 步骤 2: 如果需要,执行验证
|
||||
if (needsVerification && providerForVerification && siteKeyForVerification && secretKeyForVerification) {
|
||||
try {
|
||||
await apiClient.post('/settings/captcha/verify', {
|
||||
provider: providerForVerification,
|
||||
siteKey: siteKeyForVerification,
|
||||
secretKey: secretKeyForVerification,
|
||||
});
|
||||
// 验证成功,可以继续
|
||||
} catch (verifyError: any) {
|
||||
console.error('CAPTCHA 验证失败:', verifyError);
|
||||
captchaMessage.value = verifyError.response?.data?.message || verifyError.message || t('settings.captcha.error.verificationFailed');
|
||||
captchaSuccess.value = false;
|
||||
captchaLoading.value = false;
|
||||
return; // 验证失败,不继续保存
|
||||
}
|
||||
}
|
||||
|
||||
// 步骤 3: 准备用于保存的 DTO
|
||||
const dtoToSave: UpdateCaptchaSettingsDto = {
|
||||
enabled: captchaForm.enabled,
|
||||
provider: captchaForm.provider,
|
||||
// Site keys are not sensitive, send them if present
|
||||
hcaptchaSiteKey: captchaForm.hcaptchaSiteKey || '',
|
||||
recaptchaSiteKey: captchaForm.recaptchaSiteKey || '',
|
||||
// Site keys 总是从表单获取
|
||||
hcaptchaSiteKey: captchaForm.hcaptchaSiteKey || undefined,
|
||||
recaptchaSiteKey: captchaForm.recaptchaSiteKey || undefined,
|
||||
// Secret keys 仅在表单中提供时才包含
|
||||
hcaptchaSecretKey: captchaForm.hcaptchaSecretKey || undefined,
|
||||
recaptchaSecretKey: captchaForm.recaptchaSecretKey || undefined,
|
||||
};
|
||||
// 如果 captchaForm.provider 为 'none' 或 captchaForm.enabled 为 false,
|
||||
// 后端应负责清除所有相关的 site/secret key。
|
||||
// 如果表单中的 secret key 为空字符串,则发送 undefined,后端不应更新该特定 secret key。
|
||||
|
||||
// Only include secret keys in the DTO if the user entered a value
|
||||
if (captchaForm.hcaptchaSecretKey) {
|
||||
dto.hcaptchaSecretKey = captchaForm.hcaptchaSecretKey;
|
||||
}
|
||||
if (captchaForm.recaptchaSecretKey) {
|
||||
dto.recaptchaSecretKey = captchaForm.recaptchaSecretKey;
|
||||
}
|
||||
|
||||
await settingsStore.updateCaptchaSettings(dto);
|
||||
captchaMessage.value = t('settings.captcha.success.saved'); // Need translation
|
||||
// 步骤 4: 调用保存操作
|
||||
await settingsStore.updateCaptchaSettings(dtoToSave);
|
||||
captchaMessage.value = t('settings.captcha.success.saved');
|
||||
captchaSuccess.value = true;
|
||||
// Clear secret key fields in the form after successful save for security
|
||||
// 成功保存后清除表单中的 secret key 字段,以确保下次编辑时它们是空的,除非用户再次输入
|
||||
captchaForm.hcaptchaSecretKey = '';
|
||||
captchaForm.recaptchaSecretKey = '';
|
||||
|
||||
} catch (error: any) {
|
||||
console.error('更新 CAPTCHA 设置失败:', error);
|
||||
captchaMessage.value = error.message || t('settings.captcha.error.saveFailed'); // Need translation
|
||||
// 此 catch 块处理来自 settingsStore.updateCaptchaSettings 的错误
|
||||
// 或在 try 块中未被 'return' 语句捕获的其他错误。
|
||||
console.error('更新 CAPTCHA 设置时捕获到错误:', error);
|
||||
// 避免覆盖更具体的错误消息(例如,来自验证失败的消息)
|
||||
if (!captchaMessage.value) {
|
||||
captchaMessage.value = error.message || t('settings.captcha.error.saveFailed');
|
||||
}
|
||||
captchaSuccess.value = false;
|
||||
} finally {
|
||||
captchaLoading.value = false;
|
||||
|
||||
Reference in New Issue
Block a user