From 1460246ab090089a6cad1b3b8cb980e6c6a64de1 Mon Sep 17 00:00:00 2001 From: npmrun <1549469775@qq.com> Date: Fri, 26 Jun 2026 21:30:43 +0800 Subject: [PATCH] feat: add email configuration management for admin - Introduced new API endpoints for managing SMTP email configuration (GET, PUT, POST). - Implemented email sending functionality using Nodemailer. - Added middleware to require admin permissions for accessing admin routes. - Updated admin sidebar navigation to conditionally display items based on admin role. - Created a new email configuration page in the admin panel with form validation and test functionality. - Masked sensitive information in API responses. - Enhanced scheduler execution and task management APIs to require admin permissions. - Added a new JSON file for desktop topic titles. --- .reasonix/desktop-topic-title-sources.json | 3 + .reasonix/desktop-topic-titles.json | 3 + app/components/admin/AdminSidebarNav.vue | 1 + app/middleware/auth.global.ts | 12 + app/pages/admin.vue | 12 +- app/pages/admin/dashboard.vue | 26 +- app/pages/admin/email/index.vue | 396 +++++++++++++++++++++ bun.lock | 6 + package.json | 2 + packages/drizzle-pkg/db.sqlite | Bin 282624 -> 282624 bytes server/api/admin/email/config.get.ts | 12 + server/api/admin/email/config.put.ts | 47 +++ server/api/admin/email/send.post.ts | 22 ++ server/api/admin/email/test.post.ts | 12 + server/api/config/global.get.ts | 2 +- server/api/config/global.put.ts | 2 +- server/api/scheduler/executions.get.ts | 2 + server/api/scheduler/executions/[id].delete.ts | 2 + server/api/scheduler/executions/delete-all.post.ts | 2 + server/api/scheduler/stats.get.ts | 2 + server/api/scheduler/tasks/[id].delete.ts | 2 + server/api/scheduler/tasks/[id].get.ts | 2 + server/api/scheduler/tasks/[id].put.ts | 2 + server/api/scheduler/tasks/[id]/toggle.post.ts | 2 + server/api/scheduler/tasks/[id]/trigger.post.ts | 2 + server/api/scheduler/tasks/index.get.ts | 2 + server/api/scheduler/tasks/index.post.ts | 2 + server/api/users/[id].delete.ts | 2 + server/api/users/[id].put.ts | 2 +- server/api/users/index.get.ts | 2 + server/api/users/index.post.ts | 2 + server/service/config/normalization.ts | 8 +- server/service/config/registry.ts | 47 +++ server/service/email/index.ts | 89 +++++ 34 files changed, 712 insertions(+), 20 deletions(-) create mode 100644 .reasonix/desktop-topic-title-sources.json create mode 100644 .reasonix/desktop-topic-titles.json create mode 100644 app/pages/admin/email/index.vue create mode 100644 server/api/admin/email/config.get.ts create mode 100644 server/api/admin/email/config.put.ts create mode 100644 server/api/admin/email/send.post.ts create mode 100644 server/api/admin/email/test.post.ts create mode 100644 server/service/email/index.ts diff --git a/.reasonix/desktop-topic-title-sources.json b/.reasonix/desktop-topic-title-sources.json new file mode 100644 index 0000000..f04e650 --- /dev/null +++ b/.reasonix/desktop-topic-title-sources.json @@ -0,0 +1,3 @@ +{ + "topic_20260626-113733_9874ba9ab889b7ed": "manual" +} \ No newline at end of file diff --git a/.reasonix/desktop-topic-titles.json b/.reasonix/desktop-topic-titles.json new file mode 100644 index 0000000..cd04107 --- /dev/null +++ b/.reasonix/desktop-topic-titles.json @@ -0,0 +1,3 @@ +{ + "topic_20260626-113733_9874ba9ab889b7ed": "新的会话" +} \ No newline at end of file diff --git a/app/components/admin/AdminSidebarNav.vue b/app/components/admin/AdminSidebarNav.vue index 6497bde..020a2a3 100644 --- a/app/components/admin/AdminSidebarNav.vue +++ b/app/components/admin/AdminSidebarNav.vue @@ -4,6 +4,7 @@ export interface NavItem { to?: string icon?: string children?: NavItem[] + requireAdmin?: boolean } const props = defineProps<{ diff --git a/app/middleware/auth.global.ts b/app/middleware/auth.global.ts index 4500ed5..99d4032 100644 --- a/app/middleware/auth.global.ts +++ b/app/middleware/auth.global.ts @@ -26,6 +26,18 @@ export default defineNuxtRouteMiddleware(async (to) => { const currentFullPath = to.fullPath; const isLoggedIn = loggedIn.value; + // 管理员路由守卫:除仪表板外 /admin/* 需要管理员权限 + const isAdminRoute = currentPath.startsWith("/admin"); + const isDashboardOnly = currentPath === "/admin" || currentPath === "/admin/dashboard"; + if (isLoggedIn && isAdminRoute && !isDashboardOnly) { + const { user } = useAuthSession(); + if (user.value?.role !== "admin") { + const { $toast } = useNuxtApp(); + $toast?.warning("需要管理员权限"); + return navigateTo("/"); + } + } + if (!isLoggedIn && !isPublicRoute(currentPath)) { return navigateTo({ path: FRONTEND_LOGIN_PATH, diff --git a/app/pages/admin.vue b/app/pages/admin.vue index 5a3936b..92d938f 100644 --- a/app/pages/admin.vue +++ b/app/pages/admin.vue @@ -15,17 +15,25 @@ const adminNav: NavItem[] = [ { label: '定时任务', to: '/admin/scheduler', - icon: 'lucide:clock' + icon: 'lucide:clock', + requireAdmin: true, }, { label: '系统管理', icon: 'lucide:settings', + requireAdmin: true, children: [ { label: '用户管理', to: '/admin/users' }, + { label: '邮件配置', to: '/admin/email' }, ] }, ] +const visibleNav = computed(() => { + const isAdmin = user.value?.role === 'admin' + return adminNav.filter(item => !item.requireAdmin || isAdmin) +}) + const logout = async () => { await clear() navigateTo("/") @@ -45,7 +53,7 @@ const logout = async () => { - +