feat: 实现多用户数据隔离

This commit is contained in:
gujiangjiang committed 2025-10-09 04:24:39 +08:00
1 parent c8823eee74
commit 195751d1e2
15 files changed
+210 -179

No files matched your search

+2 -1
View File
@@ -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(
{
+2 -1
View File
@@ -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
View File
@@ -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(
{
+6 -2
View File
@@ -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);
});
+2 -2
View File
@@ -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));
}
// 批量获取客户端详情
+67 -79
View File
@@ -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));
}
+11
View File
@@ -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,
+2 -2
View File
@@ -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 || [],
+5 -5
View File
@@ -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);
+37 -35
View File
@@ -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,
+26 -25
View File
@@ -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
+2 -2
View File
@@ -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;
};
+15 -10
View File
@@ -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}
+15 -2
View File
@@ -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) {
+2 -2
View File
@@ -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 />