mirror of
https://github.com/MetaCubeX/mihomo.git
synced 2025-01-06 01:23:38 +08:00
Feature: support store group selected node to cache (enable by default)
This commit is contained in:
parent
aa81193d5b
commit
14bbf6eedc
115
component/profile/cachefile/cache.go
Normal file
115
component/profile/cachefile/cache.go
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
package cachefile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/gob"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/component/profile"
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
"github.com/Dreamacro/clash/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
initOnce sync.Once
|
||||||
|
fileMode os.FileMode = 0666
|
||||||
|
defaultCache *CacheFile
|
||||||
|
)
|
||||||
|
|
||||||
|
type cache struct {
|
||||||
|
Selected map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// CacheFile store and update the cache file
|
||||||
|
type CacheFile struct {
|
||||||
|
path string
|
||||||
|
model *cache
|
||||||
|
enc *gob.Encoder
|
||||||
|
buf *bytes.Buffer
|
||||||
|
mux sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CacheFile) SetSelected(group, selected string) {
|
||||||
|
if !profile.StoreSelected.Load() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.mux.Lock()
|
||||||
|
defer c.mux.Unlock()
|
||||||
|
|
||||||
|
model, err := c.element()
|
||||||
|
if err != nil {
|
||||||
|
log.Warnln("[CacheFile] read cache %s failed: %s", c.path, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
model.Selected[group] = selected
|
||||||
|
c.buf.Reset()
|
||||||
|
if err := c.enc.Encode(model); err != nil {
|
||||||
|
log.Warnln("[CacheFile] encode gob failed: %s", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ioutil.WriteFile(c.path, c.buf.Bytes(), fileMode); err != nil {
|
||||||
|
log.Warnln("[CacheFile] write cache to %s failed: %s", c.path, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CacheFile) SelectedMap() map[string]string {
|
||||||
|
if !profile.StoreSelected.Load() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
c.mux.Lock()
|
||||||
|
defer c.mux.Unlock()
|
||||||
|
|
||||||
|
model, err := c.element()
|
||||||
|
if err != nil {
|
||||||
|
log.Warnln("[CacheFile] read cache %s failed: %s", c.path, err.Error())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
mapping := map[string]string{}
|
||||||
|
for k, v := range model.Selected {
|
||||||
|
mapping[k] = v
|
||||||
|
}
|
||||||
|
return mapping
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CacheFile) element() (*cache, error) {
|
||||||
|
if c.model != nil {
|
||||||
|
return c.model, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
model := &cache{
|
||||||
|
Selected: map[string]string{},
|
||||||
|
}
|
||||||
|
|
||||||
|
if buf, err := ioutil.ReadFile(c.path); err == nil {
|
||||||
|
bufReader := bytes.NewBuffer(buf)
|
||||||
|
dec := gob.NewDecoder(bufReader)
|
||||||
|
if err := dec.Decode(model); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.model = model
|
||||||
|
return c.model, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache return singleton of CacheFile
|
||||||
|
func Cache() *CacheFile {
|
||||||
|
initOnce.Do(func() {
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
defaultCache = &CacheFile{
|
||||||
|
path: C.Path.Cache(),
|
||||||
|
buf: buf,
|
||||||
|
enc: gob.NewEncoder(buf),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return defaultCache
|
||||||
|
}
|
10
component/profile/profile.go
Normal file
10
component/profile/profile.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package profile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go.uber.org/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// StoreSelected is a global switch for storing selected proxy to cache
|
||||||
|
StoreSelected = atomic.NewBool(true)
|
||||||
|
)
|
@ -73,6 +73,11 @@ type FallbackFilter struct {
|
|||||||
Domain []string `yaml:"domain"`
|
Domain []string `yaml:"domain"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Profile config
|
||||||
|
type Profile struct {
|
||||||
|
StoreSelected bool `yaml:"store-selected"`
|
||||||
|
}
|
||||||
|
|
||||||
// Experimental config
|
// Experimental config
|
||||||
type Experimental struct{}
|
type Experimental struct{}
|
||||||
|
|
||||||
@ -82,6 +87,7 @@ type Config struct {
|
|||||||
DNS *DNS
|
DNS *DNS
|
||||||
Experimental *Experimental
|
Experimental *Experimental
|
||||||
Hosts *trie.DomainTrie
|
Hosts *trie.DomainTrie
|
||||||
|
Profile *Profile
|
||||||
Rules []C.Rule
|
Rules []C.Rule
|
||||||
Users []auth.AuthUser
|
Users []auth.AuthUser
|
||||||
Proxies map[string]C.Proxy
|
Proxies map[string]C.Proxy
|
||||||
@ -129,6 +135,7 @@ type RawConfig struct {
|
|||||||
Hosts map[string]string `yaml:"hosts"`
|
Hosts map[string]string `yaml:"hosts"`
|
||||||
DNS RawDNS `yaml:"dns"`
|
DNS RawDNS `yaml:"dns"`
|
||||||
Experimental Experimental `yaml:"experimental"`
|
Experimental Experimental `yaml:"experimental"`
|
||||||
|
Profile Profile `yaml:"profile"`
|
||||||
Proxy []map[string]interface{} `yaml:"proxies"`
|
Proxy []map[string]interface{} `yaml:"proxies"`
|
||||||
ProxyGroup []map[string]interface{} `yaml:"proxy-groups"`
|
ProxyGroup []map[string]interface{} `yaml:"proxy-groups"`
|
||||||
Rule []string `yaml:"rules"`
|
Rule []string `yaml:"rules"`
|
||||||
@ -145,7 +152,7 @@ func Parse(buf []byte) (*Config, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func UnmarshalRawConfig(buf []byte) (*RawConfig, error) {
|
func UnmarshalRawConfig(buf []byte) (*RawConfig, error) {
|
||||||
// config with some default value
|
// config with default value
|
||||||
rawCfg := &RawConfig{
|
rawCfg := &RawConfig{
|
||||||
AllowLan: false,
|
AllowLan: false,
|
||||||
BindAddress: "*",
|
BindAddress: "*",
|
||||||
@ -169,6 +176,9 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) {
|
|||||||
"8.8.8.8",
|
"8.8.8.8",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Profile: Profile{
|
||||||
|
StoreSelected: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := yaml.Unmarshal(buf, &rawCfg); err != nil {
|
if err := yaml.Unmarshal(buf, &rawCfg); err != nil {
|
||||||
@ -182,6 +192,7 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
|
|||||||
config := &Config{}
|
config := &Config{}
|
||||||
|
|
||||||
config.Experimental = &rawCfg.Experimental
|
config.Experimental = &rawCfg.Experimental
|
||||||
|
config.Profile = &rawCfg.Profile
|
||||||
|
|
||||||
general, err := parseGeneral(rawCfg)
|
general, err := parseGeneral(rawCfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -56,3 +56,7 @@ func (p *path) Resolve(path string) string {
|
|||||||
func (p *path) MMDB() string {
|
func (p *path) MMDB() string {
|
||||||
return P.Join(p.homeDir, "Country.mmdb")
|
return P.Join(p.homeDir, "Country.mmdb")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *path) Cache() string {
|
||||||
|
return P.Join(p.homeDir, ".cache")
|
||||||
|
}
|
||||||
|
@ -6,9 +6,13 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/adapters/outbound"
|
||||||
|
"github.com/Dreamacro/clash/adapters/outboundgroup"
|
||||||
"github.com/Dreamacro/clash/adapters/provider"
|
"github.com/Dreamacro/clash/adapters/provider"
|
||||||
"github.com/Dreamacro/clash/component/auth"
|
"github.com/Dreamacro/clash/component/auth"
|
||||||
"github.com/Dreamacro/clash/component/dialer"
|
"github.com/Dreamacro/clash/component/dialer"
|
||||||
|
"github.com/Dreamacro/clash/component/profile"
|
||||||
|
"github.com/Dreamacro/clash/component/profile/cachefile"
|
||||||
"github.com/Dreamacro/clash/component/resolver"
|
"github.com/Dreamacro/clash/component/resolver"
|
||||||
"github.com/Dreamacro/clash/component/trie"
|
"github.com/Dreamacro/clash/component/trie"
|
||||||
"github.com/Dreamacro/clash/config"
|
"github.com/Dreamacro/clash/config"
|
||||||
@ -72,6 +76,7 @@ func ApplyConfig(cfg *config.Config, force bool) {
|
|||||||
updateDNS(cfg.DNS)
|
updateDNS(cfg.DNS)
|
||||||
updateHosts(cfg.Hosts)
|
updateHosts(cfg.Hosts)
|
||||||
updateExperimental(cfg)
|
updateExperimental(cfg)
|
||||||
|
updateProfile(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetGeneral() *config.General {
|
func GetGeneral() *config.General {
|
||||||
@ -209,3 +214,38 @@ func updateUsers(users []auth.AuthUser) {
|
|||||||
log.Infoln("Authentication of local server updated")
|
log.Infoln("Authentication of local server updated")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateProfile(cfg *config.Config) {
|
||||||
|
profileCfg := cfg.Profile
|
||||||
|
|
||||||
|
profile.StoreSelected.Store(profileCfg.StoreSelected)
|
||||||
|
if profileCfg.StoreSelected {
|
||||||
|
patchSelectGroup(cfg.Proxies)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func patchSelectGroup(proxies map[string]C.Proxy) {
|
||||||
|
mapping := cachefile.Cache().SelectedMap()
|
||||||
|
if mapping == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, proxy := range proxies {
|
||||||
|
outbound, ok := proxy.(*outbound.Proxy)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
selector, ok := outbound.ProxyAdapter.(*outboundgroup.Selector)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
selected, exist := mapping[name]
|
||||||
|
if !exist {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
selector.Set(selected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"github.com/Dreamacro/clash/adapters/outbound"
|
"github.com/Dreamacro/clash/adapters/outbound"
|
||||||
"github.com/Dreamacro/clash/adapters/outboundgroup"
|
"github.com/Dreamacro/clash/adapters/outboundgroup"
|
||||||
|
"github.com/Dreamacro/clash/component/profile/cachefile"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
"github.com/Dreamacro/clash/tunnel"
|
"github.com/Dreamacro/clash/tunnel"
|
||||||
|
|
||||||
@ -91,6 +92,7 @@ func updateProxy(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cachefile.Cache().SetSelected(proxy.Name(), req.Name)
|
||||||
render.NoContent(w, r)
|
render.NoContent(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user