From 102330f968a706d9f07bb842844c2cc0418eb7dc Mon Sep 17 00:00:00 2001 From: Shtorm <108103062+shtorm-7@users.noreply.github.com> Date: Fri, 4 Sep 2026 05:19:05 +0300 Subject: [PATCH] provider: add store_providers cache_file option to gate subscription caching --- adapter/experimental.go | 1 + examples/provider/remote.json | 6 ++++++ experimental/cachefile/cache.go | 6 ++++++ option/experimental.go | 1 + provider/remote/provider.go | 6 +++--- 5 files changed, 17 insertions(+), 3 deletions(-) diff --git a/adapter/experimental.go b/adapter/experimental.go index 5b6bc229..d591d218 100644 --- a/adapter/experimental.go +++ b/adapter/experimental.go @@ -43,6 +43,7 @@ type CacheFile interface { StoreWARPConfig() bool StoreMASQUEConfig() bool + StoreProviders() bool StoreDNS() bool DNSCacheStore diff --git a/examples/provider/remote.json b/examples/provider/remote.json index ef26e2e0..e1c7f9e9 100644 --- a/examples/provider/remote.json +++ b/examples/provider/remote.json @@ -94,5 +94,11 @@ "final": "auto", "default_domain_resolver": "default", "auto_detect_interface": true + }, + "experimental": { + "cache_file": { + "enabled": true, + "store_providers": true // For persisting fetched subscriptions across restarts + } } } diff --git a/experimental/cachefile/cache.go b/experimental/cachefile/cache.go index 86db73b7..f930a531 100644 --- a/experimental/cachefile/cache.go +++ b/experimental/cachefile/cache.go @@ -51,6 +51,7 @@ type CacheFile struct { storeDNS bool storeWARPConfig bool storeMASQUEConfig bool + storeProviders bool disableExpire bool rdrcTimeout time.Duration optimisticTimeout time.Duration @@ -115,6 +116,7 @@ func New(ctx context.Context, logger logger.Logger, options option.CacheFileOpti storeDNS: options.StoreDNS, storeWARPConfig: options.StoreWARPConfig, storeMASQUEConfig: options.StoreMASQUEConfig, + storeProviders: options.StoreProviders, rdrcTimeout: rdrcTimeout, saveDomain: make(map[netip.Addr]string), saveAddress4: make(map[string]netip.Addr), @@ -485,6 +487,10 @@ func (c *CacheFile) StoreMASQUEConfig() bool { return c.storeMASQUEConfig } +func (c *CacheFile) StoreProviders() bool { + return c.storeProviders +} + func (c *CacheFile) LoadWARPConfig(tag string) *adapter.SavedBinary { var savedConfig adapter.SavedBinary err := c.DB.View(func(t *bbolt.Tx) error { diff --git a/option/experimental.go b/option/experimental.go index 7bde42e3..74df3f39 100644 --- a/option/experimental.go +++ b/option/experimental.go @@ -18,6 +18,7 @@ type CacheFileOptions struct { StoreRDRC bool `json:"store_rdrc,omitempty" schema:"omit"` StoreWARPConfig bool `json:"store_warp_config,omitempty"` StoreMASQUEConfig bool `json:"store_masque_config,omitempty"` + StoreProviders bool `json:"store_providers,omitempty"` RDRCTimeout badoption.Duration `json:"rdrc_timeout,omitempty"` StoreDNS bool `json:"store_dns,omitempty"` } diff --git a/provider/remote/provider.go b/provider/remote/provider.go index 7f575b07..a22414fd 100644 --- a/provider/remote/provider.go +++ b/provider/remote/provider.go @@ -106,7 +106,7 @@ func NewProviderRemote(ctx context.Context, router adapter.Router, logFactory lo func (s *ProviderRemote) Start() error { s.cacheFile = service.FromContext[adapter.CacheFile](s.ctx) - if s.cacheFile != nil { + if s.cacheFile != nil && s.cacheFile.StoreProviders() { if saveSub := s.cacheFile.LoadSubscription(s.Tag()); saveSub != nil { content, _ := boxCommon.DecodeBase64URLSafe(string(saveSub.Content)) firstLine, others := getFirstLine(content) @@ -207,7 +207,7 @@ func (s *ProviderRemote) fetch(ctx context.Context) error { case http.StatusNotModified: s.subscriptionInfo = info s.lastUpdated = time.Now() - if s.cacheFile != nil { + if s.cacheFile != nil && s.cacheFile.StoreProviders() { saveSub := s.cacheFile.LoadSubscription(s.Tag()) if saveSub != nil { if hasInfo { @@ -251,7 +251,7 @@ func (s *ProviderRemote) fetch(ctx context.Context) error { s.UpdateGroups() s.subscriptionInfo = info s.lastUpdated = time.Now() - if s.cacheFile != nil { + if s.cacheFile != nil && s.cacheFile.StoreProviders() { content, _ := json.Marshal(option.Options{ Outbounds: s.lastOutOpts, })