init: initial commit
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user