diff --git a/server/listen/cron_server/ssl_update.go b/server/listen/cron_server/ssl_update.go index e5f5f38..4e8ca3f 100644 --- a/server/listen/cron_server/ssl_update.go +++ b/server/listen/cron_server/ssl_update.go @@ -6,6 +6,9 @@ import ( "github.com/Jinnrry/pmail/signal" log "github.com/sirupsen/logrus" "time" + // 新增:HTTP 就绪探测 + "net/http" + "fmt" ) var expiredTime time.Time @@ -56,8 +59,33 @@ func sslCheck() { // 每天检查一遍SSL证书是否即将过期,即将过期就重新生成 func sslUpdateLoop() { for { + // 等待 HTTP 就绪后再执行 ACME 续期,避免挑战失败 + waitHTTPReady() ssl.Update(true) // 每24小时检测一次证书有效期 time.Sleep(24 * time.Hour) } } + +// 新增:HTTP 就绪探测(最多等待 ~90 秒) +func waitHTTPReady() { + port := 80 + if config.Instance != nil && config.Instance.HttpPort > 0 { + port = config.Instance.HttpPort + } + url := fmt.Sprintf("http://127.0.0.1:%d/api/ping", port) + + for i := 0; i < 90; i++ { + resp, err := http.Get(url) + if err == nil && resp != nil && resp.StatusCode == http.StatusOK { + resp.Body.Close() + log.Infof("HTTP ready: %s", url) + return + } + if resp != nil { + resp.Body.Close() + } + time.Sleep(1 * time.Second) + } + log.Warnf("HTTP not ready after 90s, skipping SSL update this round") +} diff --git a/server/listen/http_server/http_server.go b/server/listen/http_server/http_server.go index 83a9b2e..6eda257 100644 --- a/server/listen/http_server/http_server.go +++ b/server/listen/http_server/http_server.go @@ -67,6 +67,8 @@ func HttpStart() { } if config.Instance.HttpsEnabled != 2 { + // 在重定向模式下,也必须显式处理 ACME 挑战,避免跳转导致验证失败 + mux.HandleFunc("/.well-known/", controllers.AcmeChallenge) mux.HandleFunc("/api/ping", controllers.Ping) mux.HandleFunc("/", controllers.Interceptor) httpServer = &http.Server{ diff --git a/server/listen/http_server/https_server.go b/server/listen/http_server/https_server.go index b17a330..86d2816 100644 --- a/server/listen/http_server/https_server.go +++ b/server/listen/http_server/https_server.go @@ -15,6 +15,7 @@ import ( olog "log" "net/http" "time" + "errors" log "github.com/sirupsen/logrus" "github.com/spf13/cast" @@ -57,7 +58,14 @@ func HttpsStart() { } err := httpsServer.ListenAndServeTLS(config.Instance.SSLPublicKeyPath, config.Instance.SSLPrivateKeyPath) if err != nil { - panic(err) + if errors.Is(err, http.ErrServerClosed) { + // 正常关闭(重启或停机) + log.Infof("Https Server closed normally on port :%d", HttpsPort) + } else { + // 异常错误仍然打印并退出 + log.Errorf("Https Server error on port :%d, %+v", HttpsPort, err) + panic(err) + } } } } @@ -68,6 +76,8 @@ func HttpsStop() { } } +// 新增:分类处理关闭错误 + // 注入context func contextIterceptor(h controllers.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { diff --git a/server/res_init/init.go b/server/res_init/init.go index 13fba55..e85b748 100644 --- a/server/res_init/init.go +++ b/server/res_init/init.go @@ -2,6 +2,11 @@ package res_init import ( "encoding/json" + "os" + "time" + + "fmt" + "github.com/Jinnrry/pmail/config" "github.com/Jinnrry/pmail/db" "github.com/Jinnrry/pmail/dto/parsemail" @@ -15,8 +20,8 @@ import ( "github.com/Jinnrry/pmail/signal" "github.com/Jinnrry/pmail/utils/file" log "github.com/sirupsen/logrus" - "os" - "time" + // 新增:HTTP 就绪探测 + "net/http" ) func Init(serverVersion string) { @@ -31,8 +36,9 @@ func Init(serverVersion string) { for { config.Init() - // 启动前检查一遍证书 - ssl.Update(false) + // 移除:启动前立即更新证书的调用,避免 HTTP 未启动时触发 ACME + // ssl.Update(false) + parsemail.Init() err := db.Init(serverVersion) if err != nil { @@ -47,6 +53,13 @@ func Init(serverVersion string) { // http server start go http_server.HttpsStart() go http_server.HttpStart() + + // 等待 HTTP 服务就绪后再执行证书检查/续期,避免 ACME 验证失败 + waitHTTPReady() + + // 启动后检查一遍证书(非重启模式) + ssl.Update(false) + // pop3 server start go pop3_server.Start() go pop3_server.StartWithTls() @@ -104,3 +117,26 @@ func dirInit() { } } } + +// 新增:HTTP 就绪探测(最多等待 ~90 秒) +func waitHTTPReady() { + port := 80 + if config.Instance != nil && config.Instance.HttpPort > 0 { + port = config.Instance.HttpPort + } + url := fmt.Sprintf("http://127.0.0.1:%d/api/ping", port) + + for i := 0; i < 90; i++ { + resp, err := http.Get(url) + if err == nil && resp != nil && resp.StatusCode == http.StatusOK { + resp.Body.Close() + log.Infof("HTTP ready: %s", url) + return + } + if resp != nil { + resp.Body.Close() + } + time.Sleep(1 * time.Second) + } + log.Warnf("HTTP not ready after 90s, skipping immediate SSL update") +} diff --git a/server/services/setup/ssl/ssl.go b/server/services/setup/ssl/ssl.go index 3de4d3c..084c5a2 100644 --- a/server/services/setup/ssl/ssl.go +++ b/server/services/setup/ssl/ssl.go @@ -80,7 +80,7 @@ func SetSSL(sslType, priKey, crtKey string) error { func renewCertificate(privateKey *ecdsa.PrivateKey, cfg *config.Config) error { myUser := MyUser{ - Email: "i@" + cfg.Domain, + Email: "SSLMaster@" + cfg.Domain, key: privateKey, }