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 }