feat: 添加CAPTCHA验证

This commit is contained in:
Baobhan Sith
2025-05-11 19:58:49 +08:00
parent 12260681b7
commit 7ee8ffb90a
9 changed files with 298 additions and 46 deletions
+111 -18
View File
@@ -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;