- 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
178 lines
5.2 KiB
Go
178 lines
5.2 KiB
Go
package handler
|
||
|
||
import (
|
||
"net/http"
|
||
"os"
|
||
"os/exec"
|
||
"time"
|
||
|
||
"git.zkcoi.com/zkcoi/Meshray-Manager/internal/service"
|
||
"github.com/gin-gonic/gin"
|
||
"go.uber.org/zap"
|
||
)
|
||
|
||
// SystemConfigHandler 系统配置处理器
|
||
type SystemConfigHandler struct {
|
||
configService *service.SystemConfigService
|
||
logger *zap.Logger
|
||
}
|
||
|
||
// NewSystemConfigHandler 创建系统配置处理器
|
||
func NewSystemConfigHandler(configService *service.SystemConfigService, logger *zap.Logger) *SystemConfigHandler {
|
||
return &SystemConfigHandler{
|
||
configService: configService,
|
||
logger: logger,
|
||
}
|
||
}
|
||
|
||
// GetWGMode 获取当前 WG 运行模式
|
||
// @Summary 获取当前 WG 运行模式
|
||
// @Tags system-config
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Success 200 {object} object{wg_mode=string,wg_mode_display=string,actual_mode=string}
|
||
// @Router /api/v1/system-config/wg-mode [get]
|
||
func (h *SystemConfigHandler) GetWGMode(c *gin.Context) {
|
||
// 获取配置的模式(用户设置)
|
||
configMode, err := h.configService.GetWGMode()
|
||
if err != nil {
|
||
h.logger.Error("获取 WG 配置失败", zap.Error(err))
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": "获取失败"})
|
||
return
|
||
}
|
||
|
||
// ✅ 固定使用用户态模式
|
||
actualMode := "userspace"
|
||
h.logger.Debug("WG 模式:固定使用 wireguard-go 用户态")
|
||
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"wg_mode": configMode, // 配置的模式(已废弃,保留兼容)
|
||
"wg_mode_display": getModeDisplay(actualMode), // 实际运行的模式显示
|
||
"actual_mode": actualMode, // 实际运行的模式代码
|
||
})
|
||
}
|
||
|
||
// getModeDisplay 获取模式的中文显示
|
||
func getModeDisplay(mode string) string {
|
||
switch mode {
|
||
case "kernel":
|
||
return "内核态"
|
||
case "userspace":
|
||
return "用户态"
|
||
default:
|
||
return mode
|
||
}
|
||
}
|
||
|
||
// SetWGMode 设置 WG 运行模式
|
||
// @Summary 设置 WG 运行模式(需要重启 MeshRay 才能生效)
|
||
// @Tags system-config
|
||
// @Accept json
|
||
// @Produce json
|
||
// @Param mode body object{mode string} true "WG 模式 (auto|kernel|userspace)"
|
||
// @Success 200 {object} object{message=string}
|
||
// @Router /api/v1/system-config/wg-mode [put]
|
||
func (h *SystemConfigHandler) SetWGMode(c *gin.Context) {
|
||
var req struct {
|
||
Mode string `json:"mode"`
|
||
}
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数错误"})
|
||
return
|
||
}
|
||
|
||
if err := h.configService.SetWGMode(req.Mode); err != nil {
|
||
h.logger.Error("设置 WG 模式失败", zap.Error(err))
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
|
||
h.logger.Info("WG 模式已更新,需要重启 MeshRay 才能生效", zap.String("mode", req.Mode))
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"message": "设置成功,请重启 MeshRay 使新配置生效",
|
||
"wg_mode": req.Mode,
|
||
})
|
||
}
|
||
|
||
// ==================== 系统运维功能(P1/P2) ====================
|
||
|
||
// ChangePasswordRequest 修改密码请求
|
||
type ChangePasswordRequest struct {
|
||
OldPassword string `json:"old_password"`
|
||
NewPassword string `json:"new_password"`
|
||
}
|
||
|
||
// ChangePassword 修改密码(P1)
|
||
func (h *SystemConfigHandler) ChangePassword(c *gin.Context) {
|
||
var req ChangePasswordRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数错误"})
|
||
return
|
||
}
|
||
|
||
// 验证密码长度
|
||
if len(req.NewPassword) < 6 {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": "密码至少 6 个字符"})
|
||
return
|
||
}
|
||
|
||
// 从上下文获取用户 ID
|
||
userID, exists := c.Get("user_id")
|
||
if !exists {
|
||
c.JSON(http.StatusUnauthorized, gin.H{"error": "未认证"})
|
||
return
|
||
}
|
||
|
||
// 调用服务层修改密码
|
||
err := h.configService.ChangePassword(userID.(uint), req.OldPassword, req.NewPassword)
|
||
if err != nil {
|
||
h.logger.Error("修改密码失败", zap.Error(err))
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
|
||
h.logger.Info("密码修改成功", zap.Uint("user_id", userID.(uint)))
|
||
c.JSON(http.StatusOK, gin.H{"message": "密码修改成功"})
|
||
}
|
||
|
||
// RestartCoreRequest 重启核心服务请求
|
||
type RestartCoreRequest struct {
|
||
Graceful bool `json:"graceful"` // 是否优雅重启
|
||
}
|
||
|
||
// RestartCore 重启核心服务(P1 - Windows 下通过重启进程实现)
|
||
func (h *SystemConfigHandler) RestartCore(c *gin.Context) {
|
||
var req RestartCoreRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": "请求参数错误"})
|
||
return
|
||
}
|
||
|
||
h.logger.Info("收到重启核心服务请求", zap.Bool("graceful", req.Graceful))
|
||
|
||
// 获取当前可执行文件路径
|
||
execPath, err := os.Executable()
|
||
if err != nil {
|
||
h.logger.Error("获取可执行文件路径失败", zap.Error(err))
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": "无法获取程序路径"})
|
||
return
|
||
}
|
||
|
||
// 在后台启动新进程
|
||
cmd := exec.Command(execPath)
|
||
if err := cmd.Start(); err != nil {
|
||
h.logger.Error("启动新进程失败", zap.Error(err))
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": "重启失败"})
|
||
return
|
||
}
|
||
|
||
// 延迟退出,让新进程有时间启动
|
||
go func() {
|
||
time.Sleep(2 * time.Second)
|
||
os.Exit(0)
|
||
}()
|
||
|
||
h.logger.Info("核心服务将在 2 秒后重启")
|
||
c.JSON(http.StatusOK, gin.H{"message": "核心服务正在重启..."})
|
||
}
|