package store import ( "fmt" "gorm.io/driver/sqlite" "gorm.io/gorm" "gorm.io/gorm/logger" ) // Open opens (or creates) the SQLite database at path and runs GORM // AutoMigrate for all models. SQLite with WAL is used for single-instance // durability; the abstraction lives entirely in this package so a future // migration to Postgres stays localised. func Open(path string) (*gorm.DB, error) { // _busy_timeout helps under concurrent writes; _foreign_keys enforces FK // constraints that GORM expects; _journal_mode=WAL improves read/write // concurrency for the single-file database. dsn := fmt.Sprintf("%s?_busy_timeout=5000&_foreign_keys=on&_journal_mode=WAL", path) db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{ // Silent: our handlers emit their own HTTP errors and frequently run // "first-or-create" / 404 lookups whose ErrRecordNotFound would // otherwise spam the operator's logs. Logger: logger.Default.LogMode(logger.Silent), }) if err != nil { return nil, fmt.Errorf("open sqlite: %w", err) } if err := db.AutoMigrate( &AdminUser{}, &App{}, &ModelConfig{}, &Skill{}, &Session{}, &Message{}, &AuditLog{}, ); err != nil { return nil, fmt.Errorf("auto-migrate: %w", err) } // Compound uniqueness: a skill slug is unique per app. Enforced in code // to avoid depending on DB-specific index syntax during migration. return db, nil }