169 lines
4.4 KiB
Go
169 lines
4.4 KiB
Go
package handlers
|
||
|
||
import (
|
||
"strconv"
|
||
"strings"
|
||
"time"
|
||
securityEntities "hyapi-server/internal/domains/security/entities"
|
||
"hyapi-server/internal/shared/interfaces"
|
||
"hyapi-server/internal/shared/ipgeo"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
"go.uber.org/zap"
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
// AdminSecurityHandler 管理员安全数据处理器
|
||
type AdminSecurityHandler struct {
|
||
db *gorm.DB
|
||
responseBuilder interfaces.ResponseBuilder
|
||
logger *zap.Logger
|
||
ipLocator *ipgeo.Locator
|
||
}
|
||
|
||
func NewAdminSecurityHandler(
|
||
db *gorm.DB,
|
||
responseBuilder interfaces.ResponseBuilder,
|
||
logger *zap.Logger,
|
||
ipLocator *ipgeo.Locator,
|
||
) *AdminSecurityHandler {
|
||
return &AdminSecurityHandler{
|
||
db: db,
|
||
responseBuilder: responseBuilder,
|
||
logger: logger,
|
||
ipLocator: ipLocator,
|
||
}
|
||
}
|
||
|
||
func (h *AdminSecurityHandler) getIntQuery(c *gin.Context, key string, defaultValue int) int {
|
||
if value := c.Query(key); value != "" {
|
||
if intValue, err := strconv.Atoi(value); err == nil && intValue > 0 {
|
||
return intValue
|
||
}
|
||
}
|
||
return defaultValue
|
||
}
|
||
|
||
func (h *AdminSecurityHandler) parseRange(c *gin.Context) (time.Time, time.Time, bool) {
|
||
startTime := time.Now().Add(-24 * time.Hour)
|
||
endTime := time.Now()
|
||
|
||
if start := strings.TrimSpace(c.Query("start_time")); start != "" {
|
||
t, err := time.Parse("2006-01-02 15:04:05", start)
|
||
if err != nil {
|
||
h.responseBuilder.BadRequest(c, "start_time格式错误,示例:2026-03-19 10:00:00")
|
||
return time.Time{}, time.Time{}, false
|
||
}
|
||
startTime = t
|
||
}
|
||
if end := strings.TrimSpace(c.Query("end_time")); end != "" {
|
||
t, err := time.Parse("2006-01-02 15:04:05", end)
|
||
if err != nil {
|
||
h.responseBuilder.BadRequest(c, "end_time格式错误,示例:2026-03-19 12:00:00")
|
||
return time.Time{}, time.Time{}, false
|
||
}
|
||
endTime = t
|
||
}
|
||
return startTime, endTime, true
|
||
}
|
||
|
||
// ListSuspiciousIPs 获取可疑IP列表
|
||
func (h *AdminSecurityHandler) ListSuspiciousIPs(c *gin.Context) {
|
||
page := h.getIntQuery(c, "page", 1)
|
||
pageSize := h.getIntQuery(c, "page_size", 20)
|
||
if pageSize > 100 {
|
||
pageSize = 100
|
||
}
|
||
startTime, endTime, ok := h.parseRange(c)
|
||
if !ok {
|
||
return
|
||
}
|
||
|
||
ip := strings.TrimSpace(c.Query("ip"))
|
||
path := strings.TrimSpace(c.Query("path"))
|
||
|
||
query := h.db.Model(&securityEntities.SuspiciousIPRecord{}).
|
||
Where("created_at >= ? AND created_at <= ?", startTime, endTime)
|
||
if ip != "" {
|
||
query = query.Where("ip = ?", ip)
|
||
}
|
||
if path != "" {
|
||
query = query.Where("path LIKE ?", "%"+path+"%")
|
||
}
|
||
|
||
var total int64
|
||
if err := query.Count(&total).Error; err != nil {
|
||
h.logger.Error("查询可疑IP总数失败", zap.Error(err))
|
||
h.responseBuilder.InternalError(c, "查询失败")
|
||
return
|
||
}
|
||
|
||
var items []securityEntities.SuspiciousIPRecord
|
||
if err := query.Order("created_at DESC").Offset((page - 1) * pageSize).Limit(pageSize).Find(&items).Error; err != nil {
|
||
h.logger.Error("查询可疑IP列表失败", zap.Error(err))
|
||
h.responseBuilder.InternalError(c, "查询失败")
|
||
return
|
||
}
|
||
|
||
h.responseBuilder.Success(c, gin.H{
|
||
"items": items,
|
||
"total": total,
|
||
}, "获取成功")
|
||
}
|
||
|
||
type geoStreamRow struct {
|
||
IP string `json:"ip"`
|
||
Path string `json:"path"`
|
||
Count int `json:"count"`
|
||
}
|
||
|
||
// GetSuspiciousIPGeoStream 获取地球请求流数据
|
||
func (h *AdminSecurityHandler) GetSuspiciousIPGeoStream(c *gin.Context) {
|
||
startTime, endTime, ok := h.parseRange(c)
|
||
if !ok {
|
||
return
|
||
}
|
||
topN := h.getIntQuery(c, "top_n", 200)
|
||
if topN > 1000 {
|
||
topN = 1000
|
||
}
|
||
|
||
var rows []geoStreamRow
|
||
err := h.db.Model(&securityEntities.SuspiciousIPRecord{}).
|
||
Select("ip, path, COUNT(1) as count").
|
||
Where("created_at >= ? AND created_at <= ?", startTime, endTime).
|
||
Group("ip, path").
|
||
Order("count DESC").
|
||
Limit(topN).
|
||
Scan(&rows).Error
|
||
if err != nil {
|
||
h.logger.Error("查询地球请求流失败", zap.Error(err))
|
||
h.responseBuilder.InternalError(c, "查询失败")
|
||
return
|
||
}
|
||
|
||
// 目标固定服务器点位(上海)
|
||
const serverName = "HYAPI-Server"
|
||
const serverLng = 121.4737
|
||
const serverLat = 31.2304
|
||
|
||
result := make([]gin.H, 0, len(rows))
|
||
for _, row := range rows {
|
||
record := securityEntities.SuspiciousIPRecord{IP: row.IP}
|
||
fromName, fromLng, fromLat := h.ipLocator.ToGeoPoint(record)
|
||
result = append(result, gin.H{
|
||
"from_name": fromName,
|
||
"from_lng": fromLng,
|
||
"from_lat": fromLat,
|
||
"to_name": serverName,
|
||
"to_lng": serverLng,
|
||
"to_lat": serverLat,
|
||
"value": row.Count,
|
||
"path": row.Path,
|
||
"ip": row.IP,
|
||
})
|
||
}
|
||
|
||
h.responseBuilder.Success(c, result, "获取成功")
|
||
}
|