package db import ( "fmt" "log" "net/url" "os" "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 // -------------------- func Connect() { dsn := fmt.Sprintf( "host=%s user=%s password=%s dbname=%s port=%s sslmode=disable TimeZone=Asia/Kolkata", mustEnv("DB_HOST"), mustEnv("DB_USER"), mustEnv("DB_PASSWORD"), mustEnv("DB_NAME"), getEnv("DB_PORT", "5433"), ) 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() connectImageStore() } // 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() { host := getEnv("CATALOGUE_DB_HOST", "") if host == "" { fmt.Println("⚠️ Catalogue DB env vars 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(mustEnv("CATALOGUE_DB_USER"), mustEnv("CATALOGUE_DB_PASSWORD")), Host: fmt.Sprintf("%s:%s", host, getEnv("CATALOGUE_DB_PORT", "5432")), Path: "/" + mustEnv("CATALOGUE_DB_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") } // -------------------- // ENV HELPERS // -------------------- func mustEnv(key string) string { val := os.Getenv(key) if val == "" { log.Fatalf("Missing required env variable: %s", key) } return val } func getEnv(key, fallback string) string { if val := os.Getenv(key); val != "" { return val } return fallback }