Files
Meshray-Manager/internal/api/handler/pending_join.go
T
zkcoi e9ca2f7d70 fix: rename manager module to git.zkcoi.com/zkcoi/Meshray-Manager
- go.mod module path matches repo zkcoi/Meshray-Manager (case-distinct from zkcoi/Meshray/core)
- rewrite internal imports meshray/{internal,web,pkg} -> Meshray-Manager/... (core refs kept)
- sync README.md / install.sh repo URLs; add CHANGELOG entry
2026-07-15 16:25:46 +08:00

206 lines
5.6 KiB
Go

package handler
import (
"net/http"
"strconv"
"time"
"git.zkcoi.com/zkcoi/Meshray-Manager/internal/model"
"git.zkcoi.com/zkcoi/Meshray-Manager/internal/service"
sqlite "git.zkcoi.com/zkcoi/Meshray-Manager/internal/store/sqlite"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
// PendingJoinHandler 待审核 Handler
type PendingJoinHandler struct {
service *service.PendingJoinService
store *sqlite.Store // ← 新增
logger *zap.Logger
}
// NewPendingJoinHandler 创建待审核 Handler
func NewPendingJoinHandler(service *service.PendingJoinService, store *sqlite.Store, logger *zap.Logger) *PendingJoinHandler {
return &PendingJoinHandler{
service: service,
store: store, // ← 新增
logger: logger,
}
}
// ListPendingJoins 获取待审核列表
func (h *PendingJoinHandler) ListPendingJoins(c *gin.Context) {
// 获取查询参数
networkIDStr := c.DefaultQuery("network_id", "0")
status := c.DefaultQuery("status", "pending")
pageStr := c.DefaultQuery("page", "1")
sizeStr := c.DefaultQuery("size", "20")
// 转换参数
networkID, _ := strconv.ParseUint(networkIDStr, 10, 32)
page, _ := strconv.Atoi(pageStr)
size, _ := strconv.Atoi(sizeStr)
if page < 1 {
page = 1
}
if size < 1 || size > 100 {
size = 20
}
// 调用 Service
list, total, err := h.service.ListPendingJoins(uint(networkID), status, page, size)
if err != nil {
h.logger.Error("获取待审核列表失败", zap.Error(err))
c.JSON(http.StatusInternalServerError, gin.H{"error": "获取失败"})
return
}
// 转换为响应格式
type ResponseItem struct {
ID uint `json:"id"`
SeedID string `json:"seed_id"`
DeviceName string `json:"device_name"`
RequestIP string `json:"request_ip"`
Status string `json:"status"`
ExpireAt string `json:"expire_at"`
ApprovedAt string `json:"approved_at"`
RejectedAt string `json:"rejected_at"`
Reason string `json:"reason"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
NetworkID uint `json:"network_id"`
NetworkName string `json:"network_name"`
ApplicantIP string `json:"applicant_ip"`
ApplyTime string `json:"apply_time"`
Message string `json:"message"`
}
var responseList []ResponseItem
for _, item := range list {
// 通过 SeedID 关联查询 MeshSeed 获取 NetworkID
var meshSeed model.MeshSeed
networkName := "未知网络"
networkID := uint(0)
if err := h.store.DB().Where("seed_id = ?", item.SeedID).First(&meshSeed).Error; err == nil {
// 查询网络信息
var network model.Network
if err := h.store.DB().Where("id = ?", meshSeed.NetworkID).First(&network).Error; err == nil {
networkName = network.Name
networkID = uint(network.ID)
}
}
responseList = append(responseList, ResponseItem{
ID: item.ID,
SeedID: item.SeedID,
DeviceName: item.DeviceName,
RequestIP: item.RequestIP,
Status: item.Status,
ExpireAt: item.ExpireAt.Format("2006-01-02 15:04:05"),
ApprovedAt: formatTime(item.ApprovedAt),
RejectedAt: formatTime(item.RejectedAt),
Reason: item.Reason,
CreatedAt: item.CreatedAt.Format("2006-01-02 15:04:05"),
UpdatedAt: item.UpdatedAt.Format("2006-01-02 15:04:05"),
NetworkID: networkID,
NetworkName: networkName,
ApplicantIP: item.RequestIP,
ApplyTime: item.CreatedAt.Format("2006-01-02 15:04:05"),
Message: "",
})
}
c.JSON(http.StatusOK, gin.H{
"data": gin.H{
"list": responseList,
"total": total,
},
})
}
// ApproveJoin 审核通过
func (h *PendingJoinHandler) ApproveJoin(c *gin.Context) {
idStr := c.Param("id")
id, _ := strconv.ParseUint(idStr, 10, 32)
result, err := h.service.ApproveJoin(uint(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", uint64(id)),
zap.String("device_name", result.Device.Name),
zap.String("device_ip", result.Device.VirtualIP))
c.JSON(http.StatusOK, gin.H{
"message": "审核通过",
"data": gin.H{
"device": result.Device,
"private_key": result.PrivateKey,
"network": result.Network,
"wireguard_config": result.ConfigText,
},
})
}
// RejectJoin 审核拒绝
func (h *PendingJoinHandler) RejectJoin(c *gin.Context) {
idStr := c.Param("id")
id, _ := strconv.ParseUint(idStr, 10, 32)
var req struct {
Reason string `json:"reason"`
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "参数错误"})
return
}
if err := h.service.RejectJoin(uint(id), req.Reason); err != nil {
h.logger.Error("审核拒绝失败", zap.Error(err))
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "已拒绝"})
}
// CountPending 统计待审核数量
func (h *PendingJoinHandler) CountPending(c *gin.Context) {
count, err := h.service.CountPending()
if err != nil {
h.logger.Error("统计待审核数量失败", zap.Error(err))
c.JSON(http.StatusInternalServerError, gin.H{"error": "统计失败"})
return
}
c.JSON(http.StatusOK, gin.H{
"data": gin.H{
"count": count,
},
})
}
// DeleteExpired 清理过期记录
func (h *PendingJoinHandler) DeleteExpired(c *gin.Context) {
if err := h.service.DeleteExpired(); err != nil {
h.logger.Error("清理过期记录失败", zap.Error(err))
c.JSON(http.StatusInternalServerError, gin.H{"error": "清理失败"})
return
}
c.JSON(http.StatusOK, gin.H{"message": "清理完成"})
}
func formatTime(t *time.Time) string {
if t == nil {
return ""
}
return t.Format("2006-01-02 15:04:05")
}