package app import ( "context" "errors" "log/slog" "net/http" "os" "os/signal" "syscall" "git.michelsen.id/phill/preface-tools/internal/auth" "git.michelsen.id/phill/preface-tools/internal/httpserver" "git.michelsen.id/phill/preface-tools/internal/tools" "git.michelsen.id/phill/preface-tools/internal/tools/comicanimator" ) func Run() error { cfg, err := LoadConfigFromEnv() if err != nil { return err } comicCfg, err := comicanimator.LoadConfigFromEnv() if err != nil { return err } var handler slog.Handler = slog.NewTextHandler(os.Stdout, nil) if cfg.LogFormat == "json" { handler = slog.NewJSONHandler(os.Stdout, nil) } log := slog.New(handler) a := auth.New(cfg.StudentPIN, cfg.InstructorPIN, cfg.SessionSecret, cfg.SessionDuration, cfg.Environment == "production") comic, err := comicanimator.New(comicCfg, log, a) if err != nil { return err } registry := tools.NewRegistry() if err = registry.Register(comic); err != nil { return err } srv := &http.Server{Addr: cfg.Address, Handler: httpserver.New(a, registry, log).Handler(), ReadHeaderTimeout: cfg.ReadHeaderTimeout, ReadTimeout: cfg.ReadTimeout, WriteTimeout: cfg.WriteTimeout, IdleTimeout: cfg.IdleTimeout} ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer stop() workerDone := make(chan error, 1) go func() { workerDone <- comic.Run(ctx) }() serverDone := make(chan error, 1) go func() { log.Info("server starting", "address", cfg.Address); serverDone <- srv.ListenAndServe() }() select { case err = <-serverDone: if !errors.Is(err, http.ErrServerClosed) { stop() return err } case <-ctx.Done(): } shutdownCtx, cancel := context.WithTimeout(context.Background(), cfg.ShutdownTimeout) defer cancel() if err := srv.Shutdown(shutdownCtx); err != nil { return err } stop() return <-workerDone }