feat: 添加邮件功能第二阶段 - 注册邮箱验证

实现注册时的邮箱验证功能:
- 修改注册API支持邮箱验证流程
- 新增邮箱验证API (/api/verify-email/<token>)
- 新增重发验证邮件API (/api/resend-verify-email)
- 新增邮箱验证状态查询API (/api/email/verify-status)

新增文件:
- templates/email/register.html - 注册验证邮件模板
- templates/verify_success.html - 验证成功页面
- templates/verify_failed.html - 验证失败页面

修改文件:
- email_service.py - 添加发送注册验证邮件函数
- app.py - 添加邮箱验证相关API
- database.py - 添加get_user_by_email函数
- app_config.py - 添加BASE_URL配置
- templates/register.html - 支持邮箱必填切换
- templates/login.html - 添加重发验证邮件功能
- templates/admin.html - 添加注册验证开关和BASE_URL设置

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-11 21:51:07 +08:00
parent 2f762db337
commit 648cf0adf0
10 changed files with 794 additions and 20 deletions

View File

@@ -173,9 +173,9 @@
</div>
<div class="form-group">
<label for="email">邮箱</label>
<label for="email">邮箱 <span id="emailRequired" style="color: #d63031; display: none;">*</span></label>
<input type="email" id="email" name="email">
<small>选填,用于接收审核通知</small>
<small id="emailHint">选填,用于接收审核通知</small>
</div>
<div class="form-group">
<label for="captcha">验证码</label>
@@ -196,7 +196,29 @@
<script>
let captchaSession = '';
window.onload = function() { generateCaptcha(); };
let emailVerifyEnabled = false;
window.onload = async function() {
await generateCaptcha();
await checkEmailVerifyStatus();
};
async function checkEmailVerifyStatus() {
try {
const resp = await fetch('/api/email/verify-status');
const data = await resp.json();
emailVerifyEnabled = data.register_verify_enabled;
if (emailVerifyEnabled) {
document.getElementById('emailRequired').style.display = 'inline';
document.getElementById('email').required = true;
document.getElementById('emailHint').textContent = '必填,用于账号验证';
}
} catch (e) {
console.log('获取邮箱验证状态失败', e);
}
}
async function handleRegister(event) {
event.preventDefault();
@@ -229,6 +251,20 @@
return;
}
// 邮箱验证启用时必填
if (emailVerifyEnabled && !email) {
errorDiv.textContent = '请填写邮箱地址用于账号验证';
errorDiv.style.display = 'block';
return;
}
// 邮箱格式验证
if (email && !email.includes('@')) {
errorDiv.textContent = '邮箱格式不正确';
errorDiv.style.display = 'block';
return;
}
try {
const response = await fetch('/api/register', {
method: 'POST',
@@ -241,7 +277,12 @@
const data = await response.json();
if (response.ok) {
successDiv.textContent = data.message || '注册成功,请等待管理员审核';
// 根据是否需要邮箱验证显示不同的消息
if (data.need_verify) {
successDiv.innerHTML = data.message + '<br><small style="color: #666;">请检查您的邮箱(包括垃圾邮件文件夹)</small>';
} else {
successDiv.textContent = data.message || '注册成功,请等待管理员审核';
}
successDiv.style.display = 'block';
// 清空表单
@@ -254,12 +295,14 @@
} else {
errorDiv.textContent = data.error || '注册失败';
errorDiv.style.display = 'block';
refreshCaptcha();
}
} catch (error) {
errorDiv.textContent = '网络错误,请稍后重试';
errorDiv.style.display = 'block';
}
}
async function generateCaptcha() {
const resp = await fetch('/api/generate_captcha', {method: 'POST', headers: {'Content-Type': 'application/json'}});
const data = await resp.json();
@@ -268,6 +311,7 @@
document.getElementById('captchaImage').src = data.captcha_image;
}
}
async function refreshCaptcha() { await generateCaptcha(); document.getElementById('captcha').value = ''; }
</script>
</body>