feat(setting): Refactor settings to use a database-backed store

Refactors the entire settings management system from JSON files to a more robust and scalable SQLite database using GORM. This architectural change lays the foundation for future features like cloud synchronization.

Key changes:
- Introduces a new `database` package with GORM models for all settings and app data.
- Implements a backward-compatible migration system that automatically moves existing users settings from `wox.setting.json` and `wox.app.data.json` to the new `wox.db` file on the first run.
- Moves the database location to `userDataDirectory` to align with the projects data storage conventions.
- Rewrites the `setting.Manager` to be fully database-driven, replacing all file I/O with GORM operations.
- Sacrifices `FavoriteResults` during migration due to the technical limitation of its one-way hash implementation.

This resolves the issue of having scattered JSON configuration and provides a centralized, transactional data store.
This commit is contained in:
qianlifeng 2025-07-15 21:47:52 +08:00
parent 8bc44b0836
commit 247c970ed2
No known key found for this signature in database
4 changed files with 684 additions and 620 deletions

View File

@ -0,0 +1,132 @@
package database
import (
"fmt"
"path/filepath"
"sync"
"wox/common"
"wox/util"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
var (
db *gorm.DB
once sync.Once
)
const dbFileName = "wox.db"
// Models
type Setting struct {
Key string `gorm:"primaryKey"`
Value string
}
type Hotkey struct {
ID uint `gorm:"primaryKey;autoIncrement"`
Hotkey string `gorm:"unique"`
Query string
IsSilentExecution bool
}
type QueryShortcut struct {
ID uint `gorm:"primaryKey;autoIncrement"`
Shortcut string `gorm:"unique"`
Query string
}
type AIProvider struct {
ID uint `gorm:"primaryKey;autoIncrement"`
Name common.ProviderName
ApiKey string
Host string
}
type QueryHistory struct {
ID uint `gorm:"primaryKey;autoIncrement"`
Query string
Timestamp int64
}
type FavoriteResult struct {
ID uint `gorm:"primaryKey;autoIncrement"`
PluginID string `gorm:"uniqueIndex:idx_fav"`
Title string `gorm:"uniqueIndex:idx_fav"`
Subtitle string `gorm:"uniqueIndex:idx_fav"`
}
type PluginSetting struct {
ID uint `gorm:"primaryKey;autoIncrement"`
PluginID string `gorm:"uniqueIndex:idx_plugin_setting"`
Key string `gorm:"uniqueIndex:idx_plugin_setting"`
Value string
}
type ActionedResult struct {
ID uint `gorm:"primaryKey;autoIncrement"`
PluginID string
Title string
Subtitle string
Timestamp int64
Query string
}
type Oplog struct {
ID uint `gorm:"primaryKey;autoIncrement"`
EntityType string
EntityID string
Operation string
Key string
Value string
Timestamp int64
SyncedToCloud bool `gorm:"default:false"`
}
// Init initializes the database connection and migrates the schema.
func Init() error {
var err error
once.Do(func() {
dbPath := filepath.Join(util.GetLocation().GetUserDataDirectory(), dbFileName)
db, err = gorm.Open(sqlite.Open(dbPath), &gorm.Config{
Logger: logger.Default.LogMode(logger.Silent),
})
if err != nil {
err = fmt.Errorf("failed to connect to database: %w", err)
return
}
// AutoMigrate will create tables, columns, and indexes, but not delete them.
err = migrateSchema()
if err != nil {
err = fmt.Errorf("failed to migrate database schema: %w", err)
return
}
})
return err
}
// GetDB returns the GORM database instance.
func GetDB() *gorm.DB {
return db
}
// migrateSchema runs GORM's AutoMigrate function.
func migrateSchema() error {
return db.AutoMigrate(
&Setting{},
&Hotkey{},
&QueryShortcut{},
&AIProvider{},
&QueryHistory{},
&FavoriteResult{},
&PluginSetting{},
&ActionedResult{},
&Oplog{},
)
}

View File

@ -27,7 +27,6 @@ require (
github.com/olahol/melody v1.2.1
github.com/openai/openai-go v0.1.0-beta.6
github.com/otiai10/copy v1.14.0
github.com/petermattis/goid v0.0.0-20241025130422-66cb2e6d7274
github.com/robotn/gohook v0.41.0
github.com/rs/cors v1.11.1
@ -65,6 +64,7 @@ require (
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/invopop/jsonschema v0.12.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
@ -93,4 +93,6 @@ require (
google.golang.org/protobuf v1.35.1 // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gorm.io/driver/sqlite v1.6.0 // indirect
gorm.io/gorm v1.30.0 // indirect
)

View File

@ -50,6 +50,8 @@ github.com/invopop/jsonschema v0.12.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uO
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
@ -232,6 +234,10 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs=
gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM=
howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=

File diff suppressed because it is too large Load Diff