package db import ( "database/sql" "fmt" "sync" "time" "go-dy/internal/config" _ "github.com/go-sql-driver/mysql" ) var ( mu sync.Mutex conn *sql.DB ) func getDSN(cfg config.Config) string { if cfg.DBDsn != "" { return cfg.DBDsn } // sensible default for local dev return "root:password@tcp(127.0.0.1:3306)/go-dy?parseTime=true&charset=utf8mb4&loc=Local" } // Get returns a shared DB connection. It retries initialization if previous attempts failed. func Get(cfg config.Config) (*sql.DB, error) { mu.Lock() defer mu.Unlock() if conn != nil { return conn, nil } dsn := getDSN(cfg) db, err := sql.Open("mysql", dsn) if err != nil { return nil, err } db.SetMaxOpenConns(20) db.SetMaxIdleConns(5) db.SetConnMaxLifetime(30 * time.Minute) // verify connection first if e := db.Ping(); e != nil { _ = db.Close() return nil, e } // init schema after successful ping if e := initSchema(db); e != nil { _ = db.Close() return nil, e } conn = db return conn, nil } func initSchema(db *sql.DB) error { // minimal users table _, err := db.Exec(` CREATE TABLE IF NOT EXISTS users ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255) NOT NULL UNIQUE, password_hash VARCHAR(255) NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); `) if err != nil { return fmt.Errorf("init schema: %w", err) } return nil }