feat: 实现多用户数据隔离
This commit is contained in:
1 parent
c8823eee74
commit
195751d1e2
15 files changed
+210
-179
No files matched your search
@@ -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(
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
|
||||
+16
-11
@@ -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(
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
// 批量获取客户端详情
|
||||
|
||||
@@ -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<Monitor>
|
||||
) {
|
||||
// 准备更新字段
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 || [],
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -26,7 +26,7 @@ export const saveStatusPageConfig = async (
|
||||
};
|
||||
|
||||
// 获取状态页数据
|
||||
export const getStatusPageData = async (): Promise<StatusPageData> => {
|
||||
const response = await api.get<StatusPageData>("/api/status/data");
|
||||
export const getStatusPageData = async (userId: number): Promise<StatusPageData> => {
|
||||
const response = await api.get<StatusPageData>(`/api/status/public/${userId}/data`);
|
||||
return response.data;
|
||||
};
|
||||
@@ -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 = () => {
|
||||
<Heading size="9" align="center">
|
||||
{pageTitle}
|
||||
</Heading>
|
||||
<Text
|
||||
size="5"
|
||||
align="center"
|
||||
<Text
|
||||
size="5"
|
||||
align="center"
|
||||
className="whitespace-pre-wrap"
|
||||
>
|
||||
{pageDescription}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -46,7 +46,7 @@ interface LayoutWrapperProps {
|
||||
}
|
||||
const LayoutWrapper: React.FC<LayoutWrapperProps> = ({ children }) => {
|
||||
// 检查当前路径是否为状态页面,如果是则不使用Layout包裹
|
||||
const isStatusPage = window.location.pathname === "/status";
|
||||
const isStatusPage = window.location.pathname.startsWith("/status/public");
|
||||
return isStatusPage ? <>{children}</> : <Layout>{children}</Layout>;
|
||||
};
|
||||
|
||||
@@ -103,7 +103,7 @@ const protectedRoutes: RouteObject[] = [
|
||||
path: "/status",
|
||||
children: [
|
||||
{
|
||||
path: "",
|
||||
path: "public/:userId",
|
||||
element: (
|
||||
<Suspense fallback={<div>加载中...</div>}>
|
||||
<StatusPage />
|
||||
|
||||
Reference in new issue
Block a user