package app import ( "context" "database/sql" "errors" "fmt" "net/http" "time" "github.com/owen/vibeproxy/internal/metrics" ) type adminServer struct { srv *http.Server } func newAdminServer(addr string, handler http.Handler) *adminServer { return &adminServer{srv: &http.Server{ Addr: addr, Handler: handler, ReadTimeout: 30 * time.Second, WriteTimeout: 60 * time.Second, IdleTimeout: 120 * time.Second, }} } func (a *adminServer) Start(ctx context.Context) error { errCh := make(chan error, 1) go func() { if err := a.srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { errCh <- fmt.Errorf("admin listener: %w", err) } }() select { case <-ctx.Done(): shutdownCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel() return a.srv.Shutdown(shutdownCtx) case err := <-errCh: return err } } func healthzHandler() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte("ok")) }) } func readyzHandler(db *sql.DB) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if err := db.PingContext(r.Context()); err != nil { w.WriteHeader(http.StatusServiceUnavailable) w.Write([]byte("database unavailable")) return } w.WriteHeader(http.StatusOK) w.Write([]byte("ready")) }) } func metricsHandler() http.Handler { return metrics.Handler() }