package handler import ( "errors" "net/http" "time" "github.com/gin-gonic/gin" "nex/backend/internal/config" appErrors "nex/backend/pkg/errors" ) type SettingsHandler struct { runtimeCfg *config.Config mode string editable bool configPath string } func NewSettingsHandler(runtimeCfg *config.Config, mode string, editable bool, configPath string) *SettingsHandler { return &SettingsHandler{ runtimeCfg: runtimeCfg, mode: mode, editable: editable, configPath: configPath, } } type serverConfigDTO struct { Port int `json:"port"` ReadTimeout string `json:"read_timeout"` WriteTimeout string `json:"write_timeout"` } type databaseConfigDTO struct { Driver string `json:"driver"` Path string `json:"path"` Host string `json:"host"` Port int `json:"port"` User string `json:"user"` Password string `json:"password"` DBName string `json:"dbname"` MaxIdleConns int `json:"max_idle_conns"` MaxOpenConns int `json:"max_open_conns"` ConnMaxLifetime string `json:"conn_max_lifetime"` } type logConfigDTO struct { Level string `json:"level"` Path string `json:"path"` MaxSize int `json:"max_size"` MaxBackups int `json:"max_backups"` MaxAge int `json:"max_age"` Compress bool `json:"compress"` } type startupSettingsDTO struct { Server serverConfigDTO `json:"server"` Database databaseConfigDTO `json:"database"` Log logConfigDTO `json:"log"` } type startupSettingsResponse struct { Mode string `json:"mode"` Editable bool `json:"editable"` ConfigPath string `json:"config_path"` RestartRequired bool `json:"restart_required"` Config startupSettingsDTO `json:"config"` } func configToDTO(cfg *config.Config) startupSettingsDTO { return startupSettingsDTO{ Server: serverConfigDTO{ Port: cfg.Server.Port, ReadTimeout: cfg.Server.ReadTimeout.String(), WriteTimeout: cfg.Server.WriteTimeout.String(), }, Database: databaseConfigDTO{ Driver: cfg.Database.Driver, Path: cfg.Database.Path, Host: cfg.Database.Host, Port: cfg.Database.Port, User: cfg.Database.User, Password: cfg.Database.Password, DBName: cfg.Database.DBName, MaxIdleConns: cfg.Database.MaxIdleConns, MaxOpenConns: cfg.Database.MaxOpenConns, ConnMaxLifetime: cfg.Database.ConnMaxLifetime.String(), }, Log: logConfigDTO{ Level: cfg.Log.Level, Path: cfg.Log.Path, MaxSize: cfg.Log.MaxSize, MaxBackups: cfg.Log.MaxBackups, MaxAge: cfg.Log.MaxAge, Compress: cfg.Log.Compress, }, } } func dtoToConfig(dto startupSettingsDTO) (*config.Config, error) { readTimeout, err := time.ParseDuration(dto.Server.ReadTimeout) if err != nil { return nil, appErrors.WithMessage(appErrors.ErrInvalidRequest, "read_timeout 格式错误,例如 30s") } writeTimeout, err := time.ParseDuration(dto.Server.WriteTimeout) if err != nil { return nil, appErrors.WithMessage(appErrors.ErrInvalidRequest, "write_timeout 格式错误,例如 30s") } connMaxLifetime, err := time.ParseDuration(dto.Database.ConnMaxLifetime) if err != nil { return nil, appErrors.WithMessage(appErrors.ErrInvalidRequest, "conn_max_lifetime 格式错误,例如 1h") } return &config.Config{ Server: config.ServerConfig{ Port: dto.Server.Port, ReadTimeout: readTimeout, WriteTimeout: writeTimeout, }, Database: config.DatabaseConfig{ Driver: dto.Database.Driver, Path: dto.Database.Path, Host: dto.Database.Host, Port: dto.Database.Port, User: dto.Database.User, Password: dto.Database.Password, DBName: dto.Database.DBName, MaxIdleConns: dto.Database.MaxIdleConns, MaxOpenConns: dto.Database.MaxOpenConns, ConnMaxLifetime: connMaxLifetime, }, Log: config.LogConfig{ Level: dto.Log.Level, Path: dto.Log.Path, MaxSize: dto.Log.MaxSize, MaxBackups: dto.Log.MaxBackups, MaxAge: dto.Log.MaxAge, Compress: dto.Log.Compress, }, }, nil } func (h *SettingsHandler) GetStartupSettings(c *gin.Context) { var cfg *config.Config var configPath string if h.mode == "desktop" { desktopCfg, err := config.LoadDesktopConfigAtPath(h.configPath) if err != nil { writeError(c, err) return } cfg = desktopCfg configPath = h.configPath } else { cfg = h.runtimeCfg configPath = h.configPath } c.JSON(http.StatusOK, startupSettingsResponse{ Mode: h.mode, Editable: h.editable, ConfigPath: configPath, RestartRequired: h.editable, Config: configToDTO(cfg), }) } func (h *SettingsHandler) SaveStartupSettings(c *gin.Context) { if !h.editable { c.JSON(http.StatusForbidden, gin.H{ "error": "server 模式下不允许保存启动参数", "code": "forbidden", }) return } var req struct { Config startupSettingsDTO `json:"config"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": "无效的请求格式", }) return } cfg, err := dtoToConfig(req.Config) if err != nil { writeError(c, err) return } if err := cfg.Validate(); err != nil { writeError(c, err) return } if err := config.SaveConfigToPath(cfg, h.configPath); err != nil { if errors.Is(err, appErrors.ErrInvalidRequest) { writeError(c, err) return } writeError(c, appErrors.Wrap(appErrors.ErrInternal, err)) return } savedCfg, err := config.LoadDesktopConfigAtPath(h.configPath) if err != nil { writeError(c, appErrors.Wrap(appErrors.ErrInternal, err)) return } c.JSON(http.StatusOK, startupSettingsResponse{ Mode: h.mode, Editable: h.editable, ConfigPath: h.configPath, RestartRequired: true, Config: configToDTO(savedCfg), }) }