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