github.com/yaling888/clash@v1.53.0/component/profile/cachefile/cache.go (about)

     1  package cachefile
     2  
     3  import (
     4  	"os"
     5  	"sync"
     6  	"time"
     7  
     8  	"github.com/phuslu/log"
     9  	"go.etcd.io/bbolt"
    10  
    11  	"github.com/yaling888/clash/component/profile"
    12  	C "github.com/yaling888/clash/constant"
    13  )
    14  
    15  var (
    16  	fileMode os.FileMode = 0o666
    17  
    18  	bucketSelected     = []byte("selected")
    19  	bucketFakeip       = []byte("fakeip")
    20  	bucketSubscription = []byte("subscription")
    21  )
    22  
    23  // CacheFile store and update the cache file
    24  type CacheFile struct {
    25  	DB *bbolt.DB
    26  }
    27  
    28  func (c *CacheFile) SetSelected(group, selected string) {
    29  	if !profile.StoreSelected.Load() {
    30  		return
    31  	} else if c.DB == nil {
    32  		return
    33  	}
    34  
    35  	err := c.DB.Batch(func(t *bbolt.Tx) error {
    36  		bucket, err := t.CreateBucketIfNotExists(bucketSelected)
    37  		if err != nil {
    38  			return err
    39  		}
    40  		return bucket.Put([]byte(group), []byte(selected))
    41  	})
    42  	if err != nil {
    43  		log.Warn().Err(err).Msgf("[CacheFile] write cache to %s failed", c.DB.Path())
    44  		return
    45  	}
    46  }
    47  
    48  func (c *CacheFile) SelectedMap() map[string]string {
    49  	if !profile.StoreSelected.Load() {
    50  		return nil
    51  	} else if c.DB == nil {
    52  		return nil
    53  	}
    54  
    55  	mapping := map[string]string{}
    56  	_ = c.DB.View(func(t *bbolt.Tx) error {
    57  		bucket := t.Bucket(bucketSelected)
    58  		if bucket == nil {
    59  			return nil
    60  		}
    61  
    62  		c := bucket.Cursor()
    63  		for k, v := c.First(); k != nil; k, v = c.Next() {
    64  			mapping[string(k)] = string(v)
    65  		}
    66  		return nil
    67  	})
    68  	return mapping
    69  }
    70  
    71  func (c *CacheFile) PutFakeip(key, value []byte) error {
    72  	if c.DB == nil {
    73  		return nil
    74  	}
    75  
    76  	err := c.DB.Batch(func(t *bbolt.Tx) error {
    77  		bucket, err := t.CreateBucketIfNotExists(bucketFakeip)
    78  		if err != nil {
    79  			return err
    80  		}
    81  		return bucket.Put(key, value)
    82  	})
    83  	if err != nil {
    84  		log.Warn().Err(err).Msgf("[CacheFile] write cache to %s failed", c.DB.Path())
    85  	}
    86  
    87  	return err
    88  }
    89  
    90  func (c *CacheFile) DelFakeipPair(ip, host []byte) error {
    91  	if c.DB == nil {
    92  		return nil
    93  	}
    94  
    95  	err := c.DB.Batch(func(t *bbolt.Tx) error {
    96  		bucket, err := t.CreateBucketIfNotExists(bucketFakeip)
    97  		if err != nil {
    98  			return err
    99  		}
   100  		err = bucket.Delete(ip)
   101  		if len(host) > 0 {
   102  			if err := bucket.Delete(host); err != nil {
   103  				return err
   104  			}
   105  		}
   106  		return err
   107  	})
   108  	if err != nil {
   109  		log.Warn().Err(err).Msgf("[CacheFile] write cache to %s failed", c.DB.Path())
   110  	}
   111  
   112  	return err
   113  }
   114  
   115  func (c *CacheFile) GetFakeip(key []byte) []byte {
   116  	if c.DB == nil {
   117  		return nil
   118  	}
   119  
   120  	tx, err := c.DB.Begin(false)
   121  	if err != nil {
   122  		return nil
   123  	}
   124  	defer func(tx *bbolt.Tx) {
   125  		_ = tx.Rollback()
   126  	}(tx)
   127  
   128  	bucket := tx.Bucket(bucketFakeip)
   129  	if bucket == nil {
   130  		return nil
   131  	}
   132  
   133  	return bucket.Get(key)
   134  }
   135  
   136  func (c *CacheFile) FlushFakeIP() error {
   137  	err := c.DB.Batch(func(t *bbolt.Tx) error {
   138  		bucket := t.Bucket(bucketFakeip)
   139  		if bucket == nil {
   140  			return nil
   141  		}
   142  		return t.DeleteBucket(bucketFakeip)
   143  	})
   144  	return err
   145  }
   146  
   147  func (c *CacheFile) SetSubscription(key, value string) {
   148  	if c.DB == nil {
   149  		return
   150  	}
   151  
   152  	err := c.DB.Batch(func(t *bbolt.Tx) error {
   153  		bucket, err := t.CreateBucketIfNotExists(bucketSubscription)
   154  		if err != nil {
   155  			return err
   156  		}
   157  		return bucket.Put([]byte(key), []byte(value))
   158  	})
   159  	if err != nil {
   160  		log.Warn().Err(err).Msgf("[CacheFile] write cache to %s failed", c.DB.Path())
   161  		return
   162  	}
   163  }
   164  
   165  func (c *CacheFile) GetSubscription(key string) string {
   166  	if c.DB == nil {
   167  		return ""
   168  	}
   169  
   170  	tx, err := c.DB.Begin(false)
   171  	if err != nil {
   172  		return ""
   173  	}
   174  	defer func(tx *bbolt.Tx) {
   175  		_ = tx.Rollback()
   176  	}(tx)
   177  
   178  	bucket := tx.Bucket(bucketSubscription)
   179  	if bucket == nil {
   180  		return ""
   181  	}
   182  
   183  	return string(bucket.Get([]byte(key)))
   184  }
   185  
   186  func (c *CacheFile) Close() error {
   187  	return c.DB.Close()
   188  }
   189  
   190  // Cache return singleton of CacheFile
   191  var Cache = sync.OnceValue(func() *CacheFile {
   192  	options := bbolt.Options{Timeout: time.Second}
   193  	db, err := bbolt.Open(C.Path.Cache(), fileMode, &options)
   194  	switch err {
   195  	case bbolt.ErrInvalid, bbolt.ErrChecksum, bbolt.ErrVersionMismatch:
   196  		if err = os.Remove(C.Path.Cache()); err != nil {
   197  			log.Warn().Err(err).Msg("[CacheFile] remove invalid cache file failed")
   198  			break
   199  		}
   200  		log.Info().Msg("[CacheFile] remove invalid cache file and create new one")
   201  		db, err = bbolt.Open(C.Path.Cache(), fileMode, &options)
   202  	}
   203  	if err != nil {
   204  		log.Warn().Err(err).Msg("[CacheFile] open cache file failed")
   205  	}
   206  
   207  	return &CacheFile{
   208  		DB: db,
   209  	}
   210  })