package server import ( "context" "errors" "fmt" "log" "net/http" "sync" "time" "printer.backend/internal/config" "printer.backend/internal/server/admin" "printer.backend/internal/server/api" ) // Run starts the API and admin HTTP servers and blocks until ctx is cancelled. func Run(ctx context.Context, cfg *config.Config) error { apiSrv := &http.Server{ Addr: cfg.APIAddr(), Handler: api.NewHandler(), ReadTimeout: 15 * time.Second, WriteTimeout: 15 * time.Second, } apiBase := "http://" + cfg.APIAddr() adminSrv := &http.Server{ Addr: cfg.AdminAddr(), Handler: admin.NewHandler(apiBase), ReadTimeout: 15 * time.Second, WriteTimeout: 15 * time.Second, } var wg sync.WaitGroup errCh := make(chan error, 2) start := func(name string, srv *http.Server) { wg.Add(1) go func() { defer wg.Done() log.Printf("%s listening on http://%s", name, srv.Addr) if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { errCh <- fmt.Errorf("%s: %w", name, err) } }() } start("API", apiSrv) start("Admin", adminSrv) select { case <-ctx.Done(): case err := <-errCh: return err } shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() var shutdownErr error for _, srv := range []*http.Server{apiSrv, adminSrv} { if err := srv.Shutdown(shutdownCtx); err != nil { shutdownErr = errors.Join(shutdownErr, err) } } wg.Wait() return shutdownErr }