wood chen e89ef02205 特性(代理):通过端口和身份验证更新来增强服务器配置和安全性。
- 更新 docker-compose.yml 文件,使用端口 3336
- 修改 Dockerfile 以暴露端口 3336
- 重构 main.go 以支持新的路由和管理端点
- 在 auth.go 中实现健壮的身份验证中间件
- 通过加强错误检查来改进指标处理。
- 添加用于安全类型转换的实用函数
- 引入请求ID生成功能,以便更好地进行追踪。
2025-02-15 08:07:28 +08:00

142 lines
3.7 KiB
Go

package handler
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"os"
"proxy-go/internal/config"
)
// ConfigHandler 配置管理处理器
type ConfigHandler struct {
config *config.Config
}
// NewConfigHandler 创建新的配置管理处理器
func NewConfigHandler(cfg *config.Config) *ConfigHandler {
return &ConfigHandler{
config: cfg,
}
}
// ServeHTTP 实现http.Handler接口
func (h *ConfigHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/metrics/config":
h.handleConfigPage(w, r)
case "/metrics/config/get":
h.handleGetConfig(w, r)
case "/metrics/config/save":
h.handleSaveConfig(w, r)
default:
http.NotFound(w, r)
}
}
// handleConfigPage 处理配置页面请求
func (h *ConfigHandler) handleConfigPage(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "web/templates/config.html")
}
// handleGetConfig 处理获取配置请求
func (h *ConfigHandler) handleGetConfig(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")
// 读取当前配置文件
configData, err := os.ReadFile("data/config.json")
if err != nil {
http.Error(w, fmt.Sprintf("读取配置文件失败: %v", err), http.StatusInternalServerError)
return
}
w.Write(configData)
}
// handleSaveConfig 处理保存配置请求
func (h *ConfigHandler) handleSaveConfig(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "方法不允许", http.StatusMethodNotAllowed)
return
}
// 解析新配置
var newConfig config.Config
if err := json.NewDecoder(r.Body).Decode(&newConfig); err != nil {
http.Error(w, fmt.Sprintf("解析配置失败: %v", err), http.StatusBadRequest)
return
}
// 验证新配置
if err := h.validateConfig(&newConfig); err != nil {
http.Error(w, fmt.Sprintf("配置验证失败: %v", err), http.StatusBadRequest)
return
}
// 将新配置格式化为JSON
configData, err := json.MarshalIndent(newConfig, "", " ")
if err != nil {
http.Error(w, fmt.Sprintf("格式化配置失败: %v", err), http.StatusInternalServerError)
return
}
// 保存到临时文件
tempFile := "data/config.json.tmp"
if err := os.WriteFile(tempFile, configData, 0644); err != nil {
http.Error(w, fmt.Sprintf("保存配置失败: %v", err), http.StatusInternalServerError)
return
}
// 重命名临时文件为正式文件
if err := os.Rename(tempFile, "data/config.json"); err != nil {
http.Error(w, fmt.Sprintf("更新配置文件失败: %v", err), http.StatusInternalServerError)
return
}
// 更新运行时配置
*h.config = newConfig
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"message": "配置已更新并生效"}`))
}
// validateConfig 验证配置
func (h *ConfigHandler) validateConfig(cfg *config.Config) error {
if cfg == nil {
return fmt.Errorf("配置不能为空")
}
// 验证MAP配置
if cfg.MAP == nil {
return fmt.Errorf("MAP配置不能为空")
}
for path, pathConfig := range cfg.MAP {
if path == "" {
return fmt.Errorf("路径不能为空")
}
if pathConfig.DefaultTarget == "" {
return fmt.Errorf("路径 %s 的默认目标不能为空", path)
}
if _, err := url.Parse(pathConfig.DefaultTarget); err != nil {
return fmt.Errorf("路径 %s 的默认目标URL无效: %v", path, err)
}
}
// 验证FixedPaths配置
for _, fp := range cfg.FixedPaths {
if fp.Path == "" {
return fmt.Errorf("固定路径不能为空")
}
if fp.TargetURL == "" {
return fmt.Errorf("固定路径 %s 的目标URL不能为空", fp.Path)
}
if _, err := url.Parse(fp.TargetURL); err != nil {
return fmt.Errorf("固定路径 %s 的目标URL无效: %v", fp.Path, err)
}
}
return nil
}