Files
cmroubao/backend-api/cmd/api/main.go
T

114 lines
2.3 KiB
Go
Raw Normal View History

package main
import (
"context"
"errors"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"cmroubao/backend-api/internal/config"
"cmroubao/backend-api/internal/platform/database"
"cmroubao/backend-api/internal/transport/httpapi"
)
func main() {
if err := run(); err != nil {
log.Printf("cmroubao API stopped: %v", err)
os.Exit(1)
}
}
func run() error {
cfg, err := config.Load(os.LookupEnv)
if err != nil {
return err
}
startupContext, cancelStartup := context.WithTimeout(
context.Background(),
cfg.ReadTimeout,
)
defer cancelStartup()
db, err := database.Open(startupContext, cfg.DatabasePath)
if err != nil {
return errors.New("database startup failed")
}
defer func() {
if err := db.Close(); err != nil {
log.Print("database close failed")
}
}()
router, err := httpapi.NewRouter(db, func(event string) {
log.Print(event)
})
if err != nil {
return errors.New("HTTP router setup failed")
}
server := httpapi.NewServer(cfg, router)
signalContext, stopSignals := signal.NotifyContext(
context.Background(),
os.Interrupt,
syscall.SIGTERM,
)
defer stopSignals()
serverErrors := make(chan error, 1)
go func() {
serverErrors <- server.ListenAndServe()
}()
select {
case err := <-serverErrors:
if errors.Is(err, http.ErrServerClosed) {
return nil
}
return errors.New("HTTP server failed")
case <-signalContext.Done():
return shutdownServer(server, serverErrors, cfg.ShutdownTimeout)
}
}
func shutdownServer(
server *http.Server,
serverErrors <-chan error,
timeout time.Duration,
) error {
shutdownContext, cancelShutdown := context.WithTimeout(
context.Background(),
timeout,
)
defer cancelShutdown()
shutdownErr := server.Shutdown(shutdownContext)
if shutdownErr != nil {
if err := server.Close(); err != nil {
return errors.New("HTTP server force close failed")
}
}
waitTimeout := timeout
if waitTimeout > time.Second {
waitTimeout = time.Second
}
timer := time.NewTimer(waitTimeout)
defer timer.Stop()
select {
case err := <-serverErrors:
if err != nil && !errors.Is(err, http.ErrServerClosed) {
return errors.New("HTTP server failed during shutdown")
}
case <-timer.C:
_ = server.Close()
return errors.New("HTTP server did not stop")
}
if shutdownErr != nil {
return errors.New("HTTP server graceful shutdown timed out")
}
return nil
}