mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-25 10:01:30 +00:00
platform: Add group expand status
This commit is contained in:
parent
cf57e46d69
commit
9f94b21687
|
@ -23,6 +23,8 @@ type ClashServer interface {
|
||||||
type ClashCacheFile interface {
|
type ClashCacheFile interface {
|
||||||
LoadSelected(group string) string
|
LoadSelected(group string) string
|
||||||
StoreSelected(group string, selected string) error
|
StoreSelected(group string, selected string) error
|
||||||
|
LoadGroupExpand(group string) (isExpand bool, loaded bool)
|
||||||
|
StoreGroupExpand(group string, expand bool) error
|
||||||
FakeIPStorage
|
FakeIPStorage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,10 @@ import (
|
||||||
"go.etcd.io/bbolt"
|
"go.etcd.io/bbolt"
|
||||||
)
|
)
|
||||||
|
|
||||||
var bucketSelected = []byte("selected")
|
var (
|
||||||
|
bucketSelected = []byte("selected")
|
||||||
|
bucketExpand = []byte("group_expand")
|
||||||
|
)
|
||||||
|
|
||||||
var _ adapter.ClashCacheFile = (*CacheFile)(nil)
|
var _ adapter.ClashCacheFile = (*CacheFile)(nil)
|
||||||
|
|
||||||
|
@ -49,21 +52,15 @@ func Open(path string, cacheID string) (*CacheFile, error) {
|
||||||
if name[0] == 0 {
|
if name[0] == 0 {
|
||||||
return b.ForEachBucket(func(k []byte) error {
|
return b.ForEachBucket(func(k []byte) error {
|
||||||
bucketName := string(k)
|
bucketName := string(k)
|
||||||
if !(bucketName == string(bucketSelected)) {
|
if !(bucketName == string(bucketSelected) || bucketName == string(bucketExpand)) {
|
||||||
delErr := b.DeleteBucket(name)
|
_ = b.DeleteBucket(name)
|
||||||
if delErr != nil {
|
|
||||||
return delErr
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
bucketName := string(name)
|
bucketName := string(name)
|
||||||
if !(bucketName == string(bucketSelected) || strings.HasPrefix(bucketName, fakeipBucketPrefix)) {
|
if !(bucketName == string(bucketSelected) || bucketName == string(bucketExpand) || strings.HasPrefix(bucketName, fakeipBucketPrefix)) {
|
||||||
delErr := tx.DeleteBucket(name)
|
_ = tx.DeleteBucket(name)
|
||||||
if delErr != nil {
|
|
||||||
return delErr
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -129,6 +126,36 @@ func (c *CacheFile) StoreSelected(group, selected string) error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *CacheFile) LoadGroupExpand(group string) (isExpand bool, loaded bool) {
|
||||||
|
c.DB.View(func(t *bbolt.Tx) error {
|
||||||
|
bucket := c.bucket(t, bucketExpand)
|
||||||
|
if bucket == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
expandBytes := bucket.Get([]byte(group))
|
||||||
|
if len(expandBytes) == 1 {
|
||||||
|
isExpand = expandBytes[0] == 1
|
||||||
|
loaded = true
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CacheFile) StoreGroupExpand(group string, isExpand bool) error {
|
||||||
|
return c.DB.Batch(func(t *bbolt.Tx) error {
|
||||||
|
bucket, err := c.createBucket(t, bucketExpand)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if isExpand {
|
||||||
|
return bucket.Put([]byte(group), []byte{1})
|
||||||
|
} else {
|
||||||
|
return bucket.Put([]byte(group), []byte{0})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (c *CacheFile) Close() error {
|
func (c *CacheFile) Close() error {
|
||||||
return c.DB.Close()
|
return c.DB.Close()
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,7 +84,7 @@ func NewServer(ctx context.Context, router adapter.Router, logFactory log.Observ
|
||||||
if server.mode == "" {
|
if server.mode == "" {
|
||||||
server.mode = "rule"
|
server.mode = "rule"
|
||||||
}
|
}
|
||||||
if options.StoreSelected || options.StoreFakeIP {
|
if options.StoreSelected || options.StoreFakeIP || options.ExternalController == "" {
|
||||||
cachePath := os.ExpandEnv(options.CacheFile)
|
cachePath := os.ExpandEnv(options.CacheFile)
|
||||||
if cachePath == "" {
|
if cachePath == "" {
|
||||||
cachePath = "cache.db"
|
cachePath = "cache.db"
|
||||||
|
|
|
@ -8,4 +8,5 @@ const (
|
||||||
CommandGroup
|
CommandGroup
|
||||||
CommandSelectOutbound
|
CommandSelectOutbound
|
||||||
CommandURLTest
|
CommandURLTest
|
||||||
|
CommandGroupExpand
|
||||||
)
|
)
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/urltest"
|
"github.com/sagernet/sing-box/common/urltest"
|
||||||
"github.com/sagernet/sing-box/outbound"
|
"github.com/sagernet/sing-box/outbound"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/rw"
|
"github.com/sagernet/sing/common/rw"
|
||||||
"github.com/sagernet/sing/service"
|
"github.com/sagernet/sing/service"
|
||||||
)
|
)
|
||||||
|
@ -18,6 +19,7 @@ type OutboundGroup struct {
|
||||||
Type string
|
Type string
|
||||||
Selectable bool
|
Selectable bool
|
||||||
Selected string
|
Selected string
|
||||||
|
isExpand int8
|
||||||
items []*OutboundGroupItem
|
items []*OutboundGroupItem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +27,19 @@ func (g *OutboundGroup) GetItems() OutboundGroupItemIterator {
|
||||||
return newIterator(g.items)
|
return newIterator(g.items)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *OutboundGroup) IsExpand() bool {
|
||||||
|
switch g.isExpand {
|
||||||
|
case -1:
|
||||||
|
return g.Selectable
|
||||||
|
case 0:
|
||||||
|
return false
|
||||||
|
case 1:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
panic("unexpected expand value")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type OutboundGroupIterator interface {
|
type OutboundGroupIterator interface {
|
||||||
Next() *OutboundGroup
|
Next() *OutboundGroup
|
||||||
HasNext() bool
|
HasNext() bool
|
||||||
|
@ -114,6 +129,11 @@ func readGroups(reader io.Reader) (OutboundGroupIterator, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = binary.Read(reader, binary.BigEndian, &group.isExpand)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
var itemLength uint16
|
var itemLength uint16
|
||||||
err = binary.Read(reader, binary.BigEndian, &itemLength)
|
err = binary.Read(reader, binary.BigEndian, &itemLength)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -152,6 +172,10 @@ 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
|
||||||
|
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
|
||||||
|
@ -167,6 +191,15 @@ func writeGroups(writer io.Writer, boxService *BoxService) error {
|
||||||
group.Type = iGroup.Type()
|
group.Type = iGroup.Type()
|
||||||
_, group.Selectable = iGroup.(*outbound.Selector)
|
_, group.Selectable = iGroup.(*outbound.Selector)
|
||||||
group.Selected = iGroup.Now()
|
group.Selected = iGroup.Now()
|
||||||
|
if cacheFile != nil {
|
||||||
|
if isExpand, loaded := cacheFile.LoadGroupExpand(group.Tag); !loaded {
|
||||||
|
group.isExpand = -1
|
||||||
|
} else if isExpand {
|
||||||
|
group.isExpand = 1
|
||||||
|
} else {
|
||||||
|
group.isExpand = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, itemTag := range iGroup.All() {
|
for _, itemTag := range iGroup.All() {
|
||||||
itemOutbound, isLoaded := boxService.instance.Router().Outbound(itemTag)
|
itemOutbound, isLoaded := boxService.instance.Router().Outbound(itemTag)
|
||||||
|
@ -207,6 +240,10 @@ func writeGroups(writer io.Writer, boxService *BoxService) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
err = binary.Write(writer, binary.BigEndian, group.isExpand)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
err = binary.Write(writer, binary.BigEndian, uint16(len(group.items)))
|
err = binary.Write(writer, binary.BigEndian, uint16(len(group.items)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -232,3 +269,50 @@ func writeGroups(writer io.Writer, boxService *BoxService) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *CommandClient) SetGroupExpand(groupTag string, isExpand bool) error {
|
||||||
|
conn, err := c.directConnect()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
err = binary.Write(conn, binary.BigEndian, uint8(CommandGroupExpand))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = rw.WriteVString(conn, groupTag)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = binary.Write(conn, binary.BigEndian, isExpand)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return readError(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *CommandServer) handleSetGroupExpand(conn net.Conn) error {
|
||||||
|
defer conn.Close()
|
||||||
|
groupTag, err := rw.ReadVString(conn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var isExpand bool
|
||||||
|
err = binary.Read(conn, binary.BigEndian, &isExpand)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
service := s.service
|
||||||
|
if service == nil {
|
||||||
|
return writeError(conn, E.New("service not ready"))
|
||||||
|
}
|
||||||
|
if clashServer := service.instance.Router().ClashServer(); clashServer != nil {
|
||||||
|
if cacheFile := clashServer.CacheFile(); cacheFile != nil {
|
||||||
|
err = cacheFile.StoreGroupExpand(groupTag, isExpand)
|
||||||
|
if err != nil {
|
||||||
|
return writeError(conn, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return writeError(conn, nil)
|
||||||
|
}
|
||||||
|
|
|
@ -154,6 +154,8 @@ func (s *CommandServer) handleConnection(conn net.Conn) error {
|
||||||
return s.handleSelectOutbound(conn)
|
return s.handleSelectOutbound(conn)
|
||||||
case CommandURLTest:
|
case CommandURLTest:
|
||||||
return s.handleURLTest(conn)
|
return s.handleURLTest(conn)
|
||||||
|
case CommandGroupExpand:
|
||||||
|
return s.handleSetGroupExpand(conn)
|
||||||
default:
|
default:
|
||||||
return E.New("unknown command: ", command)
|
return E.New("unknown command: ", command)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package libbox
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
runtimeDebug "runtime/debug"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box"
|
"github.com/sagernet/sing-box"
|
||||||
|
@ -35,6 +36,7 @@ func NewService(configContent string, platformInterface PlatformInterface) (*Box
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
runtimeDebug.FreeOSMemory()
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
ctx = filemanager.WithDefault(ctx, sWorkingPath, sTempPath, sUserID, sGroupID)
|
ctx = filemanager.WithDefault(ctx, sWorkingPath, sTempPath, sUserID, sGroupID)
|
||||||
ctx = service.ContextWithPtr(ctx, urltest.NewHistoryStorage())
|
ctx = service.ContextWithPtr(ctx, urltest.NewHistoryStorage())
|
||||||
|
@ -49,6 +51,7 @@ func NewService(configContent string, platformInterface PlatformInterface) (*Box
|
||||||
cancel()
|
cancel()
|
||||||
return nil, E.Cause(err, "create service")
|
return nil, E.Cause(err, "create service")
|
||||||
}
|
}
|
||||||
|
runtimeDebug.FreeOSMemory()
|
||||||
return &BoxService{
|
return &BoxService{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
cancel: cancel,
|
cancel: cancel,
|
||||||
|
|
Loading…
Reference in a new issue