package data import ( "context" "fmt" "log" "os" "time" _ "github.com/go-sql-driver/mysql" "github.com/google/wire" "github.com/redis/go-redis/v9" "gorm.io/driver/mysql" "gorm.io/gorm" "kra/internal/conf" "kra/internal/data/ent" "kra/internal/data/ent/migrate" ) // ProviderSet is data providers. var ProviderSet = wire.NewSet(NewData, NewAdminRepo, NewSystemRepo, NewAccessRepo, NewSettingsRepo, NewVersionRepo, NewExportRepo, NewAuditRepo, NewTaskRepo, NewMediaRepo, NewAnnouncementRepo, NewEmailRepo, NewCache, NewFileStorage) // Data is a struct that contains the database client. type Data struct { db *ent.Client gormDB *gorm.DB redis *redis.Client } // NewData creates a new Data instance. func NewData(c *conf.Data) (*Data, func(), error) { if c == nil || c.Database == nil { return nil, nil, fmt.Errorf("database configuration is required") } db, err := ent.Open(c.Database.Driver, c.Database.Source) if err != nil { return nil, nil, fmt.Errorf("open ent database: %w", err) } gormDB, err := gorm.Open(mysql.Open(c.Database.Source), &gorm.Config{}) if err != nil { _ = db.Close() return nil, nil, err } // The announcement module owns its table lifecycle independently of the // first-run database initializer. if err = gormDB.AutoMigrate(&announcementPO{}); err != nil { _ = db.Close() return nil, nil, fmt.Errorf("migrate announcement table: %w", err) } var redisClient *redis.Client if c.Redis != nil && c.Redis.Addr != "" { options := &redis.Options{Addr: c.Redis.Addr} if c.Redis.Network != "" { options.Network = c.Redis.Network } if c.Redis.ReadTimeout != nil { options.ReadTimeout = c.Redis.ReadTimeout.AsDuration() } if c.Redis.WriteTimeout != nil { options.WriteTimeout = c.Redis.WriteTimeout.AsDuration() } candidate := redis.NewClient(options) pingCtx, cancel := context.WithTimeout(context.Background(), 800*time.Millisecond) if pingErr := candidate.Ping(pingCtx).Err(); pingErr != nil { log.Printf("redis unavailable, using in-memory cache: %v", pingErr) _ = candidate.Close() } else { redisClient = candidate } cancel() } if os.Getenv("DEPLOY_ENV") == "dev" { // Enable debug mode for detailed logging. db = db.Debug() // Run the auto migration tool. if err = db.Schema.Create(context.Background(), migrate.WithDropIndex(true)); err != nil { return nil, nil, err } } cleanup := func() { _ = db.Close() if redisClient != nil { _ = redisClient.Close() } } return &Data{ db: db, gormDB: gormDB, redis: redisClient, }, cleanup, nil }