🎨 Refactoring the extension service and the keybinding service

This commit is contained in:
2025-07-07 13:19:59 +08:00
parent 7c2318a13f
commit 7fcfc5e992
14 changed files with 680 additions and 506 deletions

View File

@@ -0,0 +1,4 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
export * from "./models.js";

View File

@@ -0,0 +1,37 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import {Create as $Create} from "@wailsio/runtime";
/**
* DB is a database handle representing a pool of zero or more
* underlying connections. It's safe for concurrent use by multiple
* goroutines.
*
* The sql package creates and frees connections automatically; it
* also maintains a free pool of idle connections. If the database has
* a concept of per-connection state, such state can be reliably observed
* within a transaction ([Tx]) or connection ([Conn]). Once [DB.Begin] is called, the
* returned [Tx] is bound to a single connection. Once [Tx.Commit] or
* [Tx.Rollback] is called on the transaction, that transaction's
* connection is returned to [DB]'s idle connection pool. The pool size
* can be controlled with [DB.SetMaxIdleConns].
*/
export class DB {
/** Creates a new DB instance. */
constructor($$source: Partial<DB> = {}) {
Object.assign(this, $$source);
}
/**
* Creates a new DB instance from a string or object.
*/
static createFrom($$source: any = {}): DB {
let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
return new DB($$parsedSource as Partial<DB>);
}
}

View File

@@ -0,0 +1,39 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
/**
* DatabaseService provides shared database functionality
* @module
*/
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import {Call as $Call, Create as $Create} from "@wailsio/runtime";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import * as sql$0 from "../../../database/sql/models.js";
/**
* GetDB returns the database connection
*/
export function GetDB(): Promise<sql$0.DB | null> & { cancel(): void } {
let $resultPromise = $Call.ByID(228760371) as any;
let $typingPromise = $resultPromise.then(($result: any) => {
return $$createType1($result);
}) as any;
$typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
return $typingPromise;
}
/**
* OnDataPathChanged handles data path changes
*/
export function OnDataPathChanged(): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(3652863491) as any;
return $resultPromise;
}
// Private type creation functions
const $$createType0 = sql$0.DB.createFrom;
const $$createType1 = $Create.Nullable($$createType0);

View File

@@ -78,14 +78,6 @@ export function ListDeletedDocumentsMeta(): Promise<(models$0.Document | null)[]
return $typingPromise;
}
/**
* OnDataPathChanged handles data path changes
*/
export function OnDataPathChanged(): Promise<void> & { cancel(): void } {
let $resultPromise = $Call.ByID(269349439) as any;
return $resultPromise;
}
/**
* RestoreDocument restores a deleted document
*/

View File

