From 195751d1e2139ea7ad72266ceeab70c178ed8516 Mon Sep 17 00:00:00 2001 From: gujiangjiang Date: Thu, 9 Oct 2025 04:24:39 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=E5=A4=9A=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E6=95=B0=E6=8D=AE=E9=9A=94=E7=A6=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/api/agents.ts | 3 +- backend/src/api/dashboard.ts | 3 +- backend/src/api/monitors.ts | 27 ++-- backend/src/api/status.ts | 8 +- backend/src/repositories/agent.ts | 4 +- backend/src/repositories/monitor.ts | 146 ++++++++---------- backend/src/repositories/status.ts | 11 ++ backend/src/services/AgentService.ts | 4 +- backend/src/services/DashboardService.ts | 10 +- backend/src/services/MonitorService.ts | 72 ++++----- backend/src/services/StatusService.ts | 51 +++--- frontend/src/api/status.ts | 4 +- frontend/src/pages/status/StatusPage.tsx | 25 +-- .../src/pages/status/StatusPageConfig.tsx | 17 +- frontend/src/router/index.tsx | 4 +- 15 files changed, 210 insertions(+), 179 deletions(-) diff --git a/backend/src/api/agents.ts b/backend/src/api/agents.ts index 3a8e056..a4a9dc9 100644 --- a/backend/src/api/agents.ts +++ b/backend/src/api/agents.ts @@ -20,7 +20,8 @@ const agents = new Hono<{ // 获取所有客户端 agents.get("/", async (c) => { - const result = await getAgents(); + const payload = c.get("jwtPayload"); + const result = await getAgents(payload.id); return c.json( { diff --git a/backend/src/api/dashboard.ts b/backend/src/api/dashboard.ts index dc12856..ae4d8ee 100644 --- a/backend/src/api/dashboard.ts +++ b/backend/src/api/dashboard.ts @@ -5,7 +5,8 @@ export const dashboard = new Hono<{ Bindings: Bindings }>(); // 获取仪表盘数据 dashboard.get("/", async (c) => { - const result = await getDashboardData(); + const payload = c.get("jwtPayload"); + const result = await getDashboardData(payload.id); return c.json( { monitors: result.monitors, diff --git a/backend/src/api/monitors.ts b/backend/src/api/monitors.ts index 75ab6a7..0b6826c 100644 --- a/backend/src/api/monitors.ts +++ b/backend/src/api/monitors.ts @@ -9,7 +9,7 @@ monitors.get("/", async (c) => { const payload = c.get("jwtPayload"); // 调用服务层获取监控列表 - const result = await MonitorService.getAllMonitors(); + const result = await MonitorService.getAllMonitors(payload.id); return c.json( { @@ -22,8 +22,9 @@ monitors.get("/", async (c) => { // 获取所有监控的每日统计数据 monitors.get("/daily", async (c) => { + const payload = c.get("jwtPayload"); // 调用服务层获取所有监控的每日统计数据 - const result = await MonitorService.getAllMonitorDailyStats(); + const result = await MonitorService.getAllMonitorDailyStats(payload.id); return c.json({ success: result.success, @@ -52,8 +53,9 @@ monitors.post("/", async (c) => { // 获取所有监控状态历史 monitors.get("/history", async (c) => { + const payload = c.get("jwtPayload"); // 调用服务层获取监控历史 - const result = await MonitorService.getAllMonitorStatusHistory(); + const result = await MonitorService.getAllMonitorStatusHistory(payload.id); return c.json( { @@ -70,7 +72,7 @@ monitors.get("/:id", async (c) => { const payload = c.get("jwtPayload"); // 调用服务层获取监控详情 - const result = await MonitorService.getMonitorById(id); + const result = await MonitorService.getMonitorById(id, payload.id, payload.role); return c.json( { @@ -86,9 +88,10 @@ monitors.get("/:id", async (c) => { monitors.put("/:id", async (c) => { const id = parseInt(c.req.param("id")); const data = await c.req.json(); + const payload = c.get("jwtPayload"); // 获取用户信息 - // 调用服务层更新监控 - const result = await MonitorService.updateMonitor(id, data); + // 调用服务层更新监控,并传入用户信息以进行权限验证 + const result = await MonitorService.updateMonitor(id, data, payload.id, payload.role); return c.json( { @@ -105,8 +108,8 @@ monitors.delete("/:id", async (c) => { const id = parseInt(c.req.param("id")); const payload = c.get("jwtPayload"); - // 调用服务层删除监控 - const result = await MonitorService.deleteMonitor(id); + // 调用服务层删除监控,并传入用户信息以进行权限验证 + const result = await MonitorService.deleteMonitor(id, payload.id, payload.role); return c.json( { @@ -142,9 +145,10 @@ monitors.get("/:id/history", async (c) => { // 获取单个监控的每日统计数据 monitors.get("/:id/daily", async (c) => { const id = parseInt(c.req.param("id")); + const payload = c.get("jwtPayload"); // 调用服务层获取每日统计数据 - const result = await MonitorService.getMonitorDailyStats(id); + const result = await MonitorService.getMonitorDailyStats(id, payload.id, payload.role); return c.json({ success: result.success, @@ -156,9 +160,10 @@ monitors.get("/:id/daily", async (c) => { // 手动检查单个监控 monitors.post("/:id/check", async (c) => { const id = parseInt(c.req.param("id")); + const payload = c.get("jwtPayload"); - // 调用服务层手动检查监控 - const result = await MonitorService.manualCheckMonitor(id); + // 调用服务层手动检查监控,并传入用户信息以进行权限验证 + const result = await MonitorService.manualCheckMonitor(id, payload.id, payload.role); return c.json( { diff --git a/backend/src/api/status.ts b/backend/src/api/status.ts index 02e738c..b0647ab 100644 --- a/backend/src/api/status.ts +++ b/backend/src/api/status.ts @@ -55,8 +55,12 @@ status.post("/config", async (c) => { } }); -status.get("/data", async (c) => { - const result = await getStatusPagePublicData(); +status.get("/public/:userId/data", async (c) => { + const userId = parseInt(c.req.param("userId")); + if (isNaN(userId)) { + return c.json({ error: "无效的用户ID" }, 400); + } + const result = await getStatusPagePublicData(userId); return c.json(result); }); diff --git a/backend/src/repositories/agent.ts b/backend/src/repositories/agent.ts index 5ac827f..a3192b0 100644 --- a/backend/src/repositories/agent.ts +++ b/backend/src/repositories/agent.ts @@ -7,8 +7,8 @@ import { desc, eq, inArray } from "drizzle-orm"; */ // 获取所有客户端 -export async function getAllAgents() { - return await db.select().from(agents).orderBy(desc(agents.created_at)); +export async function getAllAgents(userId: number) { + return await db.select().from(agents).where(eq(agents.created_by, userId)).orderBy(desc(agents.created_at)); } // 批量获取客户端详情 diff --git a/backend/src/repositories/monitor.ts b/backend/src/repositories/monitor.ts index 7c24977..efb8949 100644 --- a/backend/src/repositories/monitor.ts +++ b/backend/src/repositories/monitor.ts @@ -6,7 +6,7 @@ import { monitorStatusHistory24h, monitorDailyStats, } from "../db/schema"; -import { eq, desc, asc, lt } from "drizzle-orm"; +import { eq, desc, asc, and, inArray } from "drizzle-orm"; /** * 监控相关的数据库操作 @@ -44,34 +44,50 @@ export async function getMonitorsToCheck() { } // 获取单个监控详情 -export async function getMonitorById(id: number) { +export async function getMonitorById(id: number, userId: number, userRole: string) { const monitor = await db.select().from(monitors).where(eq(monitors.id, id)); + if (monitor.length === 0) { + return null; + } + + // 权限检查:管理员或所有者 + if (userRole !== 'admin' && monitor[0].created_by !== userId) { + return null; + } + // 解析 headers 字段 - if (monitor && typeof monitor.headers === "string") { + const monitorData = monitor[0]; + if (monitorData && typeof monitorData.headers === "string") { try { - monitor.headers = JSON.parse(monitor.headers); + // @ts-ignore + monitorData.headers = JSON.parse(monitorData.headers); } catch (e) { - monitor.headers = {}; + // @ts-ignore + monitorData.headers = {}; } } - return monitor[0]; + return monitorData; } // 获取所有监控 -export async function getAllMonitors() { +export async function getAllMonitors(userId: number) { const result = await db .select() .from(monitors) + .where(eq(monitors.created_by, userId)) .orderBy(desc(monitors.created_at)); // 解析所有监控的 headers 字段 if (result) { + // fix: 为 monitor 参数添加 Monitor 类型 result.forEach((monitor: Monitor) => { if (typeof monitor.headers === "string") { try { + // @ts-ignore monitor.headers = JSON.parse(monitor.headers); } catch (e) { + // @ts-ignore monitor.headers = {}; } } @@ -91,10 +107,17 @@ export async function getMonitorStatusHistoryIn24h(monitorId: number) { } // 获取所有监控状态历史 24小时内 -export async function getAllMonitorStatusHistoryIn24h() { +export async function getAllMonitorStatusHistoryIn24h(userId: number) { + const userMonitors = await getAllMonitors(userId); + // fix: 添加 Monitor 类型以解决 TS7006 + const monitorIds = userMonitors.map((m: Monitor) => m.id); + if (monitorIds.length === 0) return []; + return await db .select() .from(monitorStatusHistory24h) + // fix: 使用 inArray 查询多个监控项 + .where(inArray(monitorStatusHistory24h.monitor_id, monitorIds)) .orderBy(asc(monitorStatusHistory24h.timestamp)); } // 记录监控状态历史到热表 @@ -108,7 +131,7 @@ export async function insertMonitorStatusHistory( // 使用ISO格式的时间戳 const now = new Date().toISOString(); - return await db.insert(monitorStatusHistory24h).values({ + await db.insert(monitorStatusHistory24h).values({ monitor_id: monitorId, status: status, timestamp: now, @@ -127,7 +150,7 @@ export async function updateMonitorStatus( // 使用ISO格式的时间戳 const now = new Date().toISOString(); - return await db + await db .update(monitors) .set({ status: status, @@ -151,7 +174,7 @@ export async function createMonitor( ) { const now = new Date().toISOString(); - const newMonitor = await db + const [newMonitor] = await db .insert(monitors) .values({ name: name, @@ -178,8 +201,10 @@ export async function createMonitor( if (newMonitor && typeof newMonitor.headers === "string") { try { + // @ts-ignore newMonitor.headers = JSON.parse(newMonitor.headers); } catch (e) { + // @ts-ignore newMonitor.headers = {}; } } @@ -190,81 +215,35 @@ export async function createMonitor( // 更新监控配置 export async function updateMonitorConfig( id: number, + // fix: 修正 Monitor 类型的使用 updates: Partial ) { - // 准备更新字段 - const fields: string[] = []; - const values: any[] = []; + + // 准备更新数据对象 + const updateData: { [key: string]: any } = { + updated_at: new Date().toISOString(), + }; - if (updates.name !== undefined) { - fields.push("name = ?"); - values.push(updates.name); - } + if (updates.name !== undefined) updateData.name = updates.name; + if (updates.url !== undefined) updateData.url = updates.url; + if (updates.method !== undefined) updateData.method = updates.method; + if (updates.interval !== undefined) updateData.interval = updates.interval; + if (updates.timeout !== undefined) updateData.timeout = updates.timeout; + if (updates.expected_status !== undefined) updateData.expected_status = updates.expected_status; + if (updates.headers !== undefined) updateData.headers = JSON.stringify(updates.headers); + if (updates.body !== undefined) updateData.body = updates.body; + if (updates.active !== undefined) updateData.active = updates.active ? 1 : 0; - if (updates.url !== undefined) { - fields.push("url = ?"); - values.push(updates.url); - } - if (updates.method !== undefined) { - fields.push("method = ?"); - values.push(updates.method); - } - - if (updates.interval !== undefined) { - fields.push("interval = ?"); - values.push(updates.interval); - } - - if (updates.timeout !== undefined) { - fields.push("timeout = ?"); - values.push(updates.timeout); - } - - if (updates.expected_status !== undefined) { - fields.push("expected_status = ?"); - values.push(updates.expected_status); - } - - if (updates.headers !== undefined) { - fields.push("headers = ?"); - values.push(JSON.stringify(updates.headers)); - } - - if (updates.body !== undefined) { - fields.push("body = ?"); - values.push(updates.body); - } - - if (updates.active !== undefined) { - fields.push("active = ?"); - values.push(updates.active ? 1 : 0); - } - - // 添加更新时间 - fields.push("updated_at = ?"); - values.push(new Date().toISOString()); - - // 没有要更新的字段时返回 - if (fields.length === 0) { + // 如果没有要更新的字段,则提前返回 + if (Object.keys(updateData).length <= 1) { // 只有 updated_at return { message: "没有提供要更新的字段" }; } - // 添加 ID 作为 WHERE 条件 - values.push(id); - // 执行更新 - const updatedMonitor = await db + const [updatedMonitor] = await db .update(monitors) - .set({ - name: updates.name, - url: updates.url, - method: updates.method, - interval: updates.interval, - timeout: updates.timeout, - expected_status: updates.expected_status, - headers: JSON.stringify(updates.headers), - }) + .set(updateData) .where(eq(monitors.id, id)) .returning(); @@ -275,8 +254,10 @@ export async function updateMonitorConfig( // 解析 headers 字段 if (updatedMonitor && typeof updatedMonitor.headers === "string") { try { + // @ts-ignore updatedMonitor.headers = JSON.parse(updatedMonitor.headers); } catch (e) { + // @ts-ignore updatedMonitor.headers = {}; } } @@ -287,7 +268,6 @@ export async function updateMonitorConfig( // 删除监控 export async function deleteMonitor(id: number) { // 先删除关联的历史数据 - await db .delete(monitorStatusHistory24h) .where(eq(monitorStatusHistory24h.monitor_id, id)); @@ -298,7 +278,7 @@ export async function deleteMonitor(id: number) { .where(eq(monitorDailyStats.monitor_id, id)); // 执行删除监控 - return await db.delete(monitors).where(eq(monitors.id, id)); + await db.delete(monitors).where(eq(monitors.id, id)); } export async function getMonitorDailyStatsById(id: number) { @@ -310,9 +290,17 @@ export async function getMonitorDailyStatsById(id: number) { .orderBy(asc(monitorDailyStats.date)); } -export async function getAllMonitorDailyStats() { +export async function getAllMonitorDailyStats(userId: number) { + const userMonitors = await getAllMonitors(userId); + // fix: 添加 Monitor 类型以解决 TS7006 + const monitorIds = userMonitors.map((m: Monitor) => m.id); + if (monitorIds.length === 0) return []; + return await db .select() .from(monitorDailyStats) + // fix: 使用 inArray 查询多个监控项 + .where(inArray(monitorDailyStats.monitor_id, monitorIds)) .orderBy(asc(monitorDailyStats.date)); } + diff --git a/backend/src/repositories/status.ts b/backend/src/repositories/status.ts index 4e23b61..77b7ca6 100644 --- a/backend/src/repositories/status.ts +++ b/backend/src/repositories/status.ts @@ -16,6 +16,17 @@ export async function getAllStatusPageConfigs() { return await db.select().from(statusPageConfig); } +// 新增:根据用户ID获取状态页配置 +export async function getStatusPageConfigByUserId(userId: number) { + const config = await db + .select() + .from(statusPageConfig) + .where(eq(statusPageConfig.user_id, userId)) + .limit(1); + return config[0]; +} + + // 获取配置的监控项 export async function getConfigMonitors( configId: number, diff --git a/backend/src/services/AgentService.ts b/backend/src/services/AgentService.ts index d57c103..a8afdaa 100644 --- a/backend/src/services/AgentService.ts +++ b/backend/src/services/AgentService.ts @@ -8,9 +8,9 @@ import { handleAgentThresholdNotification } from "../jobs/agent-task"; * @param db 数据库连接 * @returns 客户端列表 */ -export async function getAgents() { +export async function getAgents(userId: number) { try { - const result = await AgentRepository.getAllAgents(); + const result = await AgentRepository.getAllAgents(userId); return { success: true, agents: result || [], diff --git a/backend/src/services/DashboardService.ts b/backend/src/services/DashboardService.ts index 01994c8..0dc9b18 100644 --- a/backend/src/services/DashboardService.ts +++ b/backend/src/services/DashboardService.ts @@ -1,12 +1,12 @@ import { Monitor } from "../models"; import * as repositories from "../repositories"; -export async function getDashboardData() { - const monitors = await repositories.getAllMonitors(); - const agents = await repositories.getAllAgents(); +export async function getDashboardData(userId: number) { + const monitors = await repositories.getAllMonitors(userId); + const agents = await repositories.getAllAgents(userId); - if (monitors.monitors && monitors.monitors.length > 0) { - monitors.monitors.forEach((monitor: Monitor) => { + if (monitors && monitors.length > 0) { + monitors.forEach((monitor: Monitor) => { if (typeof monitor.headers === "string") { try { monitor.headers = JSON.parse(monitor.headers); diff --git a/backend/src/services/MonitorService.ts b/backend/src/services/MonitorService.ts index 66ef14c..5ea9717 100644 --- a/backend/src/services/MonitorService.ts +++ b/backend/src/services/MonitorService.ts @@ -126,8 +126,8 @@ export async function checkMonitor(monitor: models.Monitor) { } } -export async function getAllMonitors() { - const result = await repositories.getAllMonitors(); +export async function getAllMonitors(userId: number) { + const result = await repositories.getAllMonitors(userId); return { success: true, @@ -136,11 +136,11 @@ export async function getAllMonitors() { }; } -export async function getMonitorById(id: number) { - const monitor = await repositories.getMonitorById(id); +export async function getMonitorById(id: number, userId: number, userRole: string) { + const monitor = await repositories.getMonitorById(id, userId, userRole); if (!monitor) { - return { success: false, message: "监控不存在", status: 404 }; + return { success: false, message: "监控不存在或无权访问", status: 404 }; } // 获取历史状态数据 @@ -197,13 +197,13 @@ export async function createMonitor(data: any, userId: number) { } } -export async function updateMonitor(id: number, data: any) { +export async function updateMonitor(id: number, data: any, userId: number, userRole: string) { try { - // 检查监控是否存在 - const monitor = await repositories.getMonitorById(id); + // 检查监控是否存在并验证权限 + const monitor = await repositories.getMonitorById(id, userId, userRole); if (!monitor) { - return { success: false, message: "监控不存在", status: 404 }; + return { success: false, message: "监控不存在或无权访问", status: 404 }; } // 准备更新数据 @@ -256,13 +256,13 @@ export async function updateMonitor(id: number, data: any) { } } -export async function deleteMonitor(id: number) { +export async function deleteMonitor(id: number, userId: number, userRole: string) { try { - // 检查监控是否存在 - const monitor = await repositories.getMonitorById(id); + // 检查监控是否存在并验证权限 + const monitor = await repositories.getMonitorById(id, userId, userRole); if (!monitor) { - return { success: false, message: "监控不存在", status: 404 }; + return { success: false, message: "监控不存在或无权访问", status: 404 }; } // 执行通知设置删除 @@ -276,11 +276,7 @@ export async function deleteMonitor(id: number) { } // 执行monitor删除 - const result = await repositories.deleteMonitor(id); - - if (!result.success) { - throw new Error("删除监控失败"); - } + await repositories.deleteMonitor(id); return { success: true, @@ -304,16 +300,11 @@ export async function getMonitorStatusHistoryById( userRole: string ) { try { - // 检查监控是否存在 - const monitor = await repositories.getMonitorById(id); + // 检查监控是否存在并验证权限 + const monitor = await repositories.getMonitorById(id, userId, userRole); if (!monitor) { - return { success: false, message: "监控不存在", status: 404 }; - } - - // 检查权限 - if (userRole !== "admin" && monitor.created_by !== userId) { - return { success: false, message: "无权访问此监控历史", status: 403 }; + return { success: false, message: "监控不存在或无权访问", status: 404 }; } // 获取历史状态 @@ -335,8 +326,8 @@ export async function getMonitorStatusHistoryById( } } -export async function getAllMonitorStatusHistory() { - const result = await repositories.getAllMonitorStatusHistoryIn24h(); +export async function getAllMonitorStatusHistory(userId: number) { + const result = await repositories.getAllMonitorStatusHistoryIn24h(userId); return { success: true, history: result, @@ -344,13 +335,13 @@ export async function getAllMonitorStatusHistory() { }; } -export async function manualCheckMonitor(id: number) { +export async function manualCheckMonitor(id: number, userId: number, userRole: string) { try { - // 检查监控是否存在 - const monitor = await repositories.getMonitorById(id); + // 检查监控是否存在并验证权限 + const monitor = await repositories.getMonitorById(id, userId, userRole); if (!monitor) { - return { success: false, message: "监控不存在", status: 404 }; + return { success: false, message: "监控不存在或无权访问", status: 404 }; } // 使用抽象出来的通用检查监控函数进行检查 @@ -469,7 +460,18 @@ function getExpectedStatusDisplay(expectedStatus: number): string { return String(expectedStatus); } -export async function getMonitorDailyStats(id: number) { +export async function getMonitorDailyStats(id: number, userId: number, userRole: string) { + // 权限检查 + const monitor = await repositories.getMonitorById(id, userId, userRole); + if (!monitor) { + return { + success: false, + message: "监控不存在或无权访问", + status: 404, + dailyStats: [] + }; + } + const result = await repositories.getMonitorDailyStatsById(id); return { @@ -480,9 +482,9 @@ export async function getMonitorDailyStats(id: number) { }; } -export async function getAllMonitorDailyStats() { +export async function getAllMonitorDailyStats(userId: number) { // 获取所有监控 - const result = await repositories.getAllMonitorDailyStats(); + const result = await repositories.getAllMonitorDailyStats(userId); return { success: true, dailyStats: result, diff --git a/backend/src/services/StatusService.ts b/backend/src/services/StatusService.ts index a69135b..9781aba 100644 --- a/backend/src/services/StatusService.ts +++ b/backend/src/services/StatusService.ts @@ -3,22 +3,26 @@ import { Agent } from "../models"; /** * 获取状态页配置 - * @param userId - 当前用户的ID(修复后此参数被忽略,以获取全局配置) + * @param userId - 当前用户的ID * @returns 状态页配置对象 */ export async function getStatusPageConfig(userId: number) { try { - // 修复:状态页配置是全局的,不应按用户ID查找。获取所有配置并使用第一个。 - const allConfigs = await repositories.getAllStatusPageConfigs(); + // 获取指定用户的状态页配置 + const existingConfig = await repositories.getStatusPageConfigByUserId(userId); - if (!allConfigs || allConfigs.length === 0) { - // 备注:如果系统中没有任何配置,将抛出错误。首次使用时,管理员需要先保存一次配置。 - throw new Error("状态页配置不存在"); + if (!existingConfig) { + // 如果用户没有配置,可以返回一个默认的空配置 + return { + title: "系统状态", + description: "实时监控系统运行状态", + logoUrl: "", + customCss: "", + monitors: [], + agents: [], + }; } - // 将第一个配置视为全局配置 - const existingConfig = allConfigs[0]; - console.log("使用的状态页配置:", existingConfig); // 获取被选中的监控项 @@ -26,16 +30,16 @@ export async function getStatusPageConfig(userId: number) { existingConfig.id ); - // 获取所有监控项 - const allMonitors = await repositories.getAllMonitors(); + // 获取该用户的所有监控项 + const allMonitors = await repositories.getAllMonitors(userId); // 获取被选中的客户端 const agentsResult = await repositories.getConfigAgents( existingConfig.id ); - // 获取所有客户端 - const allAgents = await repositories.getAllAgents(); + // 获取该用户的所有客户端 + const allAgents = await repositories.getAllAgents(userId); // 构建返回的监控列表,标记哪些监控项被选中 const monitors = allMonitors.map((monitor: any) => { @@ -67,6 +71,7 @@ export async function getStatusPageConfig(userId: number) { } } + /** * 保存状态页配置 * @param userId - 当前操作用户的ID @@ -85,9 +90,7 @@ export async function saveStatusPageConfig( } ) { try { - // 修复:同样,保存时也应操作全局的第一个配置 - const allConfigs = await repositories.getAllStatusPageConfigs(); - const existingConfig = (allConfigs && allConfigs.length > 0) ? allConfigs[0] : null; + const existingConfig = await repositories.getStatusPageConfigByUserId(userId); let configId: number; @@ -143,14 +146,15 @@ export async function saveStatusPageConfig( /** * 获取公共状态页所需的数据 + * @param userId - 用户ID * @returns 公共状态页数据 */ -export async function getStatusPagePublicData() { - // 获取所有配置 - const configsResult = await repositories.getAllStatusPageConfigs(); +export async function getStatusPagePublicData(userId: number) { + // 获取用户的配置 + const config = await repositories.getStatusPageConfigByUserId(userId); - if (!configsResult || configsResult.length === 0) { - console.log("没有找到任何状态页配置"); + if (!config) { + console.log(`用户 ${userId} 没有找到状态页配置`); return { title: "系统状态", description: "当前没有可用的状态页配置。", @@ -161,9 +165,6 @@ export async function getStatusPagePublicData() { }; } - // 使用第一个配置作为全局配置 - const config = configsResult[0]; - // 获取选中的监控项ID const selectedMonitors = await repositories.getSelectedMonitors( config.id as number @@ -180,7 +181,7 @@ export async function getStatusPagePublicData() { const monitorIds = selectedMonitors.map((m: any) => m.monitor_id); if (monitorIds.length > 0) { for (const monitorId of monitorIds) { - const monitor = await repositories.getMonitorById(monitorId); + const monitor = await repositories.getMonitorById(monitorId, userId, "user"); // use "user" role to avoid admin check if (monitor) { // 确保监控项存在 const monitorDailyStats = await repositories.getMonitorDailyStatsById( monitorId diff --git a/frontend/src/api/status.ts b/frontend/src/api/status.ts index f052119..d969e98 100644 --- a/frontend/src/api/status.ts +++ b/frontend/src/api/status.ts @@ -26,7 +26,7 @@ export const saveStatusPageConfig = async ( }; // 获取状态页数据 -export const getStatusPageData = async (): Promise => { - const response = await api.get("/api/status/data"); +export const getStatusPageData = async (userId: number): Promise => { + const response = await api.get(`/api/status/public/${userId}/data`); return response.data; }; diff --git a/frontend/src/pages/status/StatusPage.tsx b/frontend/src/pages/status/StatusPage.tsx index d17c23d..9296dc8 100644 --- a/frontend/src/pages/status/StatusPage.tsx +++ b/frontend/src/pages/status/StatusPage.tsx @@ -12,9 +12,11 @@ import { AgentWithLatestMetrics, } from "../../types"; import { getLatestAgentMetrics, getAgentMetrics } from "../../api/agents"; +import { useParams } from "react-router-dom"; const StatusPage = () => { const { t } = useTranslation(); + const { userId } = useParams<{ userId: string }>(); const [data, setData] = useState<{ monitors: MonitorWithDailyStatsAndStatusHistory[]; agents: AgentWithLatestMetrics[]; @@ -37,18 +39,21 @@ const StatusPage = () => { // 从API获取数据 useEffect(() => { - fetchData(); - // 设置定时刷新,每3分钟更新数据 - const intervalId = setInterval(() => { + if (userId) { fetchData(); - }, 180000); - return () => clearInterval(intervalId); - }, []); + // 设置定时刷新,每3分钟更新数据 + const intervalId = setInterval(() => { + fetchData(); + }, 180000); + return () => clearInterval(intervalId); + } + }, [userId]); // 获取数据 const fetchData = async () => { + if (!userId) return; setLoading(true); - const response = await getStatusPageData(); + const response = await getStatusPageData(parseInt(userId, 10)); if (response) { setPageTitle(response.title || t("statusPage.title")); setPageDescription( @@ -143,9 +148,9 @@ const StatusPage = () => { {pageTitle} - {pageDescription} diff --git a/frontend/src/pages/status/StatusPageConfig.tsx b/frontend/src/pages/status/StatusPageConfig.tsx index 151bc11..cb804b1 100644 --- a/frontend/src/pages/status/StatusPageConfig.tsx +++ b/frontend/src/pages/status/StatusPageConfig.tsx @@ -22,6 +22,7 @@ import { toast } from "sonner"; import { getStatusPageConfig, saveStatusPageConfig } from "../../api/status"; import { StatusPageConfig as StatusConfig } from "../../types/status"; import { useTranslation } from "react-i18next"; +import { useAuth } from "../../providers/AuthProvider"; // 监控项带选择状态 interface MonitorWithSelection { @@ -49,6 +50,7 @@ interface StatusConfigWithDetails { } const StatusPageConfig = () => { + const { user } = useAuth(); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [copied, setCopied] = useState(false); @@ -60,10 +62,19 @@ const StatusPageConfig = () => { description: t("statusPage.allOperational"), logoUrl: "", customCss: "", - publicUrl: window.location.origin + "/status", + publicUrl: "", monitors: [], agents: [], }); + + useEffect(() => { + if (user) { + setConfig(prev => ({ + ...prev, + publicUrl: `${window.location.origin}/status/public/${user.id}` + })); + } + }, [user]); // 从API获取数据 useEffect(() => { @@ -170,7 +181,9 @@ const StatusPageConfig = () => { // 预览状态页 const handlePreview = () => { // 在新标签页中打开状态页 - window.open("/status", "_blank"); + if (user) { + window.open(`/status/public/${user.id}`, "_blank"); + } }; if (loading) { diff --git a/frontend/src/router/index.tsx b/frontend/src/router/index.tsx index 097ef94..923339c 100644 --- a/frontend/src/router/index.tsx +++ b/frontend/src/router/index.tsx @@ -46,7 +46,7 @@ interface LayoutWrapperProps { } const LayoutWrapper: React.FC = ({ children }) => { // 检查当前路径是否为状态页面,如果是则不使用Layout包裹 - const isStatusPage = window.location.pathname === "/status"; + const isStatusPage = window.location.pathname.startsWith("/status/public"); return isStatusPage ? <>{children} : {children}; }; @@ -103,7 +103,7 @@ const protectedRoutes: RouteObject[] = [ path: "/status", children: [ { - path: "", + path: "public/:userId", element: ( 加载中...}>