Browse Source

Add `cache_id` option for Clash cache file

世界 2 years ago
parent
commit
3741394269

+ 10 - 3
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 ""

+ 10 - 3
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 ""

+ 32 - 5
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
 		}

+ 3 - 1
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")
 		}

+ 1 - 0
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 {