package main import ( "context" "log/slog" "net/http" "os" "os/signal" "path/filepath" "syscall" "time" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" "qbank/internal/config" "qbank/internal/db" ) func main() { cfg := config.Load() logger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) slog.SetDefault(logger) if err := os.MkdirAll(cfg.DataDir, 0755); err != nil { slog.Error("create data dir", "err", err) os.Exit(1) } database, err := db.Open(filepath.Join(cfg.DataDir, "qbank.db")) if err != nil { slog.Error("open database", "err", err) os.Exit(1) } defer database.Close() if err := db.Seed(database, cfg.AdminUsers); err != nil { slog.Error("seed users", "err", err) os.Exit(1) } _ = db.New(database) // repo will be wired into handlers in later phases r := chi.NewRouter() r.Use(middleware.RequestID) r.Use(requestLogger(logger)) r.Use(middleware.Recoverer) r.Get("/healthz", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte("OK")) }) srv := &http.Server{ Addr: ":" + cfg.Port, Handler: r, ReadTimeout: 15 * time.Second, WriteTimeout: 30 * time.Second, IdleTimeout: 60 * time.Second, } go func() { slog.Info("server starting", "port", cfg.Port) if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { slog.Error("server error", "err", err) os.Exit(1) } }() quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() if err := srv.Shutdown(ctx); err != nil { slog.Error("shutdown error", "err", err) } slog.Info("server stopped") } func requestLogger(logger *slog.Logger) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { start := time.Now() ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor) next.ServeHTTP(ww, r) logger.Info("request", "method", r.Method, "path", r.URL.Path, "status", ww.Status(), "duration_ms", time.Since(start).Milliseconds(), "request_id", middleware.GetReqID(r.Context()), ) }) } }