github.com/igoogolx/clash@v1.19.8/component/profile/cachefile/cache.go (about)

     1  package cachefile
     2  
     3  import (
     4  	"os"
     5  	"sync"
     6  	"time"
     7  
     8  	"github.com/igoogolx/clash/component/profile"
     9  	C "github.com/igoogolx/clash/constant"
    10  	"github.com/igoogolx/clash/log"
    11  
    12  	"go.etcd.io/bbolt"
    13  )
    14  
    15  var (
    16  	fileMode os.FileMode = 0o666
    17  
    18  	bucketSelected = []byte("selected")
    19  	bucketFakeip   = []byte("fakeip")
    20  )
    21  
    22  // CacheFile store and update the cache file
    23  type CacheFile struct {
    24  	DB *bbolt.DB
    25  }
    26  
    27  func (c *CacheFile) SetSelected(group, selected string) {
    28  	if !profile.StoreSelected.Load() {
    29  		return
    30  	} else if c.DB == nil {
    31  		return
    32  	}
    33  
    34  	err := c.DB.Batch(func(t *bbolt.Tx) error {
    35  		bucket, err := t.CreateBucketIfNotExists(bucketSelected)
    36  		if err != nil {
    37  			return err
    38  		}
    39  		return bucket.Put([]byte(group), []byte(selected))
    40  	})
    41  	if err != nil {
    42  		log.Warnln("[CacheFile] write cache to %s failed: %s", c.DB.Path(), err.Error())
    43  		return
    44  	}
    45  }
    46  
    47  func (c *CacheFile) SelectedMap() map[string]string {
    48  	if !profile.StoreSelected.Load() {
    49  		return nil
    50  	} else if c.DB == nil {
    51  		return nil
    52  	}
    53  
    54  	mapping := map[string]string{}
    55  	c.DB.View(func(t *bbolt.Tx) error {
    56  		bucket := t.Bucket(bucketSelected)
    57  		if bucket == nil {
    58  			return nil
    59  		}
    60  
    61  		c := bucket.Cursor()
    62  		for k, v := c.First(); k != nil; k, v = c.Next() {
    63  			mapping[string(k)] = string(v)
    64  		}
    65  		return nil
    66  	})
    67  	return mapping
    68  }
    69  
    70  func (c *CacheFile) PutFakeip(key, value []byte) error {
    71  	if c.DB == nil {
    72  		return nil
    73  	}
    74  
    75  	err := c.DB.Batch(func(t *bbolt.Tx) error {
    76  		bucket, err := t.CreateBucketIfNotExists(bucketFakeip)
    77  		if err != nil {
    78  			return err
    79  		}
    80  		return bucket.Put(key, value)
    81  	})
    82  	if err != nil {
    83  		log.Warnln("[CacheFile] write cache to %s failed: %s", c.DB.Path(), err.Error())
    84  	}
    85  
    86  	return err
    87  }
    88  
    89  func (c *CacheFile) DelFakeipPair(ip, host []byte) error {
    90  	if c.DB == nil {
    91  		return nil
    92  	}
    93  
    94  	err := c.DB.Batch(func(t *bbolt.Tx) error {
    95  		bucket, err := t.CreateBucketIfNotExists(bucketFakeip)
    96  		if err != nil {
    97  			return err
    98  		}
    99  		err = bucket.Delete(ip)
   100  		if len(host) > 0 {
   101  			if err := bucket.Delete(host); err != nil {
   102  				return err
   103  			}
   104  		}
   105  		return err
   106  	})
   107  	if err != nil {
   108  		log.Warnln("[CacheFile] write cache to %s failed: %s", c.DB.Path(), err.Error())
   109  	}
   110  
   111  	return err
   112  }
   113  
   114  func (c *CacheFile) GetFakeip(key []byte) []byte {
   115  	if c.DB == nil {
   116  		return nil
   117  	}
   118  
   119  	tx, err := c.DB.Begin(false)
   120  	if err != nil {
   121  		return nil
   122  	}
   123  	defer tx.Rollback()
   124  
   125  	bucket := tx.Bucket(bucketFakeip)
   126  	if bucket == nil {
   127  		return nil
   128  	}
   129  
   130  	return bucket.Get(key)
   131  }
   132  
   133  func (c *CacheFile) Close() error {
   134  	return c.DB.Close()
   135  }
   136  
   137  // Cache return singleton of CacheFile
   138  var Cache = sync.OnceValue(func() *CacheFile {
   139  	options := bbolt.Options{Timeout: time.Second}
   140  	db, err := bbolt.Open(C.Path.Cache(), fileMode, &options)
   141  	switch err {
   142  	case bbolt.ErrInvalid, bbolt.ErrChecksum, bbolt.ErrVersionMismatch:
   143  		if err = os.Remove(C.Path.Cache()); err != nil {
   144  			log.Warnln("[CacheFile] remove invalid cache file error: %s", err.Error())
   145  			break
   146  		}
   147  		log.Infoln("[CacheFile] remove invalid cache file and create new one")
   148  		db, err = bbolt.Open(C.Path.Cache(), fileMode, &options)
   149  	}
   150  	if err != nil {
   151  		log.Warnln("[CacheFile] can't open cache file: %s", err.Error())
   152  	}
   153  
   154  	return &CacheFile{
   155  		DB: db,
   156  	}
   157  })