129 lines
3.2 KiB
Go
129 lines
3.2 KiB
Go
package database
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
|
|
"github.com/glebarez/sqlite"
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/logger"
|
|
|
|
"AI-Expert-Sidebar/internal/models"
|
|
)
|
|
|
|
var (
|
|
mu sync.RWMutex
|
|
settingsDB *gorm.DB
|
|
activeLib *gorm.DB
|
|
activeLibNm string
|
|
DataDir string
|
|
)
|
|
|
|
// Init opens/creates the settings database ($HOME/Library/Application Support/AI-Expert-Sidebar/settings.db).
|
|
func Init() error {
|
|
dir, err := appDataDir()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
DataDir = dir
|
|
if err := os.MkdirAll(dir, 0o750); err != nil {
|
|
return fmt.Errorf("create data dir: %w", err)
|
|
}
|
|
|
|
db, err := openSQLite(filepath.Join(dir, "settings.db"))
|
|
if err != nil {
|
|
return fmt.Errorf("open settings.db: %w", err)
|
|
}
|
|
if err := db.AutoMigrate(&models.AppSetting{}, &models.Library{}); err != nil {
|
|
return fmt.Errorf("migrate settings schema: %w", err)
|
|
}
|
|
mu.Lock()
|
|
settingsDB = db
|
|
mu.Unlock()
|
|
log.Printf("[DB] Settings DB ready at %s/settings.db", dir)
|
|
return nil
|
|
}
|
|
|
|
// OpenLibrary switches the active knowledge library.
|
|
func OpenLibrary(lib models.Library) error {
|
|
db, err := openSQLite(lib.FilePath)
|
|
if err != nil {
|
|
return fmt.Errorf("open library %q: %w", lib.Name, err)
|
|
}
|
|
if err := db.AutoMigrate(&models.Entry{}); err != nil {
|
|
return fmt.Errorf("migrate library %q: %w", lib.Name, err)
|
|
}
|
|
mu.Lock()
|
|
activeLib = db
|
|
activeLibNm = lib.Name
|
|
mu.Unlock()
|
|
// Persist preference
|
|
settingsDB.Exec(
|
|
"INSERT INTO app_settings(key,value) VALUES(?,?) ON CONFLICT(key) DO UPDATE SET value=excluded.value",
|
|
"active_library", lib.Name,
|
|
)
|
|
log.Printf("[DB] Active library: %s", lib.Name)
|
|
return nil
|
|
}
|
|
|
|
// NewLibraryDB creates a fresh SQLite DB at path and migrates the Entry schema.
|
|
func NewLibraryDB(path string) error {
|
|
db, err := openSQLite(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return db.AutoMigrate(&models.Entry{})
|
|
}
|
|
|
|
// NewLibraryDBReadOnly opens an existing SQLite DB read-only (for counting etc).
|
|
func NewLibraryDBReadOnly(path string) (*gorm.DB, error) {
|
|
return openSQLite(path + "?mode=ro")
|
|
}
|
|
|
|
// GetSettings returns the global settings DB (AppSetting + Library tables).
|
|
func GetSettings() *gorm.DB {
|
|
mu.RLock()
|
|
defer mu.RUnlock()
|
|
return settingsDB
|
|
}
|
|
|
|
// Get returns the active knowledge library DB.
|
|
func Get() *gorm.DB {
|
|
mu.RLock()
|
|
defer mu.RUnlock()
|
|
return activeLib
|
|
}
|
|
|
|
// GetActiveLibName returns the display name of the currently open library.
|
|
func GetActiveLibName() string {
|
|
mu.RLock()
|
|
defer mu.RUnlock()
|
|
return activeLibNm
|
|
}
|
|
|
|
// IsReady reports whether both the settings DB and an active library are open.
|
|
func IsReady() bool {
|
|
mu.RLock()
|
|
defer mu.RUnlock()
|
|
return settingsDB != nil && activeLib != nil
|
|
}
|
|
|
|
// ── helpers ───────────────────────────────────────────────────────────────────
|
|
|
|
func openSQLite(path string) (*gorm.DB, error) {
|
|
return gorm.Open(sqlite.Open(path), &gorm.Config{
|
|
Logger: logger.Default.LogMode(logger.Warn),
|
|
})
|
|
}
|
|
|
|
func appDataDir() (string, error) {
|
|
dir, err := os.UserConfigDir()
|
|
if err != nil {
|
|
return "", fmt.Errorf("user config dir: %w", err)
|
|
}
|
|
return filepath.Join(dir, "AI-Expert-Sidebar"), nil
|
|
}
|