github.com/kelleygo/clashcore@v1.0.2/component/profile/cachefile/cache.go (about)

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