🎨 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,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()
}