package database import ( "fmt" "strings" "go.uber.org/zap" "gorm.io/driver/postgres" "gorm.io/gorm" "api_bb/pkg/logger" ) type Database struct { DB *gorm.DB cfg *Config } type Config struct { URL string } func NewDatabase(cfg *Config) *Database { return &Database{ cfg: cfg, } } // Connect устанавливает соединение с базой данных func (d *Database) Connect() error { zapLogger := logger.Get() // Логирование попытки подключения к БД zapLogger.Info("attempting to connect to database", zap.String("host", ExtractHostFromDSN(d.cfg.URL)), zap.String("database", ExtractDBNameFromDSN(d.cfg.URL)), ) db, err := gorm.Open(postgres.Open(d.cfg.URL), &gorm.Config{}) if err != nil { zapLogger.Error("failed to connect to database", zap.Error(err), zap.String("database_url", MaskPassword(d.cfg.URL)), ) return fmt.Errorf("failed to connect to database: %w", err) } d.DB = db // Логирование успешного подключения к БД zapLogger.Info("successfully connected to database", zap.String("host", ExtractHostFromDSN(d.cfg.URL)), zap.String("database", ExtractDBNameFromDSN(d.cfg.URL)), ) return nil } // Ping проверяет соединение с базой данных func (d *Database) Ping() error { zapLogger := logger.Get() sqlDB, err := d.DB.DB() if err != nil { zapLogger.Error("failed to get database instance", zap.Error(err)) return fmt.Errorf("failed to get database instance: %w", err) } if err := sqlDB.Ping(); err != nil { zapLogger.Error("database ping failed", zap.Error(err)) return fmt.Errorf("database ping failed: %w", err) } zapLogger.Info("database ping successful") return nil } // Close закрывает соединение с базой данных func (d *Database) Close() error { zapLogger := logger.Get() if d.DB == nil { return nil } sqlDB, err := d.DB.DB() if err != nil { zapLogger.Error("failed to get database instance for closing", zap.Error(err)) return fmt.Errorf("failed to get database instance: %w", err) } zapLogger.Info("closing database connection") if err := sqlDB.Close(); err != nil { zapLogger.Error("failed to close database connection", zap.Error(err)) return fmt.Errorf("failed to close database connection: %w", err) } zapLogger.Info("database connection closed successfully") return nil } // Вспомогательные функции для работы с DSN // ExtractHostFromDSN извлекает хост из DSN строки func ExtractHostFromDSN(dsn string) string { // Простая реализация для PostgreSQL DSN parts := strings.Split(dsn, " ") for _, part := range parts { if strings.HasPrefix(part, "host=") { return strings.TrimPrefix(part, "host=") } } return "unknown" } // ExtractDBNameFromDSN извлекает имя базы данных из DSN строки func ExtractDBNameFromDSN(dsn string) string { // Простая реализация для PostgreSQL DSN parts := strings.Split(dsn, " ") for _, part := range parts { if strings.HasPrefix(part, "dbname=") { return strings.TrimPrefix(part, "dbname=") } } return "unknown" } // MaskPassword маскирует пароль в DSN строке для безопасного логирования func MaskPassword(dsn string) string { // Простая реализация - заменяет пароль на *** parts := strings.Split(dsn, " ") for i, part := range parts { if strings.HasPrefix(part, "password=") { parts[i] = "password=***" break } } return strings.Join(parts, " ") }