Files
AI-Expert-Sidebar/internal/database/db.go
T
2026-04-01 14:09:33 +08:00

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
}