132 lines
3.7 KiB
Go
132 lines
3.7 KiB
Go
package service
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"AI-Expert-Sidebar/internal/database"
|
|
"AI-Expert-Sidebar/internal/models"
|
|
)
|
|
|
|
// ListLibraries returns all registered knowledge libraries with entry counts.
|
|
func ListLibraries() ([]models.Library, error) {
|
|
sdb := database.GetSettings()
|
|
if sdb == nil {
|
|
return nil, fmt.Errorf("settings DB not ready")
|
|
}
|
|
var libs []models.Library
|
|
if err := sdb.Order("created_at asc").Find(&libs).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
// Populate entry count for each library
|
|
for i, lib := range libs {
|
|
libs[i].EntryCount = countEntries(lib.FilePath)
|
|
}
|
|
return libs, nil
|
|
}
|
|
|
|
// CreateLibrary registers a new knowledge library and creates its SQLite file.
|
|
func CreateLibrary(name, description string) (*models.Library, error) {
|
|
sdb := database.GetSettings()
|
|
dir := database.DataDir
|
|
|
|
fileName := sanitizeFileName(name) + ".db"
|
|
filePath := filepath.Join(dir, fileName)
|
|
|
|
// Ensure uniqueness of file path
|
|
if _, err := os.Stat(filePath); err == nil {
|
|
filePath = filepath.Join(dir, sanitizeFileName(name)+"_"+fmt.Sprintf("%d", time.Now().Unix())+".db")
|
|
}
|
|
|
|
if err := database.NewLibraryDB(filePath); err != nil {
|
|
return nil, fmt.Errorf("create library DB: %w", err)
|
|
}
|
|
|
|
lib := models.Library{Name: name, Description: description, FilePath: filePath}
|
|
if err := sdb.Create(&lib).Error; err != nil {
|
|
os.Remove(filePath) // rollback file
|
|
return nil, err
|
|
}
|
|
log.Printf("[Library] Created: %s → %s", name, filePath)
|
|
return &lib, nil
|
|
}
|
|
|
|
// SwitchLibrary makes the named library active.
|
|
func SwitchLibrary(name string) error {
|
|
sdb := database.GetSettings()
|
|
var lib models.Library
|
|
if err := sdb.Where("name = ?", name).First(&lib).Error; err != nil {
|
|
return fmt.Errorf("library %q not found", name)
|
|
}
|
|
return database.OpenLibrary(lib)
|
|
}
|
|
|
|
// DeleteLibrary removes a library from the registry (and optionally its file).
|
|
func DeleteLibrary(name string, deleteFile bool) error {
|
|
sdb := database.GetSettings()
|
|
var lib models.Library
|
|
if err := sdb.Where("name = ?", name).First(&lib).Error; err != nil {
|
|
return fmt.Errorf("library %q not found", name)
|
|
}
|
|
if err := sdb.Delete(&lib).Error; err != nil {
|
|
return err
|
|
}
|
|
if deleteFile {
|
|
return os.Remove(lib.FilePath)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// InitLibraries restores the last active library or creates the default one.
|
|
func InitLibraries() error {
|
|
sdb := database.GetSettings()
|
|
// Check active_library preference
|
|
var setting models.AppSetting
|
|
if sdb.Where("key = ?", "active_library").First(&setting).Error == nil {
|
|
if err := SwitchLibrary(setting.Value); err == nil {
|
|
return nil // restored successfully
|
|
}
|
|
}
|
|
// No preference or stale — find first library
|
|
var lib models.Library
|
|
if sdb.Order("created_at asc").First(&lib).Error == nil {
|
|
return database.OpenLibrary(lib)
|
|
}
|
|
// No libraries at all — create default
|
|
lib2, err := CreateLibrary("默认知识库", "自动创建的默认知识库")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return database.OpenLibrary(*lib2)
|
|
}
|
|
|
|
// ── helpers ───────────────────────────────────────────────────────────────────
|
|
|
|
func countEntries(filePath string) int {
|
|
db, err := database.NewLibraryDBReadOnly(filePath)
|
|
if err != nil {
|
|
return -1
|
|
}
|
|
var count int64
|
|
db.Model(&models.Entry{}).Count(&count)
|
|
return int(count)
|
|
}
|
|
|
|
func sanitizeFileName(name string) string {
|
|
result := make([]rune, 0, len(name))
|
|
for _, r := range name {
|
|
if r == '/' || r == '\\' || r == ':' || r == '*' || r == '?' || r == '"' || r == '<' || r == '>' || r == '|' {
|
|
result = append(result, '_')
|
|
} else {
|
|
result = append(result, r)
|
|
}
|
|
}
|
|
if len(result) == 0 {
|
|
return "library"
|
|
}
|
|
return string(result)
|
|
}
|