feat: 安全增强 + 删除密码重置申请功能 + 登录提醒开关
安全增强: - 新增 SSRF、XXE、模板注入、敏感路径探测检测规则 - security/constants.py: 添加新的威胁类型和检测模式 - security/threat_detector.py: 实现新检测逻辑 删除密码重置申请功能: - 移除 /api/password_resets 相关API - 删除 password_reset_requests 数据库表 - 前端移除密码重置申请页面和菜单 - 用户只能通过邮��找回密码,未绑定邮箱需联系管理员 登录提醒全局开关: - email_service.py: 添加 login_alert_enabled 字段 - routes/api_auth.py: 检查开关状态再发送登录提醒 - EmailPage.vue: 添加新设备登录提醒开关 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -754,9 +754,6 @@
|
||||
<div id="tab-pending" class="tab-content active">
|
||||
<h3 style="margin-bottom: 15px; font-size: 16px;">用户注册审核</h3>
|
||||
<div id="pendingUsersList"></div>
|
||||
|
||||
<h3 style="margin-top: 30px; margin-bottom: 15px; font-size: 16px;">密码重置审核</h3>
|
||||
<div id="passwordResetsList"></div>
|
||||
</div>
|
||||
|
||||
<!-- 所有用户 -->
|
||||
@@ -1536,7 +1533,6 @@
|
||||
loadAnnouncements();
|
||||
loadSystemConfig();
|
||||
loadProxyConfig();
|
||||
loadPasswordResets(); // 修复: 初始化时也加载密码重置申请
|
||||
loadFeedbacks(); // 加载反馈统计更新徽章
|
||||
|
||||
// 恢复上次的标签页
|
||||
@@ -2771,112 +2767,9 @@
|
||||
} else if (tabName === 'logs') {
|
||||
loadLogUserOptions();
|
||||
loadTaskLogs();
|
||||
} else if (tabName === 'pending') {
|
||||
loadPasswordResets();
|
||||
}
|
||||
};
|
||||
|
||||
// ==================== 密码重置功能 ====================
|
||||
|
||||
let passwordResets = [];
|
||||
|
||||
// 加载密码重置申请列表
|
||||
async function loadPasswordResets() {
|
||||
try {
|
||||
const response = await fetch('/yuyx/api/password_resets');
|
||||
if (response.ok) {
|
||||
passwordResets = await response.json();
|
||||
renderPasswordResets();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载密码重置申请失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 渲染密码重置申请列表
|
||||
function renderPasswordResets() {
|
||||
const container = document.getElementById('passwordResetsList');
|
||||
|
||||
if (passwordResets.length === 0) {
|
||||
container.innerHTML = '<div class="empty-message">暂无密码重置申请</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
container.innerHTML = `
|
||||
<div class="table-container">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>申请ID</th>
|
||||
<th>用户名</th>
|
||||
<th>邮箱</th>
|
||||
<th>申请时间</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${passwordResets.map(reset => `
|
||||
<tr>
|
||||
<td>${reset.id}</td>
|
||||
<td><strong>${escapeHtml(reset.username)}</strong></td>
|
||||
<td>${escapeHtml(reset.email || '-')}</td>
|
||||
<td>${escapeHtml(reset.created_at)}</td>
|
||||
<td>
|
||||
<div class="action-buttons">
|
||||
<button class="btn btn-small btn-success" onclick="approvePasswordReset(${reset.id})">批准</button>
|
||||
<button class="btn btn-small btn-danger" onclick="rejectPasswordReset(${reset.id})">拒绝</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
`).join('')}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// 批准密码重置申请
|
||||
async function approvePasswordReset(requestId) {
|
||||
if (!confirm('确定批准该密码重置申请吗?')) return;
|
||||
|
||||
try {
|
||||
const response = await fetch(`/yuyx/api/password_resets/${requestId}/approve`, {
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
if (response.ok) {
|
||||
showNotification('密码重置申请已批准', 'success');
|
||||
loadPasswordResets();
|
||||
} else {
|
||||
showNotification('批准失败: ' + data.error, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
showNotification('批准失败: ' + error.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// 拒绝密码重置申请
|
||||
async function rejectPasswordReset(requestId) {
|
||||
if (!confirm('确定拒绝该密码重置申请吗?')) return;
|
||||
|
||||
try {
|
||||
const response = await fetch(`/yuyx/api/password_resets/${requestId}/reject`, {
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
if (response.ok) {
|
||||
showNotification('密码重置申请已拒绝', 'success');
|
||||
loadPasswordResets();
|
||||
} else {
|
||||
showNotification('拒绝失败: ' + data.error, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
showNotification('拒绝失败: ' + error.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// 管理员直接重置用户密码
|
||||
async function resetUserPassword(userId) {
|
||||
const newPassword = prompt('请输入新密码(至少6位):');
|
||||
|
||||
Reference in New Issue
Block a user