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:
@@ -189,7 +189,10 @@
|
||||
<button type="button" class="captcha-refresh" onclick="refreshCaptcha()">🔄</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="forgot-link"><a href="#" onclick="showForgotPassword(event)">忘记密码?</a></div>
|
||||
<div class="forgot-link">
|
||||
<a href="#" onclick="showForgotPassword(event)">忘记密码?</a>
|
||||
<span id="resendVerifyLink" style="display: none; margin-left: 16px;"><a href="#" onclick="showResendVerify(event)">重发验证邮件</a></span>
|
||||
</div>
|
||||
<button type="submit" class="btn-login">登 录</button>
|
||||
</form>
|
||||
<div class="register-link">还没有账号? <a href="/register">立即注册</a></div>
|
||||
@@ -213,9 +216,49 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 重发验证邮件弹窗 -->
|
||||
<div id="resendVerifyModal" class="modal-overlay" onclick="if(event.target===this)closeResendVerify()">
|
||||
<div class="modal">
|
||||
<div class="modal-header"><h2>重发验证邮件</h2><p>输入注册时使用的邮箱</p></div>
|
||||
<div class="modal-body">
|
||||
<div id="resendErrorMessage" class="message error"></div>
|
||||
<div id="resendSuccessMessage" class="message success"></div>
|
||||
<form id="resendVerifyForm" onsubmit="handleResendVerify(event)">
|
||||
<div class="form-group"><label>邮箱</label><input type="email" id="resendEmail" placeholder="请输入注册邮箱" required></div>
|
||||
<div class="form-group">
|
||||
<label>验证码</label>
|
||||
<div class="captcha-row">
|
||||
<input type="text" id="resendCaptcha" placeholder="请输入验证码" required>
|
||||
<img id="resendCaptchaImage" src="" alt="验证码" style="height: 36px; border: 1px solid #ddd; border-radius: 4px; cursor: pointer;" onclick="refreshResendCaptcha()" title="点击刷新">
|
||||
<button type="button" class="captcha-refresh" onclick="refreshResendCaptcha()">🔄</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn-secondary" onclick="closeResendVerify()">取消</button>
|
||||
<button type="button" class="btn-primary" onclick="document.getElementById('resendVerifyForm').dispatchEvent(new Event('submit'))">发送验证邮件</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
let captchaSession = '';
|
||||
let resendCaptchaSession = '';
|
||||
let needCaptcha = false;
|
||||
|
||||
// 页面加载时检查邮箱验证是否启用
|
||||
window.onload = async function() {
|
||||
try {
|
||||
const resp = await fetch('/api/email/verify-status');
|
||||
const data = await resp.json();
|
||||
if (data.register_verify_enabled) {
|
||||
document.getElementById('resendVerifyLink').style.display = 'inline';
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('获取邮箱验证状态失败', e);
|
||||
}
|
||||
};
|
||||
|
||||
async function handleLogin(event) {
|
||||
event.preventDefault();
|
||||
const username = document.getElementById('username').value.trim();
|
||||
@@ -256,7 +299,61 @@
|
||||
}
|
||||
async function generateCaptcha() { try { const response = await fetch('/api/generate_captcha', { method: 'POST', headers: { 'Content-Type': 'application/json' } }); const data = await response.json(); if (data.session_id && data.captcha_image) { captchaSession = data.session_id; document.getElementById('captchaImage').src = data.captcha_image; } } catch (error) { console.error('生成验证码失败:', error); } }
|
||||
async function refreshCaptcha() { await generateCaptcha(); document.getElementById('captcha').value = ''; }
|
||||
document.addEventListener('keydown', (e) => { if (e.key === 'Escape') closeForgotPassword(); });
|
||||
|
||||
// 重发验证邮件相关函数
|
||||
async function showResendVerify(event) {
|
||||
event.preventDefault();
|
||||
document.getElementById('resendVerifyModal').classList.add('active');
|
||||
await generateResendCaptcha();
|
||||
}
|
||||
function closeResendVerify() {
|
||||
document.getElementById('resendVerifyModal').classList.remove('active');
|
||||
document.getElementById('resendVerifyForm').reset();
|
||||
document.getElementById('resendErrorMessage').style.display = 'none';
|
||||
document.getElementById('resendSuccessMessage').style.display = 'none';
|
||||
}
|
||||
async function generateResendCaptcha() {
|
||||
try {
|
||||
const response = await fetch('/api/generate_captcha', { method: 'POST', headers: { 'Content-Type': 'application/json' } });
|
||||
const data = await response.json();
|
||||
if (data.session_id && data.captcha_image) {
|
||||
resendCaptchaSession = data.session_id;
|
||||
document.getElementById('resendCaptchaImage').src = data.captcha_image;
|
||||
}
|
||||
} catch (error) { console.error('生成验证码失败:', error); }
|
||||
}
|
||||
async function refreshResendCaptcha() { await generateResendCaptcha(); document.getElementById('resendCaptcha').value = ''; }
|
||||
async function handleResendVerify(event) {
|
||||
event.preventDefault();
|
||||
const email = document.getElementById('resendEmail').value.trim();
|
||||
const captcha = document.getElementById('resendCaptcha').value.trim();
|
||||
const errorDiv = document.getElementById('resendErrorMessage');
|
||||
const successDiv = document.getElementById('resendSuccessMessage');
|
||||
errorDiv.style.display = 'none'; successDiv.style.display = 'none';
|
||||
|
||||
if (!email) { errorDiv.textContent = '请输入邮箱'; errorDiv.style.display = 'block'; return; }
|
||||
if (!captcha) { errorDiv.textContent = '请输入验证码'; errorDiv.style.display = 'block'; return; }
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/resend-verify-email', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ email, captcha_session: resendCaptchaSession, captcha })
|
||||
});
|
||||
const data = await response.json();
|
||||
if (response.ok) {
|
||||
successDiv.textContent = data.message || '验证邮件已发送,请查收';
|
||||
successDiv.style.display = 'block';
|
||||
setTimeout(closeResendVerify, 2000);
|
||||
} else {
|
||||
errorDiv.textContent = data.error || '发送失败';
|
||||
errorDiv.style.display = 'block';
|
||||
await refreshResendCaptcha();
|
||||
}
|
||||
} catch (error) { errorDiv.textContent = '网络错误'; errorDiv.style.display = 'block'; }
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', (e) => { if (e.key === 'Escape') { closeForgotPassword(); closeResendVerify(); } });
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user