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:
@@ -16,7 +16,6 @@ import {
|
||||
|
||||
import { api } from '../api/client'
|
||||
import { fetchFeedbackStats } from '../api/feedbacks'
|
||||
import { fetchPasswordResets } from '../api/passwordResets'
|
||||
import { fetchSystemStats } from '../api/stats'
|
||||
|
||||
const route = useRoute()
|
||||
@@ -34,15 +33,11 @@ async function refreshStats() {
|
||||
}
|
||||
|
||||
const loadingBadges = ref(false)
|
||||
const pendingResetsCount = ref(0)
|
||||
const pendingFeedbackCount = ref(0)
|
||||
let badgeTimer
|
||||
|
||||
async function refreshNavBadges(partial = null) {
|
||||
if (partial && typeof partial === 'object') {
|
||||
if (Object.prototype.hasOwnProperty.call(partial, 'pendingResets')) {
|
||||
pendingResetsCount.value = Number(partial.pendingResets || 0)
|
||||
}
|
||||
if (Object.prototype.hasOwnProperty.call(partial, 'pendingFeedbacks')) {
|
||||
pendingFeedbackCount.value = Number(partial.pendingFeedbacks || 0)
|
||||
}
|
||||
@@ -53,18 +48,8 @@ async function refreshNavBadges(partial = null) {
|
||||
loadingBadges.value = true
|
||||
|
||||
try {
|
||||
const [resetsResult, feedbackResult] = await Promise.allSettled([
|
||||
fetchPasswordResets(),
|
||||
fetchFeedbackStats(),
|
||||
])
|
||||
|
||||
if (resetsResult.status === 'fulfilled') {
|
||||
pendingResetsCount.value = Array.isArray(resetsResult.value) ? resetsResult.value.length : 0
|
||||
}
|
||||
|
||||
if (feedbackResult.status === 'fulfilled') {
|
||||
pendingFeedbackCount.value = Number(feedbackResult.value?.pending || 0)
|
||||
}
|
||||
const feedbackResult = await fetchFeedbackStats()
|
||||
pendingFeedbackCount.value = Number(feedbackResult?.pending || 0)
|
||||
} finally {
|
||||
loadingBadges.value = false
|
||||
}
|
||||
@@ -100,7 +85,7 @@ onBeforeUnmount(() => {
|
||||
|
||||
const menuItems = [
|
||||
{ path: '/reports', label: '报表', icon: Document },
|
||||
{ path: '/users', label: '用户', icon: User, badgeKey: 'resets' },
|
||||
{ path: '/users', label: '用户', icon: User },
|
||||
{ path: '/feedbacks', label: '反馈', icon: ChatLineSquare, badgeKey: 'feedbacks' },
|
||||
{ path: '/logs', label: '任务日志', icon: List },
|
||||
{ path: '/announcements', label: '公告', icon: Bell },
|
||||
@@ -114,7 +99,6 @@ const activeMenu = computed(() => route.path)
|
||||
|
||||
function badgeFor(item) {
|
||||
if (!item?.badgeKey) return 0
|
||||
if (item.badgeKey === 'resets') return Number(pendingResetsCount.value || 0)
|
||||
if (item.badgeKey === 'feedbacks') {
|
||||
return Number(pendingFeedbackCount.value || 0)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user