mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-25 01:51:29 +00:00
Migrate to independent cache file
This commit is contained in:
parent
e3f8567690
commit
bf4e556f67
|
@ -13,15 +13,17 @@ type ClashServer interface {
|
||||||
PreStarter
|
PreStarter
|
||||||
Mode() string
|
Mode() string
|
||||||
ModeList() []string
|
ModeList() []string
|
||||||
StoreSelected() bool
|
|
||||||
StoreFakeIP() bool
|
|
||||||
CacheFile() ClashCacheFile
|
|
||||||
HistoryStorage() *urltest.HistoryStorage
|
HistoryStorage() *urltest.HistoryStorage
|
||||||
RoutedConnection(ctx context.Context, conn net.Conn, metadata InboundContext, matchedRule Rule) (net.Conn, Tracker)
|
RoutedConnection(ctx context.Context, conn net.Conn, metadata InboundContext, matchedRule Rule) (net.Conn, Tracker)
|
||||||
RoutedPacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext, matchedRule Rule) (N.PacketConn, Tracker)
|
RoutedPacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext, matchedRule Rule) (N.PacketConn, Tracker)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClashCacheFile interface {
|
type CacheFile interface {
|
||||||
|
Service
|
||||||
|
PreStarter
|
||||||
|
|
||||||
|
StoreFakeIP() bool
|
||||||
|
|
||||||
LoadMode() string
|
LoadMode() string
|
||||||
StoreMode(mode string) error
|
StoreMode(mode string) error
|
||||||
LoadSelected(group string) string
|
LoadSelected(group string) string
|
||||||
|
|
53
box.go
53
box.go
|
@ -10,6 +10,7 @@ import (
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/experimental"
|
"github.com/sagernet/sing-box/experimental"
|
||||||
|
"github.com/sagernet/sing-box/experimental/cachefile"
|
||||||
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
"github.com/sagernet/sing-box/experimental/libbox/platform"
|
||||||
"github.com/sagernet/sing-box/inbound"
|
"github.com/sagernet/sing-box/inbound"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
|
@ -32,7 +33,8 @@ type Box struct {
|
||||||
outbounds []adapter.Outbound
|
outbounds []adapter.Outbound
|
||||||
logFactory log.Factory
|
logFactory log.Factory
|
||||||
logger log.ContextLogger
|
logger log.ContextLogger
|
||||||
preServices map[string]adapter.Service
|
preServices1 map[string]adapter.Service
|
||||||
|
preServices2 map[string]adapter.Service
|
||||||
postServices map[string]adapter.Service
|
postServices map[string]adapter.Service
|
||||||
done chan struct{}
|
done chan struct{}
|
||||||
}
|
}
|
||||||
|
@ -45,17 +47,21 @@ type Options struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(options Options) (*Box, error) {
|
func New(options Options) (*Box, error) {
|
||||||
|
createdAt := time.Now()
|
||||||
ctx := options.Context
|
ctx := options.Context
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
ctx = context.Background()
|
ctx = context.Background()
|
||||||
}
|
}
|
||||||
ctx = service.ContextWithDefaultRegistry(ctx)
|
ctx = service.ContextWithDefaultRegistry(ctx)
|
||||||
ctx = pause.ContextWithDefaultManager(ctx)
|
ctx = pause.ContextWithDefaultManager(ctx)
|
||||||
createdAt := time.Now()
|
|
||||||
experimentalOptions := common.PtrValueOrDefault(options.Experimental)
|
experimentalOptions := common.PtrValueOrDefault(options.Experimental)
|
||||||
applyDebugOptions(common.PtrValueOrDefault(experimentalOptions.Debug))
|
applyDebugOptions(common.PtrValueOrDefault(experimentalOptions.Debug))
|
||||||
|
var needCacheFile bool
|
||||||
var needClashAPI bool
|
var needClashAPI bool
|
||||||
var needV2RayAPI bool
|
var needV2RayAPI bool
|
||||||
|
if experimentalOptions.CacheFile != nil && experimentalOptions.CacheFile.Enabled || options.PlatformLogWriter != nil {
|
||||||
|
needCacheFile = true
|
||||||
|
}
|
||||||
if experimentalOptions.ClashAPI != nil || options.PlatformLogWriter != nil {
|
if experimentalOptions.ClashAPI != nil || options.PlatformLogWriter != nil {
|
||||||
needClashAPI = true
|
needClashAPI = true
|
||||||
}
|
}
|
||||||
|
@ -145,8 +151,14 @@ func New(options Options) (*Box, error) {
|
||||||
return nil, E.Cause(err, "initialize platform interface")
|
return nil, E.Cause(err, "initialize platform interface")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
preServices := make(map[string]adapter.Service)
|
preServices1 := make(map[string]adapter.Service)
|
||||||
|
preServices2 := make(map[string]adapter.Service)
|
||||||
postServices := make(map[string]adapter.Service)
|
postServices := make(map[string]adapter.Service)
|
||||||
|
if needCacheFile {
|
||||||
|
cacheFile := cachefile.New(ctx, common.PtrValueOrDefault(experimentalOptions.CacheFile))
|
||||||
|
preServices1["cache file"] = cacheFile
|
||||||
|
service.MustRegister[adapter.CacheFile](ctx, cacheFile)
|
||||||
|
}
|
||||||
if needClashAPI {
|
if needClashAPI {
|
||||||
clashAPIOptions := common.PtrValueOrDefault(experimentalOptions.ClashAPI)
|
clashAPIOptions := common.PtrValueOrDefault(experimentalOptions.ClashAPI)
|
||||||
clashAPIOptions.ModeList = experimental.CalculateClashModeList(options.Options)
|
clashAPIOptions.ModeList = experimental.CalculateClashModeList(options.Options)
|
||||||
|
@ -155,7 +167,7 @@ func New(options Options) (*Box, error) {
|
||||||
return nil, E.Cause(err, "create clash api server")
|
return nil, E.Cause(err, "create clash api server")
|
||||||
}
|
}
|
||||||
router.SetClashServer(clashServer)
|
router.SetClashServer(clashServer)
|
||||||
preServices["clash api"] = clashServer
|
preServices2["clash api"] = clashServer
|
||||||
}
|
}
|
||||||
if needV2RayAPI {
|
if needV2RayAPI {
|
||||||
v2rayServer, err := experimental.NewV2RayServer(logFactory.NewLogger("v2ray-api"), common.PtrValueOrDefault(experimentalOptions.V2RayAPI))
|
v2rayServer, err := experimental.NewV2RayServer(logFactory.NewLogger("v2ray-api"), common.PtrValueOrDefault(experimentalOptions.V2RayAPI))
|
||||||
|
@ -163,7 +175,7 @@ func New(options Options) (*Box, error) {
|
||||||
return nil, E.Cause(err, "create v2ray api server")
|
return nil, E.Cause(err, "create v2ray api server")
|
||||||
}
|
}
|
||||||
router.SetV2RayServer(v2rayServer)
|
router.SetV2RayServer(v2rayServer)
|
||||||
preServices["v2ray api"] = v2rayServer
|
preServices2["v2ray api"] = v2rayServer
|
||||||
}
|
}
|
||||||
return &Box{
|
return &Box{
|
||||||
router: router,
|
router: router,
|
||||||
|
@ -172,7 +184,8 @@ func New(options Options) (*Box, error) {
|
||||||
createdAt: createdAt,
|
createdAt: createdAt,
|
||||||
logFactory: logFactory,
|
logFactory: logFactory,
|
||||||
logger: logFactory.Logger(),
|
logger: logFactory.Logger(),
|
||||||
preServices: preServices,
|
preServices1: preServices1,
|
||||||
|
preServices2: preServices2,
|
||||||
postServices: postServices,
|
postServices: postServices,
|
||||||
done: make(chan struct{}),
|
done: make(chan struct{}),
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -217,7 +230,16 @@ func (s *Box) Start() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Box) preStart() error {
|
func (s *Box) preStart() error {
|
||||||
for serviceName, service := range s.preServices {
|
for serviceName, service := range s.preServices1 {
|
||||||
|
if preService, isPreService := service.(adapter.PreStarter); isPreService {
|
||||||
|
s.logger.Trace("pre-start ", serviceName)
|
||||||
|
err := preService.PreStart()
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "pre-starting ", serviceName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for serviceName, service := range s.preServices2 {
|
||||||
if preService, isPreService := service.(adapter.PreStarter); isPreService {
|
if preService, isPreService := service.(adapter.PreStarter); isPreService {
|
||||||
s.logger.Trace("pre-start ", serviceName)
|
s.logger.Trace("pre-start ", serviceName)
|
||||||
err := preService.PreStart()
|
err := preService.PreStart()
|
||||||
|
@ -238,7 +260,14 @@ func (s *Box) start() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for serviceName, service := range s.preServices {
|
for serviceName, service := range s.preServices1 {
|
||||||
|
s.logger.Trace("starting ", serviceName)
|
||||||
|
err = service.Start()
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "start ", serviceName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for serviceName, service := range s.preServices2 {
|
||||||
s.logger.Trace("starting ", serviceName)
|
s.logger.Trace("starting ", serviceName)
|
||||||
err = service.Start()
|
err = service.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -314,7 +343,13 @@ func (s *Box) Close() error {
|
||||||
return E.Cause(err, "close router")
|
return E.Cause(err, "close router")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
for serviceName, service := range s.preServices {
|
for serviceName, service := range s.preServices1 {
|
||||||
|
s.logger.Trace("closing ", serviceName)
|
||||||
|
errors = E.Append(errors, service.Close(), func(err error) error {
|
||||||
|
return E.Cause(err, "close ", serviceName)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for serviceName, service := range s.preServices2 {
|
||||||
s.logger.Trace("closing ", serviceName)
|
s.logger.Trace("closing ", serviceName)
|
||||||
errors = E.Append(errors, service.Close(), func(err error) error {
|
errors = E.Append(errors, service.Close(), func(err error) error {
|
||||||
return E.Cause(err, "close ", serviceName)
|
return E.Cause(err, "close ", serviceName)
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"github.com/sagernet/bbolt"
|
"github.com/sagernet/bbolt"
|
||||||
bboltErrors "github.com/sagernet/bbolt/errors"
|
bboltErrors "github.com/sagernet/bbolt/errors"
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/service/filemanager"
|
"github.com/sagernet/sing/service/filemanager"
|
||||||
|
@ -31,11 +32,15 @@ var (
|
||||||
cacheIDDefault = []byte("default")
|
cacheIDDefault = []byte("default")
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ adapter.ClashCacheFile = (*CacheFile)(nil)
|
var _ adapter.CacheFile = (*CacheFile)(nil)
|
||||||
|
|
||||||
type CacheFile struct {
|
type CacheFile struct {
|
||||||
DB *bbolt.DB
|
ctx context.Context
|
||||||
|
path string
|
||||||
cacheID []byte
|
cacheID []byte
|
||||||
|
storeFakeIP bool
|
||||||
|
|
||||||
|
DB *bbolt.DB
|
||||||
saveAccess sync.RWMutex
|
saveAccess sync.RWMutex
|
||||||
saveDomain map[netip.Addr]string
|
saveDomain map[netip.Addr]string
|
||||||
saveAddress4 map[string]netip.Addr
|
saveAddress4 map[string]netip.Addr
|
||||||
|
@ -43,7 +48,29 @@ type CacheFile struct {
|
||||||
saveMetadataTimer *time.Timer
|
saveMetadataTimer *time.Timer
|
||||||
}
|
}
|
||||||
|
|
||||||
func Open(ctx context.Context, path string, cacheID string) (*CacheFile, error) {
|
func New(ctx context.Context, options option.CacheFileOptions) *CacheFile {
|
||||||
|
var path string
|
||||||
|
if options.Path != "" {
|
||||||
|
path = options.Path
|
||||||
|
} else {
|
||||||
|
path = "cache.db"
|
||||||
|
}
|
||||||
|
var cacheIDBytes []byte
|
||||||
|
if options.CacheID != "" {
|
||||||
|
cacheIDBytes = append([]byte{0}, []byte(options.CacheID)...)
|
||||||
|
}
|
||||||
|
return &CacheFile{
|
||||||
|
ctx: ctx,
|
||||||
|
path: filemanager.BasePath(ctx, path),
|
||||||
|
cacheID: cacheIDBytes,
|
||||||
|
storeFakeIP: options.StoreFakeIP,
|
||||||
|
saveDomain: make(map[netip.Addr]string),
|
||||||
|
saveAddress4: make(map[string]netip.Addr),
|
||||||
|
saveAddress6: make(map[string]netip.Addr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CacheFile) start() error {
|
||||||
const fileMode = 0o666
|
const fileMode = 0o666
|
||||||
options := bbolt.Options{Timeout: time.Second}
|
options := bbolt.Options{Timeout: time.Second}
|
||||||
var (
|
var (
|
||||||
|
@ -51,7 +78,7 @@ func Open(ctx context.Context, path string, cacheID string) (*CacheFile, error)
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
db, err = bbolt.Open(path, fileMode, &options)
|
db, err = bbolt.Open(c.path, fileMode, &options)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -59,23 +86,20 @@ func Open(ctx context.Context, path string, cacheID string) (*CacheFile, error)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if E.IsMulti(err, bboltErrors.ErrInvalid, bboltErrors.ErrChecksum, bboltErrors.ErrVersionMismatch) {
|
if E.IsMulti(err, bboltErrors.ErrInvalid, bboltErrors.ErrChecksum, bboltErrors.ErrVersionMismatch) {
|
||||||
rmErr := os.Remove(path)
|
rmErr := os.Remove(c.path)
|
||||||
if rmErr != nil {
|
if rmErr != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
err = filemanager.Chown(ctx, path)
|
err = filemanager.Chown(c.ctx, c.path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "platform chown")
|
db.Close()
|
||||||
}
|
return E.Cause(err, "platform chown")
|
||||||
var cacheIDBytes []byte
|
|
||||||
if cacheID != "" {
|
|
||||||
cacheIDBytes = append([]byte{0}, []byte(cacheID)...)
|
|
||||||
}
|
}
|
||||||
err = db.Batch(func(tx *bbolt.Tx) error {
|
err = db.Batch(func(tx *bbolt.Tx) error {
|
||||||
return tx.ForEach(func(name []byte, b *bbolt.Bucket) error {
|
return tx.ForEach(func(name []byte, b *bbolt.Bucket) error {
|
||||||
|
@ -97,15 +121,30 @@ func Open(ctx context.Context, path string, cacheID string) (*CacheFile, error)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
db.Close()
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
return &CacheFile{
|
c.DB = db
|
||||||
DB: db,
|
return nil
|
||||||
cacheID: cacheIDBytes,
|
}
|
||||||
saveDomain: make(map[netip.Addr]string),
|
|
||||||
saveAddress4: make(map[string]netip.Addr),
|
func (c *CacheFile) PreStart() error {
|
||||||
saveAddress6: make(map[string]netip.Addr),
|
return c.start()
|
||||||
}, nil
|
}
|
||||||
|
|
||||||
|
func (c *CacheFile) Start() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CacheFile) Close() error {
|
||||||
|
if c.DB == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return c.DB.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CacheFile) StoreFakeIP() bool {
|
||||||
|
return c.storeFakeIP
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CacheFile) LoadMode() string {
|
func (c *CacheFile) LoadMode() string {
|
||||||
|
@ -218,7 +257,3 @@ func (c *CacheFile) StoreGroupExpand(group string, isExpand bool) error {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CacheFile) Close() error {
|
|
||||||
return c.DB.Close()
|
|
||||||
}
|
|
|
@ -1,23 +1,26 @@
|
||||||
package clashapi
|
package clashapi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
|
"github.com/sagernet/sing/service"
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/go-chi/render"
|
"github.com/go-chi/render"
|
||||||
)
|
)
|
||||||
|
|
||||||
func cacheRouter(router adapter.Router) http.Handler {
|
func cacheRouter(ctx context.Context) http.Handler {
|
||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
r.Post("/fakeip/flush", flushFakeip(router))
|
r.Post("/fakeip/flush", flushFakeip(ctx))
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func flushFakeip(router adapter.Router) func(w http.ResponseWriter, r *http.Request) {
|
func flushFakeip(ctx context.Context) func(w http.ResponseWriter, r *http.Request) {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
if cacheFile := router.ClashServer().CacheFile(); cacheFile != nil {
|
cacheFile := service.FromContext[adapter.CacheFile](ctx)
|
||||||
|
if cacheFile != nil {
|
||||||
err := cacheFile.FakeIPReset()
|
err := cacheFile.FakeIPReset()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
render.Status(r, http.StatusInternalServerError)
|
render.Status(r, http.StatusInternalServerError)
|
||||||
|
|
|
@ -15,7 +15,6 @@ import (
|
||||||
"github.com/sagernet/sing-box/common/urltest"
|
"github.com/sagernet/sing-box/common/urltest"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/experimental"
|
"github.com/sagernet/sing-box/experimental"
|
||||||
"github.com/sagernet/sing-box/experimental/clashapi/cachefile"
|
|
||||||
"github.com/sagernet/sing-box/experimental/clashapi/trafficontrol"
|
"github.com/sagernet/sing-box/experimental/clashapi/trafficontrol"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
|
@ -49,12 +48,6 @@ type Server struct {
|
||||||
mode string
|
mode string
|
||||||
modeList []string
|
modeList []string
|
||||||
modeUpdateHook chan<- struct{}
|
modeUpdateHook chan<- struct{}
|
||||||
storeMode bool
|
|
||||||
storeSelected bool
|
|
||||||
storeFakeIP bool
|
|
||||||
cacheFilePath string
|
|
||||||
cacheID string
|
|
||||||
cacheFile adapter.ClashCacheFile
|
|
||||||
|
|
||||||
externalController bool
|
externalController bool
|
||||||
externalUI string
|
externalUI string
|
||||||
|
@ -76,9 +69,6 @@ func NewServer(ctx context.Context, router adapter.Router, logFactory log.Observ
|
||||||
trafficManager: trafficManager,
|
trafficManager: trafficManager,
|
||||||
modeList: options.ModeList,
|
modeList: options.ModeList,
|
||||||
externalController: options.ExternalController != "",
|
externalController: options.ExternalController != "",
|
||||||
storeMode: options.StoreMode,
|
|
||||||
storeSelected: options.StoreSelected,
|
|
||||||
storeFakeIP: options.StoreFakeIP,
|
|
||||||
externalUIDownloadURL: options.ExternalUIDownloadURL,
|
externalUIDownloadURL: options.ExternalUIDownloadURL,
|
||||||
externalUIDownloadDetour: options.ExternalUIDownloadDetour,
|
externalUIDownloadDetour: options.ExternalUIDownloadDetour,
|
||||||
}
|
}
|
||||||
|
@ -94,18 +84,10 @@ func NewServer(ctx context.Context, router adapter.Router, logFactory log.Observ
|
||||||
server.modeList = append([]string{defaultMode}, server.modeList...)
|
server.modeList = append([]string{defaultMode}, server.modeList...)
|
||||||
}
|
}
|
||||||
server.mode = defaultMode
|
server.mode = defaultMode
|
||||||
if options.StoreMode || options.StoreSelected || options.StoreFakeIP || options.ExternalController == "" {
|
//goland:noinspection GoDeprecation
|
||||||
cachePath := os.ExpandEnv(options.CacheFile)
|
//nolint:staticcheck
|
||||||
if cachePath == "" {
|
if options.StoreMode || options.StoreSelected || options.StoreFakeIP || options.CacheFile != "" || options.CacheID != "" {
|
||||||
cachePath = "cache.db"
|
return nil, E.New("cache_file and related fields in Clash API is deprecated in sing-box 1.8.0, use experimental.cache_file instead.")
|
||||||
}
|
|
||||||
if foundPath, loaded := C.FindPath(cachePath); loaded {
|
|
||||||
cachePath = foundPath
|
|
||||||
} else {
|
|
||||||
cachePath = filemanager.BasePath(ctx, cachePath)
|
|
||||||
}
|
|
||||||
server.cacheFilePath = cachePath
|
|
||||||
server.cacheID = options.CacheID
|
|
||||||
}
|
}
|
||||||
cors := cors.New(cors.Options{
|
cors := cors.New(cors.Options{
|
||||||
AllowedOrigins: []string{"*"},
|
AllowedOrigins: []string{"*"},
|
||||||
|
@ -128,7 +110,7 @@ func NewServer(ctx context.Context, router adapter.Router, logFactory log.Observ
|
||||||
r.Mount("/providers/rules", ruleProviderRouter())
|
r.Mount("/providers/rules", ruleProviderRouter())
|
||||||
r.Mount("/script", scriptRouter())
|
r.Mount("/script", scriptRouter())
|
||||||
r.Mount("/profile", profileRouter())
|
r.Mount("/profile", profileRouter())
|
||||||
r.Mount("/cache", cacheRouter(router))
|
r.Mount("/cache", cacheRouter(ctx))
|
||||||
r.Mount("/dns", dnsRouter(router))
|
r.Mount("/dns", dnsRouter(router))
|
||||||
|
|
||||||
server.setupMetaAPI(r)
|
server.setupMetaAPI(r)
|
||||||
|
@ -147,21 +129,15 @@ func NewServer(ctx context.Context, router adapter.Router, logFactory log.Observ
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) PreStart() error {
|
func (s *Server) PreStart() error {
|
||||||
if s.cacheFilePath != "" {
|
cacheFile := service.FromContext[adapter.CacheFile](s.ctx)
|
||||||
cacheFile, err := cachefile.Open(s.ctx, s.cacheFilePath, s.cacheID)
|
if cacheFile != nil {
|
||||||
if err != nil {
|
mode := cacheFile.LoadMode()
|
||||||
return E.Cause(err, "open cache file")
|
|
||||||
}
|
|
||||||
s.cacheFile = cacheFile
|
|
||||||
if s.storeMode {
|
|
||||||
mode := s.cacheFile.LoadMode()
|
|
||||||
if common.Any(s.modeList, func(it string) bool {
|
if common.Any(s.modeList, func(it string) bool {
|
||||||
return strings.EqualFold(it, mode)
|
return strings.EqualFold(it, mode)
|
||||||
}) {
|
}) {
|
||||||
s.mode = mode
|
s.mode = mode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,7 +163,6 @@ func (s *Server) Close() error {
|
||||||
return common.Close(
|
return common.Close(
|
||||||
common.PtrOrNil(s.httpServer),
|
common.PtrOrNil(s.httpServer),
|
||||||
s.trafficManager,
|
s.trafficManager,
|
||||||
s.cacheFile,
|
|
||||||
s.urlTestHistory,
|
s.urlTestHistory,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -224,8 +199,9 @@ func (s *Server) SetMode(newMode string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.router.ClearDNSCache()
|
s.router.ClearDNSCache()
|
||||||
if s.storeMode {
|
cacheFile := service.FromContext[adapter.CacheFile](s.ctx)
|
||||||
err := s.cacheFile.StoreMode(newMode)
|
if cacheFile != nil {
|
||||||
|
err := cacheFile.StoreMode(newMode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error(E.Cause(err, "save mode"))
|
s.logger.Error(E.Cause(err, "save mode"))
|
||||||
}
|
}
|
||||||
|
@ -233,18 +209,6 @@ func (s *Server) SetMode(newMode string) {
|
||||||
s.logger.Info("updated mode: ", newMode)
|
s.logger.Info("updated mode: ", newMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) StoreSelected() bool {
|
|
||||||
return s.storeSelected
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) StoreFakeIP() bool {
|
|
||||||
return s.storeFakeIP
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) CacheFile() adapter.ClashCacheFile {
|
|
||||||
return s.cacheFile
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) HistoryStorage() *urltest.HistoryStorage {
|
func (s *Server) HistoryStorage() *urltest.HistoryStorage {
|
||||||
return s.urlTestHistory
|
return s.urlTestHistory
|
||||||
}
|
}
|
||||||
|
|
|
@ -159,11 +159,7 @@ func readGroups(reader io.Reader) (OutboundGroupIterator, error) {
|
||||||
|
|
||||||
func writeGroups(writer io.Writer, boxService *BoxService) error {
|
func writeGroups(writer io.Writer, boxService *BoxService) error {
|
||||||
historyStorage := service.PtrFromContext[urltest.HistoryStorage](boxService.ctx)
|
historyStorage := service.PtrFromContext[urltest.HistoryStorage](boxService.ctx)
|
||||||
var cacheFile adapter.ClashCacheFile
|
cacheFile := service.FromContext[adapter.CacheFile](boxService.ctx)
|
||||||
if clashServer := boxService.instance.Router().ClashServer(); clashServer != nil {
|
|
||||||
cacheFile = clashServer.CacheFile()
|
|
||||||
}
|
|
||||||
|
|
||||||
outbounds := boxService.instance.Router().Outbounds()
|
outbounds := boxService.instance.Router().Outbounds()
|
||||||
var iGroups []adapter.OutboundGroup
|
var iGroups []adapter.OutboundGroup
|
||||||
for _, it := range outbounds {
|
for _, it := range outbounds {
|
||||||
|
@ -288,17 +284,16 @@ func (s *CommandServer) handleSetGroupExpand(conn net.Conn) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
service := s.service
|
serviceNow := s.service
|
||||||
if service == nil {
|
if serviceNow == nil {
|
||||||
return writeError(conn, E.New("service not ready"))
|
return writeError(conn, E.New("service not ready"))
|
||||||
}
|
}
|
||||||
if clashServer := service.instance.Router().ClashServer(); clashServer != nil {
|
cacheFile := service.FromContext[adapter.CacheFile](serviceNow.ctx)
|
||||||
if cacheFile := clashServer.CacheFile(); cacheFile != nil {
|
if cacheFile != nil {
|
||||||
err = cacheFile.StoreGroupExpand(groupTag, isExpand)
|
err = cacheFile.StoreGroupExpand(groupTag, isExpand)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return writeError(conn, err)
|
return writeError(conn, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return writeError(conn, nil)
|
return writeError(conn, nil)
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"github.com/sagernet/sing/common/batch"
|
"github.com/sagernet/sing/common/batch"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/rw"
|
"github.com/sagernet/sing/common/rw"
|
||||||
|
"github.com/sagernet/sing/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *CommandClient) URLTest(groupTag string) error {
|
func (c *CommandClient) URLTest(groupTag string) error {
|
||||||
|
@ -37,11 +38,11 @@ func (s *CommandServer) handleURLTest(conn net.Conn) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
service := s.service
|
serviceNow := s.service
|
||||||
if service == nil {
|
if serviceNow == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
abstractOutboundGroup, isLoaded := service.instance.Router().Outbound(groupTag)
|
abstractOutboundGroup, isLoaded := serviceNow.instance.Router().Outbound(groupTag)
|
||||||
if !isLoaded {
|
if !isLoaded {
|
||||||
return writeError(conn, E.New("outbound group not found: ", groupTag))
|
return writeError(conn, E.New("outbound group not found: ", groupTag))
|
||||||
}
|
}
|
||||||
|
@ -53,15 +54,9 @@ func (s *CommandServer) handleURLTest(conn net.Conn) error {
|
||||||
if isURLTest {
|
if isURLTest {
|
||||||
go urlTest.CheckOutbounds()
|
go urlTest.CheckOutbounds()
|
||||||
} else {
|
} else {
|
||||||
var historyStorage *urltest.HistoryStorage
|
historyStorage := service.PtrFromContext[urltest.HistoryStorage](serviceNow.ctx)
|
||||||
if clashServer := service.instance.Router().ClashServer(); clashServer != nil {
|
|
||||||
historyStorage = clashServer.HistoryStorage()
|
|
||||||
} else {
|
|
||||||
return writeError(conn, E.New("Clash API is required for URLTest on non-URLTest group"))
|
|
||||||
}
|
|
||||||
|
|
||||||
outbounds := common.Filter(common.Map(outboundGroup.All(), func(it string) adapter.Outbound {
|
outbounds := common.Filter(common.Map(outboundGroup.All(), func(it string) adapter.Outbound {
|
||||||
itOutbound, _ := service.instance.Router().Outbound(it)
|
itOutbound, _ := serviceNow.instance.Router().Outbound(it)
|
||||||
return itOutbound
|
return itOutbound
|
||||||
}), func(it adapter.Outbound) bool {
|
}), func(it adapter.Outbound) bool {
|
||||||
if it == nil {
|
if it == nil {
|
||||||
|
@ -73,12 +68,12 @@ func (s *CommandServer) handleURLTest(conn net.Conn) error {
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
b, _ := batch.New(service.ctx, batch.WithConcurrencyNum[any](10))
|
b, _ := batch.New(serviceNow.ctx, batch.WithConcurrencyNum[any](10))
|
||||||
for _, detour := range outbounds {
|
for _, detour := range outbounds {
|
||||||
outboundToTest := detour
|
outboundToTest := detour
|
||||||
outboundTag := outboundToTest.Tag()
|
outboundTag := outboundToTest.Tag()
|
||||||
b.Go(outboundTag, func() (any, error) {
|
b.Go(outboundTag, func() (any, error) {
|
||||||
t, err := urltest.URLTest(service.ctx, "", outboundToTest)
|
t, err := urltest.URLTest(serviceNow.ctx, "", outboundToTest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
historyStorage.DeleteURLTestHistory(outboundTag)
|
historyStorage.DeleteURLTestHistory(outboundTag)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
package option
|
|
||||||
|
|
||||||
type ClashAPIOptions struct {
|
|
||||||
ExternalController string `json:"external_controller,omitempty"`
|
|
||||||
ExternalUI string `json:"external_ui,omitempty"`
|
|
||||||
ExternalUIDownloadURL string `json:"external_ui_download_url,omitempty"`
|
|
||||||
ExternalUIDownloadDetour string `json:"external_ui_download_detour,omitempty"`
|
|
||||||
Secret string `json:"secret,omitempty"`
|
|
||||||
DefaultMode string `json:"default_mode,omitempty"`
|
|
||||||
StoreMode bool `json:"store_mode,omitempty"`
|
|
||||||
StoreSelected bool `json:"store_selected,omitempty"`
|
|
||||||
StoreFakeIP bool `json:"store_fakeip,omitempty"`
|
|
||||||
CacheFile string `json:"cache_file,omitempty"`
|
|
||||||
CacheID string `json:"cache_id,omitempty"`
|
|
||||||
|
|
||||||
ModeList []string `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type SelectorOutboundOptions struct {
|
|
||||||
Outbounds []string `json:"outbounds"`
|
|
||||||
Default string `json:"default,omitempty"`
|
|
||||||
InterruptExistConnections bool `json:"interrupt_exist_connections,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type URLTestOutboundOptions struct {
|
|
||||||
Outbounds []string `json:"outbounds"`
|
|
||||||
URL string `json:"url,omitempty"`
|
|
||||||
Interval Duration `json:"interval,omitempty"`
|
|
||||||
Tolerance uint16 `json:"tolerance,omitempty"`
|
|
||||||
InterruptExistConnections bool `json:"interrupt_exist_connections,omitempty"`
|
|
||||||
}
|
|
|
@ -1,7 +1,48 @@
|
||||||
package option
|
package option
|
||||||
|
|
||||||
type ExperimentalOptions struct {
|
type ExperimentalOptions struct {
|
||||||
|
CacheFile *CacheFileOptions `json:"cache_file,omitempty"`
|
||||||
ClashAPI *ClashAPIOptions `json:"clash_api,omitempty"`
|
ClashAPI *ClashAPIOptions `json:"clash_api,omitempty"`
|
||||||
V2RayAPI *V2RayAPIOptions `json:"v2ray_api,omitempty"`
|
V2RayAPI *V2RayAPIOptions `json:"v2ray_api,omitempty"`
|
||||||
Debug *DebugOptions `json:"debug,omitempty"`
|
Debug *DebugOptions `json:"debug,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CacheFileOptions struct {
|
||||||
|
Enabled bool `json:"enabled,omitempty"`
|
||||||
|
Path string `json:"path,omitempty"`
|
||||||
|
CacheID string `json:"cache_id,omitempty"`
|
||||||
|
StoreFakeIP bool `json:"store_fakeip,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClashAPIOptions struct {
|
||||||
|
ExternalController string `json:"external_controller,omitempty"`
|
||||||
|
ExternalUI string `json:"external_ui,omitempty"`
|
||||||
|
ExternalUIDownloadURL string `json:"external_ui_download_url,omitempty"`
|
||||||
|
ExternalUIDownloadDetour string `json:"external_ui_download_detour,omitempty"`
|
||||||
|
Secret string `json:"secret,omitempty"`
|
||||||
|
DefaultMode string `json:"default_mode,omitempty"`
|
||||||
|
ModeList []string `json:"-"`
|
||||||
|
|
||||||
|
// Deprecated: migrated to global cache file
|
||||||
|
StoreMode bool `json:"store_mode,omitempty"`
|
||||||
|
// Deprecated: migrated to global cache file
|
||||||
|
StoreSelected bool `json:"store_selected,omitempty"`
|
||||||
|
// Deprecated: migrated to global cache file
|
||||||
|
StoreFakeIP bool `json:"store_fakeip,omitempty"`
|
||||||
|
// Deprecated: migrated to global cache file
|
||||||
|
CacheFile string `json:"cache_file,omitempty"`
|
||||||
|
// Deprecated: migrated to global cache file
|
||||||
|
CacheID string `json:"cache_id,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type V2RayAPIOptions struct {
|
||||||
|
Listen string `json:"listen,omitempty"`
|
||||||
|
Stats *V2RayStatsServiceOptions `json:"stats,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type V2RayStatsServiceOptions struct {
|
||||||
|
Enabled bool `json:"enabled,omitempty"`
|
||||||
|
Inbounds []string `json:"inbounds,omitempty"`
|
||||||
|
Outbounds []string `json:"outbounds,omitempty"`
|
||||||
|
Users []string `json:"users,omitempty"`
|
||||||
|
}
|
||||||
|
|
15
option/group.go
Normal file
15
option/group.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package option
|
||||||
|
|
||||||
|
type SelectorOutboundOptions struct {
|
||||||
|
Outbounds []string `json:"outbounds"`
|
||||||
|
Default string `json:"default,omitempty"`
|
||||||
|
InterruptExistConnections bool `json:"interrupt_exist_connections,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type URLTestOutboundOptions struct {
|
||||||
|
Outbounds []string `json:"outbounds"`
|
||||||
|
URL string `json:"url,omitempty"`
|
||||||
|
Interval Duration `json:"interval,omitempty"`
|
||||||
|
Tolerance uint16 `json:"tolerance,omitempty"`
|
||||||
|
InterruptExistConnections bool `json:"interrupt_exist_connections,omitempty"`
|
||||||
|
}
|
|
@ -1,13 +1 @@
|
||||||
package option
|
package option
|
||||||
|
|
||||||
type V2RayAPIOptions struct {
|
|
||||||
Listen string `json:"listen,omitempty"`
|
|
||||||
Stats *V2RayStatsServiceOptions `json:"stats,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type V2RayStatsServiceOptions struct {
|
|
||||||
Enabled bool `json:"enabled,omitempty"`
|
|
||||||
Inbounds []string `json:"inbounds,omitempty"`
|
|
||||||
Outbounds []string `json:"outbounds,omitempty"`
|
|
||||||
Users []string `json:"users,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ func New(ctx context.Context, router adapter.Router, logger log.ContextLogger, t
|
||||||
case C.TypeHysteria2:
|
case C.TypeHysteria2:
|
||||||
return NewHysteria2(ctx, router, logger, tag, options.Hysteria2Options)
|
return NewHysteria2(ctx, router, logger, tag, options.Hysteria2Options)
|
||||||
case C.TypeSelector:
|
case C.TypeSelector:
|
||||||
return NewSelector(router, logger, tag, options.SelectorOptions)
|
return NewSelector(ctx, router, logger, tag, options.SelectorOptions)
|
||||||
case C.TypeURLTest:
|
case C.TypeURLTest:
|
||||||
return NewURLTest(ctx, router, logger, tag, options.URLTestOptions)
|
return NewURLTest(ctx, router, logger, tag, options.URLTestOptions)
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
|
"github.com/sagernet/sing/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -21,6 +22,7 @@ var (
|
||||||
|
|
||||||
type Selector struct {
|
type Selector struct {
|
||||||
myOutboundAdapter
|
myOutboundAdapter
|
||||||
|
ctx context.Context
|
||||||
tags []string
|
tags []string
|
||||||
defaultTag string
|
defaultTag string
|
||||||
outbounds map[string]adapter.Outbound
|
outbounds map[string]adapter.Outbound
|
||||||
|
@ -29,7 +31,7 @@ type Selector struct {
|
||||||
interruptExternalConnections bool
|
interruptExternalConnections bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSelector(router adapter.Router, logger log.ContextLogger, tag string, options option.SelectorOutboundOptions) (*Selector, error) {
|
func NewSelector(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.SelectorOutboundOptions) (*Selector, error) {
|
||||||
outbound := &Selector{
|
outbound := &Selector{
|
||||||
myOutboundAdapter: myOutboundAdapter{
|
myOutboundAdapter: myOutboundAdapter{
|
||||||
protocol: C.TypeSelector,
|
protocol: C.TypeSelector,
|
||||||
|
@ -38,6 +40,7 @@ func NewSelector(router adapter.Router, logger log.ContextLogger, tag string, op
|
||||||
tag: tag,
|
tag: tag,
|
||||||
dependencies: options.Outbounds,
|
dependencies: options.Outbounds,
|
||||||
},
|
},
|
||||||
|
ctx: ctx,
|
||||||
tags: options.Outbounds,
|
tags: options.Outbounds,
|
||||||
defaultTag: options.Default,
|
defaultTag: options.Default,
|
||||||
outbounds: make(map[string]adapter.Outbound),
|
outbounds: make(map[string]adapter.Outbound),
|
||||||
|
@ -67,8 +70,9 @@ func (s *Selector) Start() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.tag != "" {
|
if s.tag != "" {
|
||||||
if clashServer := s.router.ClashServer(); clashServer != nil && clashServer.StoreSelected() {
|
cacheFile := service.FromContext[adapter.CacheFile](s.ctx)
|
||||||
selected := clashServer.CacheFile().LoadSelected(s.tag)
|
if cacheFile != nil {
|
||||||
|
selected := cacheFile.LoadSelected(s.tag)
|
||||||
if selected != "" {
|
if selected != "" {
|
||||||
detour, loaded := s.outbounds[selected]
|
detour, loaded := s.outbounds[selected]
|
||||||
if loaded {
|
if loaded {
|
||||||
|
@ -110,8 +114,9 @@ func (s *Selector) SelectOutbound(tag string) bool {
|
||||||
}
|
}
|
||||||
s.selected = detour
|
s.selected = detour
|
||||||
if s.tag != "" {
|
if s.tag != "" {
|
||||||
if clashServer := s.router.ClashServer(); clashServer != nil && clashServer.StoreSelected() {
|
cacheFile := service.FromContext[adapter.CacheFile](s.ctx)
|
||||||
err := clashServer.CacheFile().StoreSelected(s.tag, tag)
|
if cacheFile != nil {
|
||||||
|
err := cacheFile.StoreSelected(s.tag, tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("store selected: ", err)
|
s.logger.Error("store selected: ", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -262,7 +262,7 @@ func NewRouter(
|
||||||
if fakeIPOptions.Inet6Range != nil {
|
if fakeIPOptions.Inet6Range != nil {
|
||||||
inet6Range = *fakeIPOptions.Inet6Range
|
inet6Range = *fakeIPOptions.Inet6Range
|
||||||
}
|
}
|
||||||
router.fakeIPStore = fakeip.NewStore(router, router.logger, inet4Range, inet6Range)
|
router.fakeIPStore = fakeip.NewStore(ctx, router.logger, inet4Range, inet6Range)
|
||||||
}
|
}
|
||||||
|
|
||||||
usePlatformDefaultInterfaceMonitor := platformInterface != nil && platformInterface.UsePlatformDefaultInterfaceMonitor()
|
usePlatformDefaultInterfaceMonitor := platformInterface != nil && platformInterface.UsePlatformDefaultInterfaceMonitor()
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
package fakeip
|
package fakeip
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
|
"github.com/sagernet/sing/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ adapter.FakeIPStore = (*Store)(nil)
|
var _ adapter.FakeIPStore = (*Store)(nil)
|
||||||
|
|
||||||
type Store struct {
|
type Store struct {
|
||||||
router adapter.Router
|
ctx context.Context
|
||||||
logger logger.Logger
|
logger logger.Logger
|
||||||
inet4Range netip.Prefix
|
inet4Range netip.Prefix
|
||||||
inet6Range netip.Prefix
|
inet6Range netip.Prefix
|
||||||
|
@ -20,9 +22,9 @@ type Store struct {
|
||||||
inet6Current netip.Addr
|
inet6Current netip.Addr
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStore(router adapter.Router, logger logger.Logger, inet4Range netip.Prefix, inet6Range netip.Prefix) *Store {
|
func NewStore(ctx context.Context, logger logger.Logger, inet4Range netip.Prefix, inet6Range netip.Prefix) *Store {
|
||||||
return &Store{
|
return &Store{
|
||||||
router: router,
|
ctx: ctx,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
inet4Range: inet4Range,
|
inet4Range: inet4Range,
|
||||||
inet6Range: inet6Range,
|
inet6Range: inet6Range,
|
||||||
|
@ -31,11 +33,10 @@ func NewStore(router adapter.Router, logger logger.Logger, inet4Range netip.Pref
|
||||||
|
|
||||||
func (s *Store) Start() error {
|
func (s *Store) Start() error {
|
||||||
var storage adapter.FakeIPStorage
|
var storage adapter.FakeIPStorage
|
||||||
if clashServer := s.router.ClashServer(); clashServer != nil && clashServer.StoreFakeIP() {
|
cacheFile := service.FromContext[adapter.CacheFile](s.ctx)
|
||||||
if cacheFile := clashServer.CacheFile(); cacheFile != nil {
|
if cacheFile != nil && cacheFile.StoreFakeIP() {
|
||||||
storage = cacheFile
|
storage = cacheFile
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if storage == nil {
|
if storage == nil {
|
||||||
storage = NewMemoryStorage()
|
storage = NewMemoryStorage()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue