package database import ( "fmt" "log" "os" "time" "github.com/glebarez/sqlite" "gorm.io/driver/mysql" "gorm.io/driver/postgres" "gorm.io/gorm" "gorm.io/gorm/logger" "oci-portal/internal/model" ) // newDBLogger 构造脱敏的 GORM 日志器(S-08):SQL 一律参数化输出, // 实参(密码哈希 / token / 任务 payload)不落日志;业务正常路径的 // record-not-found 不打 SQL。 func newDBLogger() logger.Interface { return logger.New(log.New(os.Stdout, "\r\n", log.LstdFlags), logger.Config{ SlowThreshold: 200 * time.Millisecond, LogLevel: logger.Warn, IgnoreRecordNotFoundError: true, ParameterizedQueries: true, }) } // Open 按驱动打开数据库并自动迁移全部模型。 // driver 取值 sqlite(默认)/mysql/postgres;sqlite 用 path,其余用 dsn。 func Open(driver, dsn, path string) (*gorm.DB, error) { dialector, err := buildDialector(driver, dsn, path) if err != nil { return nil, err } db, err := gorm.Open(dialector, &gorm.Config{ Logger: newDBLogger(), }) if err != nil { return nil, fmt.Errorf("open %s database: %w", dialector.Name(), err) } if err := autoMigrate(db); err != nil { return nil, fmt.Errorf("auto migrate: %w", err) } return db, nil } // buildDialector 把驱动名映射为 GORM dialector;三个驱动均为纯 Go,不引入 cgo。 // MySQL 设 DefaultStringSize=512:未标 size 的 string 建为 varchar(512), // 避免 OCID / URL 类字段被默认 varchar(191) 截断(SQLite/PG 的 string 天然无长度上限)。 func buildDialector(driver, dsn, path string) (gorm.Dialector, error) { switch driver { case "", "sqlite": return sqlite.Open(path), nil case "mysql": return mysql.New(mysql.Config{DSN: dsn, DefaultStringSize: 512}), nil case "postgres": return postgres.Open(dsn), nil default: return nil, fmt.Errorf("unsupported DB_DRIVER %q (sqlite/mysql/postgres)", driver) } } func autoMigrate(db *gorm.DB) error { return db.AutoMigrate( &model.User{}, &model.UserIdentity{}, &model.UserPasskey{}, &model.UserSession{}, &model.OciConfig{}, &model.Task{}, &model.TaskLog{}, &model.CheckSnapshot{}, &model.CostSnapshot{}, &model.RegionCache{}, &model.CompartmentCache{}, &model.Setting{}, &model.SystemLog{}, &model.LogEvent{}, &model.Proxy{}, &model.AiKey{}, &model.AiChannel{}, &model.AiModelCache{}, &model.AiModelBlacklist{}, &model.AiCallLog{}, &model.AiContentLog{}, ) }