From cd479cadccdb4ffecdcdb597e7656d7c35d1e9e9 Mon Sep 17 00:00:00 2001 From: zaunist Date: Sun, 27 Apr 2025 22:14:38 +0800 Subject: [PATCH] perf: reduce dashboard api call --- backend/src/api/dashboard.ts | 16 +++ backend/src/api/index.ts | 2 +- backend/src/api/migrate.ts | 23 ---- backend/src/api/notifications.ts | 2 +- backend/src/index.ts | 3 +- backend/src/models/db.ts | 12 ++ backend/src/models/index.ts | 2 +- backend/src/models/status.ts | 9 ++ backend/src/repositories/monitor.ts | 128 +++++++++----------- backend/src/repositories/notification.ts | 12 +- backend/src/repositories/status.ts | 56 +-------- backend/src/services/DashboardService.ts | 23 ++++ backend/src/services/NotificationService.ts | 3 +- frontend/src/pages/Dashboard.tsx | 51 ++------ frontend/src/services/api/dashboard.ts | 11 ++ frontend/src/types/auth.ts | 11 +- frontend/src/types/index.ts | 9 ++ 17 files changed, 160 insertions(+), 213 deletions(-) create mode 100644 backend/src/api/dashboard.ts delete mode 100644 backend/src/api/migrate.ts create mode 100644 backend/src/models/status.ts create mode 100644 backend/src/services/DashboardService.ts create mode 100644 frontend/src/services/api/dashboard.ts create mode 100644 frontend/src/types/index.ts diff --git a/backend/src/api/dashboard.ts b/backend/src/api/dashboard.ts new file mode 100644 index 0000000..02ba227 --- /dev/null +++ b/backend/src/api/dashboard.ts @@ -0,0 +1,16 @@ +import { Hono } from "hono"; +import { Bindings } from "../models/db"; +import { getDashboardData } from "../services/DashboardService"; +export const dashboard = new Hono<{ Bindings: Bindings }>(); + +// 获取仪表盘数据 +dashboard.get("/", async (c) => { + const result = await getDashboardData(c.env.DB); + return c.json( + { + monitors: result.monitors, + agents: result.agents, + }, + 200 + ); +}); diff --git a/backend/src/api/index.ts b/backend/src/api/index.ts index 4e429d7..d425145 100644 --- a/backend/src/api/index.ts +++ b/backend/src/api/index.ts @@ -4,4 +4,4 @@ export * from "./agents"; export * from "./users"; export * from "./status"; export * from "./notifications"; -export * from "./migrate"; +export * from "./dashboard"; diff --git a/backend/src/api/migrate.ts b/backend/src/api/migrate.ts deleted file mode 100644 index 0680e28..0000000 --- a/backend/src/api/migrate.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Hono } from "hono"; -import { Bindings } from "../models/db"; -import { runMigrations } from "../migrations/migration"; -export const migrate = new Hono<{ Bindings: Bindings }>(); - -// 获取所有监控 -migrate.get("/", async (c) => { - try { - // 调用服务层获取监控列表 - const result = await runMigrations(c.env); - return c.json( - { - success: true, - message: "迁移成功", - result: result, - }, - 200 - ); - } catch (error) { - console.error("迁移失败:", error); - return c.json({ success: false, message: "迁移失败" }, 500); - } -}); diff --git a/backend/src/api/notifications.ts b/backend/src/api/notifications.ts index 9e04f70..1271e87 100644 --- a/backend/src/api/notifications.ts +++ b/backend/src/api/notifications.ts @@ -11,7 +11,7 @@ notifications.get("/", async (c) => { const db = c.env.DB; const userId = c.get("jwtPayload").id; - const config = await NotificationService.getNotificationConfig(db, userId); + const config = await NotificationService.getNotificationConfig(db); return c.json({ success: true, diff --git a/backend/src/index.ts b/backend/src/index.ts index be431d8..6fc0947 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -32,8 +32,7 @@ app.route("/api/agents", api.agents); app.route("/api/users", api.users); app.route("/api/status", api.status); app.route("/api/notifications", api.notifications); -app.route("/api/migrate", api.migrate); - +app.route("/api/dashboard", api.dashboard); // 数据库状态标志,用于记录数据库初始化状态 let dbInitialized = false; diff --git a/backend/src/models/db.ts b/backend/src/models/db.ts index d3bb952..43967f8 100644 --- a/backend/src/models/db.ts +++ b/backend/src/models/db.ts @@ -14,6 +14,18 @@ export interface D1PreparedStatement { raw(): Promise; } +// 定义操作结果的元数据类型 +export interface DbResultMeta { + changes?: number; + [key: string]: any; +} + +// 扩展D1Result meta属性的类型 +export interface D1Meta { + last_row_id?: number; + changes?: number; +} + export interface D1Result { results?: T[]; success: boolean; diff --git a/backend/src/models/index.ts b/backend/src/models/index.ts index 3fdddca..6bac239 100644 --- a/backend/src/models/index.ts +++ b/backend/src/models/index.ts @@ -3,4 +3,4 @@ export * from './db'; export * from './monitor'; export * from './notification'; export * from './user'; - +export * from './status'; \ No newline at end of file diff --git a/backend/src/models/status.ts b/backend/src/models/status.ts new file mode 100644 index 0000000..91138c6 --- /dev/null +++ b/backend/src/models/status.ts @@ -0,0 +1,9 @@ +// 状态页配置接口定义 +export interface StatusPageConfig { + id?: number; + user_id: number; + title: string; + description: string; + logo_url: string; + custom_css: string; +} diff --git a/backend/src/repositories/monitor.ts b/backend/src/repositories/monitor.ts index eb97224..060fdbf 100644 --- a/backend/src/repositories/monitor.ts +++ b/backend/src/repositories/monitor.ts @@ -1,11 +1,9 @@ -import { Bindings } from "../models/db"; -import { Monitor, MonitorStatusHistory } from "../models/monitor"; - -// 定义操作结果的元数据类型 -interface DbResultMeta { - changes?: number; - [key: string]: any; -} +import { + Bindings, + Monitor, + MonitorStatusHistory, + DbResultMeta, +} from "../models"; /** * 监控相关的数据库操作 @@ -393,83 +391,69 @@ export async function deleteMonitor(db: Bindings["DB"], id: number) { } /** - * 获取所有监控(根据用户角色过滤) + * 获取所有监控 * @param db 数据库连接 - * @param userId 用户ID - * @param userRole 用户角色 * @returns 监控列表和操作结果 */ -export async function getAllMonitors( - db: Bindings["DB"], - userId: number, - userRole: string -) { - try { - // 根据用户角色过滤监控 - let result; - if (userRole === "admin") { - result = await db - .prepare("SELECT * FROM monitors ORDER BY created_at DESC") - .all(); - } else { - result = await db - .prepare( - "SELECT * FROM monitors WHERE created_by = ? ORDER BY created_at DESC" - ) - .bind(userId) - .all(); - } +export async function getAllMonitors(db: Bindings["DB"]) { + // 根据用户角色过滤监控 + let result; + result = await db + .prepare("SELECT * FROM monitors ORDER BY created_at DESC") + .all(); - // 解析所有监控的 headers 字段 - if (result.results && result.results.length > 0) { - result.results.forEach((monitor) => { - if (typeof monitor.headers === "string") { - try { - monitor.headers = JSON.parse(monitor.headers); - } catch (e) { - monitor.headers = {}; - } + // 解析所有监控的 headers 字段 + if (result.results && result.results.length > 0) { + result.results.forEach((monitor) => { + if (typeof monitor.headers === "string") { + try { + monitor.headers = JSON.parse(monitor.headers); + } catch (e) { + monitor.headers = {}; } - }); + } + }); - // 获取所有监控的历史状态数据 - const monitorsWithHistory = await Promise.all( - result.results.map(async (monitor) => { - const historyResult = await db - .prepare( - `SELECT * FROM monitor_status_history + // 获取所有监控的历史状态数据 + const monitorsWithHistory = await Promise.all( + result.results.map(async (monitor) => { + const historyResult = await db + .prepare( + `SELECT * FROM monitor_status_history WHERE monitor_id = ? ORDER BY timestamp ASC` - ) - .bind(monitor.id) - .all(); + ) + .bind(monitor.id) + .all(); - return { - ...monitor, - history: historyResult.results || [], - }; - }) - ); - - return { - success: true, - monitors: monitorsWithHistory, - status: 200, - }; - } + return { + ...monitor, + history: historyResult.results || [], + }; + }) + ); return { success: true, - monitors: result.results || [], + monitors: monitorsWithHistory, status: 200, }; - } catch (error) { - console.error("获取监控列表错误:", error); - return { - success: false, - message: "获取监控列表失败", - error: error instanceof Error ? error.message : String(error), - status: 500, - }; } + + return { + success: true, + monitors: result.results || [], + status: 200, + }; +} + +/** + * 获取所有监控(不包含历史状态数据) + * @param db 数据库连接 + * @returns 监控列表和操作结果 + */ +export async function getAllMonitorsWithoutHistory(db: Bindings["DB"]) { + return await db + .prepare("SELECT * FROM monitors ORDER BY created_at DESC") + .all(); } diff --git a/backend/src/repositories/notification.ts b/backend/src/repositories/notification.ts index de86c42..dc89ad3 100644 --- a/backend/src/repositories/notification.ts +++ b/backend/src/repositories/notification.ts @@ -1,17 +1,12 @@ -import { D1Database } from "../models/db"; import { NotificationChannel, NotificationTemplate, NotificationSettings, NotificationHistory, NotificationConfig, -} from "../models/notification"; - -// 扩展D1Result meta属性的类型 -interface D1Meta { - last_row_id?: number; - changes?: number; -} + D1Meta, + D1Database, +} from "../models"; // 获取所有通知渠道 export const getNotificationChannels = async ( @@ -485,7 +480,6 @@ export const getNotificationHistory = async ( // 获取完整的通知配置(用于前端) export const getNotificationConfig = async ( db: D1Database, - userId: number ): Promise => { // 获取所有渠道 const channels = await getNotificationChannels(db); diff --git a/backend/src/repositories/status.ts b/backend/src/repositories/status.ts index 046be6b..9f61fcb 100644 --- a/backend/src/repositories/status.ts +++ b/backend/src/repositories/status.ts @@ -1,58 +1,4 @@ -import { MonitorStatusHistory, Bindings } from "../models"; - -// 状态页配置接口定义 -export interface StatusPageConfig { - id?: number; - user_id: number; - title: string; - description: string; - logo_url: string; - custom_css: string; -} - -// 监控项接口 -export interface Monitor { - id: number; - name: string; - url: string; - method: string; - interval: number; - timeout: number; - expected_status: number; - headers: string; - body: string; - created_by: number; - active: boolean; - status: string; - uptime: number; - response_time: number; - last_checked?: string; - created_at: string; - updated_at: string; - history?: MonitorStatusHistory[]; -} - -// 客户端接口 -export interface Agent { - id: number; - name: string; - token: string; - created_by: number; - created_at: string; - updated_at: string; - status?: string; - cpu_usage?: number; - memory_total?: number; - memory_used?: number; - disk_total?: number; - disk_used?: number; - network_rx?: number; - network_tx?: number; - hostname?: string; - ip_addresses?: string; - os?: string; - version?: string; -} +import { StatusPageConfig, Agent, Bindings } from "../models"; /** * 状态页相关的数据库操作 diff --git a/backend/src/services/DashboardService.ts b/backend/src/services/DashboardService.ts new file mode 100644 index 0000000..55ac12b --- /dev/null +++ b/backend/src/services/DashboardService.ts @@ -0,0 +1,23 @@ +import { Bindings } from "../models"; +import * as repositories from "../repositories"; + +export async function getDashboardData(db: Bindings["DB"]) { + const monitors = await repositories.getAllMonitorsWithoutHistory(db); + const agents = await repositories.getAllAgents(db); + + if (monitors.results && monitors.results.length > 0) { + monitors.results.forEach((monitor) => { + if (typeof monitor.headers === "string") { + try { + monitor.headers = JSON.parse(monitor.headers); + } catch (e) { + monitor.headers = {}; + } + } + }); + } + return { + monitors: monitors.results, + agents: agents.results, + }; +} diff --git a/backend/src/services/NotificationService.ts b/backend/src/services/NotificationService.ts index 8109453..1ba4d6f 100644 --- a/backend/src/services/NotificationService.ts +++ b/backend/src/services/NotificationService.ts @@ -166,9 +166,8 @@ export async function deleteNotificationTemplate( // 通知设置相关服务,获取所有的通知设置 export async function getNotificationConfig( db: D1Database, - userId: number ): Promise { - return await repositories.getNotificationConfig(db, userId); + return await repositories.getNotificationConfig(db); } export async function createOrUpdateSettings( diff --git a/frontend/src/pages/Dashboard.tsx b/frontend/src/pages/Dashboard.tsx index f6bb54d..8b129e2 100644 --- a/frontend/src/pages/Dashboard.tsx +++ b/frontend/src/pages/Dashboard.tsx @@ -7,10 +7,8 @@ import { GlobeIcon, ExclamationTriangleIcon, } from "@radix-ui/react-icons"; -import { getAllMonitors } from "../services/api/monitors"; -import { Monitor } from "../types/monitors"; -import { getAllAgents } from "../services/api/agents"; -import { Agent } from "../types/agents"; +import { Monitor, Agent } from "../types"; +import { getDashboardData } from "../services/api/dashboard"; import StatusSummaryCard from "../components/StatusSummaryCard"; import "../styles/components.css"; import { useTranslation } from "react-i18next"; @@ -24,50 +22,27 @@ const Dashboard = () => { // 获取所有数据 useEffect(() => { - const fetchData = async () => { - try { - setLoading(true); - - // 同时获取监控和客户端数据 - const [monitorsResponse, agentsResponse] = await Promise.all([ - getAllMonitors(), - getAllAgents(), - ]); - - // 处理监控数据 - if (monitorsResponse.success && monitorsResponse.monitors) { - setMonitors(monitorsResponse.monitors); - } else { - console.error("获取监控数据失败:", monitorsResponse.message); - } - - // 处理客户端数据 - if (agentsResponse.success && agentsResponse.agents) { - console.log("获取到客户端列表:", agentsResponse.agents); - setAgents(agentsResponse.agents); - } else { - console.error("获取客户端数据失败:", agentsResponse.message); - } - } catch (err) { - console.error("获取数据错误:", err); - setError(t("common.error")); - } finally { - setLoading(false); - } - }; - fetchData(); - // 设置定时器,每分钟刷新一次数据 const intervalId = setInterval(() => { console.log("Dashboard: 自动刷新数据..."); fetchData(); - }, 60000); // 60000ms = 1分钟 + }, 180000); // 180000ms = 3分钟 // 组件卸载时清除定时器 return () => clearInterval(intervalId); }, [t]); + const fetchData = async () => { + setLoading(true); + const [dashboardResponse] = await Promise.all([getDashboardData()]); + if (dashboardResponse) { + setMonitors(dashboardResponse.monitors); + setAgents(dashboardResponse.agents); + } + setLoading(false); + }; + // 加载中显示 if (loading) { return ( diff --git a/frontend/src/services/api/dashboard.ts b/frontend/src/services/api/dashboard.ts new file mode 100644 index 0000000..a2c7d67 --- /dev/null +++ b/frontend/src/services/api/dashboard.ts @@ -0,0 +1,11 @@ +import api from "./index"; +import { Monitor, Agent } from "../../types"; + +// 获取仪表盘数据 +export const getDashboardData = async (): Promise<{ + monitors: Monitor[]; + agents: Agent[]; +}> => { + const response = await api.get("/api/dashboard"); + return response.data; +}; diff --git a/frontend/src/types/auth.ts b/frontend/src/types/auth.ts index 413e32d..379625a 100644 --- a/frontend/src/types/auth.ts +++ b/frontend/src/types/auth.ts @@ -1,16 +1,9 @@ +import { User } from "./users"; + /** * 认证相关类型定义 */ -export interface User { - id: number; - username: string; - email?: string; - role?: string; - created_at?: string; - updated_at?: string; -} - export interface LoginRequest { username: string; password: string; diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts new file mode 100644 index 0000000..7d34286 --- /dev/null +++ b/frontend/src/types/index.ts @@ -0,0 +1,9 @@ +export * from "./monitors"; +export * from "./agents"; +export * from "./auth"; +export * from "./users"; +export * from "./status"; +export * from "./components"; +export * from "./language"; +export * from "./notification"; +export * from "./routes";