package db import ( "fmt" "log" "nearle/config" "net/url" "time" "gorm.io/driver/postgres" "gorm.io/gorm" ) var ( DB *gorm.DB // CatalogueDB is a separate connection to the pgvector/catalogue database. // It is intentionally isolated from DB (nearledb) so catalogue integration // never touches the main database. CatalogueDB *gorm.DB ) // -------------------- // DATABASE CONNECTION // -------------------- // Connect opens the main database and then the optional catalogue database // and image store. Everything it needs has already been validated by // config.Load, so a missing variable can no longer surface here as a // log.Fatal halfway through boot. func Connect(cfg *config.Config) { dsn := fmt.Sprintf( "host=%s user=%s password=%s dbname=%s port=%s sslmode=disable TimeZone=Asia/Kolkata", cfg.DB.Host, cfg.DB.User, cfg.DB.Password, cfg.DB.Name, cfg.DB.Port, ) var err error DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{}) if err != nil { log.Fatal("❌ Could not connect to database:", err) } setupDB(DB) fmt.Println("✅ Database connected") connectCatalogueDB(cfg.Catalogue) connectImageStore(cfg.S3) } // connectCatalogueDB opens the read-only connection to the catalogue // (pgvector) database. If its env vars are not set, catalogue endpoints // are simply unavailable — this must never block startup of the main app. func connectCatalogueDB(c config.DBConfig) { if !c.Enabled() { fmt.Println("⚠️ CATALOGUE_DB_HOST not set, skipping catalogue DB connection") return } // Built as a URL (not a keyword=value DSN) and percent-encoded via // net/url, because this credential's password contains characters // (quotes, #) that the keyword=value DSN format would misparse as // quoting/comment syntax. dsnURL := url.URL{ Scheme: "postgres", User: url.UserPassword(c.User, c.Password), Host: fmt.Sprintf("%s:%s", c.Host, c.Port), Path: "/" + c.Name, } q := dsnURL.Query() q.Set("sslmode", "disable") dsnURL.RawQuery = q.Encode() catalogueDB, err := gorm.Open(postgres.Open(dsnURL.String()), &gorm.Config{}) if err != nil { log.Println("❌ Could not connect to catalogue database:", err) return } setupDB(catalogueDB) CatalogueDB = catalogueDB fmt.Println("✅ Catalogue database connected") } func setupDB(database *gorm.DB) { sqlDB, err := database.DB() if err != nil { log.Fatal("❌ Failed to get DB from GORM:", err) } sqlDB.SetMaxIdleConns(100) sqlDB.SetMaxOpenConns(1000) sqlDB.SetConnMaxLifetime(time.Minute * 5) } // -------------------- // DATABASE SHUTDOWN // -------------------- func CloseDB() { if DB != nil { if sqlDB, err := DB.DB(); err == nil { sqlDB.Close() } } if CatalogueDB != nil { if sqlDB, err := CatalogueDB.DB(); err == nil { sqlDB.Close() } } fmt.Println("Connection closed Successfully") }