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 })