@@ -2,6 +2,7 @@
// This file is automatically generated. DO NOT EDIT
import * as ConfigService from "./configservice.js";
import * as DatabaseService from "./databaseservice.js";
import * as DialogService from "./dialogservice.js";
import * as DocumentService from "./documentservice.js";
import * as ExtensionService from "./extensionservice.js";
@@ -15,6 +16,7 @@ import * as TranslationService from "./translationservice.js";
import * as TrayService from "./trayservice.js";
export {
ConfigService,
DatabaseService,
DialogService,
DocumentService,
ExtensionService,

View File

@@ -107,14 +107,7 @@ func NewDefaultExtensions() []Extension {
Enabled: true,
IsDefault: true,
Config: ExtensionConfig{
"defaultSourceLang": "auto",
"defaultTargetLang": "zh",
"defaultTranslator": "bing",
"showTranslateButton": true,
"showButtonOnSelect": true,
"buttonDisplayDelay": 300,
"tooltipTimeout": 0,
"maxTooltipWidth": 300,
"minSelectionLength": 2,
"maxTranslationLength": 5000,
},

View File

@@ -44,7 +44,7 @@ type Migratable interface {
// ConfigMigrationService 配置迁移服务
type ConfigMigrationService[T Migratable] struct {
logger *log.LoggerService
pathManager *PathManager
configDir string
configName string
targetVersion string
configPath string
@@ -60,12 +60,12 @@ type MigrationResult struct {
// NewConfigMigrationService 创建配置迁移服务
func NewConfigMigrationService[T Migratable](
logger *log.LoggerService,
pathManager *PathManager,
configDir string,
configName, targetVersion, configPath string,
) *ConfigMigrationService[T] {
return &ConfigMigrationService[T]{
logger: orDefault(logger, log.New()),
pathManager: orDefault(pathManager, NewPathManager()),
configDir: configDir,
configName: configName,
targetVersion: targetVersion,
configPath: configPath,
@@ -138,7 +138,7 @@ func (cms *ConfigMigrationService[T]) createBackupOptimized() (string, error) {
return "", nil
}
configDir := cms.pathManager.GetConfigDir()
configDir := cms.configDir
timestamp := time.Now().Format("20060102150405")
newBackupPath := filepath.Join(configDir, fmt.Sprintf(BackupFilePattern, cms.configName, timestamp))
@@ -172,7 +172,7 @@ func (cms *ConfigMigrationService[T]) tryQuickRecovery(existingConfig *koanf.Koa
// findLatestBackupQuick 快速查找最新备份(优化排序)
func (cms *ConfigMigrationService[T]) findLatestBackupQuick() string {
pattern := filepath.Join(cms.pathManager.GetConfigDir(), fmt.Sprintf("%s.backup.*.json", cms.configName))
pattern := filepath.Join(cms.configDir, fmt.Sprintf("%s.backup.*.json", cms.configName))
matches, err := filepath.Glob(pattern)
if err != nil || len(matches) == 0 {
return ""
@@ -317,18 +317,18 @@ func chainLoad(k *koanf.Koanf, loaders ...func() error) error {
}
// 工厂函数
func NewAppConfigMigrationService(logger *log.LoggerService, pathManager *PathManager) *ConfigMigrationService[*models.AppConfig] {
func NewAppConfigMigrationService(logger *log.LoggerService, configDir, settingsPath string) *ConfigMigrationService[*models.AppConfig] {
return NewConfigMigrationService[*models.AppConfig](
logger, pathManager, "settings", CurrentAppConfigVersion, pathManager.GetSettingsPath())
logger, configDir, "settings", CurrentAppConfigVersion, settingsPath)
}
func NewKeyBindingMigrationService(logger *log.LoggerService, pathManager *PathManager) *ConfigMigrationService[*models.KeyBindingConfig] {
func NewKeyBindingMigrationService(logger *log.LoggerService, configDir, keybindsPath string) *ConfigMigrationService[*models.KeyBindingConfig] {
return NewConfigMigrationService[*models.KeyBindingConfig](
logger, pathManager, "keybindings", CurrentKeyBindingConfigVersion, pathManager.GetKeybindsPath())
logger, configDir, "keybindings", CurrentKeyBindingConfigVersion, keybindsPath)
}
// NewExtensionMigrationService 创建扩展配置迁移服务
func NewExtensionMigrationService(logger *log.LoggerService, pathManager *PathManager) *ConfigMigrationService[*models.ExtensionSettings] {
func NewExtensionMigrationService(logger *log.LoggerService, configDir, extensionsPath string) *ConfigMigrationService[*models.ExtensionSettings] {
return NewConfigMigrationService[*models.ExtensionSettings](
logger, pathManager, "extensions", CurrentExtensionConfigVersion, pathManager.GetExtensionsPath())
logger, configDir, "extensions", CurrentExtensionConfigVersion, extensionsPath)
}

View File

@@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"os"
"path/filepath"
"sync"
"time"
"voidraft/internal/models"
@@ -19,7 +20,8 @@ import (
type ConfigService struct {
koanf *koanf.Koanf // koanf 实例
logger *log.LoggerService // 日志服务
pathManager *PathManager // 路径管理器
configDir string // 配置目录
settingsPath string // 设置文件路径
mu sync.RWMutex // 读写锁
fileProvider *file.File // 文件提供器,用于监听
@@ -53,41 +55,34 @@ func (e *ConfigError) Is(target error) bool {
}
// NewConfigService 创建新的配置服务实例
func NewConfigService(logger *log.LoggerService, pathManager *PathManager) *ConfigService {
// 设置日志服务
if logger == nil {
logger = log.New()
func NewConfigService(logger *log.LoggerService) *ConfigService {
// 获取用户主目录
homeDir, err := os.UserHomeDir()
if err != nil {
panic(fmt.Errorf("unable to get the user's home directory: %w", err))
}
// 设置路径管理器
if pathManager == nil {
pathManager = NewPathManager()
}
// 设置配置目录和设置文件路径
configDir := filepath.Join(homeDir, ".voidraft", "config")
settingsPath := filepath.Join(configDir, "settings.json")
// 使用"."作为键路径分隔符
k := koanf.New(".")
notificationService := NewConfigNotificationService(k, logger)
migrationService := NewAppConfigMigrationService(logger, pathManager)
// 构造配置服务实例
service := &ConfigService{
koanf: k,
cs := &ConfigService{
logger: logger,
pathManager: pathManager,
notificationService: notificationService,
migrationService: migrationService,
configDir: configDir,
settingsPath: settingsPath,
koanf: koanf.New("."),
migrationService: NewAppConfigMigrationService(logger, configDir, settingsPath),
}
// 初始化配置
if err := service.initConfig(); err != nil {
panic(err)
}
// 初始化配置通知服务
cs.notificationService = NewConfigNotificationService(cs.koanf, logger)
cs.initConfig()
// 启动配置文件监听
service.startWatching()
cs.startWatching()
return service
return cs
}
// setDefaults 设置默认配置
@@ -107,13 +102,12 @@ func (cs *ConfigService) initConfig() error {
defer cs.mu.Unlock()
// 检查配置文件是否存在
configPath := cs.pathManager.GetSettingsPath()
if _, err := os.Stat(configPath); os.IsNotExist(err) {
if _, err := os.Stat(cs.settingsPath); os.IsNotExist(err) {
return cs.createDefaultConfig()
}
// 配置文件存在,先加载现有配置
cs.fileProvider = file.Provider(configPath)
cs.fileProvider = file.Provider(cs.settingsPath)
if err := cs.koanf.Load(cs.fileProvider, jsonparser.Parser()); err != nil {
return &ConfigError{Operation: "load_config_file", Err: err}
}
@@ -127,7 +121,7 @@ func (cs *ConfigService) initConfig() error {
if result.Migrated && result.ConfigUpdated {
// 迁移完成且配置已更新,重新创建文件提供器以监听新文件
cs.fileProvider = file.Provider(configPath)
cs.fileProvider = file.Provider(cs.settingsPath)
}
}
@@ -137,7 +131,7 @@ func (cs *ConfigService) initConfig() error {
// createDefaultConfig 创建默认配置文件
func (cs *ConfigService) createDefaultConfig() error {
// 确保配置目录存在
if err := cs.pathManager.EnsureConfigDir(); err != nil {
if err := os.MkdirAll(cs.configDir, 0755); err != nil {
return &ConfigError{Operation: "create_config_dir", Err: err}
}
@@ -150,7 +144,7 @@ func (cs *ConfigService) createDefaultConfig() error {
}
// 创建文件提供器
cs.fileProvider = file.Provider(cs.pathManager.GetSettingsPath())
cs.fileProvider = file.Provider(cs.settingsPath)
if err := cs.koanf.Load(cs.fileProvider, jsonparser.Parser()); err != nil {
return &ConfigError{Operation: "load_config_file", Err: err}
@@ -252,7 +246,7 @@ func (cs *ConfigService) ResetConfig() error {
}
// 重新创建文件提供器
cs.fileProvider = file.Provider(cs.pathManager.GetSettingsPath())
cs.fileProvider = file.Provider(cs.settingsPath)
// 重新加载配置文件
if err := cs.koanf.Load(cs.fileProvider, jsonparser.Parser()); err != nil {
@@ -277,7 +271,7 @@ func (cs *ConfigService) writeConfigToFile() error {
return &ConfigError{Operation: "marshal_config", Err: err}
}
if err := os.WriteFile(cs.pathManager.GetSettingsPath(), configBytes, 0644); err != nil {
if err := os.WriteFile(cs.settingsPath, configBytes, 0644); err != nil {
return &ConfigError{Operation: "write_config_file", Err: err}
}

View File

@@ -0,0 +1,211 @@
package services
import (
"context"
"database/sql"
"fmt"
"os"
"path/filepath"
"sync"
"github.com/wailsapp/wails/v3/pkg/application"
"github.com/wailsapp/wails/v3/pkg/services/log"
_ "modernc.org/sqlite" // SQLite driver
)
const (
dbName = "voidraft.db"
// SQLite performance optimization settings
sqlOptimizationSettings = `
PRAGMA journal_mode = WAL;
PRAGMA synchronous = NORMAL;
PRAGMA cache_size = -64000;
PRAGMA temp_store = MEMORY;
PRAGMA foreign_keys = ON;`
// Documents table
sqlCreateDocumentsTable = `
CREATE TABLE IF NOT EXISTS documents (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
content TEXT DEFAULT '∞∞∞text-a',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
is_deleted INTEGER DEFAULT 0
)`
// Extensions table
sqlCreateExtensionsTable = `
CREATE TABLE IF NOT EXISTS extensions (
id TEXT PRIMARY KEY,
enabled INTEGER NOT NULL DEFAULT 1,
is_default INTEGER NOT NULL DEFAULT 0,
config TEXT DEFAULT '{}',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
)`
// Key bindings table
sqlCreateKeyBindingsTable = `
CREATE TABLE IF NOT EXISTS key_bindings (
id INTEGER PRIMARY KEY AUTOINCREMENT,
command TEXT NOT NULL,
extension TEXT NOT NULL,
key TEXT NOT NULL,
enabled INTEGER NOT NULL DEFAULT 1,
is_default INTEGER NOT NULL DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
UNIQUE(command, extension)
)`
)
// DatabaseService provides shared database functionality
type DatabaseService struct {
configService *ConfigService
logger *log.LoggerService
db *sql.DB
mu sync.RWMutex
ctx context.Context
}
// NewDatabaseService creates a new database service
func NewDatabaseService(configService *ConfigService, logger *log.LoggerService) *DatabaseService {
if logger == nil {
logger = log.New()
}
return &DatabaseService{
configService: configService,
logger: logger,
}
}
// OnStartup initializes the service when the application starts
func (ds *DatabaseService) OnStartup(ctx context.Context, _ application.ServiceOptions) error {
ds.ctx = ctx
return ds.initDatabase()
}
// initDatabase initializes the SQLite database
func (ds *DatabaseService) initDatabase() error {
dbPath, err := ds.getDatabasePath()
if err != nil {
return fmt.Errorf("failed to get database path: %w", err)
}
// 确保数据库目录存在
dbDir := filepath.Dir(dbPath)
if err := os.MkdirAll(dbDir, 0755); err != nil {
return fmt.Errorf("failed to create database directory: %w", err)
}
// 检查数据库文件是否存在,如果不存在则创建
if _, err := os.Stat(dbPath); os.IsNotExist(err) {
// 创建空文件
file, err := os.Create(dbPath)
if err != nil {
return fmt.Errorf("failed to create database file: %w", err)
}
file.Close()
}
db, err := sql.Open("sqlite", dbPath)
if err != nil {
return fmt.Errorf("failed to open database: %w", err)
}
ds.db = db
// Apply optimization settings
if _, err := db.Exec(sqlOptimizationSettings); err != nil {
return fmt.Errorf("failed to apply optimization settings: %w", err)
}
// Create all tables
if err := ds.createTables(); err != nil {
return fmt.Errorf("failed to create tables: %w", err)
}
// Create indexes
if err := ds.createIndexes(); err != nil {
return fmt.Errorf("failed to create indexes: %w", err)
}
return nil
}
// getDatabasePath gets the database file path
func (ds *DatabaseService) getDatabasePath() (string, error) {
config, err := ds.configService.GetConfig()
if err != nil {
return "", err
}
return filepath.Join(config.General.DataPath, dbName), nil
}
// createTables creates all database tables
func (ds *DatabaseService) createTables() error {
tables := []string{
sqlCreateDocumentsTable,
sqlCreateExtensionsTable,
sqlCreateKeyBindingsTable,
}
for _, table := range tables {
if _, err := ds.db.Exec(table); err != nil {
return err
}
}
return nil
}
// createIndexes creates database indexes
func (ds *DatabaseService) createIndexes() error {
indexes := []string{
// Documents indexes
`CREATE INDEX IF NOT EXISTS idx_documents_updated_at ON documents(updated_at DESC)`,
`CREATE INDEX IF NOT EXISTS idx_documents_title ON documents(title)`,
`CREATE INDEX IF NOT EXISTS idx_documents_is_deleted ON documents(is_deleted)`,
// Extensions indexes
`CREATE INDEX IF NOT EXISTS idx_extensions_enabled ON extensions(enabled)`,
// Key bindings indexes
`CREATE INDEX IF NOT EXISTS idx_key_bindings_command ON key_bindings(command)`,
`CREATE INDEX IF NOT EXISTS idx_key_bindings_extension ON key_bindings(extension)`,
`CREATE INDEX IF NOT EXISTS idx_key_bindings_enabled ON key_bindings(enabled)`,
}
for _, index := range indexes {
if _, err := ds.db.Exec(index); err != nil {
return err
}
}
return nil
}
// GetDB returns the database connection
func (ds *DatabaseService) GetDB() *sql.DB {
ds.mu.RLock()
defer ds.mu.RUnlock()
return ds.db
}
// OnShutdown shuts down the service when the application closes
func (ds *DatabaseService) OnShutdown() error {
if ds.db != nil {
return ds.db.Close()
}
return nil
}
// OnDataPathChanged handles data path changes
func (ds *DatabaseService) OnDataPathChanged() error {
// Close existing database
if ds.db != nil {
ds.db.Close()
}
// Reinitialize with new path
return ds.initDatabase()
}

View File

@@ -5,8 +5,6 @@ import (
"database/sql"
"errors"
"fmt"
"os"
"path/filepath"
"sync"
"time"
"voidraft/internal/models"
@@ -16,32 +14,8 @@ import (
_ "modernc.org/sqlite" // SQLite driver
)
// SQL constants for database operations
// SQL constants for document operations
const (
dbName = "voidraft.db"
// Database schema (simplified single table with auto-increment ID)
sqlCreateDocumentsTable = `
CREATE TABLE IF NOT EXISTS documents (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
content TEXT DEFAULT '∞∞∞text-a',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
is_deleted INTEGER DEFAULT 0
)`
// Performance optimization indexes
sqlCreateIndexUpdatedAt = `CREATE INDEX IF NOT EXISTS idx_documents_updated_at ON documents(updated_at DESC)`
sqlCreateIndexTitle = `CREATE INDEX IF NOT EXISTS idx_documents_title ON documents(title)`
sqlCreateIndexIsDeleted = `CREATE INDEX IF NOT EXISTS idx_documents_is_deleted ON documents(is_deleted)`
// SQLite performance optimization settings
sqlOptimizationSettings = `
PRAGMA journal_mode = WAL;
PRAGMA synchronous = NORMAL;
PRAGMA cache_size = -64000;
PRAGMA temp_store = MEMORY;
PRAGMA foreign_keys = ON;`
// Document operations
sqlGetDocumentByID = `
@@ -95,21 +69,20 @@ SELECT id FROM documents WHERE is_deleted = 0 ORDER BY id LIMIT 1`
// DocumentService provides document management functionality
type DocumentService struct {
configService *ConfigService
databaseService *DatabaseService
logger *log.LoggerService
db *sql.DB
mu sync.RWMutex
ctx context.Context
}
// NewDocumentService creates a new document service
func NewDocumentService(configService *ConfigService, logger *log.LoggerService) *DocumentService {
func NewDocumentService(databaseService *DatabaseService, logger *log.LoggerService) *DocumentService {
if logger == nil {
logger = log.New()
}
return &DocumentService{
configService: configService,
databaseService: databaseService,
logger: logger,
}
}
@@ -117,85 +90,10 @@ func NewDocumentService(configService *ConfigService, logger *log.LoggerService)
// OnStartup initializes the service when the application starts
func (ds *DocumentService) OnStartup(ctx context.Context, _ application.ServiceOptions) error {
ds.ctx = ctx
return ds.initDatabase()
}
// initDatabase initializes the SQLite database
func (ds *DocumentService) initDatabase() error {
dbPath, err := ds.getDatabasePath()
if err != nil {
return fmt.Errorf("failed to get database path: %w", err)
}
// 确保数据库目录存在
dbDir := filepath.Dir(dbPath)
if err := os.MkdirAll(dbDir, 0755); err != nil {
return fmt.Errorf("failed to create database directory: %w", err)
}
// 检查数据库文件是否存在,如果不存在则创建
if _, err := os.Stat(dbPath); os.IsNotExist(err) {
ds.logger.Info("Database file does not exist, creating empty file", "path", dbPath)
// 创建空文件
file, err := os.Create(dbPath)
if err != nil {
return fmt.Errorf("failed to create database file: %w", err)
}
file.Close()
}
db, err := sql.Open("sqlite", dbPath)
if err != nil {
return fmt.Errorf("failed to open database: %w", err)
}
ds.db = db
// Apply optimization settings
if _, err := db.Exec(sqlOptimizationSettings); err != nil {
return fmt.Errorf("failed to apply optimization settings: %w", err)
}
// Create table
if _, err := db.Exec(sqlCreateDocumentsTable); err != nil {
return fmt.Errorf("failed to create table: %w", err)
}
// Create indexes
if err := ds.createIndexes(); err != nil {
return fmt.Errorf("failed to create indexes: %w", err)
}
// Ensure default document exists
if err := ds.ensureDefaultDocument(); err != nil {
return fmt.Errorf("failed to ensure default document: %w", err)
}
return nil
}
// getDatabasePath gets the database file path
func (ds *DocumentService) getDatabasePath() (string, error) {
config, err := ds.configService.GetConfig()
if err != nil {
return "", err
}
return filepath.Join(config.General.DataPath, dbName), nil
}
// createIndexes creates database indexes
func (ds *DocumentService) createIndexes() error {
indexes := []string{
sqlCreateIndexUpdatedAt,
sqlCreateIndexTitle,
sqlCreateIndexIsDeleted,
}
for _, index := range indexes {
if _, err := ds.db.Exec(index); err != nil {
return err
}
}
return nil
}
@@ -203,7 +101,8 @@ func (ds *DocumentService) createIndexes() error {
func (ds *DocumentService) ensureDefaultDocument() error {
// Check if any document exists
var count int
err := ds.db.QueryRow(sqlCountDocuments).Scan(&count)
db := ds.databaseService.GetDB()
err := db.QueryRow(sqlCountDocuments).Scan(&count)
if err != nil {
return err
}
@@ -224,7 +123,8 @@ func (ds *DocumentService) GetDocumentByID(id int64) (*models.Document, error) {
var doc models.Document
var isDeletedInt int
row := ds.db.QueryRow(sqlGetDocumentByID, id)
db := ds.databaseService.GetDB()
row := db.QueryRow(sqlGetDocumentByID, id)
err := row.Scan(&doc.ID, &doc.Title, &doc.Content, &doc.CreatedAt, &doc.UpdatedAt, &isDeletedInt)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
@@ -252,7 +152,8 @@ func (ds *DocumentService) CreateDocument(title string) (*models.Document, error
IsDeleted: false,
}
result, err := ds.db.Exec(sqlInsertDocument, doc.Title, doc.Content, doc.CreatedAt, doc.UpdatedAt)
db := ds.databaseService.GetDB()
result, err := db.Exec(sqlInsertDocument, doc.Title, doc.Content, doc.CreatedAt, doc.UpdatedAt)
if err != nil {
return nil, fmt.Errorf("failed to create document: %w", err)
}
@@ -273,7 +174,8 @@ func (ds *DocumentService) UpdateDocumentContent(id int64, content string) error
ds.mu.Lock()
defer ds.mu.Unlock()
_, err := ds.db.Exec(sqlUpdateDocumentContent, content, time.Now(), id)
db := ds.databaseService.GetDB()
_, err := db.Exec(sqlUpdateDocumentContent, content, time.Now(), id)
if err != nil {
return fmt.Errorf("failed to update document content: %w", err)
}
@@ -285,7 +187,8 @@ func (ds *DocumentService) UpdateDocumentTitle(id int64, title string) error {
ds.mu.Lock()
defer ds.mu.Unlock()
_, err := ds.db.Exec(sqlUpdateDocumentTitle, title, time.Now(), id)
db := ds.databaseService.GetDB()
_, err := db.Exec(sqlUpdateDocumentTitle, title, time.Now(), id)
if err != nil {
return fmt.Errorf("failed to update document title: %w", err)
}
@@ -302,7 +205,8 @@ func (ds *DocumentService) DeleteDocument(id int64) error {
return fmt.Errorf("cannot delete the default document")
}
_, err := ds.db.Exec(sqlMarkDocumentAsDeleted, time.Now(), id)
db := ds.databaseService.GetDB()
_, err := db.Exec(sqlMarkDocumentAsDeleted, time.Now(), id)
if err != nil {
return fmt.Errorf("failed to mark document as deleted: %w", err)
}
@@ -314,7 +218,8 @@ func (ds *DocumentService) RestoreDocument(id int64) error {
ds.mu.Lock()
defer ds.mu.Unlock()
_, err := ds.db.Exec(sqlRestoreDocument, time.Now(), id)
db := ds.databaseService.GetDB()
_, err := db.Exec(sqlRestoreDocument, time.Now(), id)
if err != nil {
return fmt.Errorf("failed to restore document: %w", err)
}
@@ -326,7 +231,8 @@ func (ds *DocumentService) ListAllDocumentsMeta() ([]*models.Document, error) {
ds.mu.RLock()
defer ds.mu.RUnlock()
rows, err := ds.db.Query(sqlListAllDocumentsMeta)
db := ds.databaseService.GetDB()
rows, err := db.Query(sqlListAllDocumentsMeta)
if err != nil {
return nil, fmt.Errorf("failed to list document meta: %w", err)
}
@@ -351,7 +257,8 @@ func (ds *DocumentService) ListDeletedDocumentsMeta() ([]*models.Document, error
ds.mu.RLock()
defer ds.mu.RUnlock()
rows, err := ds.db.Query(sqlListDeletedDocumentsMeta)
db := ds.databaseService.GetDB()
rows, err := db.Query(sqlListDeletedDocumentsMeta)
if err != nil {
return nil, fmt.Errorf("failed to list deleted document meta: %w", err)
}
@@ -376,8 +283,9 @@ func (ds *DocumentService) GetFirstDocumentID() (int64, error) {
ds.mu.RLock()
defer ds.mu.RUnlock()
db := ds.databaseService.GetDB()
var id int64
err := ds.db.QueryRow(sqlGetFirstDocumentID).Scan(&id)
err := db.QueryRow(sqlGetFirstDocumentID).Scan(&id)
if err != nil {
if err == sql.ErrNoRows {
return 0, nil // No documents exist
@@ -386,22 +294,3 @@ func (ds *DocumentService) GetFirstDocumentID() (int64, error) {
}
return id, nil
}
// OnShutdown shuts down the service when the application closes
func (ds *DocumentService) OnShutdown() error {
if ds.db != nil {
return ds.db.Close()
}
return nil
}
// OnDataPathChanged handles data path changes
func (ds *DocumentService) OnDataPathChanged() error {
// Close existing database
if ds.db != nil {
ds.db.Close()
}
// Reinitialize with new path
return ds.initDatabase()
}

View File

@@ -2,33 +2,51 @@ package services
import (
"context"
"encoding/json"
"errors"
"fmt"
"os"
"sync"
"time"
"voidraft/internal/models"
jsonparser "github.com/knadh/koanf/parsers/json"
"github.com/knadh/koanf/providers/file"
"github.com/knadh/koanf/providers/structs"
"github.com/knadh/koanf/v2"
"github.com/wailsapp/wails/v3/pkg/application"
"github.com/wailsapp/wails/v3/pkg/services/log"
)
// SQL constants for extension operations
const (
// Extension operations
sqlGetAllExtensions = `
SELECT id, enabled, is_default, config
FROM extensions
ORDER BY id`
sqlGetExtensionByID = `
SELECT id, enabled, is_default, config
FROM extensions
WHERE id = ?`
sqlInsertExtension = `
INSERT INTO extensions (id, enabled, is_default, config, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?)`
sqlUpdateExtension = `
UPDATE extensions
SET enabled = ?, config = ?, updated_at = ?
WHERE id = ?`
sqlDeleteAllExtensions = `DELETE FROM extensions`
)
// ExtensionService 扩展管理服务
type ExtensionService struct {
koanf *koanf.Koanf
databaseService *DatabaseService
logger *log.LoggerService
pathManager *PathManager
fileProvider *file.File
mu sync.RWMutex
ctx context.Context
cancel context.CancelFunc
initOnce sync.Once
// 配置迁移服务
migrationService *ConfigMigrationService[*models.ExtensionSettings]
}
// ExtensionError 扩展错误
@@ -55,139 +73,152 @@ func (e *ExtensionError) Is(target error) bool {
}
// NewExtensionService 创建扩展服务实例
func NewExtensionService(logger *log.LoggerService, pathManager *PathManager) *ExtensionService {
func NewExtensionService(databaseService *DatabaseService, logger *log.LoggerService) *ExtensionService {
if logger == nil {
logger = log.New()
}
if pathManager == nil {
pathManager = NewPathManager()
}
ctx, cancel := context.WithCancel(context.Background())
k := koanf.New(".")
migrationService := NewExtensionMigrationService(logger, pathManager)
service := &ExtensionService{
koanf: k,
databaseService: databaseService,
logger: logger,
pathManager: pathManager,
ctx: ctx,
cancel: cancel,
migrationService: migrationService,
}
// 异步初始化
go service.initialize()
return service
}
// initialize 初始化配置
func (es *ExtensionService) initialize() {
es.initOnce.Do(func() {
if err := es.initConfig(); err != nil {
es.logger.Error("failed to initialize extension config", "error", err)
if err := es.initDatabase(); err != nil {
es.logger.Error("failed to initialize extension database", "error", err)
}
})
}
// setDefaults 设置默认值
func (es *ExtensionService) setDefaults() error {
defaultConfig := models.NewDefaultExtensionSettings()
if err := es.koanf.Load(structs.Provider(defaultConfig, "json"), nil); err != nil {
return &ExtensionError{"load_defaults", "", err}
}
return nil
}
// initConfig 初始化配置
func (es *ExtensionService) initConfig() error {
// initDatabase 初始化数据库数据
func (es *ExtensionService) initDatabase() error {
es.mu.Lock()
defer es.mu.Unlock()
// 检查配置文件是否存在
configPath := es.pathManager.GetExtensionsPath()
if _, err := os.Stat(configPath); os.IsNotExist(err) {
return es.createDefaultConfig()
// 检查是否已有扩展数据
db := es.databaseService.GetDB()
if db == nil {
return &ExtensionError{"get_database", "", fmt.Errorf("database connection is nil")}
}
// 配置文件存在,先加载现有配置
es.fileProvider = file.Provider(configPath)
if err := es.koanf.Load(es.fileProvider, jsonparser.Parser()); err != nil {
return &ExtensionError{"load_config_file", "", err}
}
// 检查并执行配置迁移
if es.migrationService != nil {
result, err := es.migrationService.MigrateConfig(es.koanf)
var count int
err := db.QueryRow("SELECT COUNT(*) FROM extensions").Scan(&count)
if err != nil {
return &ExtensionError{"migrate_config", "", err}
return &ExtensionError{"check_extensions_count", "", err}
}
if result.Migrated && result.ConfigUpdated {
// 迁移完成且配置已更新,重新创建文件提供器以监听新文件
es.fileProvider = file.Provider(configPath)
}
}
es.logger.Info("Extension database check", "existing_count", count)
return nil
}
// createDefaultConfig 创建默认配置文件
func (es *ExtensionService) createDefaultConfig() error {
if err := es.pathManager.EnsureConfigDir(); err != nil {
return &ExtensionError{"create_config_dir", "", err}
}
if err := es.setDefaults(); err != nil {
// 如果没有数据,插入默认配置
if count == 0 {
es.logger.Info("No extensions found, inserting default extensions...")
if err := es.insertDefaultExtensions(); err != nil {
es.logger.Error("Failed to insert default extensions", "error", err)
return err
}
configBytes, err := es.koanf.Marshal(jsonparser.Parser())
if err != nil {
return &ExtensionError{"marshal_config", "", err}
}
if err := os.WriteFile(es.pathManager.GetExtensionsPath(), configBytes, 0644); err != nil {
return &ExtensionError{"write_config", "", err}
}
// 创建文件提供器
es.fileProvider = file.Provider(es.pathManager.GetExtensionsPath())
if err = es.koanf.Load(es.fileProvider, jsonparser.Parser()); err != nil {
return err
}
return nil
}
// saveConfig 保存配置到文件
func (es *ExtensionService) saveExtensionConfig() error {
configBytes, err := es.koanf.Marshal(jsonparser.Parser())
if err != nil {
return &ExtensionError{"marshal_config", "", err}
}
if err := os.WriteFile(es.pathManager.GetExtensionsPath(), configBytes, 0644); err != nil {
return &ExtensionError{"write_config", "", err}
es.logger.Info("Default extensions inserted successfully")
} else {
es.logger.Info("Extensions already exist, skipping default insertion")
}
return nil
}
// insertDefaultExtensions 插入默认扩展配置
func (es *ExtensionService) insertDefaultExtensions() error {
defaultSettings := models.NewDefaultExtensionSettings()
db := es.databaseService.GetDB()
now := time.Now()
es.logger.Info("Starting to insert default extensions", "count", len(defaultSettings.Extensions))
for i, ext := range defaultSettings.Extensions {
es.logger.Info("Inserting extension", "index", i+1, "id", ext.ID, "enabled", ext.Enabled)
configJSON, err := json.Marshal(ext.Config)
if err != nil {
es.logger.Error("Failed to marshal config", "extension", ext.ID, "error", err)
return &ExtensionError{"marshal_config", string(ext.ID), err}
}
_, err = db.Exec(sqlInsertExtension,
string(ext.ID),
ext.Enabled,
ext.IsDefault,
string(configJSON),
now,
now,
)
if err != nil {
es.logger.Error("Failed to insert extension", "extension", ext.ID, "error", err)
return &ExtensionError{"insert_extension", string(ext.ID), err}
}
es.logger.Info("Successfully inserted extension", "id", ext.ID)
}
es.logger.Info("Completed inserting all default extensions")
return nil
}
// OnStartup 启动时调用
func (es *ExtensionService) OnStartup(ctx context.Context, _ application.ServiceOptions) error {
es.ctx = ctx
es.logger.Info("Extension service starting up")
// 初始化数据库
var initErr error
es.initOnce.Do(func() {
es.logger.Info("Initializing extension database...")
if err := es.initDatabase(); err != nil {
es.logger.Error("failed to initialize extension database", "error", err)
initErr = err
} else {
es.logger.Info("Extension database initialized successfully")
}
})
return initErr
}
// GetAllExtensions 获取所有扩展配置
func (es *ExtensionService) GetAllExtensions() ([]models.Extension, error) {
es.mu.RLock()
defer es.mu.RUnlock()
var settings models.ExtensionSettings
if err := es.koanf.Unmarshal("", &settings); err != nil {
return nil, &ExtensionError{"unmarshal_config", "", err}
db := es.databaseService.GetDB()
rows, err := db.Query(sqlGetAllExtensions)
if err != nil {
return nil, &ExtensionError{"query_extensions", "", err}
}
return settings.Extensions, nil
defer rows.Close()
var extensions []models.Extension
for rows.Next() {
var ext models.Extension
var configJSON string
if err := rows.Scan(&ext.ID, &ext.Enabled, &ext.IsDefault, &configJSON); err != nil {
return nil, &ExtensionError{"scan_extension", "", err}
}
if err := json.Unmarshal([]byte(configJSON), &ext.Config); err != nil {
return nil, &ExtensionError{"unmarshal_config", string(ext.ID), err}
}
extensions = append(extensions, ext)
}
if err := rows.Err(); err != nil {
return nil, &ExtensionError{"rows_error", "", err}
}
return extensions, nil
}
// UpdateExtensionEnabled 更新扩展启用状态
@@ -200,25 +231,28 @@ func (es *ExtensionService) UpdateExtensionState(id models.ExtensionID, enabled
es.mu.Lock()
defer es.mu.Unlock()
// 获取当前配置
var settings models.ExtensionSettings
if err := es.koanf.Unmarshal("", &settings); err != nil {
return &ExtensionError{"unmarshal_config", string(id), err}
db := es.databaseService.GetDB()
var configJSON []byte
var err error
if config != nil {
configJSON, err = json.Marshal(config)
if err != nil {
return &ExtensionError{"marshal_config", string(id), err}
}
} else {
// 如果没有提供配置,保持原有配置
var currentConfigJSON string
err = db.QueryRow("SELECT config FROM extensions WHERE id = ?", string(id)).Scan(&currentConfigJSON)
if err != nil {
return &ExtensionError{"query_current_config", string(id), err}
}
configJSON = []byte(currentConfigJSON)
}
// 更新扩展状态
if !settings.UpdateExtension(id, enabled, config) {
return &ExtensionError{"extension_not_found", string(id), nil}
}
// 重新加载到koanf
if err := es.koanf.Load(structs.Provider(&settings, "json"), nil); err != nil {
return &ExtensionError{"reload_config", string(id), err}
}
// 保存到文件
if err := es.saveExtensionConfig(); err != nil {
return &ExtensionError{"save_config", string(id), err}
_, err = db.Exec(sqlUpdateExtension, enabled, string(configJSON), time.Now(), string(id))
if err != nil {
return &ExtensionError{"update_extension", string(id), err}
}
es.logger.Info("extension state updated", "id", id, "enabled", enabled)
@@ -242,23 +276,18 @@ func (es *ExtensionService) ResetAllExtensionsToDefault() error {
es.mu.Lock()
defer es.mu.Unlock()
// 加载默认配置
defaultSettings := models.NewDefaultExtensionSettings()
if err := es.koanf.Load(structs.Provider(defaultSettings, "json"), nil); err != nil {
return &ExtensionError{"load_defaults", "", err}
// 删除所有现有扩展
db := es.databaseService.GetDB()
_, err := db.Exec(sqlDeleteAllExtensions)
if err != nil {
return &ExtensionError{"delete_all_extensions", "", err}
}
// 保存到文件
if err := es.saveExtensionConfig(); err != nil {
return &ExtensionError{"save_config", "", err}
// 插入默认扩展配置
if err := es.insertDefaultExtensions(); err != nil {
return err
}
es.logger.Info("all extensions reset to default")
return nil
}
// OnShutdown 关闭服务
func (es *ExtensionService) OnShutdown() error {
es.cancel()
return nil
}

View File

@@ -4,31 +4,59 @@ import (
"context"
"errors"
"fmt"
"os"
"sync"
"time"
"voidraft/internal/models"
jsonparser "github.com/knadh/koanf/parsers/json"
"github.com/knadh/koanf/providers/file"
"github.com/knadh/koanf/providers/structs"
"github.com/knadh/koanf/v2"
"github.com/wailsapp/wails/v3/pkg/application"
"github.com/wailsapp/wails/v3/pkg/services/log"
)
// SQL 查询语句
const (
// 快捷键操作
sqlGetAllKeyBindings = `
SELECT command, extension, key, enabled, is_default
FROM key_bindings
ORDER BY command
`
sqlGetKeyBindingByCommand = `
SELECT command, extension, key, enabled, is_default
FROM key_bindings
WHERE command = ?
`
sqlInsertKeyBinding = `
INSERT INTO key_bindings (command, extension, key, enabled, is_default, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?)
`
sqlUpdateKeyBinding = `
UPDATE key_bindings
SET extension = ?, key = ?, enabled = ?, updated_at = ?
WHERE command = ?
`
sqlDeleteKeyBinding = `
DELETE FROM key_bindings
WHERE command = ?
`
sqlDeleteAllKeyBindings = `
DELETE FROM key_bindings
`
)
// KeyBindingService 快捷键管理服务
type KeyBindingService struct {
koanf *koanf.Koanf
databaseService *DatabaseService
logger *log.LoggerService
pathManager *PathManager
fileProvider *file.File
mu sync.RWMutex
ctx context.Context
cancel context.CancelFunc
initOnce sync.Once
// 配置迁移服务
migrationService *ConfigMigrationService[*models.KeyBindingConfig]
}
// KeyBindingError 快捷键错误
@@ -55,138 +83,145 @@ func (e *KeyBindingError) Is(target error) bool {
}
// NewKeyBindingService 创建快捷键服务实例
func NewKeyBindingService(logger *log.LoggerService, pathManager *PathManager) *KeyBindingService {
func NewKeyBindingService(databaseService *DatabaseService, logger *log.LoggerService) *KeyBindingService {
if logger == nil {
logger = log.New()
}
if pathManager == nil {
pathManager = NewPathManager()
}
ctx, cancel := context.WithCancel(context.Background())
k := koanf.New(".")
migrationService := NewKeyBindingMigrationService(logger, pathManager)
service := &KeyBindingService{
koanf: k,
databaseService: databaseService,
logger: logger,
pathManager: pathManager,
ctx: ctx,
cancel: cancel,
migrationService: migrationService,
}
// 异步初始化
go service.initialize()
return service
}
// initialize 初始化配置
func (kbs *KeyBindingService) initialize() {
kbs.initOnce.Do(func() {
if err := kbs.initConfig(); err != nil {
kbs.logger.Error("failed to initialize keybinding config", "error", err)
}
})
}
// setDefaults 设置默认值
func (kbs *KeyBindingService) setDefaults() error {
defaultConfig := models.NewDefaultKeyBindingConfig()
if err := kbs.koanf.Load(structs.Provider(defaultConfig, "json"), nil); err != nil {
return &KeyBindingError{"load_defaults", "", err}
}
return nil
}
// initConfig 初始化配置
func (kbs *KeyBindingService) initConfig() error {
// initDatabase 初始化数据库数据
func (kbs *KeyBindingService) initDatabase() error {
kbs.mu.Lock()
defer kbs.mu.Unlock()
// 检查配置文件是否存在
configPath := kbs.pathManager.GetKeybindsPath()
if _, err := os.Stat(configPath); os.IsNotExist(err) {
return kbs.createDefaultConfig()
// 检查是否已有快捷键数据
db := kbs.databaseService.GetDB()
if db == nil {
return &KeyBindingError{"get_database", "", fmt.Errorf("database connection is nil")}
}
// 配置文件存在,先加载现有配置
kbs.fileProvider = file.Provider(configPath)
if err := kbs.koanf.Load(kbs.fileProvider, jsonparser.Parser()); err != nil {
return &KeyBindingError{"load_config_file", "", err}
}
// 检查并执行配置迁移
if kbs.migrationService != nil {
result, err := kbs.migrationService.MigrateConfig(kbs.koanf)
var count int
err := db.QueryRow("SELECT COUNT(*) FROM key_bindings").Scan(&count)
if err != nil {
return &KeyBindingError{"migrate_config", "", err}
return &KeyBindingError{"check_keybindings_count", "", err}
}
if result.Migrated && result.ConfigUpdated {
// 迁移完成且配置已更新,重新创建文件提供器以监听新文件
kbs.fileProvider = file.Provider(configPath)
kbs.logger.Info("KeyBinding database check", "existing_count", count)
// 如果没有数据,插入默认配置
if count == 0 {
kbs.logger.Info("No key bindings found, inserting default key bindings...")
if err := kbs.insertDefaultKeyBindings(); err != nil {
kbs.logger.Error("Failed to insert default key bindings", "error", err)
return err
}
kbs.logger.Info("Default key bindings inserted successfully")
} else {
kbs.logger.Info("Key bindings already exist, skipping default insertion")
}
return nil
}
// createDefaultConfig 创建默认配置文件
func (kbs *KeyBindingService) createDefaultConfig() error {
if err := kbs.pathManager.EnsureConfigDir(); err != nil {
return &KeyBindingError{"create_config_dir", "", err}
}
// insertDefaultKeyBindings 插入默认快捷键配置
func (kbs *KeyBindingService) insertDefaultKeyBindings() error {
defaultConfig := models.NewDefaultKeyBindingConfig()
db := kbs.databaseService.GetDB()
now := time.Now()
if err := kbs.setDefaults(); err != nil {
return err
}
kbs.logger.Info("Starting to insert default key bindings", "count", len(defaultConfig.KeyBindings))
configBytes, err := kbs.koanf.Marshal(jsonparser.Parser())
for i, kb := range defaultConfig.KeyBindings {
kbs.logger.Info("Inserting key binding", "index", i+1, "command", kb.Command, "key", kb.Key, "extension", kb.Extension)
_, err := db.Exec(sqlInsertKeyBinding,
kb.Command,
kb.Extension,
kb.Key,
kb.Enabled,
kb.IsDefault,
now,
now,
)
if err != nil {
return &KeyBindingError{"marshal_config", "", err}
kbs.logger.Error("Failed to insert key binding", "command", kb.Command, "error", err)
return &KeyBindingError{"insert_keybinding", string(kb.Command), err}
}
if err := os.WriteFile(kbs.pathManager.GetKeybindsPath(), configBytes, 0644); err != nil {
return &KeyBindingError{"write_config", "", err}
kbs.logger.Info("Successfully inserted key binding", "command", kb.Command)
}
// 创建文件提供器
kbs.fileProvider = file.Provider(kbs.pathManager.GetKeybindsPath())
if err = kbs.koanf.Load(kbs.fileProvider, jsonparser.Parser()); err != nil {
return err
}
kbs.logger.Info("Completed inserting all default key bindings")
return nil
}
// GetKeyBindingConfig 获取完整快捷键配置
func (kbs *KeyBindingService) GetKeyBindingConfig() (*models.KeyBindingConfig, error) {
kbs.mu.RLock()
defer kbs.mu.RUnlock()
var config models.KeyBindingConfig
if err := kbs.koanf.Unmarshal("", &config); err != nil {
return nil, &KeyBindingError{"unmarshal_config", "", err}
keyBindings, err := kbs.GetAllKeyBindings()
if err != nil {
return nil, err
}
return &config, nil
config := &models.KeyBindingConfig{
KeyBindings: keyBindings,
}
return config, nil
}
// GetAllKeyBindings 获取所有快捷键配置
func (kbs *KeyBindingService) GetAllKeyBindings() ([]models.KeyBinding, error) {
config, err := kbs.GetKeyBindingConfig()
kbs.mu.RLock()
defer kbs.mu.RUnlock()
db := kbs.databaseService.GetDB()
rows, err := db.Query(sqlGetAllKeyBindings)
if err != nil {
return nil, err
return nil, &KeyBindingError{"query_keybindings", "", err}
}
return config.KeyBindings, nil
defer rows.Close()
var keyBindings []models.KeyBinding
for rows.Next() {
var kb models.KeyBinding
if err := rows.Scan(&kb.Command, &kb.Extension, &kb.Key, &kb.Enabled, &kb.IsDefault); err != nil {
return nil, &KeyBindingError{"scan_keybinding", "", err}
}
keyBindings = append(keyBindings, kb)
}
// OnShutdown 关闭服务
func (kbs *KeyBindingService) OnShutdown() error {
kbs.cancel()
return nil
if err := rows.Err(); err != nil {
return nil, &KeyBindingError{"rows_error", "", err}
}
return keyBindings, nil
}
// OnStartup 启动时调用
func (kbs *KeyBindingService) OnStartup(ctx context.Context, _ application.ServiceOptions) error {
kbs.ctx = ctx
kbs.logger.Info("KeyBinding service starting up")
// 初始化数据库
var initErr error
kbs.initOnce.Do(func() {
kbs.logger.Info("Initializing keybinding database...")
if err := kbs.initDatabase(); err != nil {
kbs.logger.Error("failed to initialize keybinding database", "error", err)
initErr = err
} else {
kbs.logger.Info("KeyBinding database initialized successfully")
}
})
return initErr
}

View File

@@ -1,59 +0,0 @@
package services
import (
"os"
"path/filepath"
)
// PathManager 路径管理器
type PathManager struct {
configDir string // 配置目录
settingsPath string // 设置文件路径
keybindsPath string // 快捷键配置文件路径
extensionsPath string // 扩展配置文件路径
}
// NewPathManager 创建新的路径管理器
func NewPathManager() *PathManager {
// 获取用户配置目录
userConfigDir, err := os.UserHomeDir()
if err != nil {
// 如果获取失败,使用当前目录
userConfigDir, _ = os.Getwd()
}
// 设置voidraft配置目录
configDir := filepath.Join(userConfigDir, ".voidraft", "config")
return &PathManager{
configDir: configDir,
settingsPath: filepath.Join(configDir, "settings.json"),
keybindsPath: filepath.Join(configDir, "keybindings.json"),
extensionsPath: filepath.Join(configDir, "extensions.json"),
}
}
// GetSettingsPath 获取设置文件路径
func (pm *PathManager) GetSettingsPath() string {
return pm.settingsPath
}
// GetKeybindsPath 获取快捷键配置文件路径
func (pm *PathManager) GetKeybindsPath() string {
return pm.keybindsPath
}
// GetConfigDir 获取配置目录路径
func (pm *PathManager) GetConfigDir() string {
return pm.configDir
}
// GetExtensionsPath 获取扩展配置文件路径
func (pm *PathManager) GetExtensionsPath() string {
return pm.extensionsPath
}
// EnsureConfigDir 确保配置目录存在
func (pm *PathManager) EnsureConfigDir() error {
return os.MkdirAll(pm.configDir, 0755)
}

View File

@@ -9,8 +9,8 @@ import (
// ServiceManager 服务管理器,负责协调各个服务
type ServiceManager struct {
pathManager *PathManager
configService *ConfigService
databaseService *DatabaseService
documentService *DocumentService
migrationService *MigrationService
systemService *SystemService
@@ -30,17 +30,17 @@ func NewServiceManager() *ServiceManager {
// 初始化日志服务
logger := log.New()
// 初始化路径管理器
pathManager := NewPathManager()
// 初始化配置服务
configService := NewConfigService(logger, pathManager)
configService := NewConfigService(logger)
// 初始化数据库服务
databaseService := NewDatabaseService(configService, logger)
// 初始化迁移服务
migrationService := NewMigrationService(logger)
// 初始化文档服务
documentService := NewDocumentService(configService, logger)
documentService := NewDocumentService(databaseService, logger)
// 初始化系统服务
systemService := NewSystemService(logger)
@@ -55,10 +55,10 @@ func NewServiceManager() *ServiceManager {
trayService := NewTrayService(logger, configService)
// 初始化快捷键服务
keyBindingService := NewKeyBindingService(logger, pathManager)
keyBindingService := NewKeyBindingService(databaseService, logger)
// 初始化扩展服务
extensionService := NewExtensionService(logger, pathManager)
extensionService := NewExtensionService(databaseService, logger)
// 初始化开机启动服务
startupService := NewStartupService(configService, logger)
@@ -82,7 +82,7 @@ func NewServiceManager() *ServiceManager {
// 设置数据路径变更监听,处理配置重置和路径变更
err = configService.SetDataPathChangeCallback(func() error {
return documentService.OnDataPathChanged()
return databaseService.OnDataPathChanged()
})
if err != nil {
panic(err)
@@ -90,6 +90,7 @@ func NewServiceManager() *ServiceManager {
return &ServiceManager{
configService: configService,
databaseService: databaseService,
documentService: documentService,
migrationService: migrationService,
systemService: systemService,
@@ -106,17 +107,19 @@ func NewServiceManager() *ServiceManager {
}
// GetServices 获取所有wails服务列表
// 注意服务启动顺序很重要DatabaseService 必须在依赖数据库的服务之前启动
func (sm *ServiceManager) GetServices() []application.Service {
services := []application.Service{
application.NewService(sm.configService),
application.NewService(sm.databaseService),
application.NewService(sm.documentService),
application.NewService(sm.keyBindingService),
application.NewService(sm.extensionService),
application.NewService(sm.migrationService),
application.NewService(sm.systemService),
application.NewService(sm.hotkeyService),
application.NewService(sm.dialogService),
application.NewService(sm.trayService),
application.NewService(sm.keyBindingService),
application.NewService(sm.extensionService),
application.NewService(sm.startupService),
application.NewService(sm.selfUpdateService),
application.NewService(sm.translationService),
@@ -173,3 +176,8 @@ func (sm *ServiceManager) GetSelfUpdateService() *SelfUpdateService {
func (sm *ServiceManager) GetTranslationService() *TranslationService {
return sm.translationService
}
// GetDatabaseService 获取数据库服务实例
func (sm *ServiceManager) GetDatabaseService() *DatabaseService {
return sm.databaseService
}