2026-07-08 15:13:26 +08:00
|
|
|
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
|
|
|
|
|
);
|
2026-07-08 23:37:19 +08:00
|
|
|
|
|
|
|
|
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
|
|
|
|
|
);
|
2026-07-09 00:47:35 +08:00
|
|
|
|
|
|
|
|
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);
|
2026-07-09 01:04:50 +08:00
|
|
|
CREATE INDEX IF NOT EXISTS idx_image_tasks_created_at ON image_tasks(created_at);
|
2026-07-09 00:47:35 +08:00
|
|
|
CREATE INDEX IF NOT EXISTS idx_image_task_results_task_id ON image_task_results(task_id);
|
2026-07-09 00:55:46 +08:00
|
|
|
|
|
|
|
|
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);
|
2026-07-09 01:04:50 +08:00
|
|
|
CREATE INDEX IF NOT EXISTS idx_audit_logs_created_at ON audit_logs(created_at);
|
2026-07-08 15:13:26 +08:00
|
|
|
`)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return initDefaultData(db)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func initDefaultData(db *sql.DB) error {
|
|
|
|
|
if _, err := db.Exec(defaultAppConfigSQL()); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2026-07-09 00:34:47 +08:00
|
|
|
if _, err := db.Exec(appConfigDataMigrationSQL()); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2026-07-08 15:13:26 +08:00
|
|
|
if _, err := db.Exec(defaultHotkeysSQL()); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|