mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-17 09:24:36 +08:00
feat: migrate store layer to GORM with PostgreSQL support
- Migrate all store packages from raw database/sql to GORM ORM - Add PostgreSQL support alongside SQLite - Move EncryptedString type to crypto package for cleaner architecture - Add automatic encryption/decryption for sensitive fields (API keys, secrets) - Fix PostgreSQL AutoMigrate conflicts by skipping existing tables - Fix duplicate /klines route registration - Update tests to use GORM database connections - Add database configuration support in config package
This commit is contained in:
146
store/gorm.go
Normal file
146
store/gorm.go
Normal file
@@ -0,0 +1,146 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
)
|
||||
|
||||
// GormDB is the global GORM database connection
|
||||
var gormDB *gorm.DB
|
||||
|
||||
// DB returns the GORM database connection
|
||||
func DB() *gorm.DB {
|
||||
return gormDB
|
||||
}
|
||||
|
||||
// InitGorm initializes GORM with SQLite
|
||||
func InitGorm(dbPath string) (*gorm.DB, error) {
|
||||
db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{
|
||||
Logger: logger.Default.LogMode(logger.Silent),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open SQLite database: %w", err)
|
||||
}
|
||||
|
||||
// Set connection pool for SQLite
|
||||
sqlDB, err := db.DB()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sqlDB.SetMaxOpenConns(1)
|
||||
sqlDB.SetMaxIdleConns(1)
|
||||
|
||||
// Enable foreign keys for SQLite
|
||||
db.Exec("PRAGMA foreign_keys = ON")
|
||||
db.Exec("PRAGMA journal_mode = DELETE")
|
||||
db.Exec("PRAGMA synchronous = FULL")
|
||||
db.Exec("PRAGMA busy_timeout = 5000")
|
||||
|
||||
gormDB = db
|
||||
return db, nil
|
||||
}
|
||||
|
||||
// InitGormPostgres initializes GORM with PostgreSQL
|
||||
func InitGormPostgres(host string, port int, user, password, dbname, sslmode string) (*gorm.DB, error) {
|
||||
dsn := fmt.Sprintf(
|
||||
"host=%s port=%d user=%s password=%s dbname=%s sslmode=%s",
|
||||
host, port, user, password, dbname, sslmode,
|
||||
)
|
||||
|
||||
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
|
||||
Logger: logger.Default.LogMode(logger.Silent),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open PostgreSQL database: %w", err)
|
||||
}
|
||||
|
||||
// Set connection pool for PostgreSQL
|
||||
sqlDB, err := db.DB()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sqlDB.SetMaxOpenConns(25)
|
||||
sqlDB.SetMaxIdleConns(5)
|
||||
|
||||
gormDB = db
|
||||
return db, nil
|
||||
}
|
||||
|
||||
// InitGormWithConfig initializes GORM with provided configuration
|
||||
// Uses DBConfig from driver.go
|
||||
func InitGormWithConfig(cfg DBConfig) (*gorm.DB, error) {
|
||||
switch cfg.Type {
|
||||
case DBTypeSQLite:
|
||||
return InitGorm(cfg.Path)
|
||||
|
||||
case DBTypePostgres:
|
||||
return InitGormPostgres(
|
||||
cfg.Host,
|
||||
cfg.Port,
|
||||
cfg.User,
|
||||
cfg.Password,
|
||||
cfg.DBName,
|
||||
cfg.SSLMode,
|
||||
)
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported DB_TYPE: %s (use 'sqlite' or 'postgres')", cfg.Type)
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Query Scopes - Reusable query helpers
|
||||
// ============================================================================
|
||||
|
||||
// ForUser returns a scope that filters by user_id
|
||||
func ForUser(userID string) func(*gorm.DB) *gorm.DB {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("user_id = ?", userID)
|
||||
}
|
||||
}
|
||||
|
||||
// ForTrader returns a scope that filters by trader_id
|
||||
func ForTrader(traderID string) func(*gorm.DB) *gorm.DB {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("trader_id = ?", traderID)
|
||||
}
|
||||
}
|
||||
|
||||
// OpenPositions returns a scope for open positions
|
||||
func OpenPositions() func(*gorm.DB) *gorm.DB {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("status = ?", "OPEN")
|
||||
}
|
||||
}
|
||||
|
||||
// ClosedPositions returns a scope for closed positions
|
||||
func ClosedPositions() func(*gorm.DB) *gorm.DB {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("status = ?", "CLOSED")
|
||||
}
|
||||
}
|
||||
|
||||
// OrderByCreatedDesc returns a scope that orders by created_at DESC
|
||||
func OrderByCreatedDesc() func(*gorm.DB) *gorm.DB {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db.Order("created_at DESC")
|
||||
}
|
||||
}
|
||||
|
||||
// OrderByUpdatedDesc returns a scope that orders by updated_at DESC
|
||||
func OrderByUpdatedDesc() func(*gorm.DB) *gorm.DB {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db.Order("updated_at DESC")
|
||||
}
|
||||
}
|
||||
|
||||
// Paginate returns a scope for pagination
|
||||
func Paginate(limit, offset int) func(*gorm.DB) *gorm.DB {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db.Limit(limit).Offset(offset)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user