From 9f590fe3e9141f6209b34dc4805cf8956cb7ef13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=97=E5=AD=90?= Date: Mon, 26 Jan 2026 02:47:42 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E4=BC=98=E9=9B=85?= =?UTF-8?q?=E5=85=B3=E9=97=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/app/ace.go | 75 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 62 insertions(+), 13 deletions(-) diff --git a/internal/app/ace.go b/internal/app/ace.go index 4540fccf..8fb36e81 100644 --- a/internal/app/ace.go +++ b/internal/app/ace.go @@ -5,7 +5,11 @@ import ( "errors" "fmt" "net/http" + "os" + "os/signal" "path/filepath" + "syscall" + "time" "github.com/bddjr/hlfhr" "github.com/go-chi/chi/v5" @@ -48,23 +52,68 @@ func (r *Ace) Run() error { r.cron.Start() fmt.Println("[CRON] cron scheduler started") - // start queue - r.queue.Run(context.TODO()) + // create context for queue + queueCtx, queueCancel := context.WithCancel(context.Background()) + defer queueCancel() - // run http server - if r.conf.HTTP.TLS { - cert := filepath.Join(Root, "panel/storage/cert.pem") - key := filepath.Join(Root, "panel/storage/cert.key") - fmt.Println("[HTTP] listening and serving on port", r.conf.HTTP.Port, "with tls") - if err := r.server.ListenAndServeTLS(cert, key); !errors.Is(err, http.ErrServerClosed) { - return err - } - } else { - fmt.Println("[HTTP] listening and serving on port", r.conf.HTTP.Port) - if err := r.server.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) { + // start queue + r.queue.Run(queueCtx) + + // setup graceful shutdown + quit := make(chan os.Signal, 1) + signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) + + // run http server in goroutine + serverErr := make(chan error, 1) + go func() { + if r.conf.HTTP.TLS { + cert := filepath.Join(Root, "panel/storage/cert.pem") + key := filepath.Join(Root, "panel/storage/cert.key") + fmt.Println("[HTTP] listening and serving on port", r.conf.HTTP.Port, "with tls") + if err := r.server.ListenAndServeTLS(cert, key); !errors.Is(err, http.ErrServerClosed) { + serverErr <- err + } + } else { + fmt.Println("[HTTP] listening and serving on port", r.conf.HTTP.Port) + if err := r.server.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) { + serverErr <- err + } + } + close(serverErr) + }() + + // wait for shutdown signal or server error + select { + case err := <-serverErr: + if err != nil { return err } + case sig := <-quit: + fmt.Println("\n[APP] received signal:", sig) } + // graceful shutdown + fmt.Println("[APP] shutting down gracefully...") + + // stop cron scheduler + ctx := r.cron.Stop() + <-ctx.Done() + fmt.Println("[CRON] cron scheduler stopped") + + // stop queue + queueCancel() + fmt.Println("[QUEUE] queue stopped") + + // shutdown http server + shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 30*time.Second) + defer shutdownCancel() + + if err := r.server.Shutdown(shutdownCtx); err != nil { + fmt.Println("[HTTP] server shutdown error:", err) + return err + } + fmt.Println("[HTTP] server stopped") + + fmt.Println("[APP] shutdown complete") return nil }