From 374139426934b6b95af9c1f895ebd699928a9bc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Tue, 9 May 2023 17:58:59 +0800 Subject: [PATCH] Add `cache_id` option for Clash cache file --- docs/configuration/experimental/index.md | 13 ++++++-- docs/configuration/experimental/index.zh.md | 13 ++++++-- experimental/clashapi/cachefile/cache.go | 37 ++++++++++++++++++--- experimental/clashapi/server.go | 4 ++- option/clash.go | 1 + 5 files changed, 56 insertions(+), 12 deletions(-) diff --git a/docs/configuration/experimental/index.md b/docs/configuration/experimental/index.md index 4aa61676..0f9a09ec 100644 --- a/docs/configuration/experimental/index.md +++ b/docs/configuration/experimental/index.md @@ -7,13 +7,14 @@ "experimental": { "clash_api": { "external_controller": "127.0.0.1:9090", - "external_ui": "folder", + "external_ui": "", "external_ui_download_url": "", "external_ui_download_detour": "", "secret": "", - "default_mode": "rule", + "default_mode": "", "store_selected": false, - "cache_file": "cache.db" + "cache_file": "", + "cache_id": "" }, "v2ray_api": { "listen": "127.0.0.1:8080", @@ -91,6 +92,12 @@ Store selected outbound for the `Selector` outbound in cache file. Cache file path, `cache.db` will be used if empty. +#### cache_id + +Cache ID. + +If not empty, `store_selected` will use a separate store keyed by it. + ### V2Ray API Fields !!! error "" diff --git a/docs/configuration/experimental/index.zh.md b/docs/configuration/experimental/index.zh.md index 23d52b60..d9999388 100644 --- a/docs/configuration/experimental/index.zh.md +++ b/docs/configuration/experimental/index.zh.md @@ -7,13 +7,14 @@ "experimental": { "clash_api": { "external_controller": "127.0.0.1:9090", - "external_ui": "folder", + "external_ui": "", "external_ui_download_url": "", "external_ui_download_detour": "", "secret": "", - "default_mode": "rule", + "default_mode": "", "store_selected": false, - "cache_file": "cache.db" + "cache_file": "", + "cache_id": "" }, "v2ray_api": { "listen": "127.0.0.1:8080", @@ -89,6 +90,12 @@ Clash 中的默认模式,默认使用 `rule`。 缓存文件路径,默认使用`cache.db`。 +#### cache_id + +缓存 ID。 + +如果不为空,`store_selected` 将会使用以此为键的独立存储。 + ### V2Ray API 字段 !!! error "" diff --git a/experimental/clashapi/cachefile/cache.go b/experimental/clashapi/cachefile/cache.go index aa81ff8c..5d805052 100644 --- a/experimental/clashapi/cachefile/cache.go +++ b/experimental/clashapi/cachefile/cache.go @@ -14,10 +14,11 @@ var bucketSelected = []byte("selected") var _ adapter.ClashCacheFile = (*CacheFile)(nil) type CacheFile struct { - DB *bbolt.DB + DB *bbolt.DB + cacheID []byte } -func Open(path string) (*CacheFile, error) { +func Open(path string, cacheID string) (*CacheFile, error) { const fileMode = 0o666 options := bbolt.Options{Timeout: time.Second} db, err := bbolt.Open(path, fileMode, &options) @@ -31,13 +32,39 @@ func Open(path string) (*CacheFile, error) { if err != nil { return nil, err } - return &CacheFile{db}, nil + var cacheIDBytes []byte + if cacheID != "" { + cacheIDBytes = append([]byte{0}, []byte(cacheID)...) + } + return &CacheFile{db, cacheIDBytes}, nil +} + +func (c *CacheFile) bucket(t *bbolt.Tx, key []byte) *bbolt.Bucket { + if c.cacheID == nil { + return t.Bucket(key) + } + bucket := t.Bucket(c.cacheID) + if bucket == nil { + return nil + } + return bucket.Bucket(key) +} + +func (c *CacheFile) createBucket(t *bbolt.Tx, key []byte) (*bbolt.Bucket, error) { + if c.cacheID == nil { + return t.CreateBucketIfNotExists(key) + } + bucket, err := t.CreateBucketIfNotExists(c.cacheID) + if bucket == nil { + return nil, err + } + return bucket.CreateBucketIfNotExists(key) } func (c *CacheFile) LoadSelected(group string) string { var selected string c.DB.View(func(t *bbolt.Tx) error { - bucket := t.Bucket(bucketSelected) + bucket := c.bucket(t, bucketSelected) if bucket == nil { return nil } @@ -52,7 +79,7 @@ func (c *CacheFile) LoadSelected(group string) string { func (c *CacheFile) StoreSelected(group, selected string) error { return c.DB.Batch(func(t *bbolt.Tx) error { - bucket, err := t.CreateBucketIfNotExists(bucketSelected) + bucket, err := c.createBucket(t, bucketSelected) if err != nil { return err } diff --git a/experimental/clashapi/server.go b/experimental/clashapi/server.go index 6a5fb458..a365d0c9 100644 --- a/experimental/clashapi/server.go +++ b/experimental/clashapi/server.go @@ -48,6 +48,7 @@ type Server struct { storeSelected bool storeFakeIP bool cacheFilePath string + cacheID string cacheFile adapter.ClashCacheFile externalUI string @@ -88,6 +89,7 @@ func NewServer(ctx context.Context, router adapter.Router, logFactory log.Observ cachePath = filemanager.BasePath(ctx, cachePath) } server.cacheFilePath = cachePath + server.cacheID = options.CacheID } cors := cors.New(cors.Options{ AllowedOrigins: []string{"*"}, @@ -130,7 +132,7 @@ func NewServer(ctx context.Context, router adapter.Router, logFactory log.Observ func (s *Server) PreStart() error { if s.cacheFilePath != "" { - cacheFile, err := cachefile.Open(s.cacheFilePath) + cacheFile, err := cachefile.Open(s.cacheFilePath, s.cacheID) if err != nil { return E.Cause(err, "open cache file") } diff --git a/option/clash.go b/option/clash.go index 1a02cdcb..a0ba8a80 100644 --- a/option/clash.go +++ b/option/clash.go @@ -10,6 +10,7 @@ type ClashAPIOptions struct { StoreSelected bool `json:"store_selected,omitempty"` StoreFakeIP bool `json:"store_fakeip,omitempty"` CacheFile string `json:"cache_file,omitempty"` + CacheID string `json:"cache_id,omitempty"` } type SelectorOutboundOptions struct {