对探针和监控服务增加是否登录可见参数
This commit is contained in:
1 parent
46eaca187d
commit
1b0d589e0d
14 files changed
+300
-107
No files matched your search
+48
-14
@@ -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 进行认证
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"` // 更新时间(时间戳毫秒)
|
||||
|
||||
@@ -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 监控配置
|
||||
|
||||
@@ -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,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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -29,7 +29,7 @@ export const deleteMonitor = (id: number) => {
|
||||
|
||||
// 公开接口 - 获取监控配置及聚合统计
|
||||
export const getPublicMonitors = () => {
|
||||
return get<PublicMonitor[]>('/monitors/stats');
|
||||
return get<PublicMonitor[]>('/monitors');
|
||||
};
|
||||
|
||||
// 公开接口 - 获取指定监控的统计数据
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user