mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-05-14 00:51:12 +03:00
130 lines
3.6 KiB
Go
130 lines
3.6 KiB
Go
package provider
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"github.com/sagernet/fswatch"
|
|
"github.com/sagernet/sing-box/adapter"
|
|
"github.com/sagernet/sing-box/adapter/provider"
|
|
C "github.com/sagernet/sing-box/constant"
|
|
"github.com/sagernet/sing-box/log"
|
|
"github.com/sagernet/sing-box/option"
|
|
"github.com/sagernet/sing-box/parser"
|
|
"github.com/sagernet/sing/common"
|
|
E "github.com/sagernet/sing/common/exceptions"
|
|
F "github.com/sagernet/sing/common/format"
|
|
"github.com/sagernet/sing/service"
|
|
"github.com/sagernet/sing/service/filemanager"
|
|
)
|
|
|
|
func RegisterProviderLocal(registry *provider.Registry) {
|
|
provider.Register[option.ProviderLocalOptions](registry, C.ProviderTypeLocal, NewProviderLocal)
|
|
}
|
|
|
|
func RegisterProviderInline(registry *provider.Registry) {
|
|
provider.Register[option.ProviderInlineOptions](registry, C.ProviderTypeInline, NewProviderInline)
|
|
}
|
|
|
|
var _ adapter.Provider = (*ProviderLocal)(nil)
|
|
|
|
type ProviderLocal struct {
|
|
provider.Adapter
|
|
ctx context.Context
|
|
logger log.ContextLogger
|
|
provider adapter.ProviderManager
|
|
path string
|
|
lastOutOpts []option.Outbound
|
|
lastUpdated time.Time
|
|
watcher *fswatch.Watcher
|
|
}
|
|
|
|
func NewProviderInline(ctx context.Context, router adapter.Router, logFactory log.Factory, tag string, options option.ProviderInlineOptions) (adapter.Provider, error) {
|
|
var (
|
|
outbound = service.FromContext[adapter.OutboundManager](ctx)
|
|
logger = logFactory.NewLogger(F.ToString("provider/inline", "[", tag, "]"))
|
|
)
|
|
provider := &ProviderLocal{
|
|
Adapter: provider.NewAdapter(ctx, router, outbound, logFactory, logger, tag, C.ProviderTypeInline, options.HealthCheck),
|
|
ctx: ctx,
|
|
logger: logger,
|
|
}
|
|
provider.UpdateOutbounds(nil, options.Outbounds)
|
|
return provider, nil
|
|
}
|
|
|
|
func NewProviderLocal(ctx context.Context, router adapter.Router, logFactory log.Factory, tag string, options option.ProviderLocalOptions) (adapter.Provider, error) {
|
|
if options.Path == "" {
|
|
return nil, E.New("provider path is required")
|
|
}
|
|
var (
|
|
outbound = service.FromContext[adapter.OutboundManager](ctx)
|
|
logger = logFactory.NewLogger(F.ToString("provider/local", "[", tag, "]"))
|
|
)
|
|
provider := &ProviderLocal{
|
|
Adapter: provider.NewAdapter(ctx, router, outbound, logFactory, logger, tag, C.ProviderTypeLocal, options.HealthCheck),
|
|
ctx: ctx,
|
|
logger: logger,
|
|
provider: service.FromContext[adapter.ProviderManager](ctx),
|
|
}
|
|
filePath := filemanager.BasePath(ctx, options.Path)
|
|
provider.path, _ = filepath.Abs(filePath)
|
|
watcher, err := fswatch.NewWatcher(fswatch.Options{
|
|
Path: []string{filePath},
|
|
Callback: func(path string) {
|
|
uErr := provider.reloadFile(path)
|
|
if uErr != nil {
|
|
logger.Error(E.Cause(uErr, "reload provider ", tag))
|
|
}
|
|
provider.UpdateGroups()
|
|
},
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
provider.watcher = watcher
|
|
return provider, nil
|
|
}
|
|
|
|
func (s *ProviderLocal) Start() error {
|
|
err := s.reloadFile(s.path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
s.UpdateGroups()
|
|
if s.watcher != nil {
|
|
err := s.watcher.Start()
|
|
if err != nil {
|
|
s.logger.Error(E.Cause(err, "watch provider file"))
|
|
}
|
|
}
|
|
return s.Adapter.Start()
|
|
}
|
|
|
|
func (s *ProviderLocal) UpdatedAt() time.Time {
|
|
return s.lastUpdated
|
|
}
|
|
|
|
func (s *ProviderLocal) reloadFile(path string) error {
|
|
if fileInfo, err := os.Stat(path); err == nil {
|
|
s.lastUpdated = fileInfo.ModTime()
|
|
}
|
|
content, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
outboundOpts, err := parser.ParseSubscription(s.ctx, string(content))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
s.UpdateOutbounds(s.lastOutOpts, outboundOpts)
|
|
s.lastOutOpts = outboundOpts
|
|
return nil
|
|
}
|
|
|
|
func (s *ProviderLocal) Close() error {
|
|
return common.Close(&s.Adapter, common.PtrOrNil(s.watcher))
|
|
}
|