Replace template.extra_groups.per_subscription with target

This commit is contained in:
世界 2024-07-22 08:48:10 +08:00
parent 53227cd88a
commit 7f454a1133
No known key found for this signature in database
GPG Key ID: CD109927C34A63C4
3 changed files with 100 additions and 77 deletions

View File

@ -4,6 +4,7 @@
{
"name": "",
"extend": "",
// Global
"log": {},
@ -12,6 +13,7 @@
"disable_traffic_bypass": false,
"disable_rule_set": false,
"remote_resolve": false,
// DNS
"dns": "",
@ -19,6 +21,7 @@
"enable_fakeip": false,
"pre_dns_rules": [],
"custom_dns_rules": [],
// Inbound
"inbounds": [],
@ -27,14 +30,14 @@
"disable_system_proxy": false,
"custom_tun": {},
"custom_mixed": {},
// Outbound
"extra_groups": [
{
"tag": "",
"type": "",
"exclude_outbounds": false,
"per_subscription": false,
"target": "",
"tag_per_subscription": "",
"filter": "",
"exclude": "",
@ -42,13 +45,13 @@
"custom_urltest": {}
}
],
"generate_global_urltest": false,
"direct_tag": "",
"default_tag": "",
"urltest_tag": "",
"custom_direct": {},
"custom_selector": {},
"custom_urltest": {},
// Route
"pre_rules": [],
@ -58,6 +61,7 @@
"custom_geosite": {},
"custom_rule_set": [],
"post_rule_set": [],
// Experimental
"disable_cache_file": false,
@ -66,6 +70,7 @@
"clash_mode_global": "",
"clash_mode_direct": "",
"custom_clash_api": {},
// Debug
"pprof_listen": "",
@ -190,17 +195,17 @@ Tag of the group outbound.
Type of the group outbound.
#### extra_groups.exclude_outbounds
#### extra_groups.target
Only include subscriptions but not custom outbounds.
#### extra_groups.per_subscription
Generate a internal group for every subscription instead of a global group.
| Value | Description |
|----------------|------------------------------------------------------------|
| `default` | No additional behaviors. |
| `global` | Generate a group and add it to default selector. |
| `subscription` | Generate a internal group for every subscription selector. |
#### extra_groups.tag_per_subscription
Tag for every new subscription internal group.
Tag for every new subscription internal group when `target` is `subscription`.
`{{ .tag }} ({{ .subscription_name }})` is used by default.
@ -220,10 +225,6 @@ Custom [Selector](https://sing-box.sagernet.org/configuration/outbound/selector/
Custom [URLTest](https://sing-box.sagernet.org/configuration/outbound/urltest/) template.
#### generate_global_urltest
Generate a global `URLTest` outbound with all global outbounds.
#### direct_tag
Custom direct outbound tag.

View File

@ -4,6 +4,7 @@ import (
C "github.com/sagernet/serenity/constant"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-dns"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/json"
)
@ -129,8 +130,7 @@ func (t Template) DisableIPv6() bool {
type ExtraGroup struct {
Tag string `json:"tag,omitempty"`
ExcludeOutbounds bool `json:"exclude_outbounds,omitempty"`
PerSubscription bool `json:"per_subscription,omitempty"`
Target ExtraGroupTarget `json:"target,omitempty"`
TagPerSubscription string `json:"tag_per_subscription,omitempty"`
Type string `json:"type,omitempty"`
Filter option.Listable[string] `json:"filter,omitempty"`
@ -138,3 +138,47 @@ type ExtraGroup struct {
CustomSelector *option.SelectorOutboundOptions `json:"custom_selector,omitempty"`
CustomURLTest *option.URLTestOutboundOptions `json:"custom_urltest,omitempty"`
}
type ExtraGroupTarget uint8
const (
ExtraGroupTargetDefault ExtraGroupTarget = iota
ExtraGroupTargetGlobal
ExtraGroupTargetSubscription
)
func (t ExtraGroupTarget) String() string {
switch t {
case ExtraGroupTargetDefault:
return "default"
case ExtraGroupTargetGlobal:
return "global"
case ExtraGroupTargetSubscription:
return "subscription"
default:
return "unknown"
}
}
func (t ExtraGroupTarget) MarshalJSON() ([]byte, error) {
return json.Marshal(t.String())
}
func (t *ExtraGroupTarget) UnmarshalJSON(bytes []byte) error {
var stringValue string
err := json.Unmarshal(bytes, &stringValue)
if err != nil {
return err
}
switch stringValue {
case "default":
*t = ExtraGroupTargetDefault
case "global":
*t = ExtraGroupTargetGlobal
case "subscription":
*t = ExtraGroupTargetSubscription
default:
return E.New("unknown extra group target: ", stringValue)
}
return nil
}

View File

@ -6,15 +6,16 @@ import (
"text/template"
M "github.com/sagernet/serenity/common/metadata"
"github.com/sagernet/serenity/option"
"github.com/sagernet/serenity/subscription"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
boxOption "github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
)
func (t *Template) renderOutbounds(metadata M.Metadata, options *option.Options, outbounds [][]option.Outbound, subscriptions []*subscription.Subscription) error {
func (t *Template) renderOutbounds(metadata M.Metadata, options *boxOption.Options, outbounds [][]boxOption.Outbound, subscriptions []*subscription.Subscription) error {
defaultTag := t.DefaultTag
if defaultTag == "" {
defaultTag = DefaultDefaultTag
@ -28,7 +29,7 @@ func (t *Template) renderOutbounds(metadata M.Metadata, options *option.Options,
if blockTag == "" {
blockTag = DefaultBlockTag
}
options.Outbounds = []option.Outbound{
options.Outbounds = []boxOption.Outbound{
{
Tag: directTag,
Type: C.TypeDirect,
@ -52,26 +53,22 @@ func (t *Template) renderOutbounds(metadata M.Metadata, options *option.Options,
if urlTestTag == "" {
urlTestTag = DefaultURLTestTag
}
outboundToString := func(it option.Outbound) string {
outboundToString := func(it boxOption.Outbound) string {
return it.Tag
}
var (
globalOutbounds []option.Outbound
globalOutboundTags []string
)
var globalOutboundTags []string
if len(outbounds) > 0 {
for _, outbound := range outbounds {
options.Outbounds = append(options.Outbounds, outbound...)
}
globalOutbounds = common.Map(outbounds, func(it []option.Outbound) option.Outbound {
return it[0]
globalOutboundTags = common.Map(outbounds, func(it []boxOption.Outbound) string {
return it[0].Tag
})
globalOutboundTags = common.Map(globalOutbounds, outboundToString)
}
var (
allGroups []option.Outbound
allGroupOutbounds []option.Outbound
allGroups []boxOption.Outbound
allGroupOutbounds []boxOption.Outbound
groupTags []string
)
@ -79,11 +76,11 @@ func (t *Template) renderOutbounds(metadata M.Metadata, options *option.Options,
if len(it.Servers) == 0 {
continue
}
joinOutbounds := common.Map(it.Servers, func(it option.Outbound) string {
joinOutbounds := common.Map(it.Servers, func(it boxOption.Outbound) string {
return it.Tag
})
if it.GenerateSelector {
selectorOutbound := option.Outbound{
selectorOutbound := boxOption.Outbound{
Type: C.TypeSelector,
Tag: it.Name,
SelectorOptions: common.PtrValueOrDefault(it.CustomSelector),
@ -101,7 +98,7 @@ func (t *Template) renderOutbounds(metadata M.Metadata, options *option.Options,
} else {
urltestTag = it.Name + " - URLTest"
}
urltestOutbound := option.Outbound{
urltestOutbound := boxOption.Outbound{
Type: C.TypeURLTest,
Tag: urltestTag,
URLTestOptions: common.PtrValueOrDefault(t.CustomURLTest),
@ -116,10 +113,11 @@ func (t *Template) renderOutbounds(metadata M.Metadata, options *option.Options,
allGroupOutbounds = append(allGroupOutbounds, it.Servers...)
}
globalOutbounds = append(globalOutbounds, allGroups...)
globalOutbounds = append(globalOutbounds, allGroupOutbounds...)
allExtraGroups := make(map[string][]option.Outbound)
var (
defaultGroups []boxOption.Outbound
globalGroups []boxOption.Outbound
subscriptionGroups = make(map[string][]boxOption.Outbound)
)
for _, extraGroup := range t.groups {
myFilter := func(outboundTag string) bool {
if len(extraGroup.filter) > 0 {
@ -138,19 +136,14 @@ func (t *Template) renderOutbounds(metadata M.Metadata, options *option.Options,
}
return true
}
if !extraGroup.PerSubscription {
var extraTags []string
if extraGroup.ExcludeOutbounds {
extraTags = common.Filter(common.FlatMap(subscriptions, func(it *subscription.Subscription) []string {
if extraGroup.Target != option.ExtraGroupTargetSubscription {
extraTags := common.Filter(common.FlatMap(subscriptions, func(it *subscription.Subscription) []string {
return common.Map(it.Servers, outboundToString)
}), myFilter)
} else {
extraTags = common.Filter(common.Map(globalOutbounds, outboundToString), myFilter)
}
if len(extraTags) == 0 {
continue
}
groupOutbound := option.Outbound{
groupOutbound := boxOption.Outbound{
Tag: extraGroup.Tag,
Type: extraGroup.Type,
SelectorOptions: common.PtrValueOrDefault(extraGroup.CustomSelector),
@ -162,7 +155,11 @@ func (t *Template) renderOutbounds(metadata M.Metadata, options *option.Options,
case C.TypeURLTest:
groupOutbound.URLTestOptions.Outbounds = append(groupOutbound.URLTestOptions.Outbounds, extraTags...)
}
allExtraGroups[""] = append(allExtraGroups[""], groupOutbound)
if extraGroup.Target == option.ExtraGroupTargetDefault {
defaultGroups = append(defaultGroups, groupOutbound)
} else {
globalGroups = append(globalGroups, groupOutbound)
}
} else {
tmpl := template.New("tag")
if extraGroup.TagPerSubscription != "" {
@ -174,26 +171,6 @@ func (t *Template) renderOutbounds(metadata M.Metadata, options *option.Options,
common.Must1(tmpl.Parse("{{ .tag }} ({{ .subscription_name }})"))
}
var outboundTags []string
if !extraGroup.ExcludeOutbounds {
outboundTags = common.Filter(common.FlatMap(outbounds, func(it []option.Outbound) []string {
return common.Map(it, outboundToString)
}), myFilter)
}
if len(outboundTags) > 0 {
groupOutbound := option.Outbound{
Tag: extraGroup.Tag,
Type: extraGroup.Type,
SelectorOptions: common.PtrValueOrDefault(extraGroup.CustomSelector),
URLTestOptions: common.PtrValueOrDefault(extraGroup.CustomURLTest),
}
switch extraGroup.Type {
case C.TypeSelector:
groupOutbound.SelectorOptions.Outbounds = append(groupOutbound.SelectorOptions.Outbounds, outboundTags...)
case C.TypeURLTest:
groupOutbound.URLTestOptions.Outbounds = append(groupOutbound.URLTestOptions.Outbounds, outboundTags...)
}
allExtraGroups[""] = append(allExtraGroups[""], groupOutbound)
}
for _, it := range subscriptions {
subscriptionTags := common.Filter(common.Map(it.Servers, outboundToString), myFilter)
if len(subscriptionTags) == 0 {
@ -213,7 +190,7 @@ func (t *Template) renderOutbounds(metadata M.Metadata, options *option.Options,
}
tagPerSubscription = buffer.String()
}
groupOutboundPerSubscription := option.Outbound{
groupOutboundPerSubscription := boxOption.Outbound{
Tag: tagPerSubscription,
Type: extraGroup.Type,
SelectorOptions: common.PtrValueOrDefault(extraGroup.CustomSelector),
@ -225,20 +202,21 @@ func (t *Template) renderOutbounds(metadata M.Metadata, options *option.Options,
case C.TypeURLTest:
groupOutboundPerSubscription.URLTestOptions.Outbounds = append(groupOutboundPerSubscription.URLTestOptions.Outbounds, subscriptionTags...)
}
allExtraGroups[it.Name] = append(allExtraGroups[it.Name], groupOutboundPerSubscription)
subscriptionGroups[it.Name] = append(subscriptionGroups[it.Name], groupOutboundPerSubscription)
}
}
}
options.Outbounds = append(options.Outbounds, allGroups...)
defaultExtraGroupOutbounds := allExtraGroups[""]
if len(defaultExtraGroupOutbounds) > 0 {
options.Outbounds = append(options.Outbounds, defaultExtraGroupOutbounds...)
options.Outbounds = groupJoin(options.Outbounds, defaultTag, false, common.Map(defaultExtraGroupOutbounds, outboundToString)...)
if len(defaultGroups) > 0 {
options.Outbounds = append(options.Outbounds, defaultGroups...)
}
if len(globalGroups) > 0 {
options.Outbounds = append(options.Outbounds, globalGroups...)
options.Outbounds = groupJoin(options.Outbounds, defaultTag, false, common.Map(globalGroups, outboundToString)...)
}
for _, it := range subscriptions {
extraGroupOutboundsForSubscription := allExtraGroups[it.Name]
extraGroupOutboundsForSubscription := subscriptionGroups[it.Name]
if len(extraGroupOutboundsForSubscription) > 0 {
options.Outbounds = append(options.Outbounds, extraGroupOutboundsForSubscription...)
options.Outbounds = groupJoin(options.Outbounds, it.Name, true, common.Map(extraGroupOutboundsForSubscription, outboundToString)...)
@ -251,8 +229,8 @@ func (t *Template) renderOutbounds(metadata M.Metadata, options *option.Options,
return nil
}
func groupJoin(outbounds []option.Outbound, groupTag string, appendFront bool, groupOutbounds ...string) []option.Outbound {
groupIndex := common.Index(outbounds, func(it option.Outbound) bool {
func groupJoin(outbounds []boxOption.Outbound, groupTag string, appendFront bool, groupOutbounds ...string) []boxOption.Outbound {
groupIndex := common.Index(outbounds, func(it boxOption.Outbound) bool {
return it.Tag == groupTag
})
if groupIndex == -1 {