package handler import ( "net/http" "strconv" "git.zkcoi.com/zkcoi/meshray/internal/api/dto" "git.zkcoi.com/zkcoi/meshray/internal/model" "git.zkcoi.com/zkcoi/meshray/internal/service" "github.com/gin-gonic/gin" "go.uber.org/zap" ) // DeviceHandler 设备管理 Handler type DeviceHandler struct { deviceService *service.DeviceService logger *zap.Logger } // NewDeviceHandler 创建设备处理器 func NewDeviceHandler(deviceService *service.DeviceService, logger *zap.Logger) *DeviceHandler { return &DeviceHandler{ deviceService: deviceService, logger: logger, } } // CreateDevice 创建设备 // @Summary 在指定网络下创建设备 // @Tags devices // @Accept json // @Produce json // @Param network_id path int true "网络 ID" // @Param device body model.Device true "设备配置" // @Success 200 {object} model.Device // @Router /api/v1/networks/:network_id/devices [post] func (h *DeviceHandler) CreateDevice(c *gin.Context) { // 从 Query 参数获取 network_id networkIDStr := c.Query("network_id") networkID, err := strconv.ParseUint(networkIDStr, 10, 64) if err != nil { h.logger.Error("解析网络 ID 失败", zap.Error(err)) c.JSON(http.StatusBadRequest, gin.H{"error": "无效的网络 ID"}) return } var req service.CreateDeviceRequest req.NetworkID = networkID if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数错误"}) return } // 使用 Service 层创建设备(返回完整配置) result, err := h.deviceService.CreateDevice(&req) if err != nil { h.logger.Error("创建设备失败", zap.Error(err)) c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } h.logger.Info("设备创建成功", zap.Uint64("id", result.Device.ID), zap.String("name", result.Device.Name), zap.Uint64("network_id", result.Device.NetworkID)) // 返回完整配置(包含私钥和 WG 配置) c.JSON(http.StatusOK, gin.H{ "message": "设备创建成功", "data": gin.H{ "device": result.Device, "private_key": result.PrivateKey, "wireguard_config": result.ConfigText, }, }) } // GetDevice 获取设备详情 // @Summary 获取设备详细信息 // @Tags devices // @Accept json // @Produce json // @Param id path int true "设备 ID" // @Success 200 {object} model.Device // @Router /api/v1/devices/:id [get] // GetDevice 获取设备详情 func (h *DeviceHandler) GetDevice(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseUint(idStr, 10, 64) if err != nil { h.logger.Error("解析设备 ID 失败", zap.Error(err)) c.JSON(http.StatusBadRequest, gin.H{"error": "无效的设备 ID"}) return } device, err := h.deviceService.GetDevice(id) if err != nil { h.logger.Error("获取设备失败", zap.Error(err)) c.JSON(http.StatusNotFound, gin.H{"error": "设备不存在"}) return } // 使用 DTO 转换 resp := dto.ToDeviceResponse(device) c.JSON(http.StatusOK, gin.H{ "data": resp, }) } // ListDevices 获取设备列表 // @Summary 获取指定网络下的设备列表 // @Tags devices // @Accept json // @Produce json // @Param network_id path int true "网络 ID" // @Success 200 {array} model.Device // @Router /api/v1/networks/:network_id/devices [get] // ListDevices 获取设备列表 func (h *DeviceHandler) ListDevices(c *gin.Context) { networkIDStr := c.Query("network_id") var devices []model.Device var err error if networkIDStr != "" { networkID, err := strconv.ParseUint(networkIDStr, 10, 64) if err != nil { h.logger.Error("解析网络 ID 失败", zap.Error(err)) c.JSON(http.StatusBadRequest, gin.H{"error": "无效的网络 ID"}) return } devices, err = h.deviceService.ListDevicesByNetwork(networkID) } else { devices, err = h.deviceService.ListAllDevices() } if err != nil { h.logger.Error("查询设备列表失败", zap.Error(err)) c.JSON(http.StatusInternalServerError, gin.H{"error": "查询失败"}) return } // 使用 DTO 批量转换 respList := dto.ToDeviceResponseList(devices) c.JSON(http.StatusOK, gin.H{ "data": respList, }) } // UpdateDevice 更新设备 // @Summary 更新设备信息 // @Tags devices // @Accept json // @Produce json // @Param id path int true "设备 ID" // @Param device body model.Device true "设备配置" // @Success 200 {object} model.Device // @Router /api/v1/devices/:id [put] func (h *DeviceHandler) UpdateDevice(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseUint(idStr, 10, 64) if err != nil { h.logger.Error("解析设备 ID 失败", zap.Error(err)) c.JSON(http.StatusBadRequest, gin.H{"error": "无效的设备 ID"}) return } var req struct { Name string `json:"name"` VirtualIP string `json:"virtual_ip"` Status string `json:"status"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数错误"}) return } // 构建更新字段 updates := make(map[string]interface{}) if req.Name != "" { updates["name"] = req.Name } if req.VirtualIP != "" { updates["virtual_ip"] = req.VirtualIP } if req.Status != "" { updates["status"] = req.Status } // 使用 Service 层更新 device, err := h.deviceService.UpdateDevice(id, updates) if err != nil { h.logger.Error("更新设备失败", zap.Error(err)) c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } h.logger.Info("设备更新成功", zap.Uint64("id", device.ID)) c.JSON(http.StatusOK, gin.H{ "message": "更新成功", "data": device, }) } // DeleteDevice 删除设备 // @Summary 删除指定设备 // @Tags devices // @Accept json // @Produce json // @Param id path int true "设备 ID" // @Success 200 // @Router /api/v1/devices/:id [delete] func (h *DeviceHandler) DeleteDevice(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseUint(idStr, 10, 64) if err != nil { h.logger.Error("解析设备 ID 失败", zap.Error(err)) c.JSON(http.StatusBadRequest, gin.H{"error": "无效的设备 ID"}) return } // 使用 Service 层删除 err = h.deviceService.DeleteDevice(id) if err != nil { h.logger.Error("删除设备失败", zap.Error(err)) c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } h.logger.Info("设备已删除", zap.Uint64("id", id)) c.JSON(http.StatusOK, gin.H{ "message": "删除成功", }) } // GenerateDeviceConfig 生成设备配置文件 // @Summary 生成 WireGuard 设备配置 // @Tags devices // @Accept json // @Produce json // @Param id path int true "设备 ID" // @Success 200 {string} string "配置文件内容" // @Router /api/v1/devices/:id/config [get] func (h *DeviceHandler) GenerateDeviceConfig(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseUint(idStr, 10, 64) if err != nil { h.logger.Error("解析设备 ID 失败", zap.Error(err)) c.JSON(http.StatusBadRequest, gin.H{"error": "无效的设备 ID"}) return } // 调用 Service 层生成配置(包含密钥生成) config, err := h.deviceService.GenerateDeviceConfig(id) if err != nil { h.logger.Error("生成配置失败", zap.Error(err)) c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.Data(http.StatusOK, "text/plain", []byte(config)) }