优化
This commit is contained in:
truewhile
2026-08-25 14:12:59 +08:00
parent eb09251424
commit da1fb02c9d
18 changed files with 1011 additions and 86 deletions
+82
View File
@@ -8,6 +8,7 @@ import (
"time"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
"github.com/ShukeBta/MMTL/internal/model"
"github.com/ShukeBta/MMTL/internal/service"
@@ -50,6 +51,10 @@ func updateSettingHandler(svc *service.Container) gin.HandlerFunc {
_ = svc.Repo.DB.WithContext(c.Request.Context()).Model(&model.User{}).Where("hide_adult = ?", false).Update("hide_adult", true).Error
}
service.ApplyRuntimeSetting(svc.Cfg, req.Key, req.Value)
if err := applyHTTPSetting(svc, req.Key, req.Value); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if svc.FFprobe != nil && (req.Key == "ffprobe.max_concurrent" || req.Key == "app.ffprobe_max_concurrent") {
svc.FFprobe.SetMaxConcurrent(svc.Cfg.App.FFprobeMaxConcurrent)
}
@@ -63,6 +68,83 @@ func updateSettingHandler(svc *service.Container) gin.HandlerFunc {
}
}
// applyHTTPSetting 校验 HTTPS 相关设置,并在可行时热重载监听。
// 必须在 ApplyRuntimeSetting 之后调用,这样 svc.Cfg 已反映刚保存的值。
//
// 规则:
// - https.enabled=true 时强制要求证书与私钥都已配置(内容或路径均可)且匹配,
// 否则返回错误("如果启用就必须配置 SSL 证书和密钥");
// - 证书/私钥(内容或路径)单独保存时只校验格式;若 HTTPS 已开启且新的整体
// 配置可解析匹配才触发重载,避免"只存了新证书、私钥还没保存"时用旧私钥带
// 新证书对外提供服务。
func applyHTTPSetting(svc *service.Container, key, value string) error {
skipReload := func(reason string) {
if svc.Log != nil {
svc.Log.Warn("https setting saved but not applied yet", zap.String("key", key), zap.String("reason", reason))
}
}
switch key {
case "https.enabled":
if svc.Cfg.App.HTTPSEnabled {
if _, err := service.ResolveSSLKeyPair(svc.Cfg.App.SSLCert, svc.Cfg.App.SSLCertPath, svc.Cfg.App.SSLKey, svc.Cfg.App.SSLKeyPath); err != nil {
return fmt.Errorf("启用 HTTPS 失败:%v", err)
}
}
case "https.cert", "https.cert_path", "https.key", "https.key_path":
if err := validateSSLMaterialSource(key, value); err != nil {
return err
}
if !svc.Cfg.App.HTTPSEnabled {
return nil
}
if !httpsPairReady(svc) {
skipReload("证书与私钥尚未匹配,等待另一半保存后生效")
return nil
}
default:
return nil
}
if svc.ReloadHTTPServer != nil {
return svc.ReloadHTTPServer()
}
return nil
}
// validateSSLMaterialSource 校验刚保存的证书/私钥来源(内容或路径)本身格式合法。
func validateSSLMaterialSource(key, value string) error {
switch key {
case "https.cert":
return service.ValidateSSLCert(value)
case "https.cert_path":
if strings.TrimSpace(value) == "" {
return nil // 清空路径也允许,启用时由整体校验把关
}
pemStr, err := service.ResolveSSLMaterial("", value, "证书")
if err != nil {
return err
}
return service.ValidateSSLCert(pemStr)
case "https.key":
return service.ValidateSSLKey(value)
case "https.key_path":
if strings.TrimSpace(value) == "" {
return nil
}
pemStr, err := service.ResolveSSLMaterial("", value, "私钥")
if err != nil {
return err
}
return service.ValidateSSLKey(pemStr)
}
return nil
}
// httpsPairReady 判断基于当前配置解析出的证书/私钥是否完整且匹配。
func httpsPairReady(svc *service.Container) bool {
_, err := service.ResolveSSLKeyPair(svc.Cfg.App.SSLCert, svc.Cfg.App.SSLCertPath, svc.Cfg.App.SSLKey, svc.Cfg.App.SSLKeyPath)
return err == nil
}
type testAdultScraperReq struct {
Engine string `json:"engine"`
ServerURL string `json:"server_url"`