github.com/chwjbn/xclash@v0.2.0/component/profile/cachefile/cache.go (about) 1 package cachefile 2 3 import ( 4 "os" 5 "sync" 6 "time" 7 8 "github.com/chwjbn/xclash/component/profile" 9 C "github.com/chwjbn/xclash/constant" 10 "github.com/chwjbn/xclash/log" 11 12 "go.etcd.io/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) Close() error { 136 return c.DB.Close() 137 } 138 139 func initCache() { 140 options := bbolt.Options{Timeout: time.Second} 141 db, err := bbolt.Open(C.Path.Cache(), fileMode, &options) 142 switch err { 143 case bbolt.ErrInvalid, bbolt.ErrChecksum, bbolt.ErrVersionMismatch: 144 if err = os.Remove(C.Path.Cache()); err != nil { 145 log.Warnln("[CacheFile] remove invalid cache file error: %s", err.Error()) 146 break 147 } 148 log.Infoln("[CacheFile] remove invalid cache file and create new one") 149 db, err = bbolt.Open(C.Path.Cache(), fileMode, &options) 150 } 151 if err != nil { 152 log.Warnln("[CacheFile] can't open cache file: %s", err.Error()) 153 } 154 155 defaultCache = &CacheFile{ 156 DB: db, 157 } 158 } 159 160 // Cache return singleton of CacheFile 161 func Cache() *CacheFile { 162 initOnce.Do(initCache) 163 164 return defaultCache 165 }