对探针和监控服务增加是否登录可见参数

This commit is contained in:
dushixiang committed 2025-11-22 20:52:48 +08:00
1 parent 46eaca187d
commit 1b0d589e0d
14 files changed
+300 -107

No files matched your search

+48 -14
View File
@@ -132,21 +132,26 @@ func setupApi(app *orz.App, components *AppComponents) {
publicApi.GET("/auth/oidc/url", components.AccountHandler.GetOIDCAuthURL)
publicApi.GET("/auth/github/url", components.AccountHandler.GetGitHubAuthURL)
// 探针信息(公开访问)- 用于公共展示页面
publicApi.GET("/agents", components.AgentHandler.GetAgents)
publicApi.GET("/agents/:id", components.AgentHandler.Get)
publicApi.GET("/agents/:id/metrics", components.AgentHandler.GetMetrics)
publicApi.GET("/agents/:id/metrics/latest", components.AgentHandler.GetLatestMetrics)
publicApi.GET("/agents/:id/metrics/network-by-interface", components.AgentHandler.GetNetworkMetricsByInterface)
// Agent 版本和下载
// Agent 版本和下载(完全公开,无需任何认证)
publicApi.GET("/agent/version", components.AgentHandler.GetAgentVersion)
publicApi.GET("/agent/downloads/:filename", components.AgentHandler.DownloadAgent)
}
// 监控统计数据(公开访问)- 用于公共展示页面
publicApi.GET("/monitors/stats", components.MonitorHandler.GetAllStats)
publicApi.GET("/monitors/:id/stats", components.MonitorHandler.GetStatsByID)
publicApi.GET("/monitors/:id/history", components.MonitorHandler.GetHistoryByID)
// 公开接口(支持可选认证)- 已登录返回全部数据,未登录只返回公开数据
publicApiWithOptionalAuth := e.Group("/api")
publicApiWithOptionalAuth.Use(OptionalJWTAuthMiddleware(components.AccountHandler))
{
// 探针信息(公开访问,支持可选认证)- 用于公共展示页面
publicApiWithOptionalAuth.GET("/agents", components.AgentHandler.GetAgents)
publicApiWithOptionalAuth.GET("/agents/:id", components.AgentHandler.Get)
publicApiWithOptionalAuth.GET("/agents/:id/metrics", components.AgentHandler.GetMetrics)
publicApiWithOptionalAuth.GET("/agents/:id/metrics/latest", components.AgentHandler.GetLatestMetrics)
publicApiWithOptionalAuth.GET("/agents/:id/metrics/network-by-interface", components.AgentHandler.GetNetworkMetricsByInterface)
// 监控统计数据(公开访问,支持可选认证)- 用于公共展示页面
publicApiWithOptionalAuth.GET("/monitors", components.MonitorHandler.GetMonitors)
publicApiWithOptionalAuth.GET("/monitors/:id/stats", components.MonitorHandler.GetStatsByID)
publicApiWithOptionalAuth.GET("/monitors/:id/history", components.MonitorHandler.GetHistoryByID)
}
// WebSocket 路由(探针连接)
@@ -173,7 +178,6 @@ func setupApi(app *orz.App, components *AppComponents) {
adminApi.GET("/agents", components.AgentHandler.Paging)
adminApi.GET("/agents/statistics", components.AgentHandler.GetStatistics)
adminApi.GET("/agents/:id", components.AgentHandler.GetForAdmin)
adminApi.PUT("/agents/:id/name", components.AgentHandler.UpdateName)
adminApi.PUT("/agents/:id", components.AgentHandler.UpdateInfo)
adminApi.DELETE("/agents/:id", components.AgentHandler.Delete)
adminApi.POST("/agents/:id/command", components.AgentHandler.SendCommand)
@@ -376,7 +380,7 @@ func startMonitorStatsCalculation(ctx context.Context, components *AppComponents
}
}
// JWTAuthMiddleware JWT 认证中间件
// JWTAuthMiddleware JWT 认证中间件(必须登录)
func JWTAuthMiddleware(accountHandler *handler.AccountHandler) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
@@ -403,10 +407,40 @@ func JWTAuthMiddleware(accountHandler *handler.AccountHandler) echo.MiddlewareFu
// 将用户信息存入 context
c.Set("userID", claims.UserID)
c.Set("username", claims.Username)
c.Set("authenticated", true)
return next(c)
}
}
}
// OptionalJWTAuthMiddleware 可选 JWT 认证中间件(尝试解析 token,但不强制要求)
func OptionalJWTAuthMiddleware(accountHandler *handler.AccountHandler) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
// 从 Authorization header 获取 token
authHeader := c.Request().Header.Get("Authorization")
if authHeader != "" {
// 检查 Bearer 前缀
const bearerPrefix = "Bearer "
if len(authHeader) >= len(bearerPrefix) && authHeader[:len(bearerPrefix)] == bearerPrefix {
tokenString := authHeader[len(bearerPrefix):]
// 尝试验证 token
claims, err := accountHandler.ValidateToken(tokenString)
if err == nil {
// token 有效,将用户信息存入 context
c.Set("userID", claims.UserID)
c.Set("username", claims.Username)
c.Set("authenticated", true)
}
}
}
// 无论 token 是否有效,都继续处理请求
return next(c)
}
}
}
// APIKeyAuthMiddleware 使用 API Key 进行认证
+52 -56
View File
@@ -14,6 +14,7 @@ import (
"github.com/dushixiang/pika/internal/models"
"github.com/dushixiang/pika/internal/protocol"
"github.com/dushixiang/pika/internal/service"
"github.com/dushixiang/pika/internal/utils"
ws "github.com/dushixiang/pika/internal/websocket"
"github.com/dushixiang/pika/pkg/version"
"github.com/go-orz/orz"
@@ -230,17 +231,23 @@ func (h *AgentHandler) Paging(c echo.Context) error {
return orz.Ok(c, page)
}
// Get 获取探针详情(公开接口,隐藏敏感信息)
// Get 获取探针详情(公开接口,已登录返回全部,未登录返回公开可见)
func (h *AgentHandler) Get(c echo.Context) error {
id := c.Param("id")
ctx := c.Request().Context()
agent, err := h.agentService.GetAgent(ctx, id)
// 根据认证状态返回相应的探针
isAuthenticated := utils.IsAuthenticated(c)
agent, err := h.agentService.GetAgentByAuth(ctx, id, isAuthenticated)
if err != nil {
return err
}
agent.IP = ""
agent.Hostname = ""
// 未登录时隐藏敏感信息
if !isAuthenticated {
agent.IP = ""
agent.Hostname = ""
}
return orz.Ok(c, agent)
}
@@ -258,9 +265,16 @@ func (h *AgentHandler) GetForAdmin(c echo.Context) error {
return orz.Ok(c, agent)
}
// GetMetrics 获取探针聚合指标
// GetMetrics 获取探针聚合指标(公开接口,已登录返回全部,未登录返回公开可见)
func (h *AgentHandler) GetMetrics(c echo.Context) error {
agentID := c.Param("id")
ctx := c.Request().Context()
// 验证探针访问权限
if _, err := h.agentService.GetAgentByAuth(ctx, agentID, utils.IsAuthenticated(c)); err != nil {
return err
}
metricType := c.QueryParam("type")
rangeParam := c.QueryParam("range")
@@ -302,7 +316,6 @@ func (h *AgentHandler) GetMetrics(c echo.Context) error {
// 服务端自动计算最优聚合间隔
interval := service.CalculateInterval(start, end)
ctx := c.Request().Context()
metrics, err := h.agentService.GetMetrics(ctx, agentID, metricType, start, end, interval)
if err != nil {
return err
@@ -319,9 +332,16 @@ func (h *AgentHandler) GetMetrics(c echo.Context) error {
})
}
// GetNetworkMetricsByInterface 获取按网卡接口分组的网络指标
// GetNetworkMetricsByInterface 获取按网卡接口分组的网络指标(公开接口,已登录返回全部,未登录返回公开可见)
func (h *AgentHandler) GetNetworkMetricsByInterface(c echo.Context) error {
agentID := c.Param("id")
ctx := c.Request().Context()
// 验证探针访问权限
if _, err := h.agentService.GetAgentByAuth(ctx, agentID, utils.IsAuthenticated(c)); err != nil {
return err
}
rangeParam := c.QueryParam("range")
// 根据 range 参数自动计算时间范围
@@ -350,7 +370,6 @@ func (h *AgentHandler) GetNetworkMetricsByInterface(c echo.Context) error {
// 服务端自动计算最优聚合间隔
interval := service.CalculateInterval(start, end)
ctx := c.Request().Context()
metrics, err := h.agentService.GetNetworkMetricsByInterface(ctx, agentID, start, end, interval)
if err != nil {
return err
@@ -367,11 +386,16 @@ func (h *AgentHandler) GetNetworkMetricsByInterface(c echo.Context) error {
})
}
// GetLatestMetrics 获取探针最新指标
// GetLatestMetrics 获取探针最新指标(公开接口,已登录返回全部,未登录返回公开可见)
func (h *AgentHandler) GetLatestMetrics(c echo.Context) error {
id := c.Param("id")
ctx := c.Request().Context()
// 验证探针访问权限
if _, err := h.agentService.GetAgentByAuth(ctx, id, utils.IsAuthenticated(c)); err != nil {
return err
}
metrics, err := h.agentService.GetLatestMetrics(ctx, id)
if err != nil {
return err
@@ -380,10 +404,12 @@ func (h *AgentHandler) GetLatestMetrics(c echo.Context) error {
return orz.Ok(c, metrics)
}
// GetAgents 获取探针列表
// GetAgents 获取探针列表(公开接口,已登录返回全部,未登录返回公开可见)
func (h *AgentHandler) GetAgents(c echo.Context) error {
ctx := c.Request().Context()
agents, err := h.agentService.ListAgents(ctx)
// 根据认证状态返回相应的探针列表
agents, err := h.agentService.ListByAuth(ctx, utils.IsAuthenticated(c))
if err != nil {
return err
}
@@ -411,6 +437,7 @@ func (h *AgentHandler) GetAgents(c echo.Context) error {
"expireTime": agent.ExpireTime,
"status": agent.Status,
"lastSeenAt": agent.LastSeenAt,
"visibility": agent.Visibility,
}
// 获取最新指标数据
@@ -534,65 +561,34 @@ func (h *AgentHandler) ListAuditResults(c echo.Context) error {
})
}
// UpdateName 更新探针名称
func (h *AgentHandler) UpdateName(c echo.Context) error {
agentID := c.Param("id")
var req struct {
Name string `json:"name" validate:"required"`
}
if err := c.Bind(&req); err != nil {
return orz.NewError(400, "请求参数错误")
}
if err := c.Validate(&req); err != nil {
return orz.NewError(400, "名称不能为空")
}
ctx := c.Request().Context()
if err := h.agentService.UpdateAgentName(ctx, agentID, req.Name); err != nil {
return err
}
return orz.Ok(c, orz.Map{
"message": "更新成功",
})
}
// UpdateInfo 更新探针信息(名称、平台、位置、到期时间)
func (h *AgentHandler) UpdateInfo(c echo.Context) error {
agentID := c.Param("id")
var req struct {
Name *string `json:"name"`
Platform *string `json:"platform"`
Location *string `json:"location"`
ExpireTime *int64 `json:"expireTime"`
Name string `json:"name"`
Platform string `json:"platform"`
Location string `json:"location"`
ExpireTime int64 `json:"expireTime"`
Visibility string `json:"visibility"`
}
if err := c.Bind(&req); err != nil {
return orz.NewError(400, "请求参数错误")
}
// 构建更新字段
updates := make(map[string]interface{})
if req.Name != nil {
updates["name"] = *req.Name
}
if req.Platform != nil {
updates["platform"] = *req.Platform
}
if req.Location != nil {
updates["location"] = *req.Location
}
if req.ExpireTime != nil {
updates["expire_time"] = *req.ExpireTime
}
if len(updates) == 0 {
return orz.NewError(400, "没有需要更新的字段")
var updates = models.Agent{
ID: agentID,
Name: req.Name,
Platform: req.Platform,
Location: req.Location,
ExpireTime: req.ExpireTime,
Visibility: req.Visibility,
UpdatedAt: time.Now().UnixMilli(),
}
ctx := c.Request().Context()
if err := h.agentService.UpdateAgentInfo(ctx, agentID, updates); err != nil {
if err := h.agentService.AgentRepo.UpdateById(ctx, &updates); err != nil {
return err
}
+19 -7
View File
@@ -2,6 +2,7 @@ package handler
import (
"github.com/dushixiang/pika/internal/service"
"github.com/dushixiang/pika/internal/utils"
"github.com/go-orz/orz"
"github.com/labstack/echo/v4"
"go.uber.org/zap"
@@ -118,10 +119,10 @@ func (h *MonitorHandler) Delete(c echo.Context) error {
return nil
}
// GetAllStats 获取所有监控统计数据
func (h *MonitorHandler) GetAllStats(c echo.Context) error {
// GetMonitors 获取所有监控统计数据
func (h *MonitorHandler) GetMonitors(c echo.Context) error {
ctx := c.Request().Context()
stats, err := h.monitorService.GetPublicMonitorOverview(ctx)
stats, err := h.monitorService.ListByAuth(ctx, utils.IsAuthenticated(c))
if err != nil {
return err
}
@@ -129,11 +130,16 @@ func (h *MonitorHandler) GetAllStats(c echo.Context) error {
return orz.Ok(c, stats)
}
// GetStatsByID 获取指定监控任务的统计数据(所有探针)
// GetStatsByID 获取指定监控任务的统计数据(公开接口,已登录返回全部,未登录返回公开可见)
func (h *MonitorHandler) GetStatsByID(c echo.Context) error {
id := c.Param("id")
ctx := c.Request().Context()
// 验证监控任务访问权限
if _, err := h.monitorService.GetMonitorByAuth(ctx, id, utils.IsAuthenticated(c)); err != nil {
return err
}
stats, err := h.monitorService.GetMonitorStatsByID(ctx, id)
if err != nil {
return err
@@ -142,9 +148,16 @@ func (h *MonitorHandler) GetStatsByID(c echo.Context) error {
return orz.Ok(c, stats)
}
// GetHistoryByID 获取指定监控任务的历史响应时间数据
// GetHistoryByID 获取指定监控任务的历史响应时间数据(公开接口,已登录返回全部,未登录返回公开可见)
func (h *MonitorHandler) GetHistoryByID(c echo.Context) error {
id := c.Param("id")
ctx := c.Request().Context()
// 验证监控任务访问权限
if _, err := h.monitorService.GetMonitorByAuth(ctx, id, utils.IsAuthenticated(c)); err != nil {
return err
}
timeRange := c.QueryParam("range")
// 默认时间范围为 5 分钟
@@ -152,7 +165,6 @@ func (h *MonitorHandler) GetHistoryByID(c echo.Context) error {
timeRange = "5m"
}
ctx := c.Request().Context()
history, err := h.monitorService.GetMonitorHistory(ctx, id, timeRange)
if err != nil {
return err
+1
View File
@@ -13,6 +13,7 @@ type Agent struct {
Location string `json:"location"` // 位置
ExpireTime int64 `json:"expireTime"` // 到期时间(时间戳毫秒)
Status int `json:"status"` // 状态: 0-离线, 1-在线
Visibility string `gorm:"default:public" json:"visibility"` // 可见性: public-匿名可见, private-登录可见
LastSeenAt int64 `gorm:"index" json:"lastSeenAt"` // 最后上线时间(时间戳毫秒)
CreatedAt int64 `json:"createdAt"` // 创建时间(时间戳毫秒)
UpdatedAt int64 `json:"updatedAt" gorm:"autoUpdateTime:milli"` // 更新时间(时间戳毫秒)
+1
View File
@@ -14,6 +14,7 @@ type MonitorTask struct {
Description string `json:"description"` // 描述信息
Enabled bool `json:"enabled"` // 是否启用
ShowTargetPublic bool `json:"showTargetPublic"` // 在公开页面是否显示目标地址
Visibility string `gorm:"default:public" json:"visibility"` // 可见性: public-匿名可见, private-登录可见
Interval int `json:"interval"` // 检测频率(秒),默认 60
AgentIds datatypes.JSONSlice[string] `json:"agentIds"` // 指定的探针 ID 列表(JSON 数组)
HTTPConfig datatypes.JSONType[protocol.HTTPMonitorConfig] `json:"httpConfig"` // HTTP 监控配置
+21 -8
View File
@@ -106,14 +106,6 @@ func (r *AgentRepo) ListAuditResults(ctx context.Context, agentID string) ([]mod
return audits, err
}
// UpdateName 更新探针名称
func (r *AgentRepo) UpdateName(ctx context.Context, agentID string, name string) error {
return r.db.WithContext(ctx).
Model(&models.Agent{}).
Where("id = ?", agentID).
Update("name", name).Error
}
// UpdateInfo 更新探针信息(名称、平台、位置、到期时间)
func (r *AgentRepo) UpdateInfo(ctx context.Context, agentID string, updates map[string]interface{}) error {
return r.db.WithContext(ctx).
@@ -159,3 +151,24 @@ func (r *AgentRepo) DeleteAuditResults(ctx context.Context, agentID string) erro
Where("agent_id = ?", agentID).
Delete(&models.AuditResult{}).Error
}
// FindPublicAgents 查找所有公开可见的探针
func (r *AgentRepo) FindPublicAgents(ctx context.Context) ([]models.Agent, error) {
var agents []models.Agent
err := r.db.WithContext(ctx).
Where("visibility = ?", "public").
Find(&agents).Error
return agents, err
}
// FindPublicAgentByID 查找指定ID的公开可见探针
func (r *AgentRepo) FindPublicAgentByID(ctx context.Context, id string) (*models.Agent, error) {
var agent models.Agent
err := r.db.WithContext(ctx).
Where("id = ? AND visibility = ?", id, "public").
First(&agent).Error
if err != nil {
return nil, err
}
return &agent, nil
}
+29
View File
@@ -29,3 +29,32 @@ func (r *MonitorRepo) FindByEnabledAndAgentId(ctx context.Context, agentId strin
return tasks, nil
}
func (r *MonitorRepo) FindByAuth(ctx context.Context, isAuthenticated bool) ([]models.MonitorTask, error) {
var monitors []models.MonitorTask
if err := r.GetDB(ctx).
Where("enabled = ?", true).
Order("name ASC").
Find(&monitors).Error; err != nil {
return nil, err
}
var filteredMonitors []models.MonitorTask
for _, monitor := range monitors {
if isAuthenticated || monitor.Visibility == "public" {
filteredMonitors = append(filteredMonitors, monitor)
}
}
return filteredMonitors, nil
}
// FindPublicMonitorByID 查找指定ID的公开可见监控任务
func (r *MonitorRepo) FindPublicMonitorByID(ctx context.Context, id string) (*models.MonitorTask, error) {
var task models.MonitorTask
err := r.GetDB(ctx).
Where("id = ? AND visibility = ?", id, "public").
First(&task).Error
if err != nil {
return nil, err
}
return &task, nil
}
+20 -10
View File
@@ -698,16 +698,6 @@ func (s *AgentService) ListAuditResults(ctx context.Context, agentID string) ([]
return results, nil
}
// UpdateAgentName 更新探针名称
func (s *AgentService) UpdateAgentName(ctx context.Context, agentID string, name string) error {
return s.AgentRepo.UpdateName(ctx, agentID, name)
}
// UpdateAgentInfo 更新探针信息(名称、平台、位置、到期时间)
func (s *AgentService) UpdateAgentInfo(ctx context.Context, agentID string, updates map[string]interface{}) error {
return s.AgentRepo.UpdateInfo(ctx, agentID, updates)
}
// GetStatistics 获取探针统计数据
func (s *AgentService) GetStatistics(ctx context.Context) (map[string]interface{}, error) {
total, online, err := s.AgentRepo.GetStatistics(ctx)
@@ -776,3 +766,23 @@ func (s *AgentService) DeleteAgent(ctx context.Context, agentID string) error {
return nil
})
}
// ListByAuth 根据认证状态列出探针(已登录返回全部,未登录返回公开可见)
func (s *AgentService) ListByAuth(ctx context.Context, isAuthenticated bool) ([]models.Agent, error) {
if isAuthenticated {
return s.AgentRepo.FindAll(ctx)
}
return s.AgentRepo.FindPublicAgents(ctx)
}
// GetAgentByAuth 根据认证状态获取探针(已登录返回全部,未登录返回公开可见)
func (s *AgentService) GetAgentByAuth(ctx context.Context, id string, isAuthenticated bool) (*models.Agent, error) {
if isAuthenticated {
agent, err := s.AgentRepo.FindById(ctx, id)
if err != nil {
return nil, err
}
return &agent, nil
}
return s.AgentRepo.FindPublicAgentByID(ctx, id)
}
+37 -9
View File
@@ -3,6 +3,7 @@ package service
import (
"context"
"encoding/json"
"fmt"
"strings"
"time"
@@ -46,6 +47,7 @@ type MonitorTaskRequest struct {
Description string `json:"description"`
Enabled bool `json:"enabled,omitempty"`
ShowTargetPublic bool `json:"showTargetPublic,omitempty"` // 在公开页面是否显示目标地址
Visibility string `json:"visibility,omitempty"` // 可见性: public-匿名可见, private-登录可见
Interval int `json:"interval"` // 检测频率(秒)
HTTPConfig protocol.HTTPMonitorConfig `json:"httpConfig,omitempty"`
TCPConfig protocol.TCPMonitorConfig `json:"tcpConfig,omitempty"`
@@ -81,6 +83,12 @@ func (s *MonitorService) CreateMonitor(ctx context.Context, req *MonitorTaskRequ
interval = 60 // 默认 60 秒
}
// 设置默认可见性
visibility := req.Visibility
if visibility == "" {
visibility = "public" // 默认公开可见
}
task := &models.MonitorTask{
ID: uuid.NewString(),
Name: strings.TrimSpace(req.Name),
@@ -89,6 +97,7 @@ func (s *MonitorService) CreateMonitor(ctx context.Context, req *MonitorTaskRequ
Description: req.Description,
Enabled: req.Enabled,
ShowTargetPublic: req.ShowTargetPublic,
Visibility: visibility,
Interval: interval,
AgentIds: datatypes.JSONSlice[string](req.AgentIds),
HTTPConfig: datatypes.NewJSONType(req.HTTPConfig),
@@ -116,6 +125,7 @@ func (s *MonitorService) UpdateMonitor(ctx context.Context, id string, req *Moni
task.Target = strings.TrimSpace(req.Target)
task.Description = req.Description
task.ShowTargetPublic = req.ShowTargetPublic
task.Visibility = req.Visibility
// 更新检测频率
interval := req.Interval
@@ -158,16 +168,12 @@ func (s *MonitorService) DeleteMonitor(ctx context.Context, id string) error {
})
}
// GetPublicMonitorOverview 返回公开展示所需的监控配置和汇总统计
func (s *MonitorService) GetPublicMonitorOverview(ctx context.Context) ([]PublicMonitorOverview, error) {
var monitors []models.MonitorTask
if err := s.MonitorRepo.GetDB(ctx).
Where("enabled = ?", true).
Order("name ASC").
Find(&monitors).Error; err != nil {
// ListByAuth 返回公开展示所需的监控配置和汇总统计
func (s *MonitorService) ListByAuth(ctx context.Context, isAuthenticated bool) ([]PublicMonitorOverview, error) {
monitors, err := s.FindByAuth(ctx, isAuthenticated)
if err != nil {
return nil, err
}
monitorIds := make([]string, 0, len(monitors))
for _, monitor := range monitors {
monitorIds = append(monitorIds, monitor.ID)
@@ -190,7 +196,7 @@ func (s *MonitorService) GetPublicMonitorOverview(ctx context.Context) ([]Public
// 根据 ShowTargetPublic 字段决定是否返回真实的 Target
target := monitor.Target
if !monitor.ShowTargetPublic {
target = "***"
target = "******"
}
item := PublicMonitorOverview{
@@ -712,3 +718,25 @@ func (s *MonitorService) GetMonitorHistory(ctx context.Context, monitorID, timeR
return s.metricRepo.GetAggregatedMonitorMetrics(ctx, monitor.ID, start, end, interval)
}
// GetMonitorByAuth 根据认证状态获取监控任务(已登录返回全部,未登录返回公开可见)
func (s *MonitorService) GetMonitorByAuth(ctx context.Context, id string, isAuthenticated bool) (*models.MonitorTask, error) {
if isAuthenticated {
monitor, err := s.MonitorRepo.FindById(ctx, id)
if err != nil {
return nil, err
}
if !monitor.Enabled {
return nil, fmt.Errorf("monitor is disabled")
}
return &monitor, nil
}
monitor, err := s.MonitorRepo.FindPublicMonitorByID(ctx, id)
if err != nil {
return nil, err
}
if !monitor.Enabled {
return nil, fmt.Errorf("monitor is disabled")
}
return monitor, nil
}
+9
View File
@@ -0,0 +1,9 @@
package utils
import "github.com/labstack/echo/v4"
// IsAuthenticated 检查用户是否已登录
func IsAuthenticated(c echo.Context) bool {
authenticated, _ := c.Get("authenticated").(bool)
return authenticated
}
+1 -1
View File
@@ -29,7 +29,7 @@ export const deleteMonitor = (id: number) => {
// 公开接口 - 获取监控配置及聚合统计
export const getPublicMonitors = () => {
return get<PublicMonitor[]>('/monitors/stats');
return get<PublicMonitor[]>('/monitors');
};
// 公开接口 - 获取指定监控的统计数据
+29 -1
View File
@@ -2,7 +2,7 @@ import {useRef, useState} from 'react';
import {useNavigate} from 'react-router-dom';
import type {ActionType, ProColumns} from '@ant-design/pro-components';
import {ProTable} from '@ant-design/pro-components';
import {App, Button, DatePicker, Divider, Dropdown, Form, Input, Modal, Space, Tag} from 'antd';
import {App, Button, DatePicker, Divider, Dropdown, Form, Input, Modal, Select, Space, Tag} from 'antd';
import type {MenuProps} from 'antd';
import {Edit, Eye, RefreshCw, Plus, Shield, Trash2, MoreVertical} from 'lucide-react';
import {deleteAgent, getAgentPaging, updateAgentInfo} from '../../api/agent';
@@ -28,6 +28,7 @@ const AgentList = () => {
platform: agent.platform,
location: agent.location,
expireTime: agent.expireTime ? dayjs(agent.expireTime) : null,
visibility: agent.visibility || 'public',
});
setEditModalVisible(true);
};
@@ -46,6 +47,7 @@ const AgentList = () => {
name: values.name,
platform: values.platform,
location: values.location,
visibility: values.visibility || 'public',
};
if (values.expireTime) {
@@ -158,6 +160,18 @@ const AgentList = () => {
</Tag>
),
},
{
title: '可见性',
dataIndex: 'visibility',
key: 'visibility',
hideInSearch: true,
width: 100,
render: (visibility) => (
<Tag color={visibility === 'public' ? 'green' : 'orange'}>
{visibility === 'public' ? '匿名可见' : '登录可见'}
</Tag>
),
},
{
title: '主机名',
dataIndex: 'hostname',
@@ -356,6 +370,20 @@ const AgentList = () => {
placeholder="请选择到期时间"
/>
</Form.Item>
<Form.Item
label="可见性"
name="visibility"
rules={[{required: true, message: '请选择可见性'}]}
extra="控制探针在公开页面的可见性"
>
<Select
placeholder="请选择可见性"
options={[
{label: '匿名可见', value: 'public'},
{label: '登录可见', value: 'private'},
]}
/>
</Form.Item>
</Form>
</Modal>
</div>
+30 -1
View File
@@ -60,6 +60,7 @@ const MonitorList = () => {
description: '',
enabled: true,
showTargetPublic: true,
visibility: 'public',
interval: 60,
agentIds: [],
httpMethod: 'GET',
@@ -89,6 +90,7 @@ const MonitorList = () => {
description: monitor.description,
enabled: monitor.enabled,
showTargetPublic: monitor.showTargetPublic ?? true,
visibility: monitor.visibility || 'public',
interval: monitor.interval || 60,
agentIds: monitor.agentIds || [],
httpMethod: monitor.httpConfig?.method || 'GET',
@@ -133,6 +135,7 @@ const MonitorList = () => {
description: values.description?.trim(),
enabled: values.enabled,
showTargetPublic: values.showTargetPublic ?? true,
visibility: values.visibility || 'public',
interval: values.interval || 60,
agentIds: values.agentIds || [],
};
@@ -247,6 +250,16 @@ const MonitorList = () => {
<Tag color={enabled ? 'green' : 'red'}>{enabled ? '启用' : '禁用'}</Tag>
),
},
{
title: '可见性',
dataIndex: 'visibility',
width: 100,
render: (visibility: string) => (
<Tag color={visibility === 'public' ? 'green' : 'orange'}>
{visibility === 'public' ? '匿名可见' : '登录可见'}
</Tag>
),
},
{
title: '更新时间',
dataIndex: 'updatedAt',
@@ -435,6 +448,21 @@ const MonitorList = () => {
<Switch checkedChildren="显示" unCheckedChildren="隐藏"/>
</Form.Item>
<Form.Item
label="可见性"
name="visibility"
rules={[{required: true, message: '请选择可见性'}]}
extra="控制监控任务在公开页面的可见性"
>
<Select
placeholder="请选择可见性"
options={[
{label: '匿名可见', value: 'public'},
{label: '登录可见', value: 'private'},
]}
/>
</Form.Item>
{watchType === 'tcp' ? (
<Form.Item label="连接超时 (秒)" name="tcpTimeout" initialValue={5}>
<InputNumber min={1} max={120} style={{width: '100%'}}/>
@@ -445,7 +473,8 @@ const MonitorList = () => {
<InputNumber min={1} max={60} style={{width: '100%'}}/>
</Form.Item>
<Form.Item label="Ping 次数" name="icmpCount" initialValue={4} extra="单次检测发送的 ICMP 包数量">
<Form.Item label="Ping 次数" name="icmpCount" initialValue={4}
extra="单次检测发送的 ICMP 包数量">
<InputNumber min={1} max={10} style={{width: '100%'}}/>
</Form.Item>
</>
+3
View File
@@ -26,6 +26,7 @@ export interface Agent {
location?: string; // 位置
expireTime?: number; // 到期时间(时间戳毫秒)
status: number;
visibility?: string; // 可见性: public-匿名可见, private-登录可见
lastSeenAt: string | number; // 支持字符串或时间戳
createdAt?: string;
updatedAt?: string;
@@ -247,6 +248,7 @@ export interface MonitorTask {
description?: string;
enabled: boolean;
showTargetPublic: boolean;
visibility?: string; // 可见性: public-匿名可见, private-登录可见
interval: number;
httpConfig?: MonitorHttpConfig | null;
tcpConfig?: MonitorTcpConfig | null;
@@ -263,6 +265,7 @@ export interface MonitorTaskRequest {
description?: string;
enabled?: boolean;
showTargetPublic?: boolean;
visibility?: string; // 可见性: public-匿名可见, private-登录可见
interval: number;
httpConfig?: MonitorHttpConfig | null;
tcpConfig?: MonitorTcpConfig | null;