package handler import ( "net/http" "strconv" "time" "proxy-platform/internal/models" "proxy-platform/internal/repository" "github.com/gin-gonic/gin" "go.uber.org/zap" "golang.org/x/crypto/bcrypt" ) // ListUsers 获取用户列表 func ListUsers(repo *repository.UserRepository) gin.HandlerFunc { return func(c *gin.Context) { page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) offset := (page - 1) * pageSize users, total, err := repo.List(offset, pageSize) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{ "data": users, "total": total, "page": page, "page_size": pageSize, }) } } // CreateUser 创建用户 func CreateUser(repo *repository.UserRepository) gin.HandlerFunc { return func(c *gin.Context) { var req struct { Username string `json:"username" binding:"required"` Password string `json:"password" binding:"required"` TrafficQuota int64 `json:"traffic_quota"` ExpireDays int `json:"expire_days"` NodeGroupID *uint `json:"node_group_id"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // 密码加密 hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "密码加密失败"}) return } user := &models.User{ Username: req.Username, PasswordHash: string(hashedPassword), TrafficQuota: req.TrafficQuota, NodeGroupID: req.NodeGroupID, Status: "active", } if req.ExpireDays > 0 { expireAt := time.Now().AddDate(0, 0, req.ExpireDays) user.ExpireAt = &expireAt } if err := repo.Create(user); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusCreated, user) } } // GetUser 获取用户 func GetUser(repo *repository.UserRepository) gin.HandlerFunc { return func(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "无效的 ID"}) return } user, err := repo.FindByID(uint(id)) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "用户不存在"}) return } c.JSON(http.StatusOK, user) } } // UpdateUser 更新用户 func UpdateUser(repo *repository.UserRepository) gin.HandlerFunc { return func(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "无效的 ID"}) return } user, err := repo.FindByID(uint(id)) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "用户不存在"}) return } var req struct { Password *string `json:"password"` TrafficQuota *int64 `json:"traffic_quota"` ExpireDays *int `json:"expire_days"` NodeGroupID *uint `json:"node_group_id"` Status *string `json:"status"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if req.Password != nil { hashedPassword, _ := bcrypt.GenerateFromPassword([]byte(*req.Password), bcrypt.DefaultCost) user.PasswordHash = string(hashedPassword) } if req.TrafficQuota != nil { user.TrafficQuota = *req.TrafficQuota } if req.ExpireDays != nil { expireAt := time.Now().AddDate(0, 0, *req.ExpireDays) user.ExpireAt = &expireAt } if req.NodeGroupID != nil { user.NodeGroupID = req.NodeGroupID } if req.Status != nil { user.Status = *req.Status } if err := repo.Update(user); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, user) } } // DeleteUser 删除用户 func DeleteUser(repo *repository.UserRepository) gin.HandlerFunc { return func(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "无效的 ID"}) return } if err := repo.Delete(uint(id)); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "删除成功"}) } } // ListNodes 获取节点列表 func ListNodes(repo *repository.NodeRepository) gin.HandlerFunc { return func(c *gin.Context) { nodes, err := repo.List() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"data": nodes}) } } // CreateNode 创建节点 func CreateNode(repo *repository.NodeRepository) gin.HandlerFunc { return func(c *gin.Context) { var req struct { NodeID string `json:"node_id" binding:"required"` Name string `json:"name" binding:"required"` Host string `json:"host" binding:"required"` Port int `json:"port"` Region string `json:"region"` Country string `json:"country"` Weight int `json:"weight"` MaxConnections int `json:"max_connections"` NodeGroupID *uint `json:"node_group_id"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if req.Port == 0 { req.Port = 1080 } if req.Weight == 0 { req.Weight = 100 } if req.MaxConnections == 0 { req.MaxConnections = 1000 } node := &models.Node{ NodeID: req.NodeID, Name: req.Name, Host: req.Host, Port: req.Port, Region: req.Region, Country: req.Country, Weight: req.Weight, MaxConnections: req.MaxConnections, NodeGroupID: req.NodeGroupID, Status: "offline", WARPStatus: "disconnected", } if err := repo.Create(node); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusCreated, node) } } // GetNode 获取节点 func GetNode(repo *repository.NodeRepository) gin.HandlerFunc { return func(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "无效的 ID"}) return } node, err := repo.FindByID(uint(id)) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "节点不存在"}) return } c.JSON(http.StatusOK, node) } } // UpdateNode 更新节点 func UpdateNode(repo *repository.NodeRepository) gin.HandlerFunc { return func(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "无效的 ID"}) return } node, err := repo.FindByID(uint(id)) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "节点不存在"}) return } var req struct { Name *string `json:"name"` Host *string `json:"host"` Port *int `json:"port"` Weight *int `json:"weight"` MaxConnections *int `json:"max_connections"` Status *string `json:"status"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if req.Name != nil { node.Name = *req.Name } if req.Host != nil { node.Host = *req.Host } if req.Port != nil { node.Port = *req.Port } if req.Weight != nil { node.Weight = *req.Weight } if req.MaxConnections != nil { node.MaxConnections = *req.MaxConnections } if req.Status != nil { node.Status = *req.Status } if err := repo.Update(node); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, node) } } // DeleteNode 删除节点 func DeleteNode(repo *repository.NodeRepository) gin.HandlerFunc { return func(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "无效的 ID"}) return } if err := repo.Delete(uint(id)); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "删除成功"}) } } // RefreshNodeIP 刷新节点 IP func RefreshNodeIP(repo *repository.NodeRepository, logger *zap.Logger) gin.HandlerFunc { return func(c *gin.Context) { id := c.Param("id") logger.Info("收到刷新 IP 请求", zap.String("node_id", id)) // TODO: 发送刷新指令到 Agent c.JSON(http.StatusOK, gin.H{ "message": "刷新指令已发送", "node_id": id, }) } } // AgentHeartbeat Agent 心跳 func AgentHeartbeat(repo *repository.NodeRepository, logger *zap.Logger) gin.HandlerFunc { return func(c *gin.Context) { var req struct { NodeID string `json:"node_id"` Online bool `json:"online"` WARPConnected bool `json:"warp_connected"` CurrentIP string `json:"current_ip"` IPRegion string `json:"ip_region"` Connections int `json:"connections"` TrafficUsedGB float64 `json:"traffic_used_gb"` Unlocks map[string]bool `json:"unlocks"` CPUUsage float64 `json:"cpu_usage"` MemoryUsage float64 `json:"memory_usage"` NetworkInMbps float64 `json:"network_in_mbps"` NetworkOutMbps float64 `json:"network_out_mbps"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } logger.Info("收到心跳", zap.String("node_id", req.NodeID), zap.Bool("online", req.Online), zap.String("ip", req.CurrentIP), ) // 更新节点状态 warpStatus := "disconnected" if req.WARPConnected { warpStatus = "connected" } status := "offline" if req.Online { status = "online" } if err := repo.UpdateStatus(req.NodeID, status, warpStatus); err != nil { logger.Error("更新节点状态失败", zap.Error(err)) } if req.CurrentIP != "" { if err := repo.UpdateIP(req.NodeID, req.CurrentIP, req.IPRegion); err != nil { logger.Error("更新节点 IP 失败", zap.Error(err)) } } if err := repo.UpdateConnections(req.NodeID, req.Connections); err != nil { logger.Error("更新节点连接数失败", zap.Error(err)) } // 返回指令(如果有) commands := []interface{}{} c.JSON(http.StatusOK, gin.H{ "message": "心跳已接收", "commands": commands, }) } } // ReportUnlockStatus 上报解锁状态 func ReportUnlockStatus(repo *repository.UnlockStatusRepository, logger *zap.Logger) gin.HandlerFunc { return func(c *gin.Context) { var req struct { NodeID string `json:"node_id"` Unlocks map[string]struct { Unlocked bool `json:"unlocked"` Region string `json:"region"` } `json:"unlocks"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } logger.Info("收到解锁状态上报", zap.String("node_id", req.NodeID), zap.Any("unlocks", req.Unlocks), ) // 查找节点 node, err := (&repository.NodeRepository{}).FindByNodeID(req.NodeID) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "节点不存在"}) return } // 更新解锁状态 for service, status := range req.Unlocks { if err := repo.Upsert(node.ID, service, status.Unlocked, status.Region); err != nil { logger.Error("更新解锁状态失败", zap.String("service", service), zap.Error(err), ) } } c.JSON(http.StatusOK, gin.H{"message": "解锁状态已更新"}) } } // ReportIPChange 上报 IP 变更 func ReportIPChange(nodeRepo *repository.NodeRepository, logRepo *repository.IPChangeLogRepository, logger *zap.Logger) gin.HandlerFunc { return func(c *gin.Context) { var req struct { NodeID string `json:"node_id"` OldIP string `json:"old_ip"` NewIP string `json:"new_ip"` Success bool `json:"success"` Reason string `json:"reason"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } logger.Info("收到 IP 变更上报", zap.String("node_id", req.NodeID), zap.String("old_ip", req.OldIP), zap.String("new_ip", req.NewIP), zap.Bool("success", req.Success), ) // 查找节点 node, err := nodeRepo.FindByNodeID(req.NodeID) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "节点不存在"}) return } // 记录日志 log := &models.IPChangeLog{ NodeID: node.ID, OldIP: req.OldIP, NewIP: req.NewIP, Reason: req.Reason, Success: req.Success, } logRepo.Create(log) // 更新节点 IP if req.Success && req.NewIP != "" { nodeRepo.UpdateIP(req.NodeID, req.NewIP, "") } c.JSON(http.StatusOK, gin.H{"message": "IP 变更已记录"}) } } // ListRules 获取规则列表 func ListRules(repo *repository.IPRefreshRuleRepository) gin.HandlerFunc { return func(c *gin.Context) { rules, err := repo.List() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"data": rules}) } } // CreateRule 创建规则 func CreateRule(repo *repository.IPRefreshRuleRepository) gin.HandlerFunc { return func(c *gin.Context) { var req models.IPRefreshRule if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if err := repo.Create(&req); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusCreated, req) } } // GetRule 获取规则 func GetRule(repo *repository.IPRefreshRuleRepository) gin.HandlerFunc { return func(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "无效的 ID"}) return } rule, err := repo.FindByID(uint(id)) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "规则不存在"}) return } c.JSON(http.StatusOK, rule) } } // UpdateRule 更新规则 func UpdateRule(repo *repository.IPRefreshRuleRepository) gin.HandlerFunc { return func(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "无效的 ID"}) return } rule, err := repo.FindByID(uint(id)) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "规则不存在"}) return } if err := c.ShouldBindJSON(rule); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if err := repo.Update(rule); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, rule) } } // DeleteRule 删除规则 func DeleteRule(repo *repository.IPRefreshRuleRepository) gin.HandlerFunc { return func(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 32) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "无效的 ID"}) return } if err := repo.Delete(uint(id)); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "删除成功"}) } } // GetOverview 获取概览统计 func GetOverview(repos *repository.Repositories) gin.HandlerFunc { return func(c *gin.Context) { // TODO: 实现统计逻辑 c.JSON(http.StatusOK, gin.H{ "total_nodes": 0, "online_nodes": 0, "total_users": 0, "active_users": 0, "today_traffic": 0, "total_traffic": 0, }) } } // GetTrafficStats 获取流量统计 func GetTrafficStats(repo *repository.ConnectionLogRepository) gin.HandlerFunc { return func(c *gin.Context) { // TODO: 实现统计逻辑 c.JSON(http.StatusOK, gin.H{ "data": []interface{}{}, }) } }