package services import ( "database/sql" "os" "path/filepath" _ "modernc.org/sqlite" ) const ( appConfigDirName = "cmbone" databaseFileName = "cmbone.db" ) type SuiStore struct { DB *sql.DB } func getSafeDBPath() (string, error) { configDir, err := os.UserConfigDir() if err != nil { return "", err } appDir := filepath.Join(configDir, appConfigDirName) err = os.MkdirAll(appDir, 0755) if err != nil { return "", err } return filepath.Join(appDir, databaseFileName), nil } func NewSuiStore() (*SuiStore, error) { dbPath, err := getSafeDBPath() if err != nil { return nil, err } db, err := sql.Open("sqlite", dbPath) if err != nil { return nil, err } err = Migrate(db) if err != nil { _ = db.Close() return nil, err } return &SuiStore{DB: db}, nil } func (cs *SuiStore) Close() { if cs != nil && cs.DB != nil { _ = cs.DB.Close() } } func (cs *SuiStore) Start() error { // 这里可以初始化数据库或其它启动逻辑 return nil } func (cs *SuiStore) Stop() error { cs.Close() return nil } func Migrate(db *sql.DB) error { _, err := db.Exec(` CREATE TABLE IF NOT EXISTS hotkeys ( id INTEGER PRIMARY KEY AUTOINCREMENT, keycode INTEGER NOT NULL, modifiers INTEGER NOT NULL, description TEXT, target TEXT ); CREATE TABLE IF NOT EXISTS appconfig ( id INTEGER PRIMARY KEY AUTOINCREMENT, key TEXT UNIQUE, type TEXT, value TEXT, description TEXT, state INTEGER DEFAULT 0 ); CREATE TABLE IF NOT EXISTS auth_sessions ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id TEXT NOT NULL, username TEXT NOT NULL, provider TEXT NOT NULL, token TEXT NOT NULL, created_at TEXT NOT NULL, expires_at TEXT NOT NULL, active INTEGER NOT NULL DEFAULT 1 ); CREATE TABLE IF NOT EXISTS audit_logs ( id INTEGER PRIMARY KEY AUTOINCREMENT, actor TEXT NOT NULL, action TEXT NOT NULL, target_type TEXT NOT NULL, target_id TEXT NOT NULL, detail TEXT NOT NULL, created_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS image_tasks ( id INTEGER PRIMARY KEY AUTOINCREMENT, source_path TEXT NOT NULL, status TEXT NOT NULL, operation TEXT NOT NULL, duration_ms INTEGER NOT NULL DEFAULT 0, error_message TEXT NOT NULL DEFAULT '', created_at TEXT NOT NULL, updated_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS image_task_results ( id INTEGER PRIMARY KEY AUTOINCREMENT, task_id INTEGER NOT NULL, output_path TEXT NOT NULL, before_size INTEGER NOT NULL DEFAULT 0, after_size INTEGER NOT NULL DEFAULT 0, created_at TEXT NOT NULL, FOREIGN KEY(task_id) REFERENCES image_tasks(id) ); CREATE INDEX IF NOT EXISTS idx_image_tasks_status_created_at ON image_tasks(status, created_at); CREATE INDEX IF NOT EXISTS idx_image_tasks_created_at ON image_tasks(created_at); CREATE INDEX IF NOT EXISTS idx_image_task_results_task_id ON image_task_results(task_id); CREATE TABLE IF NOT EXISTS app_logs ( id INTEGER PRIMARY KEY AUTOINCREMENT, level TEXT NOT NULL, module TEXT NOT NULL, message TEXT NOT NULL, detail TEXT NOT NULL, created_at TEXT NOT NULL ); CREATE INDEX IF NOT EXISTS idx_app_logs_level_created_at ON app_logs(level, created_at); CREATE INDEX IF NOT EXISTS idx_audit_logs_action_created_at ON audit_logs(action, created_at); CREATE INDEX IF NOT EXISTS idx_audit_logs_created_at ON audit_logs(created_at); `) if err != nil { return err } return initDefaultData(db) } func initDefaultData(db *sql.DB) error { if _, err := db.Exec(defaultAppConfigSQL()); err != nil { return err } if _, err := db.Exec(appConfigDataMigrationSQL()); err != nil { return err } if _, err := db.Exec(defaultHotkeysSQL()); err != nil { return err } return nil }