Files
backend_fiesta/db/connect.go
Suriya fab06bb33e Bridge global catalogue DB to per-store product catalogue
Adds a separate CatalogueDB (pgvector) connection alongside the main
nearledb, plus a new catalogue module (repository/service/controller/
routes) to browse it by brand, category, and keyword, with brand
optional so the whole ~237-product catalogue can be browsed unfiltered.

Adds the actual bridge: importing a catalogue product snapshots it into
the tenant's own products table (keyed on brand+catalogueid, since a
catalogue row's bare id is only unique within its own brand table),
then links it via the existing productlocations upsert. Re-importing
tops up stock and refreshes price instead of duplicating. Also adds an
imported-refs endpoint so the frontend can badge already-imported items
without diffing full product lists, and wires the new AWS S3 image
store used to resolve catalogue product photos.

Bumps Go/Docker to 1.24 for the AWS SDK dependency this needs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 17:34:20 +05:30

130 lines
3.0 KiB
Go

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
}