package main import ( "context" "log" "net/http" "os" "os/signal" "syscall" "time" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" "github.com/jackc/pgx/v5/pgxpool" ) func main() { // Load configuration from environment port := getEnv("PORT", "8080") dbURL := getEnv("DATABASE_URL", "postgres://flowreader:flowreader@db:5432/flowreader?sslmode=disable") // Initialize database connection pool ctx := context.Background() pool, err := pgxpool.New(ctx, dbURL) if err != nil { log.Fatalf("Unable to create connection pool: %v", err) } defer pool.Close() // Verify database connection if err := pool.Ping(ctx); err != nil { log.Fatalf("Unable to ping database: %v", err) } log.Println("Connected to database") // Initialize router r := chi.NewRouter() // Middleware r.Use(middleware.Logger) r.Use(middleware.Recoverer) r.Use(middleware.RequestID) r.Use(middleware.RealIP) r.Use(middleware.Timeout(30 * time.Second)) // Health check endpoint r.Get("/health", func(w http.ResponseWriter, r *http.Request) { // Check database connection if err := pool.Ping(r.Context()); err != nil { http.Error(w, "Database connection failed", http.StatusServiceUnavailable) return } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) w.Write([]byte(`{"status":"ok","service":"flowreader"}`)) }) // API routes placeholder r.Route("/api/v1", func(r chi.Router) { r.Get("/", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.Write([]byte(`{"message":"FlowReader API v1"}`)) }) }) // Create server srv := &http.Server{ Addr: ":" + port, Handler: r, ReadTimeout: 15 * time.Second, WriteTimeout: 15 * time.Second, IdleTimeout: 60 * time.Second, } // Graceful shutdown go func() { log.Printf("Server starting on port %s", port) if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { log.Fatalf("Server failed: %v", err) } }() // Wait for interrupt signal quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit log.Println("Shutting down server...") ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() if err := srv.Shutdown(ctx); err != nil { log.Fatalf("Server forced to shutdown: %v", err) } log.Println("Server exited properly") } // getEnv returns the value of an environment variable or a default value func getEnv(key, defaultValue string) string { if value, exists := os.LookupEnv(key); exists { return value } return defaultValue }