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) }