package main import ( "context" "database/sql" "fmt" "log" "net/http" "os" "time" "github.com/joho/godotenv" _ "github.com/lib/pq" "github.com/lyx0/nourybot/internal/data" "go.uber.org/zap" ) type config struct { port int db struct { dsn string maxOpenConns int maxIdleConns int maxIdleTime string } } type application struct { Logger *zap.SugaredLogger Db *sql.DB Models data.Models } func main() { var cfg config // Initialize a new sugared logger that we'll pass on // down through the application. sugar := zap.NewExample().Sugar() defer sugar.Sync() err := godotenv.Load() if err != nil { log.Fatal("Error loading .env file") } // Database cfg.db.dsn = os.Getenv("DB_DSN") cfg.port = 3000 cfg.db.maxOpenConns = 25 cfg.db.maxIdleConns = 25 cfg.db.maxIdleTime = "15m" // Establish database connection db, err := openDB(cfg) if err != nil { sugar.Fatal(err) } // Initialize Application app := &application{ Logger: sugar, Db: db, Models: data.NewModels(db), } srv := &http.Server{ Addr: fmt.Sprintf(":%d", cfg.port), Handler: app.routes(), IdleTimeout: time.Minute, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, } err = srv.ListenAndServe() sugar.Fatal(err) } // openDB returns the sql.DB connection pool. func openDB(cfg config) (*sql.DB, error) { // sql.Open() creates an empty connection pool with the provided DSN db, err := sql.Open("postgres", cfg.db.dsn) if err != nil { return nil, err } // Set database restraints. db.SetMaxOpenConns(cfg.db.maxOpenConns) db.SetMaxIdleConns(cfg.db.maxIdleConns) // Parse the maxIdleTime string into an actual duration and set it. duration, err := time.ParseDuration(cfg.db.maxIdleTime) if err != nil { return nil, err } db.SetConnMaxIdleTime(duration) // Create a new context with a 5 second timeout. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() // db.PingContext() is needed to actually check if the // connection to the database was successful. err = db.PingContext(ctx) if err != nil { return nil, err } return db, nil }