mirror of
https://github.com/bolucat/Archive.git
synced 2026-04-22 16:07:49 +08:00
Update On Tue Mar 11 19:38:08 CET 2025
This commit is contained in:
@@ -938,3 +938,4 @@ Update On Fri Mar 7 19:34:47 CET 2025
|
||||
Update On Sat Mar 8 19:28:33 CET 2025
|
||||
Update On Sun Mar 9 19:28:11 CET 2025
|
||||
Update On Mon Mar 10 19:34:25 CET 2025
|
||||
Update On Tue Mar 11 19:37:59 CET 2025
|
||||
|
||||
@@ -329,7 +329,6 @@ jobs:
|
||||
run: |
|
||||
echo ${VERSION} > version.txt
|
||||
shell: bash
|
||||
|
||||
- name: Archive production artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
@@ -340,6 +339,7 @@ jobs:
|
||||
mihomo*.rpm
|
||||
mihomo*.zip
|
||||
version.txt
|
||||
checksums.txt
|
||||
|
||||
Upload-Prerelease:
|
||||
permissions: write-all
|
||||
@@ -353,6 +353,13 @@ jobs:
|
||||
path: bin/
|
||||
merge-multiple: true
|
||||
|
||||
- name: Calculate checksums
|
||||
run: |
|
||||
cd bin/
|
||||
find . -type f -not -name "checksums.*" -not -name "version.txt" | sort | xargs sha256sum > checksums.txt
|
||||
cat checksums.txt
|
||||
shell: bash
|
||||
|
||||
- name: Delete current release assets
|
||||
uses: 8Mi-Tech/delete-release-assets-action@main
|
||||
with:
|
||||
|
||||
@@ -37,7 +37,7 @@ func (f *Fallback) DialContext(ctx context.Context, metadata *C.Metadata, opts .
|
||||
if err == nil {
|
||||
c.AppendToChains(f)
|
||||
} else {
|
||||
f.onDialFailed(proxy.Type(), err)
|
||||
f.onDialFailed(proxy.Type(), err, f.healthCheck)
|
||||
}
|
||||
|
||||
if N.NeedHandshake(c) {
|
||||
@@ -45,7 +45,7 @@ func (f *Fallback) DialContext(ctx context.Context, metadata *C.Metadata, opts .
|
||||
if err == nil {
|
||||
f.onDialSuccess()
|
||||
} else {
|
||||
f.onDialFailed(proxy.Type(), err)
|
||||
f.onDialFailed(proxy.Type(), err, f.healthCheck)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package outboundgroup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -17,22 +18,26 @@ import (
|
||||
"github.com/metacubex/mihomo/tunnel"
|
||||
|
||||
"github.com/dlclark/regexp2"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
type GroupBase struct {
|
||||
*outbound.Base
|
||||
filterRegs []*regexp2.Regexp
|
||||
excludeFilterReg *regexp2.Regexp
|
||||
excludeTypeArray []string
|
||||
providers []provider.ProxyProvider
|
||||
failedTestMux sync.Mutex
|
||||
failedTimes int
|
||||
failedTime time.Time
|
||||
failedTesting atomic.Bool
|
||||
proxies [][]C.Proxy
|
||||
versions []atomic.Uint32
|
||||
TestTimeout int
|
||||
maxFailedTimes int
|
||||
filterRegs []*regexp2.Regexp
|
||||
excludeFilterRegs []*regexp2.Regexp
|
||||
excludeTypeArray []string
|
||||
providers []provider.ProxyProvider
|
||||
failedTestMux sync.Mutex
|
||||
failedTimes int
|
||||
failedTime time.Time
|
||||
failedTesting atomic.Bool
|
||||
TestTimeout int
|
||||
maxFailedTimes int
|
||||
|
||||
// for GetProxies
|
||||
getProxiesMutex sync.Mutex
|
||||
providerVersions []uint32
|
||||
providerProxies []C.Proxy
|
||||
}
|
||||
|
||||
type GroupBaseOption struct {
|
||||
@@ -53,15 +58,19 @@ func NewGroupBase(opt GroupBaseOption) *GroupBase {
|
||||
log.Warnln("The group [%s] with interface-name configuration is deprecated, please set it directly on the proxy instead", opt.Name)
|
||||
}
|
||||
|
||||
var excludeFilterReg *regexp2.Regexp
|
||||
if opt.excludeFilter != "" {
|
||||
excludeFilterReg = regexp2.MustCompile(opt.excludeFilter, regexp2.None)
|
||||
}
|
||||
var excludeTypeArray []string
|
||||
if opt.excludeType != "" {
|
||||
excludeTypeArray = strings.Split(opt.excludeType, "|")
|
||||
}
|
||||
|
||||
var excludeFilterRegs []*regexp2.Regexp
|
||||
if opt.excludeFilter != "" {
|
||||
for _, excludeFilter := range strings.Split(opt.excludeFilter, "`") {
|
||||
excludeFilterReg := regexp2.MustCompile(excludeFilter, regexp2.None)
|
||||
excludeFilterRegs = append(excludeFilterRegs, excludeFilterReg)
|
||||
}
|
||||
}
|
||||
|
||||
var filterRegs []*regexp2.Regexp
|
||||
if opt.filter != "" {
|
||||
for _, filter := range strings.Split(opt.filter, "`") {
|
||||
@@ -71,14 +80,14 @@ func NewGroupBase(opt GroupBaseOption) *GroupBase {
|
||||
}
|
||||
|
||||
gb := &GroupBase{
|
||||
Base: outbound.NewBase(opt.BaseOption),
|
||||
filterRegs: filterRegs,
|
||||
excludeFilterReg: excludeFilterReg,
|
||||
excludeTypeArray: excludeTypeArray,
|
||||
providers: opt.providers,
|
||||
failedTesting: atomic.NewBool(false),
|
||||
TestTimeout: opt.TestTimeout,
|
||||
maxFailedTimes: opt.maxFailedTimes,
|
||||
Base: outbound.NewBase(opt.BaseOption),
|
||||
filterRegs: filterRegs,
|
||||
excludeFilterRegs: excludeFilterRegs,
|
||||
excludeTypeArray: excludeTypeArray,
|
||||
providers: opt.providers,
|
||||
failedTesting: atomic.NewBool(false),
|
||||
TestTimeout: opt.TestTimeout,
|
||||
maxFailedTimes: opt.maxFailedTimes,
|
||||
}
|
||||
|
||||
if gb.TestTimeout == 0 {
|
||||
@@ -88,9 +97,6 @@ func NewGroupBase(opt GroupBaseOption) *GroupBase {
|
||||
gb.maxFailedTimes = 5
|
||||
}
|
||||
|
||||
gb.proxies = make([][]C.Proxy, len(opt.providers))
|
||||
gb.versions = make([]atomic.Uint32, len(opt.providers))
|
||||
|
||||
return gb
|
||||
}
|
||||
|
||||
@@ -101,56 +107,55 @@ func (gb *GroupBase) Touch() {
|
||||
}
|
||||
|
||||
func (gb *GroupBase) GetProxies(touch bool) []C.Proxy {
|
||||
providerVersions := make([]uint32, len(gb.providers))
|
||||
for i, pd := range gb.providers {
|
||||
if touch { // touch first
|
||||
pd.Touch()
|
||||
}
|
||||
providerVersions[i] = pd.Version()
|
||||
}
|
||||
|
||||
// thread safe
|
||||
gb.getProxiesMutex.Lock()
|
||||
defer gb.getProxiesMutex.Unlock()
|
||||
|
||||
// return the cached proxies if version not changed
|
||||
if slices.Equal(providerVersions, gb.providerVersions) {
|
||||
return gb.providerProxies
|
||||
}
|
||||
|
||||
var proxies []C.Proxy
|
||||
if len(gb.filterRegs) == 0 {
|
||||
for _, pd := range gb.providers {
|
||||
if touch {
|
||||
pd.Touch()
|
||||
}
|
||||
proxies = append(proxies, pd.Proxies()...)
|
||||
}
|
||||
} else {
|
||||
for i, pd := range gb.providers {
|
||||
if touch {
|
||||
pd.Touch()
|
||||
}
|
||||
|
||||
if pd.VehicleType() == types.Compatible {
|
||||
gb.versions[i].Store(pd.Version())
|
||||
gb.proxies[i] = pd.Proxies()
|
||||
for _, pd := range gb.providers {
|
||||
if pd.VehicleType() == types.Compatible { // compatible provider unneeded filter
|
||||
proxies = append(proxies, pd.Proxies()...)
|
||||
continue
|
||||
}
|
||||
|
||||
version := gb.versions[i].Load()
|
||||
if version != pd.Version() && gb.versions[i].CompareAndSwap(version, pd.Version()) {
|
||||
var (
|
||||
proxies []C.Proxy
|
||||
newProxies []C.Proxy
|
||||
)
|
||||
|
||||
proxies = pd.Proxies()
|
||||
proxiesSet := map[string]struct{}{}
|
||||
for _, filterReg := range gb.filterRegs {
|
||||
for _, p := range proxies {
|
||||
name := p.Name()
|
||||
if mat, _ := filterReg.MatchString(name); mat {
|
||||
if _, ok := proxiesSet[name]; !ok {
|
||||
proxiesSet[name] = struct{}{}
|
||||
newProxies = append(newProxies, p)
|
||||
}
|
||||
var newProxies []C.Proxy
|
||||
proxiesSet := map[string]struct{}{}
|
||||
for _, filterReg := range gb.filterRegs {
|
||||
for _, p := range pd.Proxies() {
|
||||
name := p.Name()
|
||||
if mat, _ := filterReg.MatchString(name); mat {
|
||||
if _, ok := proxiesSet[name]; !ok {
|
||||
proxiesSet[name] = struct{}{}
|
||||
newProxies = append(newProxies, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gb.proxies[i] = newProxies
|
||||
}
|
||||
}
|
||||
|
||||
for _, p := range gb.proxies {
|
||||
proxies = append(proxies, p...)
|
||||
proxies = append(proxies, newProxies...)
|
||||
}
|
||||
}
|
||||
|
||||
// Multiple filers means that proxies are sorted in the order in which the filers appear.
|
||||
// Although the filter has been performed once in the previous process,
|
||||
// when there are multiple providers, the array needs to be reordered as a whole.
|
||||
if len(gb.providers) > 1 && len(gb.filterRegs) > 1 {
|
||||
var newProxies []C.Proxy
|
||||
proxiesSet := map[string]struct{}{}
|
||||
@@ -174,32 +179,31 @@ func (gb *GroupBase) GetProxies(touch bool) []C.Proxy {
|
||||
}
|
||||
proxies = newProxies
|
||||
}
|
||||
if gb.excludeTypeArray != nil {
|
||||
var newProxies []C.Proxy
|
||||
for _, p := range proxies {
|
||||
mType := p.Type().String()
|
||||
flag := false
|
||||
for i := range gb.excludeTypeArray {
|
||||
if strings.EqualFold(mType, gb.excludeTypeArray[i]) {
|
||||
flag = true
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
if flag {
|
||||
continue
|
||||
if len(gb.excludeFilterRegs) > 0 {
|
||||
var newProxies []C.Proxy
|
||||
LOOP1:
|
||||
for _, p := range proxies {
|
||||
name := p.Name()
|
||||
for _, excludeFilterReg := range gb.excludeFilterRegs {
|
||||
if mat, _ := excludeFilterReg.MatchString(name); mat {
|
||||
continue LOOP1
|
||||
}
|
||||
}
|
||||
newProxies = append(newProxies, p)
|
||||
}
|
||||
proxies = newProxies
|
||||
}
|
||||
|
||||
if gb.excludeFilterReg != nil {
|
||||
if gb.excludeTypeArray != nil {
|
||||
var newProxies []C.Proxy
|
||||
LOOP2:
|
||||
for _, p := range proxies {
|
||||
name := p.Name()
|
||||
if mat, _ := gb.excludeFilterReg.MatchString(name); mat {
|
||||
continue
|
||||
mType := p.Type().String()
|
||||
for _, excludeType := range gb.excludeTypeArray {
|
||||
if strings.EqualFold(mType, excludeType) {
|
||||
continue LOOP2
|
||||
}
|
||||
}
|
||||
newProxies = append(newProxies, p)
|
||||
}
|
||||
@@ -207,9 +211,13 @@ func (gb *GroupBase) GetProxies(touch bool) []C.Proxy {
|
||||
}
|
||||
|
||||
if len(proxies) == 0 {
|
||||
return append(proxies, tunnel.Proxies()["COMPATIBLE"])
|
||||
return []C.Proxy{tunnel.Proxies()["COMPATIBLE"]}
|
||||
}
|
||||
|
||||
// only cache when proxies not empty
|
||||
gb.providerVersions = providerVersions
|
||||
gb.providerProxies = proxies
|
||||
|
||||
return proxies
|
||||
}
|
||||
|
||||
@@ -241,17 +249,21 @@ func (gb *GroupBase) URLTest(ctx context.Context, url string, expectedStatus uti
|
||||
}
|
||||
}
|
||||
|
||||
func (gb *GroupBase) onDialFailed(adapterType C.AdapterType, err error) {
|
||||
func (gb *GroupBase) onDialFailed(adapterType C.AdapterType, err error, fn func()) {
|
||||
if adapterType == C.Direct || adapterType == C.Compatible || adapterType == C.Reject || adapterType == C.Pass || adapterType == C.RejectDrop {
|
||||
return
|
||||
}
|
||||
|
||||
if strings.Contains(err.Error(), "connection refused") {
|
||||
go gb.healthCheck()
|
||||
if errors.Is(err, C.ErrNotSupport) {
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
if strings.Contains(err.Error(), "connection refused") {
|
||||
fn()
|
||||
return
|
||||
}
|
||||
|
||||
gb.failedTestMux.Lock()
|
||||
defer gb.failedTestMux.Unlock()
|
||||
|
||||
@@ -268,7 +280,7 @@ func (gb *GroupBase) onDialFailed(adapterType C.AdapterType, err error) {
|
||||
log.Debugln("ProxyGroup: %s failed count: %d", gb.Name(), gb.failedTimes)
|
||||
if gb.failedTimes >= gb.maxFailedTimes {
|
||||
log.Warnln("because %s failed multiple times, active health check", gb.Name())
|
||||
gb.healthCheck()
|
||||
fn()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -95,7 +95,7 @@ func (lb *LoadBalance) DialContext(ctx context.Context, metadata *C.Metadata, op
|
||||
if err == nil {
|
||||
c.AppendToChains(lb)
|
||||
} else {
|
||||
lb.onDialFailed(proxy.Type(), err)
|
||||
lb.onDialFailed(proxy.Type(), err, lb.healthCheck)
|
||||
}
|
||||
|
||||
if N.NeedHandshake(c) {
|
||||
@@ -103,7 +103,7 @@ func (lb *LoadBalance) DialContext(ctx context.Context, metadata *C.Metadata, op
|
||||
if err == nil {
|
||||
lb.onDialSuccess()
|
||||
} else {
|
||||
lb.onDialFailed(proxy.Type(), err)
|
||||
lb.onDialFailed(proxy.Type(), err, lb.healthCheck)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/mihomo/adapter/outbound"
|
||||
@@ -54,13 +52,13 @@ func (u *URLTest) Set(name string) error {
|
||||
if p == nil {
|
||||
return errors.New("proxy not exist")
|
||||
}
|
||||
u.selected = name
|
||||
u.fast(false)
|
||||
u.ForceSet(name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *URLTest) ForceSet(name string) {
|
||||
u.selected = name
|
||||
u.fastSingle.Reset()
|
||||
}
|
||||
|
||||
// DialContext implements C.ProxyAdapter
|
||||
@@ -70,7 +68,7 @@ func (u *URLTest) DialContext(ctx context.Context, metadata *C.Metadata, opts ..
|
||||
if err == nil {
|
||||
c.AppendToChains(u)
|
||||
} else {
|
||||
u.onDialFailed(proxy.Type(), err)
|
||||
u.onDialFailed(proxy.Type(), err, u.healthCheck)
|
||||
}
|
||||
|
||||
if N.NeedHandshake(c) {
|
||||
@@ -78,7 +76,7 @@ func (u *URLTest) DialContext(ctx context.Context, metadata *C.Metadata, opts ..
|
||||
if err == nil {
|
||||
u.onDialSuccess()
|
||||
} else {
|
||||
u.onDialFailed(proxy.Type(), err)
|
||||
u.onDialFailed(proxy.Type(), err, u.healthCheck)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -88,9 +86,12 @@ func (u *URLTest) DialContext(ctx context.Context, metadata *C.Metadata, opts ..
|
||||
|
||||
// ListenPacketContext implements C.ProxyAdapter
|
||||
func (u *URLTest) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
|
||||
pc, err := u.fast(true).ListenPacketContext(ctx, metadata, u.Base.DialOptions(opts...)...)
|
||||
proxy := u.fast(true)
|
||||
pc, err := proxy.ListenPacketContext(ctx, metadata, u.Base.DialOptions(opts...)...)
|
||||
if err == nil {
|
||||
pc.AppendToChains(u)
|
||||
} else {
|
||||
u.onDialFailed(proxy.Type(), err, u.healthCheck)
|
||||
}
|
||||
|
||||
return pc, err
|
||||
@@ -101,22 +102,27 @@ func (u *URLTest) Unwrap(metadata *C.Metadata, touch bool) C.Proxy {
|
||||
return u.fast(touch)
|
||||
}
|
||||
|
||||
func (u *URLTest) fast(touch bool) C.Proxy {
|
||||
func (u *URLTest) healthCheck() {
|
||||
u.fastSingle.Reset()
|
||||
u.GroupBase.healthCheck()
|
||||
u.fastSingle.Reset()
|
||||
}
|
||||
|
||||
proxies := u.GetProxies(touch)
|
||||
if u.selected != "" {
|
||||
for _, proxy := range proxies {
|
||||
if !proxy.AliveForTestUrl(u.testUrl) {
|
||||
continue
|
||||
}
|
||||
if proxy.Name() == u.selected {
|
||||
u.fastNode = proxy
|
||||
return proxy
|
||||
func (u *URLTest) fast(touch bool) C.Proxy {
|
||||
elm, _, shared := u.fastSingle.Do(func() (C.Proxy, error) {
|
||||
proxies := u.GetProxies(touch)
|
||||
if u.selected != "" {
|
||||
for _, proxy := range proxies {
|
||||
if !proxy.AliveForTestUrl(u.testUrl) {
|
||||
continue
|
||||
}
|
||||
if proxy.Name() == u.selected {
|
||||
u.fastNode = proxy
|
||||
return proxy, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
elm, _, shared := u.fastSingle.Do(func() (C.Proxy, error) {
|
||||
fast := proxies[0]
|
||||
minDelay := fast.LastDelayForTestUrl(u.testUrl)
|
||||
fastNotExist := true
|
||||
@@ -182,31 +188,7 @@ func (u *URLTest) MarshalJSON() ([]byte, error) {
|
||||
}
|
||||
|
||||
func (u *URLTest) URLTest(ctx context.Context, url string, expectedStatus utils.IntRanges[uint16]) (map[string]uint16, error) {
|
||||
var wg sync.WaitGroup
|
||||
var lock sync.Mutex
|
||||
mp := map[string]uint16{}
|
||||
proxies := u.GetProxies(false)
|
||||
for _, proxy := range proxies {
|
||||
proxy := proxy
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
delay, err := proxy.URLTest(ctx, u.testUrl, expectedStatus)
|
||||
if err == nil {
|
||||
lock.Lock()
|
||||
mp[proxy.Name()] = delay
|
||||
lock.Unlock()
|
||||
}
|
||||
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
if len(mp) == 0 {
|
||||
return mp, fmt.Errorf("get delay: all proxies timeout")
|
||||
} else {
|
||||
return mp, nil
|
||||
}
|
||||
return u.GroupBase.URLTest(ctx, u.testUrl, expectedStatus)
|
||||
}
|
||||
|
||||
func parseURLTestOption(config map[string]any) []urlTestOption {
|
||||
|
||||
@@ -87,6 +87,7 @@ func (bp *baseProvider) RegisterHealthCheckTask(url string, expectedStatus utils
|
||||
|
||||
func (bp *baseProvider) setProxies(proxies []C.Proxy) {
|
||||
bp.proxies = proxies
|
||||
bp.version += 1
|
||||
bp.healthCheck.setProxy(proxies)
|
||||
if bp.healthCheck.auto() {
|
||||
go bp.healthCheck.check()
|
||||
@@ -173,7 +174,7 @@ func NewProxySetProvider(name string, interval time.Duration, parser resource.Pa
|
||||
},
|
||||
}
|
||||
|
||||
fetcher := resource.NewFetcher[[]C.Proxy](name, interval, vehicle, parser, proxiesOnUpdate(pd))
|
||||
fetcher := resource.NewFetcher[[]C.Proxy](name, interval, vehicle, parser, pd.setProxies)
|
||||
pd.Fetcher = fetcher
|
||||
if httpVehicle, ok := vehicle.(*resource.HTTPVehicle); ok {
|
||||
httpVehicle.SetInRead(func(resp *http.Response) {
|
||||
|
||||
@@ -88,6 +88,15 @@ func ListenPacket(ctx context.Context, network, address string, rAddrPort netip.
|
||||
if DefaultSocketHook != nil { // ignore interfaceName, routingMark when DefaultSocketHook not null (in CMFA)
|
||||
socketHookToListenConfig(lc)
|
||||
} else {
|
||||
if cfg.interfaceName == "" {
|
||||
if finder := DefaultInterfaceFinder.Load(); finder != nil {
|
||||
cfg.interfaceName = finder.FindInterfaceName(rAddrPort.Addr())
|
||||
}
|
||||
}
|
||||
if rAddrPort.Addr().Unmap().IsLoopback() {
|
||||
// avoid "The requested address is not valid in its context."
|
||||
cfg.interfaceName = ""
|
||||
}
|
||||
if cfg.interfaceName != "" {
|
||||
bind := bindIfaceToListenConfig
|
||||
if cfg.fallbackBind {
|
||||
@@ -153,6 +162,11 @@ func dialContext(ctx context.Context, network string, destination netip.Addr, po
|
||||
if DefaultSocketHook != nil { // ignore interfaceName, routingMark and tfo when DefaultSocketHook not null (in CMFA)
|
||||
socketHookToToDialer(dialer)
|
||||
} else {
|
||||
if opt.interfaceName == "" {
|
||||
if finder := DefaultInterfaceFinder.Load(); finder != nil {
|
||||
opt.interfaceName = finder.FindInterfaceName(destination)
|
||||
}
|
||||
}
|
||||
if opt.interfaceName != "" {
|
||||
bind := bindIfaceToDialer
|
||||
if opt.fallbackBind {
|
||||
@@ -373,12 +387,7 @@ func (d Dialer) DialContext(ctx context.Context, network, address string) (net.C
|
||||
}
|
||||
|
||||
func (d Dialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) {
|
||||
opt := d.Opt // make a copy
|
||||
if rAddrPort.Addr().Unmap().IsLoopback() {
|
||||
// avoid "The requested address is not valid in its context."
|
||||
WithInterface("")(&opt)
|
||||
}
|
||||
return ListenPacket(ctx, ParseNetwork(network, rAddrPort.Addr()), address, rAddrPort, WithOption(opt))
|
||||
return ListenPacket(ctx, ParseNetwork(network, rAddrPort.Addr()), address, rAddrPort, WithOption(d.Opt))
|
||||
}
|
||||
|
||||
func NewDialer(options ...Option) Dialer {
|
||||
|
||||
@@ -3,6 +3,7 @@ package dialer
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/netip"
|
||||
|
||||
"github.com/metacubex/mihomo/common/atomic"
|
||||
"github.com/metacubex/mihomo/component/resolver"
|
||||
@@ -12,8 +13,14 @@ var (
|
||||
DefaultOptions []Option
|
||||
DefaultInterface = atomic.NewTypedValue[string]("")
|
||||
DefaultRoutingMark = atomic.NewInt32(0)
|
||||
|
||||
DefaultInterfaceFinder = atomic.NewTypedValue[InterfaceFinder](nil)
|
||||
)
|
||||
|
||||
type InterfaceFinder interface {
|
||||
FindInterfaceName(destination netip.Addr) string
|
||||
}
|
||||
|
||||
type NetDialer interface {
|
||||
DialContext(ctx context.Context, network, address string) (net.Conn, error)
|
||||
}
|
||||
|
||||
@@ -153,7 +153,6 @@ type ProxyAdapter interface {
|
||||
|
||||
type Group interface {
|
||||
URLTest(ctx context.Context, url string, expectedStatus utils.IntRanges[uint16]) (mp map[string]uint16, err error)
|
||||
GetProxies(touch bool) []Proxy
|
||||
Touch()
|
||||
}
|
||||
|
||||
|
||||
@@ -512,8 +512,8 @@ func ReCreateTun(tunConf LC.Tun, tunnel C.Tunnel) {
|
||||
}()
|
||||
|
||||
if tunConf.Equal(LastTunConf) {
|
||||
if tunLister != nil {
|
||||
tunLister.FlushDefaultInterface()
|
||||
if tunLister != nil { // some default value in dialer maybe changed when config reload, reset at here
|
||||
tunLister.OnReload()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -52,6 +52,8 @@ type Listener struct {
|
||||
autoRedirect tun.AutoRedirect
|
||||
autoRedirectOutputMark int32
|
||||
|
||||
cDialerInterfaceFinder dialer.InterfaceFinder
|
||||
|
||||
ruleUpdateCallbackCloser io.Closer
|
||||
ruleUpdateMutex sync.Mutex
|
||||
routeAddressMap map[string]*netipx.IPSet
|
||||
@@ -290,13 +292,25 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis
|
||||
}
|
||||
l.defaultInterfaceMonitor = defaultInterfaceMonitor
|
||||
defaultInterfaceMonitor.RegisterCallback(func(event int) {
|
||||
l.FlushDefaultInterface()
|
||||
iface.FlushCache()
|
||||
resolver.ResetConnection() // reset resolver's connection after default interface changed
|
||||
})
|
||||
err = defaultInterfaceMonitor.Start()
|
||||
if err != nil {
|
||||
err = E.Cause(err, "start DefaultInterfaceMonitor")
|
||||
return
|
||||
}
|
||||
|
||||
if options.AutoDetectInterface {
|
||||
l.cDialerInterfaceFinder = &cDialerInterfaceFinder{
|
||||
tunName: tunName,
|
||||
defaultInterfaceMonitor: defaultInterfaceMonitor,
|
||||
}
|
||||
if !dialer.DefaultInterfaceFinder.CompareAndSwap(nil, l.cDialerInterfaceFinder) {
|
||||
err = E.New("not allowed two tun listener using auto-detect-interface")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tunOptions := tun.Options{
|
||||
@@ -436,7 +450,10 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis
|
||||
}
|
||||
if tunOptions.AutoRedirectMarkMode {
|
||||
l.autoRedirectOutputMark = int32(outputMark)
|
||||
dialer.DefaultRoutingMark.Store(l.autoRedirectOutputMark)
|
||||
if !dialer.DefaultRoutingMark.CompareAndSwap(0, l.autoRedirectOutputMark) {
|
||||
err = E.New("not allowed setting global routing-mark when working with autoRedirectMarkMode")
|
||||
return
|
||||
}
|
||||
l.autoRedirect.UpdateRouteAddressSet()
|
||||
l.ruleUpdateCallbackCloser = rpTunnel.RuleUpdateCallback().Register(l.ruleUpdateCallback)
|
||||
}
|
||||
@@ -503,27 +520,34 @@ func (l *Listener) updateRule(ruleProvider provider.RuleProvider, exclude bool,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Listener) FlushDefaultInterface() {
|
||||
if l.options.AutoDetectInterface && l.defaultInterfaceMonitor != nil {
|
||||
for _, destination := range []netip.Addr{netip.IPv4Unspecified(), netip.IPv6Unspecified(), netip.MustParseAddr("1.1.1.1")} {
|
||||
autoDetectInterfaceName := l.defaultInterfaceMonitor.DefaultInterfaceName(destination)
|
||||
if autoDetectInterfaceName == l.tunName {
|
||||
log.Warnln("[TUN] Auto detect interface by %s get same name with tun", destination.String())
|
||||
} else if autoDetectInterfaceName == "" || autoDetectInterfaceName == "<nil>" {
|
||||
log.Warnln("[TUN] Auto detect interface by %s get empty name.", destination.String())
|
||||
} else {
|
||||
if old := dialer.DefaultInterface.Swap(autoDetectInterfaceName); old != autoDetectInterfaceName {
|
||||
log.Warnln("[TUN] default interface changed by monitor, %s => %s", old, autoDetectInterfaceName)
|
||||
iface.FlushCache()
|
||||
resolver.ResetConnection() // reset resolver's connection after default interface changed
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
if dialer.DefaultInterface.CompareAndSwap("", "<invalid>") {
|
||||
log.Warnln("[TUN] Auto detect interface failed, set '<invalid>' to DefaultInterface to avoid lookback")
|
||||
func (l *Listener) OnReload() {
|
||||
if l.autoRedirectOutputMark != 0 {
|
||||
dialer.DefaultRoutingMark.CompareAndSwap(0, l.autoRedirectOutputMark)
|
||||
}
|
||||
if l.cDialerInterfaceFinder != nil {
|
||||
dialer.DefaultInterfaceFinder.CompareAndSwap(nil, l.cDialerInterfaceFinder)
|
||||
}
|
||||
}
|
||||
|
||||
type cDialerInterfaceFinder struct {
|
||||
tunName string
|
||||
defaultInterfaceMonitor tun.DefaultInterfaceMonitor
|
||||
}
|
||||
|
||||
func (d *cDialerInterfaceFinder) FindInterfaceName(destination netip.Addr) string {
|
||||
for _, dest := range []netip.Addr{destination, netip.IPv4Unspecified(), netip.IPv6Unspecified()} {
|
||||
autoDetectInterfaceName := d.defaultInterfaceMonitor.DefaultInterfaceName(dest)
|
||||
if autoDetectInterfaceName == d.tunName {
|
||||
log.Warnln("[TUN] Auto detect interface for %s get same name with tun", destination.String())
|
||||
} else if autoDetectInterfaceName == "" || autoDetectInterfaceName == "<nil>" {
|
||||
log.Warnln("[TUN] Auto detect interface for %s get empty name.", destination.String())
|
||||
} else {
|
||||
log.Debugln("[TUN] Auto detect interface for %s --> %s", destination, autoDetectInterfaceName)
|
||||
return autoDetectInterfaceName
|
||||
}
|
||||
}
|
||||
log.Warnln("[TUN] Auto detect interface for %s failed, return '<invalid>' to avoid lookback", destination)
|
||||
return "<invalid>"
|
||||
}
|
||||
|
||||
func uidToRange(uidList []uint32) []ranges.Range[uint32] {
|
||||
@@ -564,6 +588,9 @@ func (l *Listener) Close() error {
|
||||
if l.autoRedirectOutputMark != 0 {
|
||||
dialer.DefaultRoutingMark.CompareAndSwap(l.autoRedirectOutputMark, 0)
|
||||
}
|
||||
if l.cDialerInterfaceFinder != nil {
|
||||
dialer.DefaultInterfaceFinder.CompareAndSwap(l.cDialerInterfaceFinder, nil)
|
||||
}
|
||||
return common.Close(
|
||||
l.ruleUpdateCallbackCloser,
|
||||
l.tunStack,
|
||||
|
||||
+8
-2
@@ -404,8 +404,14 @@ func Dial(network, address string) (*TCPConn, error) {
|
||||
|
||||
var lTcpAddr *net.TCPAddr
|
||||
var lIpAddr *net.IPAddr
|
||||
if ifaceName := dialer.DefaultInterface.Load(); len(ifaceName) > 0 {
|
||||
rAddrPort := raddr.AddrPort()
|
||||
rAddrPort := raddr.AddrPort()
|
||||
ifaceName := dialer.DefaultInterface.Load()
|
||||
if ifaceName == "" {
|
||||
if finder := dialer.DefaultInterfaceFinder.Load(); finder != nil {
|
||||
ifaceName = finder.FindInterfaceName(rAddrPort.Addr())
|
||||
}
|
||||
}
|
||||
if len(ifaceName) > 0 {
|
||||
addr, err := dialer.LookupLocalAddrFromIfaceName(ifaceName, network, rAddrPort.Addr(), int(rAddrPort.Port()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -37,7 +37,7 @@ func (f *Fallback) DialContext(ctx context.Context, metadata *C.Metadata, opts .
|
||||
if err == nil {
|
||||
c.AppendToChains(f)
|
||||
} else {
|
||||
f.onDialFailed(proxy.Type(), err)
|
||||
f.onDialFailed(proxy.Type(), err, f.healthCheck)
|
||||
}
|
||||
|
||||
if N.NeedHandshake(c) {
|
||||
@@ -45,7 +45,7 @@ func (f *Fallback) DialContext(ctx context.Context, metadata *C.Metadata, opts .
|
||||
if err == nil {
|
||||
f.onDialSuccess()
|
||||
} else {
|
||||
f.onDialFailed(proxy.Type(), err)
|
||||
f.onDialFailed(proxy.Type(), err, f.healthCheck)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package outboundgroup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -17,22 +18,26 @@ import (
|
||||
"github.com/metacubex/mihomo/tunnel"
|
||||
|
||||
"github.com/dlclark/regexp2"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
type GroupBase struct {
|
||||
*outbound.Base
|
||||
filterRegs []*regexp2.Regexp
|
||||
excludeFilterReg *regexp2.Regexp
|
||||
excludeTypeArray []string
|
||||
providers []provider.ProxyProvider
|
||||
failedTestMux sync.Mutex
|
||||
failedTimes int
|
||||
failedTime time.Time
|
||||
failedTesting atomic.Bool
|
||||
proxies [][]C.Proxy
|
||||
versions []atomic.Uint32
|
||||
TestTimeout int
|
||||
maxFailedTimes int
|
||||
filterRegs []*regexp2.Regexp
|
||||
excludeFilterRegs []*regexp2.Regexp
|
||||
excludeTypeArray []string
|
||||
providers []provider.ProxyProvider
|
||||
failedTestMux sync.Mutex
|
||||
failedTimes int
|
||||
failedTime time.Time
|
||||
failedTesting atomic.Bool
|
||||
TestTimeout int
|
||||
maxFailedTimes int
|
||||
|
||||
// for GetProxies
|
||||
getProxiesMutex sync.Mutex
|
||||
providerVersions []uint32
|
||||
providerProxies []C.Proxy
|
||||
}
|
||||
|
||||
type GroupBaseOption struct {
|
||||
@@ -53,15 +58,19 @@ func NewGroupBase(opt GroupBaseOption) *GroupBase {
|
||||
log.Warnln("The group [%s] with interface-name configuration is deprecated, please set it directly on the proxy instead", opt.Name)
|
||||
}
|
||||
|
||||
var excludeFilterReg *regexp2.Regexp
|
||||
if opt.excludeFilter != "" {
|
||||
excludeFilterReg = regexp2.MustCompile(opt.excludeFilter, regexp2.None)
|
||||
}
|
||||
var excludeTypeArray []string
|
||||
if opt.excludeType != "" {
|
||||
excludeTypeArray = strings.Split(opt.excludeType, "|")
|
||||
}
|
||||
|
||||
var excludeFilterRegs []*regexp2.Regexp
|
||||
if opt.excludeFilter != "" {
|
||||
for _, excludeFilter := range strings.Split(opt.excludeFilter, "`") {
|
||||
excludeFilterReg := regexp2.MustCompile(excludeFilter, regexp2.None)
|
||||
excludeFilterRegs = append(excludeFilterRegs, excludeFilterReg)
|
||||
}
|
||||
}
|
||||
|
||||
var filterRegs []*regexp2.Regexp
|
||||
if opt.filter != "" {
|
||||
for _, filter := range strings.Split(opt.filter, "`") {
|
||||
@@ -71,14 +80,14 @@ func NewGroupBase(opt GroupBaseOption) *GroupBase {
|
||||
}
|
||||
|
||||
gb := &GroupBase{
|
||||
Base: outbound.NewBase(opt.BaseOption),
|
||||
filterRegs: filterRegs,
|
||||
excludeFilterReg: excludeFilterReg,
|
||||
excludeTypeArray: excludeTypeArray,
|
||||
providers: opt.providers,
|
||||
failedTesting: atomic.NewBool(false),
|
||||
TestTimeout: opt.TestTimeout,
|
||||
maxFailedTimes: opt.maxFailedTimes,
|
||||
Base: outbound.NewBase(opt.BaseOption),
|
||||
filterRegs: filterRegs,
|
||||
excludeFilterRegs: excludeFilterRegs,
|
||||
excludeTypeArray: excludeTypeArray,
|
||||
providers: opt.providers,
|
||||
failedTesting: atomic.NewBool(false),
|
||||
TestTimeout: opt.TestTimeout,
|
||||
maxFailedTimes: opt.maxFailedTimes,
|
||||
}
|
||||
|
||||
if gb.TestTimeout == 0 {
|
||||
@@ -88,9 +97,6 @@ func NewGroupBase(opt GroupBaseOption) *GroupBase {
|
||||
gb.maxFailedTimes = 5
|
||||
}
|
||||
|
||||
gb.proxies = make([][]C.Proxy, len(opt.providers))
|
||||
gb.versions = make([]atomic.Uint32, len(opt.providers))
|
||||
|
||||
return gb
|
||||
}
|
||||
|
||||
@@ -101,56 +107,55 @@ func (gb *GroupBase) Touch() {
|
||||
}
|
||||
|
||||
func (gb *GroupBase) GetProxies(touch bool) []C.Proxy {
|
||||
providerVersions := make([]uint32, len(gb.providers))
|
||||
for i, pd := range gb.providers {
|
||||
if touch { // touch first
|
||||
pd.Touch()
|
||||
}
|
||||
providerVersions[i] = pd.Version()
|
||||
}
|
||||
|
||||
// thread safe
|
||||
gb.getProxiesMutex.Lock()
|
||||
defer gb.getProxiesMutex.Unlock()
|
||||
|
||||
// return the cached proxies if version not changed
|
||||
if slices.Equal(providerVersions, gb.providerVersions) {
|
||||
return gb.providerProxies
|
||||
}
|
||||
|
||||
var proxies []C.Proxy
|
||||
if len(gb.filterRegs) == 0 {
|
||||
for _, pd := range gb.providers {
|
||||
if touch {
|
||||
pd.Touch()
|
||||
}
|
||||
proxies = append(proxies, pd.Proxies()...)
|
||||
}
|
||||
} else {
|
||||
for i, pd := range gb.providers {
|
||||
if touch {
|
||||
pd.Touch()
|
||||
}
|
||||
|
||||
if pd.VehicleType() == types.Compatible {
|
||||
gb.versions[i].Store(pd.Version())
|
||||
gb.proxies[i] = pd.Proxies()
|
||||
for _, pd := range gb.providers {
|
||||
if pd.VehicleType() == types.Compatible { // compatible provider unneeded filter
|
||||
proxies = append(proxies, pd.Proxies()...)
|
||||
continue
|
||||
}
|
||||
|
||||
version := gb.versions[i].Load()
|
||||
if version != pd.Version() && gb.versions[i].CompareAndSwap(version, pd.Version()) {
|
||||
var (
|
||||
proxies []C.Proxy
|
||||
newProxies []C.Proxy
|
||||
)
|
||||
|
||||
proxies = pd.Proxies()
|
||||
proxiesSet := map[string]struct{}{}
|
||||
for _, filterReg := range gb.filterRegs {
|
||||
for _, p := range proxies {
|
||||
name := p.Name()
|
||||
if mat, _ := filterReg.MatchString(name); mat {
|
||||
if _, ok := proxiesSet[name]; !ok {
|
||||
proxiesSet[name] = struct{}{}
|
||||
newProxies = append(newProxies, p)
|
||||
}
|
||||
var newProxies []C.Proxy
|
||||
proxiesSet := map[string]struct{}{}
|
||||
for _, filterReg := range gb.filterRegs {
|
||||
for _, p := range pd.Proxies() {
|
||||
name := p.Name()
|
||||
if mat, _ := filterReg.MatchString(name); mat {
|
||||
if _, ok := proxiesSet[name]; !ok {
|
||||
proxiesSet[name] = struct{}{}
|
||||
newProxies = append(newProxies, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gb.proxies[i] = newProxies
|
||||
}
|
||||
}
|
||||
|
||||
for _, p := range gb.proxies {
|
||||
proxies = append(proxies, p...)
|
||||
proxies = append(proxies, newProxies...)
|
||||
}
|
||||
}
|
||||
|
||||
// Multiple filers means that proxies are sorted in the order in which the filers appear.
|
||||
// Although the filter has been performed once in the previous process,
|
||||
// when there are multiple providers, the array needs to be reordered as a whole.
|
||||
if len(gb.providers) > 1 && len(gb.filterRegs) > 1 {
|
||||
var newProxies []C.Proxy
|
||||
proxiesSet := map[string]struct{}{}
|
||||
@@ -174,32 +179,31 @@ func (gb *GroupBase) GetProxies(touch bool) []C.Proxy {
|
||||
}
|
||||
proxies = newProxies
|
||||
}
|
||||
if gb.excludeTypeArray != nil {
|
||||
var newProxies []C.Proxy
|
||||
for _, p := range proxies {
|
||||
mType := p.Type().String()
|
||||
flag := false
|
||||
for i := range gb.excludeTypeArray {
|
||||
if strings.EqualFold(mType, gb.excludeTypeArray[i]) {
|
||||
flag = true
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
if flag {
|
||||
continue
|
||||
if len(gb.excludeFilterRegs) > 0 {
|
||||
var newProxies []C.Proxy
|
||||
LOOP1:
|
||||
for _, p := range proxies {
|
||||
name := p.Name()
|
||||
for _, excludeFilterReg := range gb.excludeFilterRegs {
|
||||
if mat, _ := excludeFilterReg.MatchString(name); mat {
|
||||
continue LOOP1
|
||||
}
|
||||
}
|
||||
newProxies = append(newProxies, p)
|
||||
}
|
||||
proxies = newProxies
|
||||
}
|
||||
|
||||
if gb.excludeFilterReg != nil {
|
||||
if gb.excludeTypeArray != nil {
|
||||
var newProxies []C.Proxy
|
||||
LOOP2:
|
||||
for _, p := range proxies {
|
||||
name := p.Name()
|
||||
if mat, _ := gb.excludeFilterReg.MatchString(name); mat {
|
||||
continue
|
||||
mType := p.Type().String()
|
||||
for _, excludeType := range gb.excludeTypeArray {
|
||||
if strings.EqualFold(mType, excludeType) {
|
||||
continue LOOP2
|
||||
}
|
||||
}
|
||||
newProxies = append(newProxies, p)
|
||||
}
|
||||
@@ -207,9 +211,13 @@ func (gb *GroupBase) GetProxies(touch bool) []C.Proxy {
|
||||
}
|
||||
|
||||
if len(proxies) == 0 {
|
||||
return append(proxies, tunnel.Proxies()["COMPATIBLE"])
|
||||
return []C.Proxy{tunnel.Proxies()["COMPATIBLE"]}
|
||||
}
|
||||
|
||||
// only cache when proxies not empty
|
||||
gb.providerVersions = providerVersions
|
||||
gb.providerProxies = proxies
|
||||
|
||||
return proxies
|
||||
}
|
||||
|
||||
@@ -241,17 +249,21 @@ func (gb *GroupBase) URLTest(ctx context.Context, url string, expectedStatus uti
|
||||
}
|
||||
}
|
||||
|
||||
func (gb *GroupBase) onDialFailed(adapterType C.AdapterType, err error) {
|
||||
func (gb *GroupBase) onDialFailed(adapterType C.AdapterType, err error, fn func()) {
|
||||
if adapterType == C.Direct || adapterType == C.Compatible || adapterType == C.Reject || adapterType == C.Pass || adapterType == C.RejectDrop {
|
||||
return
|
||||
}
|
||||
|
||||
if strings.Contains(err.Error(), "connection refused") {
|
||||
go gb.healthCheck()
|
||||
if errors.Is(err, C.ErrNotSupport) {
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
if strings.Contains(err.Error(), "connection refused") {
|
||||
fn()
|
||||
return
|
||||
}
|
||||
|
||||
gb.failedTestMux.Lock()
|
||||
defer gb.failedTestMux.Unlock()
|
||||
|
||||
@@ -268,7 +280,7 @@ func (gb *GroupBase) onDialFailed(adapterType C.AdapterType, err error) {
|
||||
log.Debugln("ProxyGroup: %s failed count: %d", gb.Name(), gb.failedTimes)
|
||||
if gb.failedTimes >= gb.maxFailedTimes {
|
||||
log.Warnln("because %s failed multiple times, active health check", gb.Name())
|
||||
gb.healthCheck()
|
||||
fn()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -95,7 +95,7 @@ func (lb *LoadBalance) DialContext(ctx context.Context, metadata *C.Metadata, op
|
||||
if err == nil {
|
||||
c.AppendToChains(lb)
|
||||
} else {
|
||||
lb.onDialFailed(proxy.Type(), err)
|
||||
lb.onDialFailed(proxy.Type(), err, lb.healthCheck)
|
||||
}
|
||||
|
||||
if N.NeedHandshake(c) {
|
||||
@@ -103,7 +103,7 @@ func (lb *LoadBalance) DialContext(ctx context.Context, metadata *C.Metadata, op
|
||||
if err == nil {
|
||||
lb.onDialSuccess()
|
||||
} else {
|
||||
lb.onDialFailed(proxy.Type(), err)
|
||||
lb.onDialFailed(proxy.Type(), err, lb.healthCheck)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/mihomo/adapter/outbound"
|
||||
@@ -54,13 +52,13 @@ func (u *URLTest) Set(name string) error {
|
||||
if p == nil {
|
||||
return errors.New("proxy not exist")
|
||||
}
|
||||
u.selected = name
|
||||
u.fast(false)
|
||||
u.ForceSet(name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *URLTest) ForceSet(name string) {
|
||||
u.selected = name
|
||||
u.fastSingle.Reset()
|
||||
}
|
||||
|
||||
// DialContext implements C.ProxyAdapter
|
||||
@@ -70,7 +68,7 @@ func (u *URLTest) DialContext(ctx context.Context, metadata *C.Metadata, opts ..
|
||||
if err == nil {
|
||||
c.AppendToChains(u)
|
||||
} else {
|
||||
u.onDialFailed(proxy.Type(), err)
|
||||
u.onDialFailed(proxy.Type(), err, u.healthCheck)
|
||||
}
|
||||
|
||||
if N.NeedHandshake(c) {
|
||||
@@ -78,7 +76,7 @@ func (u *URLTest) DialContext(ctx context.Context, metadata *C.Metadata, opts ..
|
||||
if err == nil {
|
||||
u.onDialSuccess()
|
||||
} else {
|
||||
u.onDialFailed(proxy.Type(), err)
|
||||
u.onDialFailed(proxy.Type(), err, u.healthCheck)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -88,9 +86,12 @@ func (u *URLTest) DialContext(ctx context.Context, metadata *C.Metadata, opts ..
|
||||
|
||||
// ListenPacketContext implements C.ProxyAdapter
|
||||
func (u *URLTest) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
|
||||
pc, err := u.fast(true).ListenPacketContext(ctx, metadata, u.Base.DialOptions(opts...)...)
|
||||
proxy := u.fast(true)
|
||||
pc, err := proxy.ListenPacketContext(ctx, metadata, u.Base.DialOptions(opts...)...)
|
||||
if err == nil {
|
||||
pc.AppendToChains(u)
|
||||
} else {
|
||||
u.onDialFailed(proxy.Type(), err, u.healthCheck)
|
||||
}
|
||||
|
||||
return pc, err
|
||||
@@ -101,22 +102,27 @@ func (u *URLTest) Unwrap(metadata *C.Metadata, touch bool) C.Proxy {
|
||||
return u.fast(touch)
|
||||
}
|
||||
|
||||
func (u *URLTest) fast(touch bool) C.Proxy {
|
||||
func (u *URLTest) healthCheck() {
|
||||
u.fastSingle.Reset()
|
||||
u.GroupBase.healthCheck()
|
||||
u.fastSingle.Reset()
|
||||
}
|
||||
|
||||
proxies := u.GetProxies(touch)
|
||||
if u.selected != "" {
|
||||
for _, proxy := range proxies {
|
||||
if !proxy.AliveForTestUrl(u.testUrl) {
|
||||
continue
|
||||
}
|
||||
if proxy.Name() == u.selected {
|
||||
u.fastNode = proxy
|
||||
return proxy
|
||||
func (u *URLTest) fast(touch bool) C.Proxy {
|
||||
elm, _, shared := u.fastSingle.Do(func() (C.Proxy, error) {
|
||||
proxies := u.GetProxies(touch)
|
||||
if u.selected != "" {
|
||||
for _, proxy := range proxies {
|
||||
if !proxy.AliveForTestUrl(u.testUrl) {
|
||||
continue
|
||||
}
|
||||
if proxy.Name() == u.selected {
|
||||
u.fastNode = proxy
|
||||
return proxy, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
elm, _, shared := u.fastSingle.Do(func() (C.Proxy, error) {
|
||||
fast := proxies[0]
|
||||
minDelay := fast.LastDelayForTestUrl(u.testUrl)
|
||||
fastNotExist := true
|
||||
@@ -182,31 +188,7 @@ func (u *URLTest) MarshalJSON() ([]byte, error) {
|
||||
}
|
||||
|
||||
func (u *URLTest) URLTest(ctx context.Context, url string, expectedStatus utils.IntRanges[uint16]) (map[string]uint16, error) {
|
||||
var wg sync.WaitGroup
|
||||
var lock sync.Mutex
|
||||
mp := map[string]uint16{}
|
||||
proxies := u.GetProxies(false)
|
||||
for _, proxy := range proxies {
|
||||
proxy := proxy
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
delay, err := proxy.URLTest(ctx, u.testUrl, expectedStatus)
|
||||
if err == nil {
|
||||
lock.Lock()
|
||||
mp[proxy.Name()] = delay
|
||||
lock.Unlock()
|
||||
}
|
||||
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
if len(mp) == 0 {
|
||||
return mp, fmt.Errorf("get delay: all proxies timeout")
|
||||
} else {
|
||||
return mp, nil
|
||||
}
|
||||
return u.GroupBase.URLTest(ctx, u.testUrl, expectedStatus)
|
||||
}
|
||||
|
||||
func parseURLTestOption(config map[string]any) []urlTestOption {
|
||||
|
||||
@@ -87,6 +87,7 @@ func (bp *baseProvider) RegisterHealthCheckTask(url string, expectedStatus utils
|
||||
|
||||
func (bp *baseProvider) setProxies(proxies []C.Proxy) {
|
||||
bp.proxies = proxies
|
||||
bp.version += 1
|
||||
bp.healthCheck.setProxy(proxies)
|
||||
if bp.healthCheck.auto() {
|
||||
go bp.healthCheck.check()
|
||||
@@ -173,7 +174,7 @@ func NewProxySetProvider(name string, interval time.Duration, parser resource.Pa
|
||||
},
|
||||
}
|
||||
|
||||
fetcher := resource.NewFetcher[[]C.Proxy](name, interval, vehicle, parser, proxiesOnUpdate(pd))
|
||||
fetcher := resource.NewFetcher[[]C.Proxy](name, interval, vehicle, parser, pd.setProxies)
|
||||
pd.Fetcher = fetcher
|
||||
if httpVehicle, ok := vehicle.(*resource.HTTPVehicle); ok {
|
||||
httpVehicle.SetInRead(func(resp *http.Response) {
|
||||
|
||||
@@ -153,7 +153,6 @@ type ProxyAdapter interface {
|
||||
|
||||
type Group interface {
|
||||
URLTest(ctx context.Context, url string, expectedStatus utils.IntRanges[uint16]) (mp map[string]uint16, err error)
|
||||
GetProxies(touch bool) []Proxy
|
||||
Touch()
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,19 @@ pub fn setup<R: tauri::Runtime, M: tauri::Manager<R>>(manager: &M) -> anyhow::Re
|
||||
// TODO: refactor it while clash core manager use tauri event dispatcher to notify the core state changed
|
||||
{
|
||||
tokio::time::sleep(std::time::Duration::from_secs(10)).await;
|
||||
ws_connector.start().await.unwrap();
|
||||
|
||||
// TODO: clash-rs ws authorization is not working
|
||||
match ws_connector.start().await {
|
||||
Ok(_) => {
|
||||
tracing::info!(
|
||||
"ws_connector started successfully clash-rs may be errored here."
|
||||
);
|
||||
}
|
||||
// TODO: wait for clash-rs to fix
|
||||
Err(e) => {
|
||||
tracing::error!("ws_connector failed to start: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut rx = ws_connector.subscribe();
|
||||
while let Ok(event) = rx.recv().await {
|
||||
|
||||
@@ -193,8 +193,9 @@ pub async fn proxies_updated_receiver() {
|
||||
TrayUpdateType::Part(action_list) => {
|
||||
debug!("should do partial update, op list: {:?}", action_list);
|
||||
tray_proxies_holder = current_tray_proxies;
|
||||
platform_impl::update_selected_proxies(&action_list);
|
||||
debug!("update selected proxies success");
|
||||
debug!("todo: platform_impl::update_selected_proxies(&action_list)");
|
||||
// platform_impl::update_selected_proxies(&action_list);
|
||||
// debug!("update selected proxies success");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
//! Setup logic for the app
|
||||
use anyhow::Context;
|
||||
|
||||
use crate::logging;
|
||||
|
||||
pub fn setup<R: tauri::Runtime, M: tauri::Manager<R>>(app: &M) -> Result<(), anyhow::Error> {
|
||||
let app_handle = app.app_handle().clone();
|
||||
#[cfg(target_os = "windows")]
|
||||
@@ -12,6 +10,7 @@ pub fn setup<R: tauri::Runtime, M: tauri::Manager<R>>(app: &M) -> Result<(), any
|
||||
})
|
||||
.context("Failed to setup the shutdown hook")?;
|
||||
|
||||
logging::setup(app).context("Failed to setup logging")?;
|
||||
// FIXME: this is a background setup, so be careful use this state in ipc.
|
||||
// crate::logging::setup(app).context("Failed to setup logging")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,19 +1,79 @@
|
||||
/**
|
||||
* Nyanpasu backend event name, use tauri event api to listen this event
|
||||
*/
|
||||
export const NYANPASU_BACKEND_EVENT_NAME = 'nyanpasu://mutation'
|
||||
|
||||
/**
|
||||
* Nyanpasu setting query key, used by useSettings hook
|
||||
*/
|
||||
export const NYANPASU_SETTING_QUERY_KEY = 'settings'
|
||||
|
||||
/**
|
||||
* Nyanpasu system proxy query key, used by useSystemProxy hook
|
||||
*/
|
||||
export const NYANPASU_SYSTEM_PROXY_QUERY_KEY = 'system-proxy'
|
||||
|
||||
/**
|
||||
* Clash version query key, used to fetch clash version from query
|
||||
*/
|
||||
export const CLASH_VERSION_QUERY_KEY = 'clash-version'
|
||||
|
||||
/**
|
||||
* Nyanpasu profile query key, used to fetch profiles from query
|
||||
*/
|
||||
export const ROFILES_QUERY_KEY = 'profiles'
|
||||
|
||||
/**
|
||||
* Clash log query key, used by clash ws provider to mutate logs via clash logs ws api
|
||||
*/
|
||||
export const CLASH_LOGS_QUERY_KEY = 'clash-logs'
|
||||
|
||||
/**
|
||||
* Clash traffic query key, used by clash ws provider to mutate memory via clash traffic ws api
|
||||
*/
|
||||
export const CLASH_TRAAFFIC_QUERY_KEY = 'clash-traffic'
|
||||
|
||||
/**
|
||||
* Clash memory query key, used by clash ws provider to mutate memory via clash memory ws api
|
||||
*/
|
||||
export const CLASH_MEMORY_QUERY_KEY = 'clash-memory'
|
||||
|
||||
/**
|
||||
* Clash connections query key, used by clash ws provider to mutate connections via clash connections ws api
|
||||
*/
|
||||
export const CLASH_CONNECTIONS_QUERY_KEY = 'clash-connections'
|
||||
|
||||
/**
|
||||
* Clash config query key, used by useClashConfig hook
|
||||
*/
|
||||
export const CLASH_CONFIG_QUERY_KEY = 'clash-config'
|
||||
|
||||
/**
|
||||
* Clash core query key, used by useClashCores hook
|
||||
*/
|
||||
export const CLASH_CORE_QUERY_KEY = 'clash-core'
|
||||
|
||||
/**
|
||||
* Clash info query key, used by useClashInfo hook
|
||||
*/
|
||||
export const CLASH_INFO_QUERY_KEY = 'clash-info'
|
||||
|
||||
/**
|
||||
* Maximum connections history length, used by clash ws provider to limit connections history length
|
||||
*/
|
||||
export const MAX_CONNECTIONS_HISTORY = 32
|
||||
|
||||
/**
|
||||
* Maximum memory history length, used by clash ws provider to limit memory history length
|
||||
*/
|
||||
export const MAX_MEMORY_HISTORY = 32
|
||||
|
||||
/**
|
||||
* Maximum traffic history length, used by clash ws provider to limit traffic history length
|
||||
*/
|
||||
export const MAX_TRAFFIC_HISTORY = 32
|
||||
|
||||
/**
|
||||
* Maximum logs history length, used by clash ws provider to limit logs history length
|
||||
*/
|
||||
export const MAX_LOGS_HISTORY = 1024
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { ClashConfig, useClashAPI } from '../service/clash-api'
|
||||
import { unwrapResult } from '../utils'
|
||||
import { commands, PatchRuntimeConfig } from './bindings'
|
||||
import { CLASH_CONFIG_QUERY_KEY } from './consts'
|
||||
|
||||
/**
|
||||
* A hook that manages fetching and updating the Clash configuration.
|
||||
@@ -38,7 +39,7 @@ export const useClashConfig = () => {
|
||||
* @see useQuery - For additional configuration options and usage details.
|
||||
*/
|
||||
const query = useQuery({
|
||||
queryKey: ['clash-config'],
|
||||
queryKey: [CLASH_CONFIG_QUERY_KEY],
|
||||
queryFn: configs,
|
||||
})
|
||||
|
||||
@@ -64,7 +65,7 @@ export const useClashConfig = () => {
|
||||
return unwrapResult(await commands.patchClashConfig(payload))
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['clash-config'] })
|
||||
queryClient.invalidateQueries({ queryKey: [CLASH_CONFIG_QUERY_KEY] })
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -2,7 +2,11 @@ import { kebabCase } from 'lodash-es'
|
||||
import { unwrapResult } from '@/utils'
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { commands, type ClashCore } from './bindings'
|
||||
import { CLASH_VERSION_QUERY_KEY } from './consts'
|
||||
import {
|
||||
CLASH_CORE_QUERY_KEY,
|
||||
CLASH_VERSION_QUERY_KEY,
|
||||
NYANPASU_SETTING_QUERY_KEY,
|
||||
} from './consts'
|
||||
|
||||
export const ClashCores = {
|
||||
clash: 'Clash Premium',
|
||||
@@ -24,7 +28,7 @@ export const useClashCores = () => {
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const query = useQuery({
|
||||
queryKey: ['clash-core'],
|
||||
queryKey: [CLASH_CORE_QUERY_KEY],
|
||||
queryFn: async () => {
|
||||
return await Object.keys(ClashCores).reduce(
|
||||
async (acc, key) => {
|
||||
@@ -61,7 +65,7 @@ export const useClashCores = () => {
|
||||
}
|
||||
|
||||
const currentData = queryClient.getQueryData([
|
||||
'clash-core',
|
||||
CLASH_CORE_QUERY_KEY,
|
||||
]) as ClashCoresInfo
|
||||
|
||||
if (currentData && results) {
|
||||
@@ -78,7 +82,7 @@ export const useClashCores = () => {
|
||||
}
|
||||
})
|
||||
|
||||
queryClient.setQueryData(['clash-core'], updatedData)
|
||||
queryClient.setQueryData([CLASH_CORE_QUERY_KEY], updatedData)
|
||||
}
|
||||
return results
|
||||
},
|
||||
@@ -89,7 +93,7 @@ export const useClashCores = () => {
|
||||
return unwrapResult(await commands.updateCore(core))
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['clash-core'] })
|
||||
queryClient.invalidateQueries({ queryKey: [CLASH_CORE_QUERY_KEY] })
|
||||
},
|
||||
})
|
||||
|
||||
@@ -98,8 +102,8 @@ export const useClashCores = () => {
|
||||
return unwrapResult(await commands.changeClashCore(core))
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['clash-core'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['settings'] })
|
||||
queryClient.invalidateQueries({ queryKey: [CLASH_CORE_QUERY_KEY] })
|
||||
queryClient.invalidateQueries({ queryKey: [NYANPASU_SETTING_QUERY_KEY] })
|
||||
queryClient.invalidateQueries({ queryKey: [CLASH_VERSION_QUERY_KEY] })
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { unwrapResult } from '@/utils'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { commands } from './bindings'
|
||||
import { CLASH_INFO_QUERY_KEY } from './consts'
|
||||
|
||||
/**
|
||||
* A hook that retrieves and returns clash information using react-query.
|
||||
@@ -14,7 +15,7 @@ import { commands } from './bindings'
|
||||
*/
|
||||
export const useClashInfo = () => {
|
||||
const query = useQuery({
|
||||
queryKey: ['clash-info'],
|
||||
queryKey: [CLASH_INFO_QUERY_KEY],
|
||||
queryFn: async () => {
|
||||
return unwrapResult(await commands.getClashInfo())
|
||||
},
|
||||
|
||||
@@ -2,6 +2,7 @@ import { merge } from 'lodash-es'
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { unwrapResult } from '../utils'
|
||||
import { commands, type IVerge } from './bindings'
|
||||
import { NYANPASU_SETTING_QUERY_KEY } from './consts'
|
||||
|
||||
/**
|
||||
* Custom hook for managing Verge configuration settings using React Query.
|
||||
@@ -41,7 +42,7 @@ export const useSettings = () => {
|
||||
* - other standard React Query properties
|
||||
*/
|
||||
const query = useQuery({
|
||||
queryKey: ['settings'],
|
||||
queryKey: [NYANPASU_SETTING_QUERY_KEY],
|
||||
queryFn: async () => {
|
||||
return unwrapResult(await commands.getVergeConfig())
|
||||
},
|
||||
@@ -68,7 +69,9 @@ export const useSettings = () => {
|
||||
return unwrapResult(await commands.patchVergeConfig(options as IVerge))
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['settings'] })
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [NYANPASU_SETTING_QUERY_KEY],
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { unwrapResult } from '../utils'
|
||||
import { commands } from './bindings'
|
||||
import { NYANPASU_SYSTEM_PROXY_QUERY_KEY } from './consts'
|
||||
|
||||
/**
|
||||
* Custom hook to fetch and manage the system proxy settings.
|
||||
@@ -14,7 +15,7 @@ import { commands } from './bindings'
|
||||
*/
|
||||
export const useSystemProxy = () => {
|
||||
const query = useQuery({
|
||||
queryKey: ['system-proxy'],
|
||||
queryKey: [NYANPASU_SYSTEM_PROXY_QUERY_KEY],
|
||||
queryFn: async () => {
|
||||
return unwrapResult(await commands.getSysProxy())
|
||||
},
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import type { PropsWithChildren } from 'react'
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||
import { ClashWSProvider } from './clash-ws-provider'
|
||||
import { MutationProvider } from './mutation-provider'
|
||||
|
||||
const queryClient = new QueryClient()
|
||||
|
||||
export const NyanpasuProvider = ({ children }: PropsWithChildren) => {
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<ClashWSProvider>{children}</ClashWSProvider>
|
||||
<MutationProvider>
|
||||
<ClashWSProvider>{children}</ClashWSProvider>
|
||||
</MutationProvider>
|
||||
</QueryClientProvider>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
import { useMount } from 'ahooks'
|
||||
import { PropsWithChildren, useRef } from 'react'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import { listen, type UnlistenFn } from '@tauri-apps/api/event'
|
||||
import {
|
||||
CLASH_CONFIG_QUERY_KEY,
|
||||
CLASH_INFO_QUERY_KEY,
|
||||
CLASH_VERSION_QUERY_KEY,
|
||||
NYANPASU_BACKEND_EVENT_NAME,
|
||||
NYANPASU_SETTING_QUERY_KEY,
|
||||
NYANPASU_SYSTEM_PROXY_QUERY_KEY,
|
||||
} from '../ipc/consts'
|
||||
|
||||
type EventPayload = 'nyanpasu_config' | 'clash_config' | 'proxies' | 'profiles'
|
||||
|
||||
const NYANPASU_CONFIG_MUTATION_KEYS = [
|
||||
NYANPASU_SETTING_QUERY_KEY,
|
||||
NYANPASU_SYSTEM_PROXY_QUERY_KEY,
|
||||
// TODO: proxies hook refetch
|
||||
// TODO: profiles hook refetch
|
||||
] as const
|
||||
|
||||
const CLASH_CONFIG_MUTATION_KEYS = [
|
||||
CLASH_VERSION_QUERY_KEY,
|
||||
CLASH_INFO_QUERY_KEY,
|
||||
CLASH_CONFIG_QUERY_KEY,
|
||||
// TODO: clash rules hook refetch
|
||||
// TODO: clash rules providers hook refetch
|
||||
// TODO: proxies hook refetch
|
||||
// TODO: proxies providers hook refetch
|
||||
// TODO: profiles hook refetch
|
||||
// TODO: all profiles providers hook refetch, key.includes('getAllProxiesProviders')
|
||||
] as const
|
||||
|
||||
const PROFILES_MUTATION_KEYS = [
|
||||
CLASH_VERSION_QUERY_KEY,
|
||||
CLASH_INFO_QUERY_KEY,
|
||||
// TODO: clash rules hook refetch
|
||||
// TODO: clash rules providers hook refetch
|
||||
// TODO: proxies hook refetch
|
||||
// TODO: proxies providers hook refetch
|
||||
// TODO: profiles hook refetch
|
||||
// TODO: all profiles providers hook refetch, key.includes('getAllProxiesProviders')
|
||||
]
|
||||
|
||||
const PROXIES_MUTATION_KEYS = [
|
||||
// TODO: key.includes('getProxies')
|
||||
] as const
|
||||
|
||||
export const MutationProvider = ({ children }: PropsWithChildren) => {
|
||||
const unlistenFn = useRef<UnlistenFn>(null)
|
||||
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const refetchQueries = (keys: readonly string[]) => {
|
||||
Promise.all(
|
||||
keys.map((key) =>
|
||||
queryClient.refetchQueries({
|
||||
queryKey: [key],
|
||||
}),
|
||||
),
|
||||
).catch((e) => console.error(e))
|
||||
}
|
||||
|
||||
useMount(() => {
|
||||
listen<EventPayload>(NYANPASU_BACKEND_EVENT_NAME, ({ payload }) => {
|
||||
console.log('MutationProvider', payload)
|
||||
|
||||
switch (payload) {
|
||||
case 'nyanpasu_config':
|
||||
refetchQueries(NYANPASU_CONFIG_MUTATION_KEYS)
|
||||
break
|
||||
case 'clash_config':
|
||||
refetchQueries(CLASH_CONFIG_MUTATION_KEYS)
|
||||
break
|
||||
case 'profiles':
|
||||
refetchQueries(PROFILES_MUTATION_KEYS)
|
||||
break
|
||||
case 'proxies':
|
||||
refetchQueries(PROXIES_MUTATION_KEYS)
|
||||
break
|
||||
}
|
||||
})
|
||||
.then((unlisten) => {
|
||||
unlistenFn.current = unlisten
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e)
|
||||
})
|
||||
})
|
||||
|
||||
return children
|
||||
}
|
||||
@@ -42,7 +42,7 @@
|
||||
"react-fast-marquee": "1.6.5",
|
||||
"react-hook-form-mui": "7.5.0",
|
||||
"react-i18next": "15.4.1",
|
||||
"react-markdown": "10.0.1",
|
||||
"react-markdown": "10.1.0",
|
||||
"react-split-grid": "1.0.4",
|
||||
"react-use": "17.6.0",
|
||||
"swr": "2.3.3",
|
||||
@@ -56,9 +56,9 @@
|
||||
"@iconify/json": "2.2.315",
|
||||
"@monaco-editor/react": "4.7.0",
|
||||
"@tanstack/react-query": "5.67.2",
|
||||
"@tanstack/react-router": "1.114.4",
|
||||
"@tanstack/router-devtools": "1.114.4",
|
||||
"@tanstack/router-plugin": "1.114.4",
|
||||
"@tanstack/react-router": "1.114.13",
|
||||
"@tanstack/router-devtools": "1.114.13",
|
||||
"@tanstack/router-plugin": "1.114.13",
|
||||
"@tauri-apps/plugin-clipboard-manager": "2.2.1",
|
||||
"@tauri-apps/plugin-dialog": "2.2.0",
|
||||
"@tauri-apps/plugin-fs": "2.2.0",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"manifest_version": 1,
|
||||
"latest": {
|
||||
"mihomo": "v1.19.3",
|
||||
"mihomo_alpha": "alpha-8bc6f77",
|
||||
"mihomo_alpha": "alpha-c0de3c0",
|
||||
"clash_rs": "v0.7.6",
|
||||
"clash_premium": "2023-09-05-gdcc8d87",
|
||||
"clash_rs_alpha": "0.7.6-alpha+sha.167eccd"
|
||||
@@ -69,5 +69,5 @@
|
||||
"linux-armv7hf": "clash-armv7-unknown-linux-gnueabihf"
|
||||
}
|
||||
},
|
||||
"updated_at": "2025-03-03T22:31:23.606Z"
|
||||
"updated_at": "2025-03-10T22:20:44.919Z"
|
||||
}
|
||||
|
||||
@@ -66,8 +66,8 @@
|
||||
"@types/fs-extra": "11.0.4",
|
||||
"@types/lodash-es": "4.17.12",
|
||||
"@types/node": "22.13.10",
|
||||
"@typescript-eslint/eslint-plugin": "8.26.0",
|
||||
"@typescript-eslint/parser": "8.26.0",
|
||||
"@typescript-eslint/eslint-plugin": "8.26.1",
|
||||
"@typescript-eslint/parser": "8.26.1",
|
||||
"autoprefixer": "10.4.21",
|
||||
"conventional-changelog-conventionalcommits": "8.0.0",
|
||||
"cross-env": "7.0.3",
|
||||
@@ -107,7 +107,7 @@
|
||||
"tailwindcss": "4.0.12",
|
||||
"tsx": "4.19.3",
|
||||
"typescript": "5.8.2",
|
||||
"typescript-eslint": "8.26.0"
|
||||
"typescript-eslint": "8.26.1"
|
||||
},
|
||||
"packageManager": "pnpm@10.6.2",
|
||||
"engines": {
|
||||
|
||||
Generated
+136
-135
@@ -46,11 +46,11 @@ importers:
|
||||
specifier: 22.13.10
|
||||
version: 22.13.10
|
||||
'@typescript-eslint/eslint-plugin':
|
||||
specifier: 8.26.0
|
||||
version: 8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)
|
||||
specifier: 8.26.1
|
||||
version: 8.26.1(@typescript-eslint/parser@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)
|
||||
'@typescript-eslint/parser':
|
||||
specifier: 8.26.0
|
||||
version: 8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)
|
||||
specifier: 8.26.1
|
||||
version: 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)
|
||||
autoprefixer:
|
||||
specifier: 10.4.21
|
||||
version: 10.4.21(postcss@8.5.3)
|
||||
@@ -71,16 +71,16 @@ importers:
|
||||
version: 10.1.1(eslint@9.22.0(jiti@2.4.2))
|
||||
eslint-config-standard:
|
||||
specifier: 17.1.0
|
||||
version: 17.1.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2)))(eslint-plugin-n@17.16.2(eslint@9.22.0(jiti@2.4.2)))(eslint-plugin-promise@7.2.1(eslint@9.22.0(jiti@2.4.2)))(eslint@9.22.0(jiti@2.4.2))
|
||||
version: 17.1.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2)))(eslint-plugin-n@17.16.2(eslint@9.22.0(jiti@2.4.2)))(eslint-plugin-promise@7.2.1(eslint@9.22.0(jiti@2.4.2)))(eslint@9.22.0(jiti@2.4.2))
|
||||
eslint-import-resolver-alias:
|
||||
specifier: 1.1.2
|
||||
version: 1.1.2(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2)))
|
||||
version: 1.1.2(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2)))
|
||||
eslint-plugin-html:
|
||||
specifier: 8.1.2
|
||||
version: 8.1.2
|
||||
eslint-plugin-import:
|
||||
specifier: 2.31.0
|
||||
version: 2.31.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2))
|
||||
version: 2.31.0(@typescript-eslint/parser@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2))
|
||||
eslint-plugin-n:
|
||||
specifier: 17.16.2
|
||||
version: 17.16.2(eslint@9.22.0(jiti@2.4.2))
|
||||
@@ -110,7 +110,7 @@ importers:
|
||||
version: 15.4.3
|
||||
neostandard:
|
||||
specifier: 0.12.1
|
||||
version: 0.12.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)
|
||||
version: 0.12.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)
|
||||
npm-run-all2:
|
||||
specifier: 7.0.2
|
||||
version: 7.0.2
|
||||
@@ -169,8 +169,8 @@ importers:
|
||||
specifier: 5.8.2
|
||||
version: 5.8.2
|
||||
typescript-eslint:
|
||||
specifier: 8.26.0
|
||||
version: 8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)
|
||||
specifier: 8.26.1
|
||||
version: 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)
|
||||
|
||||
frontend/interface:
|
||||
dependencies:
|
||||
@@ -246,7 +246,7 @@ importers:
|
||||
version: 4.0.12
|
||||
'@tanstack/router-zod-adapter':
|
||||
specifier: 1.81.5
|
||||
version: 1.81.5(@tanstack/react-router@1.114.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(zod@3.24.2)
|
||||
version: 1.81.5(@tanstack/react-router@1.114.13(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(zod@3.24.2)
|
||||
'@tauri-apps/api':
|
||||
specifier: 2.3.0
|
||||
version: 2.3.0
|
||||
@@ -305,8 +305,8 @@ importers:
|
||||
specifier: 15.4.1
|
||||
version: 15.4.1(i18next@24.2.2(typescript@5.8.2))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
react-markdown:
|
||||
specifier: 10.0.1
|
||||
version: 10.0.1(@types/react@19.0.10)(react@19.0.0)
|
||||
specifier: 10.1.0
|
||||
version: 10.1.0(@types/react@19.0.10)(react@19.0.0)
|
||||
react-split-grid:
|
||||
specifier: 1.0.4
|
||||
version: 1.0.4(react@19.0.0)
|
||||
@@ -342,14 +342,14 @@ importers:
|
||||
specifier: 5.67.2
|
||||
version: 5.67.2(react@19.0.0)
|
||||
'@tanstack/react-router':
|
||||
specifier: 1.114.4
|
||||
version: 1.114.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
specifier: 1.114.13
|
||||
version: 1.114.13(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@tanstack/router-devtools':
|
||||
specifier: 1.114.4
|
||||
version: 1.114.4(@tanstack/react-router@1.114.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@tanstack/router-devtools-core@1.114.3(@tanstack/router-core@1.114.3)(csstype@3.1.3)(solid-js@1.9.5)(tiny-invariant@1.3.3))(csstype@3.1.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
specifier: 1.114.13
|
||||
version: 1.114.13(@tanstack/react-router@1.114.13(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@tanstack/router-devtools-core@1.114.3(@tanstack/router-core@1.114.12)(csstype@3.1.3)(solid-js@1.9.5)(tiny-invariant@1.3.3))(csstype@3.1.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@tanstack/router-plugin':
|
||||
specifier: 1.114.4
|
||||
version: 1.114.4(@tanstack/react-router@1.114.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(vite@6.2.1(@types/node@22.13.10)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.1)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0))
|
||||
specifier: 1.114.13
|
||||
version: 1.114.13(@tanstack/react-router@1.114.13(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(vite@6.2.1(@types/node@22.13.10)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.1)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0))
|
||||
'@tauri-apps/plugin-clipboard-manager':
|
||||
specifier: 2.2.1
|
||||
version: 2.2.1
|
||||
@@ -2726,8 +2726,8 @@ packages:
|
||||
'@tailwindcss/postcss@4.0.12':
|
||||
resolution: {integrity: sha512-r59Sdr8djCW4dL3kvc4aWU8PHdUAVM3O3te2nbYzXsWwKLlHPCuUoZAc9FafXb/YyNDZOMI7sTbKTKFmwOrMjw==}
|
||||
|
||||
'@tanstack/history@1.114.3':
|
||||
resolution: {integrity: sha512-UVJEleUvkVfTwDecq9VbGJKcYQg8J3wMuMn1DGCX4wkNLM2EEQ7BI3oeESmD+DU1uDD/yueSplRjGq8nTtZT2Q==}
|
||||
'@tanstack/history@1.114.12':
|
||||
resolution: {integrity: sha512-br0AiFLCdTqqgPoZ6V35k1VoDmli1gGVM/YI1YbLZSA1kUnqFlY+Fx5NOgbRfxa7+oYyHUXkmkp3lJ5LueTrhQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
'@tanstack/match-sorter-utils@8.19.4':
|
||||
@@ -2742,17 +2742,17 @@ packages:
|
||||
peerDependencies:
|
||||
react: ^18 || ^19
|
||||
|
||||
'@tanstack/react-router-devtools@1.114.4':
|
||||
resolution: {integrity: sha512-lhnVlTXPqKvc30u17fNekrLA2vN0Nme+xqyj5+TyK+CkGRvQYL+MiNmXO6zBU33Eqo+plmrvXsbfoKY77DK0kw==}
|
||||
'@tanstack/react-router-devtools@1.114.13':
|
||||
resolution: {integrity: sha512-YFAer7H4H8Io34mMu+kQU1p2/Vl4bon62g8Tha+b0pfnMnWhTjkJAapYIr8JCs1HsHD1F/R4CXR0REbOUMWEzQ==}
|
||||
engines: {node: '>=12'}
|
||||
peerDependencies:
|
||||
'@tanstack/react-router': ^1.114.4
|
||||
'@tanstack/router-devtools-core': ^1.114.3
|
||||
'@tanstack/react-router': ^1.114.13
|
||||
'@tanstack/router-devtools-core': ^1.114.12
|
||||
react: '>=18.0.0 || >=19.0.0'
|
||||
react-dom: '>=18.0.0 || >=19.0.0'
|
||||
|
||||
'@tanstack/react-router@1.114.4':
|
||||
resolution: {integrity: sha512-FVvKSrZ5o3Yrh+Iep0Dx/X3Ybyazop6n2n/0u+E0LTIhS60fDknE2jJZRqUGgKz+DaxIEJurqdfw6PWRVzx3BA==}
|
||||
'@tanstack/react-router@1.114.13':
|
||||
resolution: {integrity: sha512-cW216LAfdmviaIU3enpWE/lS05cOo8zAeBZ8GHGpmFU/z+4ZHIcTMvs4pG8WnDA5hMFnpaLXBmYQx1Z4URXKkQ==}
|
||||
engines: {node: '>=12'}
|
||||
peerDependencies:
|
||||
react: '>=18.0.0 || >=19.0.0'
|
||||
@@ -2777,8 +2777,8 @@ packages:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
|
||||
'@tanstack/router-core@1.114.3':
|
||||
resolution: {integrity: sha512-uKez7bBnFDwqyCJlOtAvu456vx9VCEu/VFw9gs5AALKm6ONXbK66sehn+1sBYgHhkPBnllIaKnmDIGhGuSCheg==}
|
||||
'@tanstack/router-core@1.114.12':
|
||||
resolution: {integrity: sha512-cHhQJz3yJ5BkRe3JxtRfvmk4ynV89k1yudMQRK/1PeUV5eVEB0cIEECDk8Nlp3w9mPFS3U1iYby9hje4kP4h4g==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
'@tanstack/router-devtools-core@1.114.3':
|
||||
@@ -2793,11 +2793,11 @@ packages:
|
||||
csstype:
|
||||
optional: true
|
||||
|
||||
'@tanstack/router-devtools@1.114.4':
|
||||
resolution: {integrity: sha512-LvL0bjnOBEzo5sr3SI4omVahyNMSMzUTc7EAkKqTpWQ5bHvQbN4ytx9LftVRoVz4BGH+nW07DY13keHlkvHdVQ==}
|
||||
'@tanstack/router-devtools@1.114.13':
|
||||
resolution: {integrity: sha512-5bG7eplJFxNVVj9dJWSS61kfHNFtkDBvK9pURU/tIpR2oKoq9uaaIOTLPa2ZiLi4XiTa6sfW9BZ2OXyCDh1v+g==}
|
||||
engines: {node: '>=12'}
|
||||
peerDependencies:
|
||||
'@tanstack/react-router': ^1.114.4
|
||||
'@tanstack/react-router': ^1.114.13
|
||||
csstype: ^3.0.10
|
||||
react: '>=18.0.0 || >=19.0.0'
|
||||
react-dom: '>=18.0.0 || >=19.0.0'
|
||||
@@ -2805,21 +2805,21 @@ packages:
|
||||
csstype:
|
||||
optional: true
|
||||
|
||||
'@tanstack/router-generator@1.114.4':
|
||||
resolution: {integrity: sha512-AxjwdYBsm5fYZOi8l1AX59SSIHGqu18gNEWh5eH/1HGNZwOibOkalJ/NI1gmMSf9NMCF/0/qpyCXGHenbDPpGw==}
|
||||
'@tanstack/router-generator@1.114.13':
|
||||
resolution: {integrity: sha512-ZsQnrXIbhFLDABBY6lWP5Ds4pxc0LFfLaVEvqemIgtf3LLGcNbEEJ1pKgDswHm6F+aay2TQaHeUbBdNZv1+8uQ==}
|
||||
engines: {node: '>=12'}
|
||||
peerDependencies:
|
||||
'@tanstack/react-router': ^1.114.4
|
||||
'@tanstack/react-router': ^1.114.13
|
||||
peerDependenciesMeta:
|
||||
'@tanstack/react-router':
|
||||
optional: true
|
||||
|
||||
'@tanstack/router-plugin@1.114.4':
|
||||
resolution: {integrity: sha512-YkUxfAdYXtTDIqSbQzphcIZGXue+8c8+c8xuyxQrq2EaYgBiQfOtilVFFltQ5So2XshtM6disCPtNlzTEfaFAA==}
|
||||
'@tanstack/router-plugin@1.114.13':
|
||||
resolution: {integrity: sha512-/03evwDwaAiwD+hGSbHoP60o+eV3icdadJgh5wWWkMEQpXU21c5sBq/3MDxY1NgUjnCEeFInN0crkKC1Uguo5w==}
|
||||
engines: {node: '>=12'}
|
||||
peerDependencies:
|
||||
'@rsbuild/core': '>=1.0.2'
|
||||
'@tanstack/react-router': ^1.114.4
|
||||
'@tanstack/react-router': ^1.114.13
|
||||
vite: '>=5.0.0 || >=6.0.0'
|
||||
vite-plugin-solid: ^2.11.2
|
||||
webpack: '>=5.92.0'
|
||||
@@ -2835,8 +2835,8 @@ packages:
|
||||
webpack:
|
||||
optional: true
|
||||
|
||||
'@tanstack/router-utils@1.114.3':
|
||||
resolution: {integrity: sha512-r7eBYB0jqVSBr+sDXCh3CEkWsAS36fL1/Xo7jcmcjKaoHoGumWChopnCFNnkB8iRiroLY6V0+z9LpjL6fqL5ug==}
|
||||
'@tanstack/router-utils@1.114.12':
|
||||
resolution: {integrity: sha512-W4tltvM9FQuDEJejz/JJD3q/pVHBXBb8VmA77pZlj4IBW97RnUNy8CUwZUgSYcb9OReoO4i/VjjQCUq9ZdiDmg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
'@tanstack/router-zod-adapter@1.81.5':
|
||||
@@ -2856,8 +2856,8 @@ packages:
|
||||
'@tanstack/virtual-core@3.11.2':
|
||||
resolution: {integrity: sha512-vTtpNt7mKCiZ1pwU9hfKPhpdVO2sVzFQsxoVBGtOSHxlrRRzYr8iQ2TlwbAcRYCcEiZ9ECAM8kBzH0v2+VzfKw==}
|
||||
|
||||
'@tanstack/virtual-file-routes@1.114.3':
|
||||
resolution: {integrity: sha512-EKrqoko3+rWal3s/EzkR5cCQcTjZjIWa3ssqjyw62dnlLvMB8yhY3humIrA4HJ+b0AUJKvkOTqxafWx2rmRoQA==}
|
||||
'@tanstack/virtual-file-routes@1.114.12':
|
||||
resolution: {integrity: sha512-aR13V1kSE/kUkP4a8snmqvj82OUlR5Q/rzxICmObLCsERGfzikUc4wquOy1d/RzJgsLb8o+FiOjSWynt4T7Jhg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
'@taplo/core@0.1.1':
|
||||
@@ -3202,16 +3202,16 @@ packages:
|
||||
'@types/yauzl@2.10.3':
|
||||
resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==}
|
||||
|
||||
'@typescript-eslint/eslint-plugin@8.26.0':
|
||||
resolution: {integrity: sha512-cLr1J6pe56zjKYajK6SSSre6nl1Gj6xDp1TY0trpgPzjVbgDwd09v2Ws37LABxzkicmUjhEeg/fAUjPJJB1v5Q==}
|
||||
'@typescript-eslint/eslint-plugin@8.26.1':
|
||||
resolution: {integrity: sha512-2X3mwqsj9Bd3Ciz508ZUtoQQYpOhU/kWoUqIf49H8Z0+Vbh6UF/y0OEYp0Q0axOGzaBGs7QxRwq0knSQ8khQNA==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
'@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
typescript: '>=4.8.4 <5.9.0'
|
||||
|
||||
'@typescript-eslint/parser@8.26.0':
|
||||
resolution: {integrity: sha512-mNtXP9LTVBy14ZF3o7JG69gRPBK/2QWtQd0j0oH26HcY/foyJJau6pNUez7QrM5UHnSvwlQcJXKsk0I99B9pOA==}
|
||||
'@typescript-eslint/parser@8.26.1':
|
||||
resolution: {integrity: sha512-w6HZUV4NWxqd8BdeFf81t07d7/YV9s7TCWrQQbG5uhuvGUAW+fq1usZ1Hmz9UPNLniFnD8GLSsDpjP0hm1S4lQ==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
@@ -3221,12 +3221,12 @@ packages:
|
||||
resolution: {integrity: sha512-OdQr6BNBzwRjNEXMQyaGyZzgg7wzjYKfX2ZBV3E04hUCBDv3GQCHiz9RpqdUIiVrMgJGkXm3tcEh4vFSHreS2Q==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@typescript-eslint/scope-manager@8.26.0':
|
||||
resolution: {integrity: sha512-E0ntLvsfPqnPwng8b8y4OGuzh/iIOm2z8U3S9zic2TeMLW61u5IH2Q1wu0oSTkfrSzwbDJIB/Lm8O3//8BWMPA==}
|
||||
'@typescript-eslint/scope-manager@8.26.1':
|
||||
resolution: {integrity: sha512-6EIvbE5cNER8sqBu6V7+KeMZIC1664d2Yjt+B9EWUXrsyWpxx4lEZrmvxgSKRC6gX+efDL/UY9OpPZ267io3mg==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@typescript-eslint/type-utils@8.26.0':
|
||||
resolution: {integrity: sha512-ruk0RNChLKz3zKGn2LwXuVoeBcUMh+jaqzN461uMMdxy5H9epZqIBtYj7UiPXRuOpaALXGbmRuZQhmwHhaS04Q==}
|
||||
'@typescript-eslint/type-utils@8.26.1':
|
||||
resolution: {integrity: sha512-Kcj/TagJLwoY/5w9JGEFV0dclQdyqw9+VMndxOJKtoFSjfZhLXhYjzsQEeyza03rwHx2vFEGvrJWJBXKleRvZg==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
@@ -3236,8 +3236,8 @@ packages:
|
||||
resolution: {integrity: sha512-9kqJ+2DkUXiuhoiYIUvIYjGcwle8pcPpdlfkemGvTObzgmYfJ5d0Qm6jwb4NBXP9W1I5tss0VIAnWFumz3mC5A==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@typescript-eslint/types@8.26.0':
|
||||
resolution: {integrity: sha512-89B1eP3tnpr9A8L6PZlSjBvnJhWXtYfZhECqlBl1D9Lme9mHO6iWlsprBtVenQvY1HMhax1mWOjhtL3fh/u+pA==}
|
||||
'@typescript-eslint/types@8.26.1':
|
||||
resolution: {integrity: sha512-n4THUQW27VmQMx+3P+B0Yptl7ydfceUj4ON/AQILAASwgYdZ/2dhfymRMh5egRUrvK5lSmaOm77Ry+lmXPOgBQ==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@typescript-eslint/typescript-estree@8.24.1':
|
||||
@@ -3246,8 +3246,8 @@ packages:
|
||||
peerDependencies:
|
||||
typescript: '>=4.8.4 <5.8.0'
|
||||
|
||||
'@typescript-eslint/typescript-estree@8.26.0':
|
||||
resolution: {integrity: sha512-tiJ1Hvy/V/oMVRTbEOIeemA2XoylimlDQ03CgPPNaHYZbpsc78Hmngnt+WXZfJX1pjQ711V7g0H7cSJThGYfPQ==}
|
||||
'@typescript-eslint/typescript-estree@8.26.1':
|
||||
resolution: {integrity: sha512-yUwPpUHDgdrv1QJ7YQal3cMVBGWfnuCdKbXw1yyjArax3353rEJP1ZA+4F8nOlQ3RfS2hUN/wze3nlY+ZOhvoA==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
typescript: '>=4.8.4 <5.9.0'
|
||||
@@ -3259,8 +3259,8 @@ packages:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
typescript: '>=4.8.4 <5.8.0'
|
||||
|
||||
'@typescript-eslint/utils@8.26.0':
|
||||
resolution: {integrity: sha512-2L2tU3FVwhvU14LndnQCA2frYC8JnPDVKyQtWFPf8IYFMt/ykEN1bPolNhNbCVgOmdzTlWdusCTKA/9nKrf8Ig==}
|
||||
'@typescript-eslint/utils@8.26.1':
|
||||
resolution: {integrity: sha512-V4Urxa/XtSUroUrnI7q6yUTD3hDtfJ2jzVfeT3VK0ciizfK2q/zGC0iDh1lFMUZR8cImRrep6/q0xd/1ZGPQpg==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
@@ -3270,8 +3270,8 @@ packages:
|
||||
resolution: {integrity: sha512-EwVHlp5l+2vp8CoqJm9KikPZgi3gbdZAtabKT9KPShGeOcJhsv4Zdo3oc8T8I0uKEmYoU4ItyxbptjF08enaxg==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@typescript-eslint/visitor-keys@8.26.0':
|
||||
resolution: {integrity: sha512-2z8JQJWAzPdDd51dRQ/oqIJxe99/hoLIqmf8RMCAJQtYDc535W/Jt2+RTP4bP0aKeBG1F65yjIZuczOXCmbWwg==}
|
||||
'@typescript-eslint/visitor-keys@8.26.1':
|
||||
resolution: {integrity: sha512-AjOC3zfnxd6S4Eiy3jwktJPclqhFHNyd8L6Gycf9WUPoKZpgM5PjkxY1X7uSy61xVpiJDhhk7XT2NVsN3ALTWg==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@ungap/structured-clone@1.2.0':
|
||||
@@ -6706,8 +6706,8 @@ packages:
|
||||
react-is@19.0.0:
|
||||
resolution: {integrity: sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g==}
|
||||
|
||||
react-markdown@10.0.1:
|
||||
resolution: {integrity: sha512-Qt9TWsQJ75np2AVoKftns5eI7r50H6u3qwp+TSihlxOcw8ZaStmR0FEeeENU+mWSxyAgOmqMYjiIKn7ibMheKA==}
|
||||
react-markdown@10.1.0:
|
||||
resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==}
|
||||
peerDependencies:
|
||||
'@types/react': '>=18'
|
||||
react: '>=18'
|
||||
@@ -7652,8 +7652,8 @@ packages:
|
||||
typedarray-to-buffer@3.1.5:
|
||||
resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==}
|
||||
|
||||
typescript-eslint@8.26.0:
|
||||
resolution: {integrity: sha512-PtVz9nAnuNJuAVeUFvwztjuUgSnJInODAUx47VDwWPXzd5vismPOtPtt83tzNXyOjVQbPRp786D6WFW/M2koIA==}
|
||||
typescript-eslint@8.26.1:
|
||||
resolution: {integrity: sha512-t/oIs9mYyrwZGRpDv3g+3K6nZ5uhKEMt2oNmAPwaY4/ye0+EH4nXIPYNtkYFS6QHm+1DFg34DbglYBz5P9Xysg==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
@@ -10468,7 +10468,7 @@ snapshots:
|
||||
postcss: 8.5.3
|
||||
tailwindcss: 4.0.12
|
||||
|
||||
'@tanstack/history@1.114.3': {}
|
||||
'@tanstack/history@1.114.12': {}
|
||||
|
||||
'@tanstack/match-sorter-utils@8.19.4':
|
||||
dependencies:
|
||||
@@ -10481,18 +10481,19 @@ snapshots:
|
||||
'@tanstack/query-core': 5.67.2
|
||||
react: 19.0.0
|
||||
|
||||
'@tanstack/react-router-devtools@1.114.4(@tanstack/react-router@1.114.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@tanstack/router-devtools-core@1.114.3(@tanstack/router-core@1.114.3)(csstype@3.1.3)(solid-js@1.9.5)(tiny-invariant@1.3.3))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||
'@tanstack/react-router-devtools@1.114.13(@tanstack/react-router@1.114.13(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@tanstack/router-devtools-core@1.114.3(@tanstack/router-core@1.114.12)(csstype@3.1.3)(solid-js@1.9.5)(tiny-invariant@1.3.3))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||
dependencies:
|
||||
'@tanstack/react-router': 1.114.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@tanstack/router-devtools-core': 1.114.3(@tanstack/router-core@1.114.3)(csstype@3.1.3)(solid-js@1.9.5)(tiny-invariant@1.3.3)
|
||||
'@tanstack/react-router': 1.114.13(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@tanstack/router-devtools-core': 1.114.3(@tanstack/router-core@1.114.12)(csstype@3.1.3)(solid-js@1.9.5)(tiny-invariant@1.3.3)
|
||||
react: 19.0.0
|
||||
react-dom: 19.0.0(react@19.0.0)
|
||||
solid-js: 1.9.5
|
||||
|
||||
'@tanstack/react-router@1.114.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||
'@tanstack/react-router@1.114.13(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||
dependencies:
|
||||
'@tanstack/history': 1.114.3
|
||||
'@tanstack/history': 1.114.12
|
||||
'@tanstack/react-store': 0.7.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@tanstack/router-core': 1.114.3
|
||||
'@tanstack/router-core': 1.114.12
|
||||
jsesc: 3.1.0
|
||||
react: 19.0.0
|
||||
react-dom: 19.0.0(react@19.0.0)
|
||||
@@ -10518,14 +10519,14 @@ snapshots:
|
||||
react: 19.0.0
|
||||
react-dom: 19.0.0(react@19.0.0)
|
||||
|
||||
'@tanstack/router-core@1.114.3':
|
||||
'@tanstack/router-core@1.114.12':
|
||||
dependencies:
|
||||
'@tanstack/history': 1.114.3
|
||||
'@tanstack/history': 1.114.12
|
||||
'@tanstack/store': 0.7.0
|
||||
|
||||
'@tanstack/router-devtools-core@1.114.3(@tanstack/router-core@1.114.3)(csstype@3.1.3)(solid-js@1.9.5)(tiny-invariant@1.3.3)':
|
||||
'@tanstack/router-devtools-core@1.114.3(@tanstack/router-core@1.114.12)(csstype@3.1.3)(solid-js@1.9.5)(tiny-invariant@1.3.3)':
|
||||
dependencies:
|
||||
'@tanstack/router-core': 1.114.3
|
||||
'@tanstack/router-core': 1.114.12
|
||||
clsx: 2.1.1
|
||||
goober: 2.1.16(csstype@3.1.3)
|
||||
solid-js: 1.9.5
|
||||
@@ -10533,10 +10534,10 @@ snapshots:
|
||||
optionalDependencies:
|
||||
csstype: 3.1.3
|
||||
|
||||
'@tanstack/router-devtools@1.114.4(@tanstack/react-router@1.114.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@tanstack/router-devtools-core@1.114.3(@tanstack/router-core@1.114.3)(csstype@3.1.3)(solid-js@1.9.5)(tiny-invariant@1.3.3))(csstype@3.1.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||
'@tanstack/router-devtools@1.114.13(@tanstack/react-router@1.114.13(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@tanstack/router-devtools-core@1.114.3(@tanstack/router-core@1.114.12)(csstype@3.1.3)(solid-js@1.9.5)(tiny-invariant@1.3.3))(csstype@3.1.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||
dependencies:
|
||||
'@tanstack/react-router': 1.114.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@tanstack/react-router-devtools': 1.114.4(@tanstack/react-router@1.114.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@tanstack/router-devtools-core@1.114.3(@tanstack/router-core@1.114.3)(csstype@3.1.3)(solid-js@1.9.5)(tiny-invariant@1.3.3))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@tanstack/react-router': 1.114.13(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@tanstack/react-router-devtools': 1.114.13(@tanstack/react-router@1.114.13(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@tanstack/router-devtools-core@1.114.3(@tanstack/router-core@1.114.12)(csstype@3.1.3)(solid-js@1.9.5)(tiny-invariant@1.3.3))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
clsx: 2.1.1
|
||||
goober: 2.1.16(csstype@3.1.3)
|
||||
react: 19.0.0
|
||||
@@ -10546,16 +10547,16 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- '@tanstack/router-devtools-core'
|
||||
|
||||
'@tanstack/router-generator@1.114.4(@tanstack/react-router@1.114.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0))':
|
||||
'@tanstack/router-generator@1.114.13(@tanstack/react-router@1.114.13(react-dom@19.0.0(react@19.0.0))(react@19.0.0))':
|
||||
dependencies:
|
||||
'@tanstack/virtual-file-routes': 1.114.3
|
||||
'@tanstack/virtual-file-routes': 1.114.12
|
||||
prettier: 3.5.3
|
||||
tsx: 4.19.3
|
||||
zod: 3.24.2
|
||||
optionalDependencies:
|
||||
'@tanstack/react-router': 1.114.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@tanstack/react-router': 1.114.13(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
|
||||
'@tanstack/router-plugin@1.114.4(@tanstack/react-router@1.114.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(vite@6.2.1(@types/node@22.13.10)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.1)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0))':
|
||||
'@tanstack/router-plugin@1.114.13(@tanstack/react-router@1.114.13(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(vite@6.2.1(@types/node@22.13.10)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.1)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0))':
|
||||
dependencies:
|
||||
'@babel/core': 7.26.9
|
||||
'@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.9)
|
||||
@@ -10563,10 +10564,10 @@ snapshots:
|
||||
'@babel/template': 7.26.9
|
||||
'@babel/traverse': 7.26.9
|
||||
'@babel/types': 7.26.9
|
||||
'@tanstack/router-core': 1.114.3
|
||||
'@tanstack/router-generator': 1.114.4(@tanstack/react-router@1.114.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0))
|
||||
'@tanstack/router-utils': 1.114.3
|
||||
'@tanstack/virtual-file-routes': 1.114.3
|
||||
'@tanstack/router-core': 1.114.12
|
||||
'@tanstack/router-generator': 1.114.13(@tanstack/react-router@1.114.13(react-dom@19.0.0(react@19.0.0))(react@19.0.0))
|
||||
'@tanstack/router-utils': 1.114.12
|
||||
'@tanstack/virtual-file-routes': 1.114.12
|
||||
'@types/babel__core': 7.20.5
|
||||
'@types/babel__template': 7.4.4
|
||||
'@types/babel__traverse': 7.20.6
|
||||
@@ -10575,21 +10576,21 @@ snapshots:
|
||||
unplugin: 2.2.0
|
||||
zod: 3.24.2
|
||||
optionalDependencies:
|
||||
'@tanstack/react-router': 1.114.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@tanstack/react-router': 1.114.13(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
vite: 6.2.1(@types/node@22.13.10)(jiti@2.4.2)(less@4.2.0)(lightningcss@1.29.1)(sass-embedded@1.85.1)(sass@1.83.0)(stylus@0.62.0)(terser@5.36.0)(tsx@4.19.3)(yaml@2.7.0)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@tanstack/router-utils@1.114.3':
|
||||
'@tanstack/router-utils@1.114.12':
|
||||
dependencies:
|
||||
'@babel/generator': 7.26.9
|
||||
'@babel/parser': 7.26.9
|
||||
ansis: 3.12.0
|
||||
diff: 7.0.0
|
||||
|
||||
'@tanstack/router-zod-adapter@1.81.5(@tanstack/react-router@1.114.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(zod@3.24.2)':
|
||||
'@tanstack/router-zod-adapter@1.81.5(@tanstack/react-router@1.114.13(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(zod@3.24.2)':
|
||||
dependencies:
|
||||
'@tanstack/react-router': 1.114.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@tanstack/react-router': 1.114.13(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
zod: 3.24.2
|
||||
|
||||
'@tanstack/store@0.7.0': {}
|
||||
@@ -10598,7 +10599,7 @@ snapshots:
|
||||
|
||||
'@tanstack/virtual-core@3.11.2': {}
|
||||
|
||||
'@tanstack/virtual-file-routes@1.114.3': {}
|
||||
'@tanstack/virtual-file-routes@1.114.12': {}
|
||||
|
||||
'@taplo/core@0.1.1': {}
|
||||
|
||||
@@ -10965,14 +10966,14 @@ snapshots:
|
||||
'@types/node': 22.13.10
|
||||
optional: true
|
||||
|
||||
'@typescript-eslint/eslint-plugin@8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)':
|
||||
'@typescript-eslint/eslint-plugin@8.26.1(@typescript-eslint/parser@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)':
|
||||
dependencies:
|
||||
'@eslint-community/regexpp': 4.12.1
|
||||
'@typescript-eslint/parser': 8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)
|
||||
'@typescript-eslint/scope-manager': 8.26.0
|
||||
'@typescript-eslint/type-utils': 8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)
|
||||
'@typescript-eslint/utils': 8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)
|
||||
'@typescript-eslint/visitor-keys': 8.26.0
|
||||
'@typescript-eslint/parser': 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)
|
||||
'@typescript-eslint/scope-manager': 8.26.1
|
||||
'@typescript-eslint/type-utils': 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)
|
||||
'@typescript-eslint/utils': 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)
|
||||
'@typescript-eslint/visitor-keys': 8.26.1
|
||||
eslint: 9.22.0(jiti@2.4.2)
|
||||
graphemer: 1.4.0
|
||||
ignore: 5.3.2
|
||||
@@ -10982,12 +10983,12 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)':
|
||||
'@typescript-eslint/parser@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)':
|
||||
dependencies:
|
||||
'@typescript-eslint/scope-manager': 8.26.0
|
||||
'@typescript-eslint/types': 8.26.0
|
||||
'@typescript-eslint/typescript-estree': 8.26.0(typescript@5.8.2)
|
||||
'@typescript-eslint/visitor-keys': 8.26.0
|
||||
'@typescript-eslint/scope-manager': 8.26.1
|
||||
'@typescript-eslint/types': 8.26.1
|
||||
'@typescript-eslint/typescript-estree': 8.26.1(typescript@5.8.2)
|
||||
'@typescript-eslint/visitor-keys': 8.26.1
|
||||
debug: 4.4.0
|
||||
eslint: 9.22.0(jiti@2.4.2)
|
||||
typescript: 5.8.2
|
||||
@@ -10999,15 +11000,15 @@ snapshots:
|
||||
'@typescript-eslint/types': 8.24.1
|
||||
'@typescript-eslint/visitor-keys': 8.24.1
|
||||
|
||||
'@typescript-eslint/scope-manager@8.26.0':
|
||||
'@typescript-eslint/scope-manager@8.26.1':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.26.0
|
||||
'@typescript-eslint/visitor-keys': 8.26.0
|
||||
'@typescript-eslint/types': 8.26.1
|
||||
'@typescript-eslint/visitor-keys': 8.26.1
|
||||
|
||||
'@typescript-eslint/type-utils@8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)':
|
||||
'@typescript-eslint/type-utils@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)':
|
||||
dependencies:
|
||||
'@typescript-eslint/typescript-estree': 8.26.0(typescript@5.8.2)
|
||||
'@typescript-eslint/utils': 8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)
|
||||
'@typescript-eslint/typescript-estree': 8.26.1(typescript@5.8.2)
|
||||
'@typescript-eslint/utils': 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)
|
||||
debug: 4.4.0
|
||||
eslint: 9.22.0(jiti@2.4.2)
|
||||
ts-api-utils: 2.0.1(typescript@5.8.2)
|
||||
@@ -11017,7 +11018,7 @@ snapshots:
|
||||
|
||||
'@typescript-eslint/types@8.24.1': {}
|
||||
|
||||
'@typescript-eslint/types@8.26.0': {}
|
||||
'@typescript-eslint/types@8.26.1': {}
|
||||
|
||||
'@typescript-eslint/typescript-estree@8.24.1(typescript@5.8.2)':
|
||||
dependencies:
|
||||
@@ -11033,10 +11034,10 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/typescript-estree@8.26.0(typescript@5.8.2)':
|
||||
'@typescript-eslint/typescript-estree@8.26.1(typescript@5.8.2)':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.26.0
|
||||
'@typescript-eslint/visitor-keys': 8.26.0
|
||||
'@typescript-eslint/types': 8.26.1
|
||||
'@typescript-eslint/visitor-keys': 8.26.1
|
||||
debug: 4.4.0
|
||||
fast-glob: 3.3.3
|
||||
is-glob: 4.0.3
|
||||
@@ -11058,12 +11059,12 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/utils@8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)':
|
||||
'@typescript-eslint/utils@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)':
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.4.1(eslint@9.22.0(jiti@2.4.2))
|
||||
'@typescript-eslint/scope-manager': 8.26.0
|
||||
'@typescript-eslint/types': 8.26.0
|
||||
'@typescript-eslint/typescript-estree': 8.26.0(typescript@5.8.2)
|
||||
'@typescript-eslint/scope-manager': 8.26.1
|
||||
'@typescript-eslint/types': 8.26.1
|
||||
'@typescript-eslint/typescript-estree': 8.26.1(typescript@5.8.2)
|
||||
eslint: 9.22.0(jiti@2.4.2)
|
||||
typescript: 5.8.2
|
||||
transitivePeerDependencies:
|
||||
@@ -11074,9 +11075,9 @@ snapshots:
|
||||
'@typescript-eslint/types': 8.24.1
|
||||
eslint-visitor-keys: 4.2.0
|
||||
|
||||
'@typescript-eslint/visitor-keys@8.26.0':
|
||||
'@typescript-eslint/visitor-keys@8.26.1':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.26.0
|
||||
'@typescript-eslint/types': 8.26.1
|
||||
eslint-visitor-keys: 4.2.0
|
||||
|
||||
'@ungap/structured-clone@1.2.0': {}
|
||||
@@ -12474,16 +12475,16 @@ snapshots:
|
||||
dependencies:
|
||||
eslint: 9.22.0(jiti@2.4.2)
|
||||
|
||||
eslint-config-standard@17.1.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2)))(eslint-plugin-n@17.16.2(eslint@9.22.0(jiti@2.4.2)))(eslint-plugin-promise@7.2.1(eslint@9.22.0(jiti@2.4.2)))(eslint@9.22.0(jiti@2.4.2)):
|
||||
eslint-config-standard@17.1.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2)))(eslint-plugin-n@17.16.2(eslint@9.22.0(jiti@2.4.2)))(eslint-plugin-promise@7.2.1(eslint@9.22.0(jiti@2.4.2)))(eslint@9.22.0(jiti@2.4.2)):
|
||||
dependencies:
|
||||
eslint: 9.22.0(jiti@2.4.2)
|
||||
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2))
|
||||
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2))
|
||||
eslint-plugin-n: 17.16.2(eslint@9.22.0(jiti@2.4.2))
|
||||
eslint-plugin-promise: 7.2.1(eslint@9.22.0(jiti@2.4.2))
|
||||
|
||||
eslint-import-resolver-alias@1.1.2(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2))):
|
||||
eslint-import-resolver-alias@1.1.2(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2))):
|
||||
dependencies:
|
||||
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2))
|
||||
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2))
|
||||
|
||||
eslint-import-resolver-node@0.3.9:
|
||||
dependencies:
|
||||
@@ -12493,7 +12494,7 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
eslint-import-resolver-typescript@3.7.0(eslint-plugin-import-x@4.5.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.22.0(jiti@2.4.2)):
|
||||
eslint-import-resolver-typescript@3.7.0(eslint-plugin-import-x@4.5.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.22.0(jiti@2.4.2)):
|
||||
dependencies:
|
||||
'@nolyfill/is-core-module': 1.0.39
|
||||
debug: 4.4.0
|
||||
@@ -12505,16 +12506,16 @@ snapshots:
|
||||
is-glob: 4.0.3
|
||||
stable-hash: 0.0.4
|
||||
optionalDependencies:
|
||||
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2))
|
||||
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2))
|
||||
eslint-plugin-import-x: 4.5.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
eslint-module-utils@2.12.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint@9.22.0(jiti@2.4.2)):
|
||||
eslint-module-utils@2.12.0(@typescript-eslint/parser@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint@9.22.0(jiti@2.4.2)):
|
||||
dependencies:
|
||||
debug: 3.2.7
|
||||
optionalDependencies:
|
||||
'@typescript-eslint/parser': 8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)
|
||||
'@typescript-eslint/parser': 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)
|
||||
eslint: 9.22.0(jiti@2.4.2)
|
||||
eslint-import-resolver-node: 0.3.9
|
||||
transitivePeerDependencies:
|
||||
@@ -12549,7 +12550,7 @@ snapshots:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2)):
|
||||
eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2)):
|
||||
dependencies:
|
||||
'@rtsao/scc': 1.1.0
|
||||
array-includes: 3.1.8
|
||||
@@ -12560,7 +12561,7 @@ snapshots:
|
||||
doctrine: 2.1.0
|
||||
eslint: 9.22.0(jiti@2.4.2)
|
||||
eslint-import-resolver-node: 0.3.9
|
||||
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint@9.22.0(jiti@2.4.2))
|
||||
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint@9.22.0(jiti@2.4.2))
|
||||
hasown: 2.0.2
|
||||
is-core-module: 2.15.1
|
||||
is-glob: 4.0.3
|
||||
@@ -12572,7 +12573,7 @@ snapshots:
|
||||
string.prototype.trimend: 1.0.8
|
||||
tsconfig-paths: 3.15.0
|
||||
optionalDependencies:
|
||||
'@typescript-eslint/parser': 8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)
|
||||
'@typescript-eslint/parser': 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)
|
||||
transitivePeerDependencies:
|
||||
- eslint-import-resolver-typescript
|
||||
- eslint-import-resolver-webpack
|
||||
@@ -14339,12 +14340,12 @@ snapshots:
|
||||
sax: 1.3.0
|
||||
optional: true
|
||||
|
||||
neostandard@0.12.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2):
|
||||
neostandard@0.12.1(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2):
|
||||
dependencies:
|
||||
'@humanwhocodes/gitignore-to-minimatch': 1.0.2
|
||||
'@stylistic/eslint-plugin': 2.11.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)
|
||||
eslint: 9.22.0(jiti@2.4.2)
|
||||
eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import-x@4.5.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.22.0(jiti@2.4.2))
|
||||
eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import-x@4.5.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.22.0(jiti@2.4.2))
|
||||
eslint-plugin-import-x: 4.5.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)
|
||||
eslint-plugin-n: 17.16.2(eslint@9.22.0(jiti@2.4.2))
|
||||
eslint-plugin-promise: 7.2.1(eslint@9.22.0(jiti@2.4.2))
|
||||
@@ -14352,7 +14353,7 @@ snapshots:
|
||||
find-up: 5.0.0
|
||||
globals: 15.15.0
|
||||
peowly: 1.3.2
|
||||
typescript-eslint: 8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)
|
||||
typescript-eslint: 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)
|
||||
transitivePeerDependencies:
|
||||
- eslint-plugin-import
|
||||
- supports-color
|
||||
@@ -14896,7 +14897,7 @@ snapshots:
|
||||
|
||||
react-is@19.0.0: {}
|
||||
|
||||
react-markdown@10.0.1(@types/react@19.0.10)(react@19.0.0):
|
||||
react-markdown@10.1.0(@types/react@19.0.10)(react@19.0.0):
|
||||
dependencies:
|
||||
'@types/hast': 3.0.4
|
||||
'@types/mdast': 4.0.3
|
||||
@@ -15979,11 +15980,11 @@ snapshots:
|
||||
dependencies:
|
||||
is-typedarray: 1.0.0
|
||||
|
||||
typescript-eslint@8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2):
|
||||
typescript-eslint@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2):
|
||||
dependencies:
|
||||
'@typescript-eslint/eslint-plugin': 8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)
|
||||
'@typescript-eslint/parser': 8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)
|
||||
'@typescript-eslint/utils': 8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)
|
||||
'@typescript-eslint/eslint-plugin': 8.26.1(@typescript-eslint/parser@8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)
|
||||
'@typescript-eslint/parser': 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)
|
||||
'@typescript-eslint/utils': 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)
|
||||
eslint: 9.22.0(jiti@2.4.2)
|
||||
typescript: 5.8.2
|
||||
transitivePeerDependencies:
|
||||
|
||||
@@ -58,8 +58,8 @@ case "$FIRMWARE" in
|
||||
qcom,ap-dk01.1-c1)
|
||||
caldata_extract "ART" 0x1000 0x2f20
|
||||
;;
|
||||
alibaba,ap4220|\
|
||||
alibaba,ap4220-48m)
|
||||
alibaba,ap4220-48m|\
|
||||
alibaba,ap4220-128m)
|
||||
caldata_extract "ART" 0x1000 0x2f20
|
||||
ath10k_patch_mac $(macaddr_add "$(mtd_get_mac_text product_info 0x40)" 2)
|
||||
;;
|
||||
@@ -148,8 +148,8 @@ case "$FIRMWARE" in
|
||||
qcom,ap-dk01.1-c1)
|
||||
caldata_extract "ART" 0x5000 0x2f20
|
||||
;;
|
||||
alibaba,ap4220|\
|
||||
alibaba,ap4220-48m)
|
||||
alibaba,ap4220-48m|\
|
||||
alibaba,ap4220-128m)
|
||||
caldata_extract "ART" 0x5000 0x2f20
|
||||
ath10k_patch_mac $(macaddr_add "$(mtd_get_mac_text product_info 0x40)" 3)
|
||||
;;
|
||||
|
||||
@@ -7,6 +7,6 @@
|
||||
compatible = "alibaba,ap4220-128m";
|
||||
};
|
||||
|
||||
&nand_rootfs {
|
||||
&ubi_rootfs {
|
||||
reg = <0x0 0x08000000>;
|
||||
};
|
||||
|
||||
@@ -7,6 +7,6 @@
|
||||
compatible = "alibaba,ap4220-48m";
|
||||
};
|
||||
|
||||
&nand_rootfs {
|
||||
&ubi_rootfs {
|
||||
reg = <0x0 0x03000000>;
|
||||
};
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
};
|
||||
|
||||
chosen {
|
||||
bootargs-append = " root=/dev/ubiblock0_1";
|
||||
bootargs-append = " ubi.mtd=rootfs root=/dev/ubiblock0_1";
|
||||
};
|
||||
|
||||
soc {
|
||||
@@ -164,7 +164,6 @@
|
||||
partition@e0000 {
|
||||
label = "APPSBLENV";
|
||||
reg = <0xe0000 0x10000>;
|
||||
read-only;
|
||||
};
|
||||
|
||||
partition@f0000 {
|
||||
@@ -177,17 +176,6 @@
|
||||
label = "ART";
|
||||
reg = <0x170000 0x10000>;
|
||||
read-only;
|
||||
compatible = "nvmem-cells";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
precal_art_1000: precal@1000 {
|
||||
reg = <0x1000 0x2f20>;
|
||||
};
|
||||
|
||||
precal_art_5000: precal@5000 {
|
||||
reg = <0x5000 0x2f20>;
|
||||
};
|
||||
};
|
||||
|
||||
partition@180000 {
|
||||
@@ -199,7 +187,6 @@
|
||||
partition@190000 {
|
||||
label = "mtdoops";
|
||||
reg = <0x00190000 0x00020000>;
|
||||
read-only;
|
||||
};
|
||||
|
||||
partition@1b0000 {
|
||||
@@ -232,7 +219,7 @@
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
|
||||
nand_rootfs: partition@0 {
|
||||
ubi_rootfs: partition@0 {
|
||||
label = "rootfs";
|
||||
/* reg defined in 48M/128M variant dts. */
|
||||
};
|
||||
@@ -330,20 +317,14 @@
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&vqmmc {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&wifi0 {
|
||||
status = "okay";
|
||||
nvmem-cell-names = "pre-calibration";
|
||||
nvmem-cells = <&precal_art_1000>;
|
||||
|
||||
qcom,ath10k-calibration-variant = "Alibaba-AP4220";
|
||||
};
|
||||
|
||||
&wifi1 {
|
||||
status = "okay";
|
||||
nvmem-cell-names = "pre-calibration";
|
||||
nvmem-cells = <&precal_art_5000>;
|
||||
|
||||
qcom,ath10k-calibration-variant = "Alibaba-AP4220";
|
||||
};
|
||||
|
||||
@@ -37,7 +37,7 @@ func (f *Fallback) DialContext(ctx context.Context, metadata *C.Metadata, opts .
|
||||
if err == nil {
|
||||
c.AppendToChains(f)
|
||||
} else {
|
||||
f.onDialFailed(proxy.Type(), err)
|
||||
f.onDialFailed(proxy.Type(), err, f.healthCheck)
|
||||
}
|
||||
|
||||
if N.NeedHandshake(c) {
|
||||
@@ -45,7 +45,7 @@ func (f *Fallback) DialContext(ctx context.Context, metadata *C.Metadata, opts .
|
||||
if err == nil {
|
||||
f.onDialSuccess()
|
||||
} else {
|
||||
f.onDialFailed(proxy.Type(), err)
|
||||
f.onDialFailed(proxy.Type(), err, f.healthCheck)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package outboundgroup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -17,22 +18,26 @@ import (
|
||||
"github.com/metacubex/mihomo/tunnel"
|
||||
|
||||
"github.com/dlclark/regexp2"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
type GroupBase struct {
|
||||
*outbound.Base
|
||||
filterRegs []*regexp2.Regexp
|
||||
excludeFilterReg *regexp2.Regexp
|
||||
excludeTypeArray []string
|
||||
providers []provider.ProxyProvider
|
||||
failedTestMux sync.Mutex
|
||||
failedTimes int
|
||||
failedTime time.Time
|
||||
failedTesting atomic.Bool
|
||||
proxies [][]C.Proxy
|
||||
versions []atomic.Uint32
|
||||
TestTimeout int
|
||||
maxFailedTimes int
|
||||
filterRegs []*regexp2.Regexp
|
||||
excludeFilterRegs []*regexp2.Regexp
|
||||
excludeTypeArray []string
|
||||
providers []provider.ProxyProvider
|
||||
failedTestMux sync.Mutex
|
||||
failedTimes int
|
||||
failedTime time.Time
|
||||
failedTesting atomic.Bool
|
||||
TestTimeout int
|
||||
maxFailedTimes int
|
||||
|
||||
// for GetProxies
|
||||
getProxiesMutex sync.Mutex
|
||||
providerVersions []uint32
|
||||
providerProxies []C.Proxy
|
||||
}
|
||||
|
||||
type GroupBaseOption struct {
|
||||
@@ -53,15 +58,19 @@ func NewGroupBase(opt GroupBaseOption) *GroupBase {
|
||||
log.Warnln("The group [%s] with interface-name configuration is deprecated, please set it directly on the proxy instead", opt.Name)
|
||||
}
|
||||
|
||||
var excludeFilterReg *regexp2.Regexp
|
||||
if opt.excludeFilter != "" {
|
||||
excludeFilterReg = regexp2.MustCompile(opt.excludeFilter, regexp2.None)
|
||||
}
|
||||
var excludeTypeArray []string
|
||||
if opt.excludeType != "" {
|
||||
excludeTypeArray = strings.Split(opt.excludeType, "|")
|
||||
}
|
||||
|
||||
var excludeFilterRegs []*regexp2.Regexp
|
||||
if opt.excludeFilter != "" {
|
||||
for _, excludeFilter := range strings.Split(opt.excludeFilter, "`") {
|
||||
excludeFilterReg := regexp2.MustCompile(excludeFilter, regexp2.None)
|
||||
excludeFilterRegs = append(excludeFilterRegs, excludeFilterReg)
|
||||
}
|
||||
}
|
||||
|
||||
var filterRegs []*regexp2.Regexp
|
||||
if opt.filter != "" {
|
||||
for _, filter := range strings.Split(opt.filter, "`") {
|
||||
@@ -71,14 +80,14 @@ func NewGroupBase(opt GroupBaseOption) *GroupBase {
|
||||
}
|
||||
|
||||
gb := &GroupBase{
|
||||
Base: outbound.NewBase(opt.BaseOption),
|
||||
filterRegs: filterRegs,
|
||||
excludeFilterReg: excludeFilterReg,
|
||||
excludeTypeArray: excludeTypeArray,
|
||||
providers: opt.providers,
|
||||
failedTesting: atomic.NewBool(false),
|
||||
TestTimeout: opt.TestTimeout,
|
||||
maxFailedTimes: opt.maxFailedTimes,
|
||||
Base: outbound.NewBase(opt.BaseOption),
|
||||
filterRegs: filterRegs,
|
||||
excludeFilterRegs: excludeFilterRegs,
|
||||
excludeTypeArray: excludeTypeArray,
|
||||
providers: opt.providers,
|
||||
failedTesting: atomic.NewBool(false),
|
||||
TestTimeout: opt.TestTimeout,
|
||||
maxFailedTimes: opt.maxFailedTimes,
|
||||
}
|
||||
|
||||
if gb.TestTimeout == 0 {
|
||||
@@ -88,9 +97,6 @@ func NewGroupBase(opt GroupBaseOption) *GroupBase {
|
||||
gb.maxFailedTimes = 5
|
||||
}
|
||||
|
||||
gb.proxies = make([][]C.Proxy, len(opt.providers))
|
||||
gb.versions = make([]atomic.Uint32, len(opt.providers))
|
||||
|
||||
return gb
|
||||
}
|
||||
|
||||
@@ -101,56 +107,55 @@ func (gb *GroupBase) Touch() {
|
||||
}
|
||||
|
||||
func (gb *GroupBase) GetProxies(touch bool) []C.Proxy {
|
||||
providerVersions := make([]uint32, len(gb.providers))
|
||||
for i, pd := range gb.providers {
|
||||
if touch { // touch first
|
||||
pd.Touch()
|
||||
}
|
||||
providerVersions[i] = pd.Version()
|
||||
}
|
||||
|
||||
// thread safe
|
||||
gb.getProxiesMutex.Lock()
|
||||
defer gb.getProxiesMutex.Unlock()
|
||||
|
||||
// return the cached proxies if version not changed
|
||||
if slices.Equal(providerVersions, gb.providerVersions) {
|
||||
return gb.providerProxies
|
||||
}
|
||||
|
||||
var proxies []C.Proxy
|
||||
if len(gb.filterRegs) == 0 {
|
||||
for _, pd := range gb.providers {
|
||||
if touch {
|
||||
pd.Touch()
|
||||
}
|
||||
proxies = append(proxies, pd.Proxies()...)
|
||||
}
|
||||
} else {
|
||||
for i, pd := range gb.providers {
|
||||
if touch {
|
||||
pd.Touch()
|
||||
}
|
||||
|
||||
if pd.VehicleType() == types.Compatible {
|
||||
gb.versions[i].Store(pd.Version())
|
||||
gb.proxies[i] = pd.Proxies()
|
||||
for _, pd := range gb.providers {
|
||||
if pd.VehicleType() == types.Compatible { // compatible provider unneeded filter
|
||||
proxies = append(proxies, pd.Proxies()...)
|
||||
continue
|
||||
}
|
||||
|
||||
version := gb.versions[i].Load()
|
||||
if version != pd.Version() && gb.versions[i].CompareAndSwap(version, pd.Version()) {
|
||||
var (
|
||||
proxies []C.Proxy
|
||||
newProxies []C.Proxy
|
||||
)
|
||||
|
||||
proxies = pd.Proxies()
|
||||
proxiesSet := map[string]struct{}{}
|
||||
for _, filterReg := range gb.filterRegs {
|
||||
for _, p := range proxies {
|
||||
name := p.Name()
|
||||
if mat, _ := filterReg.MatchString(name); mat {
|
||||
if _, ok := proxiesSet[name]; !ok {
|
||||
proxiesSet[name] = struct{}{}
|
||||
newProxies = append(newProxies, p)
|
||||
}
|
||||
var newProxies []C.Proxy
|
||||
proxiesSet := map[string]struct{}{}
|
||||
for _, filterReg := range gb.filterRegs {
|
||||
for _, p := range pd.Proxies() {
|
||||
name := p.Name()
|
||||
if mat, _ := filterReg.MatchString(name); mat {
|
||||
if _, ok := proxiesSet[name]; !ok {
|
||||
proxiesSet[name] = struct{}{}
|
||||
newProxies = append(newProxies, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gb.proxies[i] = newProxies
|
||||
}
|
||||
}
|
||||
|
||||
for _, p := range gb.proxies {
|
||||
proxies = append(proxies, p...)
|
||||
proxies = append(proxies, newProxies...)
|
||||
}
|
||||
}
|
||||
|
||||
// Multiple filers means that proxies are sorted in the order in which the filers appear.
|
||||
// Although the filter has been performed once in the previous process,
|
||||
// when there are multiple providers, the array needs to be reordered as a whole.
|
||||
if len(gb.providers) > 1 && len(gb.filterRegs) > 1 {
|
||||
var newProxies []C.Proxy
|
||||
proxiesSet := map[string]struct{}{}
|
||||
@@ -174,32 +179,31 @@ func (gb *GroupBase) GetProxies(touch bool) []C.Proxy {
|
||||
}
|
||||
proxies = newProxies
|
||||
}
|
||||
if gb.excludeTypeArray != nil {
|
||||
var newProxies []C.Proxy
|
||||
for _, p := range proxies {
|
||||
mType := p.Type().String()
|
||||
flag := false
|
||||
for i := range gb.excludeTypeArray {
|
||||
if strings.EqualFold(mType, gb.excludeTypeArray[i]) {
|
||||
flag = true
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
if flag {
|
||||
continue
|
||||
if len(gb.excludeFilterRegs) > 0 {
|
||||
var newProxies []C.Proxy
|
||||
LOOP1:
|
||||
for _, p := range proxies {
|
||||
name := p.Name()
|
||||
for _, excludeFilterReg := range gb.excludeFilterRegs {
|
||||
if mat, _ := excludeFilterReg.MatchString(name); mat {
|
||||
continue LOOP1
|
||||
}
|
||||
}
|
||||
newProxies = append(newProxies, p)
|
||||
}
|
||||
proxies = newProxies
|
||||
}
|
||||
|
||||
if gb.excludeFilterReg != nil {
|
||||
if gb.excludeTypeArray != nil {
|
||||
var newProxies []C.Proxy
|
||||
LOOP2:
|
||||
for _, p := range proxies {
|
||||
name := p.Name()
|
||||
if mat, _ := gb.excludeFilterReg.MatchString(name); mat {
|
||||
continue
|
||||
mType := p.Type().String()
|
||||
for _, excludeType := range gb.excludeTypeArray {
|
||||
if strings.EqualFold(mType, excludeType) {
|
||||
continue LOOP2
|
||||
}
|
||||
}
|
||||
newProxies = append(newProxies, p)
|
||||
}
|
||||
@@ -207,9 +211,13 @@ func (gb *GroupBase) GetProxies(touch bool) []C.Proxy {
|
||||
}
|
||||
|
||||
if len(proxies) == 0 {
|
||||
return append(proxies, tunnel.Proxies()["COMPATIBLE"])
|
||||
return []C.Proxy{tunnel.Proxies()["COMPATIBLE"]}
|
||||
}
|
||||
|
||||
// only cache when proxies not empty
|
||||
gb.providerVersions = providerVersions
|
||||
gb.providerProxies = proxies
|
||||
|
||||
return proxies
|
||||
}
|
||||
|
||||
@@ -241,17 +249,21 @@ func (gb *GroupBase) URLTest(ctx context.Context, url string, expectedStatus uti
|
||||
}
|
||||
}
|
||||
|
||||
func (gb *GroupBase) onDialFailed(adapterType C.AdapterType, err error) {
|
||||
func (gb *GroupBase) onDialFailed(adapterType C.AdapterType, err error, fn func()) {
|
||||
if adapterType == C.Direct || adapterType == C.Compatible || adapterType == C.Reject || adapterType == C.Pass || adapterType == C.RejectDrop {
|
||||
return
|
||||
}
|
||||
|
||||
if strings.Contains(err.Error(), "connection refused") {
|
||||
go gb.healthCheck()
|
||||
if errors.Is(err, C.ErrNotSupport) {
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
if strings.Contains(err.Error(), "connection refused") {
|
||||
fn()
|
||||
return
|
||||
}
|
||||
|
||||
gb.failedTestMux.Lock()
|
||||
defer gb.failedTestMux.Unlock()
|
||||
|
||||
@@ -268,7 +280,7 @@ func (gb *GroupBase) onDialFailed(adapterType C.AdapterType, err error) {
|
||||
log.Debugln("ProxyGroup: %s failed count: %d", gb.Name(), gb.failedTimes)
|
||||
if gb.failedTimes >= gb.maxFailedTimes {
|
||||
log.Warnln("because %s failed multiple times, active health check", gb.Name())
|
||||
gb.healthCheck()
|
||||
fn()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -95,7 +95,7 @@ func (lb *LoadBalance) DialContext(ctx context.Context, metadata *C.Metadata, op
|
||||
if err == nil {
|
||||
c.AppendToChains(lb)
|
||||
} else {
|
||||
lb.onDialFailed(proxy.Type(), err)
|
||||
lb.onDialFailed(proxy.Type(), err, lb.healthCheck)
|
||||
}
|
||||
|
||||
if N.NeedHandshake(c) {
|
||||
@@ -103,7 +103,7 @@ func (lb *LoadBalance) DialContext(ctx context.Context, metadata *C.Metadata, op
|
||||
if err == nil {
|
||||
lb.onDialSuccess()
|
||||
} else {
|
||||
lb.onDialFailed(proxy.Type(), err)
|
||||
lb.onDialFailed(proxy.Type(), err, lb.healthCheck)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/mihomo/adapter/outbound"
|
||||
@@ -54,13 +52,13 @@ func (u *URLTest) Set(name string) error {
|
||||
if p == nil {
|
||||
return errors.New("proxy not exist")
|
||||
}
|
||||
u.selected = name
|
||||
u.fast(false)
|
||||
u.ForceSet(name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *URLTest) ForceSet(name string) {
|
||||
u.selected = name
|
||||
u.fastSingle.Reset()
|
||||
}
|
||||
|
||||
// DialContext implements C.ProxyAdapter
|
||||
@@ -70,7 +68,7 @@ func (u *URLTest) DialContext(ctx context.Context, metadata *C.Metadata, opts ..
|
||||
if err == nil {
|
||||
c.AppendToChains(u)
|
||||
} else {
|
||||
u.onDialFailed(proxy.Type(), err)
|
||||
u.onDialFailed(proxy.Type(), err, u.healthCheck)
|
||||
}
|
||||
|
||||
if N.NeedHandshake(c) {
|
||||
@@ -78,7 +76,7 @@ func (u *URLTest) DialContext(ctx context.Context, metadata *C.Metadata, opts ..
|
||||
if err == nil {
|
||||
u.onDialSuccess()
|
||||
} else {
|
||||
u.onDialFailed(proxy.Type(), err)
|
||||
u.onDialFailed(proxy.Type(), err, u.healthCheck)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -88,9 +86,12 @@ func (u *URLTest) DialContext(ctx context.Context, metadata *C.Metadata, opts ..
|
||||
|
||||
// ListenPacketContext implements C.ProxyAdapter
|
||||
func (u *URLTest) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
|
||||
pc, err := u.fast(true).ListenPacketContext(ctx, metadata, u.Base.DialOptions(opts...)...)
|
||||
proxy := u.fast(true)
|
||||
pc, err := proxy.ListenPacketContext(ctx, metadata, u.Base.DialOptions(opts...)...)
|
||||
if err == nil {
|
||||
pc.AppendToChains(u)
|
||||
} else {
|
||||
u.onDialFailed(proxy.Type(), err, u.healthCheck)
|
||||
}
|
||||
|
||||
return pc, err
|
||||
@@ -101,22 +102,27 @@ func (u *URLTest) Unwrap(metadata *C.Metadata, touch bool) C.Proxy {
|
||||
return u.fast(touch)
|
||||
}
|
||||
|
||||
func (u *URLTest) fast(touch bool) C.Proxy {
|
||||
func (u *URLTest) healthCheck() {
|
||||
u.fastSingle.Reset()
|
||||
u.GroupBase.healthCheck()
|
||||
u.fastSingle.Reset()
|
||||
}
|
||||
|
||||
proxies := u.GetProxies(touch)
|
||||
if u.selected != "" {
|
||||
for _, proxy := range proxies {
|
||||
if !proxy.AliveForTestUrl(u.testUrl) {
|
||||
continue
|
||||
}
|
||||
if proxy.Name() == u.selected {
|
||||
u.fastNode = proxy
|
||||
return proxy
|
||||
func (u *URLTest) fast(touch bool) C.Proxy {
|
||||
elm, _, shared := u.fastSingle.Do(func() (C.Proxy, error) {
|
||||
proxies := u.GetProxies(touch)
|
||||
if u.selected != "" {
|
||||
for _, proxy := range proxies {
|
||||
if !proxy.AliveForTestUrl(u.testUrl) {
|
||||
continue
|
||||
}
|
||||
if proxy.Name() == u.selected {
|
||||
u.fastNode = proxy
|
||||
return proxy, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
elm, _, shared := u.fastSingle.Do(func() (C.Proxy, error) {
|
||||
fast := proxies[0]
|
||||
minDelay := fast.LastDelayForTestUrl(u.testUrl)
|
||||
fastNotExist := true
|
||||
@@ -182,31 +188,7 @@ func (u *URLTest) MarshalJSON() ([]byte, error) {
|
||||
}
|
||||
|
||||
func (u *URLTest) URLTest(ctx context.Context, url string, expectedStatus utils.IntRanges[uint16]) (map[string]uint16, error) {
|
||||
var wg sync.WaitGroup
|
||||
var lock sync.Mutex
|
||||
mp := map[string]uint16{}
|
||||
proxies := u.GetProxies(false)
|
||||
for _, proxy := range proxies {
|
||||
proxy := proxy
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
delay, err := proxy.URLTest(ctx, u.testUrl, expectedStatus)
|
||||
if err == nil {
|
||||
lock.Lock()
|
||||
mp[proxy.Name()] = delay
|
||||
lock.Unlock()
|
||||
}
|
||||
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
if len(mp) == 0 {
|
||||
return mp, fmt.Errorf("get delay: all proxies timeout")
|
||||
} else {
|
||||
return mp, nil
|
||||
}
|
||||
return u.GroupBase.URLTest(ctx, u.testUrl, expectedStatus)
|
||||
}
|
||||
|
||||
func parseURLTestOption(config map[string]any) []urlTestOption {
|
||||
|
||||
@@ -87,6 +87,7 @@ func (bp *baseProvider) RegisterHealthCheckTask(url string, expectedStatus utils
|
||||
|
||||
func (bp *baseProvider) setProxies(proxies []C.Proxy) {
|
||||
bp.proxies = proxies
|
||||
bp.version += 1
|
||||
bp.healthCheck.setProxy(proxies)
|
||||
if bp.healthCheck.auto() {
|
||||
go bp.healthCheck.check()
|
||||
@@ -173,7 +174,7 @@ func NewProxySetProvider(name string, interval time.Duration, parser resource.Pa
|
||||
},
|
||||
}
|
||||
|
||||
fetcher := resource.NewFetcher[[]C.Proxy](name, interval, vehicle, parser, proxiesOnUpdate(pd))
|
||||
fetcher := resource.NewFetcher[[]C.Proxy](name, interval, vehicle, parser, pd.setProxies)
|
||||
pd.Fetcher = fetcher
|
||||
if httpVehicle, ok := vehicle.(*resource.HTTPVehicle); ok {
|
||||
httpVehicle.SetInRead(func(resp *http.Response) {
|
||||
|
||||
@@ -153,7 +153,6 @@ type ProxyAdapter interface {
|
||||
|
||||
type Group interface {
|
||||
URLTest(ctx context.Context, url string, expectedStatus utils.IntRanges[uint16]) (mp map[string]uint16, err error)
|
||||
GetProxies(touch bool) []Proxy
|
||||
Touch()
|
||||
}
|
||||
|
||||
|
||||
@@ -495,8 +495,6 @@ if DEFAULT_TAG == "none_noip" then table.insert(config_lines, "noip-as-chnip") e
|
||||
if DEFAULT_TAG == nil or DEFAULT_TAG == "smart" or DEFAULT_TAG == "none_noip" then DEFAULT_TAG = "none" end
|
||||
|
||||
table.insert(config_lines, "default-tag " .. DEFAULT_TAG)
|
||||
table.insert(config_lines, "cache 4096")
|
||||
table.insert(config_lines, "cache-stale 3600")
|
||||
|
||||
if DEFAULT_TAG == "none" then
|
||||
table.insert(config_lines, "verdict-cache 5000")
|
||||
|
||||
Vendored
+1
-1
@@ -155,7 +155,7 @@ jobs:
|
||||
uses: goreleaser/goreleaser-action@v6
|
||||
with:
|
||||
distribution: goreleaser-pro
|
||||
version: '~> v2'
|
||||
version: nightly
|
||||
install-only: true
|
||||
- name: Extract signing key
|
||||
run: |-
|
||||
|
||||
+8
-1
@@ -216,8 +216,15 @@ func New(options Options) (*Box, error) {
|
||||
} else {
|
||||
tag = F.ToString(i)
|
||||
}
|
||||
endpointCtx := ctx
|
||||
if tag != "" {
|
||||
// TODO: remove this
|
||||
endpointCtx = adapter.WithContext(endpointCtx, &adapter.InboundContext{
|
||||
Outbound: tag,
|
||||
})
|
||||
}
|
||||
err = endpointManager.Create(
|
||||
ctx,
|
||||
endpointCtx,
|
||||
router,
|
||||
logFactory.NewLogger(F.ToString("endpoint/", endpointOptions.Type, "[", tag, "]")),
|
||||
tag,
|
||||
|
||||
@@ -10,6 +10,8 @@ icon: material/alert-decagram
|
||||
|
||||
* Fixes and improvements
|
||||
|
||||
_We are temporarily unable to update sing-box apps on the App Store because the reviewer mistakenly found that we violated the rules (TestFlight users are not affected)._
|
||||
|
||||
#### 1.12.0-alpha.13
|
||||
|
||||
* Move `predefined` DNS server to DNS rule action **1**
|
||||
|
||||
@@ -7,6 +7,10 @@ icon: material/apple
|
||||
SFI/SFM/SFT allows users to manage and run local or remote sing-box configuration files, and provides
|
||||
platform-specific function implementation, such as TUN transparent proxy implementation.
|
||||
|
||||
!!! failure ""
|
||||
|
||||
We are temporarily unable to update sing-box apps on the App Store because the reviewer mistakenly found that we violated the rules (TestFlight users are not affected).
|
||||
|
||||
## :material-graph: Requirements
|
||||
|
||||
* iOS 15.0+ / macOS 13.0+ / Apple tvOS 17.0+
|
||||
|
||||
@@ -56,7 +56,12 @@ func (m *platformDefaultInterfaceMonitor) UnregisterCallback(element *list.Eleme
|
||||
|
||||
func (m *platformDefaultInterfaceMonitor) UpdateDefaultInterface(interfaceName string, interfaceIndex32 int32, isExpensive bool, isConstrained bool) {
|
||||
if sFixAndroidStack {
|
||||
go m.updateDefaultInterface(interfaceName, interfaceIndex32, isExpensive, isConstrained)
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
m.updateDefaultInterface(interfaceName, interfaceIndex32, isExpensive, isConstrained)
|
||||
close(done)
|
||||
}()
|
||||
<-done
|
||||
} else {
|
||||
m.updateDefaultInterface(interfaceName, interfaceIndex32, isExpensive, isConstrained)
|
||||
}
|
||||
|
||||
@@ -165,16 +165,15 @@ func (m *ConnectionManager) NewPacketConnection(ctx context.Context, this N.Dial
|
||||
} else {
|
||||
originDestination = metadata.Destination
|
||||
}
|
||||
if metadata.Destination != M.SocksaddrFrom(destinationAddress, metadata.Destination.Port) {
|
||||
if natConn, loaded := common.Cast[bufio.NATPacketConn](conn); loaded {
|
||||
natConn.UpdateDestination(destinationAddress)
|
||||
} else if metadata.Destination != M.SocksaddrFrom(destinationAddress, metadata.Destination.Port) {
|
||||
if metadata.UDPDisableDomainUnmapping {
|
||||
remotePacketConn = bufio.NewUnidirectionalNATPacketConn(bufio.NewPacketConn(remotePacketConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), originDestination)
|
||||
} else {
|
||||
remotePacketConn = bufio.NewNATPacketConn(bufio.NewPacketConn(remotePacketConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), originDestination)
|
||||
}
|
||||
}
|
||||
if natConn, loaded := common.Cast[bufio.NATPacketConn](conn); loaded {
|
||||
natConn.UpdateDestination(destinationAddress)
|
||||
}
|
||||
} else if metadata.RouteOriginalDestination.IsValid() && metadata.RouteOriginalDestination != metadata.Destination {
|
||||
remotePacketConn = bufio.NewDestinationNATPacketConn(bufio.NewPacketConn(remotePacketConn), metadata.Destination, metadata.RouteOriginalDestination)
|
||||
}
|
||||
|
||||
@@ -405,15 +405,69 @@ const CBIHandleImport = baseclass.extend(/** @lends hm.HandleImport.prototype */
|
||||
return calcStringMD5(String.format('%s:%s', field, name));
|
||||
},
|
||||
|
||||
handleFn(textarea, save) {
|
||||
if (save) {
|
||||
return uci.save()
|
||||
.then(L.bind(this.map.load, this.map))
|
||||
.then(L.bind(this.map.reset, this.map))
|
||||
.then(L.ui.hideModal)
|
||||
.catch(() => {});
|
||||
} else
|
||||
return ui.hideModal();
|
||||
handleFn(textarea) {
|
||||
const modaltitle = this.section.hm_modaltitle[0];
|
||||
const field = this.section.hm_field;
|
||||
|
||||
let content = textarea.getValue().trim();
|
||||
let command = `.["${field}"]`;
|
||||
if (['proxy-providers', 'rule-providers'].includes(field))
|
||||
content = content.replace(/(\s*payload:)/g, "$1 |-") /* payload to text */
|
||||
|
||||
return yaml2json(content, command).then((res) => {
|
||||
let imported_count = 0;
|
||||
//let type_file_count = 0;
|
||||
|
||||
//console.info(JSON.stringify(res, null, 2));
|
||||
if (!isEmpty(res) && typeof res === 'object') {
|
||||
if (Array.isArray(res))
|
||||
res.forEach((cfg) => {
|
||||
let config = this.parseYaml(field, null, cfg);
|
||||
//console.info(JSON.stringify(config, null, 2));
|
||||
if (config) {
|
||||
this.write(config);
|
||||
imported_count++;
|
||||
}
|
||||
})
|
||||
else
|
||||
for (let name in res) {
|
||||
let config = this.parseYaml(field, name, res[name]);
|
||||
//console.info(JSON.stringify(config, null, 2));
|
||||
if (config) {
|
||||
this.write(config);
|
||||
imported_count++;
|
||||
//if (config.type === 'file')
|
||||
// type_file_count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (imported_count === 0)
|
||||
ui.addNotification(null, E('p', _('No valid %s found.').format(modaltitle)));
|
||||
else
|
||||
ui.addNotification(null, E('p', [
|
||||
_('Successfully imported %s %s of total %s.')
|
||||
.format(imported_count, modaltitle, Object.keys(res).length),
|
||||
E('br'),
|
||||
//type_file_count ? _("%s rule-set of type '%s' need to be filled in manually.")
|
||||
// .format(type_file_count, 'file') : ''
|
||||
]));
|
||||
}
|
||||
|
||||
if (imported_count)
|
||||
return this.save();
|
||||
else
|
||||
return ui.hideModal();
|
||||
});
|
||||
},
|
||||
|
||||
parseYaml(field, name, cfg) {
|
||||
if (isEmpty(cfg))
|
||||
return null;
|
||||
|
||||
cfg.hm_id = this.calcID(field, name ?? cfg.name);
|
||||
cfg.hm_label = '%s %s'.format(name ?? cfg.name, _('(Imported)'));
|
||||
|
||||
return cfg;
|
||||
},
|
||||
|
||||
render() {
|
||||
@@ -438,6 +492,24 @@ const CBIHandleImport = baseclass.extend(/** @lends hm.HandleImport.prototype */
|
||||
}, [ _('Import') ])
|
||||
])
|
||||
]);
|
||||
},
|
||||
|
||||
save() {
|
||||
return uci.save()
|
||||
.then(L.bind(this.map.load, this.map))
|
||||
.then(L.bind(this.map.reset, this.map))
|
||||
.then(L.ui.hideModal)
|
||||
.catch(() => {});
|
||||
},
|
||||
|
||||
write(config) {
|
||||
const uciconfig = this.uciconfig || this.section.uciconfig || this.map.config;
|
||||
const section_type = this.section.sectiontype;
|
||||
|
||||
let sid = uci.add(uciconfig, section_type, config.id);
|
||||
delete config.id;
|
||||
for (let k in config)
|
||||
uci.set(uciconfig, sid, k, config[k] ?? '');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -232,8 +232,8 @@ return view.extend({
|
||||
})
|
||||
so.rmempty = false;
|
||||
if (!features.has_stunclient) {
|
||||
so.description = _('To check NAT Behavior you need to install <a href="%s"><b>stuntman-client</b></a> first')
|
||||
.format('https://github.com/muink/openwrt-stuntman');
|
||||
so.description = _('To check NAT Behavior you need to install <a href="%s"><b>%s</b></a> first')
|
||||
.format(L.url('admin/system/package-manager') + '?query=stuntman-client', 'stuntman-client');
|
||||
so.readonly = true;
|
||||
} else {
|
||||
so.renderWidget = function(section_id, option_index, cfgvalue) {
|
||||
|
||||
@@ -8,31 +8,31 @@
|
||||
'require tools.widgets as widgets';
|
||||
|
||||
function parseProviderYaml(field, name, cfg) {
|
||||
if (hm.isEmpty(cfg))
|
||||
return null;
|
||||
|
||||
if (!cfg.type)
|
||||
return null;
|
||||
|
||||
// key mapping
|
||||
let config = hm.removeBlankAttrs({
|
||||
id: cfg.hm_id,
|
||||
label: cfg.hm_label,
|
||||
type: cfg.type,
|
||||
...(cfg.type === 'inline' ? {
|
||||
//dialer_proxy: cfg["dialer-proxy"],
|
||||
payload: cfg.payload, // string: array
|
||||
} : {
|
||||
id: cfg.path,
|
||||
url: cfg.url,
|
||||
size_limit: cfg["size-limit"],
|
||||
interval: cfg.interval,
|
||||
proxy: cfg.proxy,
|
||||
proxy: cfg.proxy ? hm.preset_outbound.full.map(([key, label]) => key).includes(cfg.proxy) ? cfg.proxy : this.calcID(hm.glossary["proxy_group"].field, cfg.proxy) : null,
|
||||
header: cfg.header ? JSON.stringify(cfg.header, null, 2) : null, // string: object
|
||||
/* Health fields */
|
||||
health_enable: hm.bool2str(hm.getValue(cfg, "health-check.enable")), // bool
|
||||
health_url: hm.getValue(cfg, "health-check.url"),
|
||||
health_interval: hm.getValue(cfg, "health-check.interval"),
|
||||
health_timeout: hm.getValue(cfg, "health-check.timeout"),
|
||||
health_lazy: hm.bool2str(hm.getValue(cfg, "health-check.lazy")), // bool
|
||||
health_expected_status: hm.getValue(cfg, "health-check.expected-status"),
|
||||
/* Override fields */
|
||||
override_prefix: hm.getValue(cfg, "override.additional-prefix"),
|
||||
override_suffix: hm.getValue(cfg, "override.additional-suffix"),
|
||||
override_replace: (hm.getValue(cfg, "override.proxy-name") || []).map((obj) => JSON.stringify(obj)), // array.string: array.object
|
||||
@@ -47,21 +47,13 @@ function parseProviderYaml(field, name, cfg) {
|
||||
override_interface_name: hm.getValue(cfg, "override.interface-name"),
|
||||
override_routing_mark: hm.getValue(cfg, "override.routing-mark"),
|
||||
override_ip_version: hm.getValue(cfg, "override.ip-version"),
|
||||
/* General fields */
|
||||
filter: [cfg.filter], // array: string
|
||||
exclude_filter: [cfg["exclude-filter"]], // array.string: string
|
||||
exclude_type: [cfg["exclude-type"]] // array.string: string
|
||||
})
|
||||
});
|
||||
|
||||
// value rocessing
|
||||
config = Object.assign(config, {
|
||||
id: this.calcID(field, name),
|
||||
label: '%s %s'.format(name, _('(Imported)')),
|
||||
...(config.proxy ? {
|
||||
proxy: hm.preset_outbound.full.map(([key, label]) => key).includes(config.proxy) ? config.proxy : this.calcID(hm.glossary["proxy_group"].field, config.proxy)
|
||||
} : {}),
|
||||
});
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
@@ -876,7 +868,6 @@ return view.extend({
|
||||
ss.hm_lowcase_only = false;
|
||||
/* Import mihomo config and Remove idle files start */
|
||||
ss.handleYamlImport = function() {
|
||||
const section_type = this.sectiontype;
|
||||
const field = this.hm_field;
|
||||
const o = new hm.HandleImport(this.map, this, _('Import mihomo config'),
|
||||
_('Please type <code>%s</code> fields of mihomo config.</br>')
|
||||
@@ -936,6 +927,11 @@ return view.extend({
|
||||
' port: 443\n' +
|
||||
' cipher: chacha20-ietf-poly1305\n' +
|
||||
' password: "password"\n' +
|
||||
' provider3:\n' +
|
||||
' type: http\n' +
|
||||
' url: "http://test.com"\n' +
|
||||
' path: ./proxy_providers/provider3.yaml\n' +
|
||||
' proxy: proxy\n' +
|
||||
' test:\n' +
|
||||
' type: file\n' +
|
||||
' path: /test.yaml\n' +
|
||||
@@ -944,45 +940,11 @@ return view.extend({
|
||||
' interval: 36000\n' +
|
||||
' url: https://cp.cloudflare.com/generate_204\n' +
|
||||
' ...'
|
||||
o.handleFn = L.bind(function(textarea, save) {
|
||||
const content = textarea.getValue().trim();
|
||||
const command = `.["${field}"]`;
|
||||
return hm.yaml2json(content.replace(/(\s*payload:)/g, "$1 |-") /* payload to text */, command).then((res) => {
|
||||
//alert(JSON.stringify(res, null, 2));
|
||||
let imported_count = 0;
|
||||
let type_file_count = 0;
|
||||
if (!hm.isEmpty(res)) {
|
||||
for (let name in res) {
|
||||
let config = parseProviderYaml.call(this, field, name, res[name]);
|
||||
//alert(JSON.stringify(config, null, 2));
|
||||
if (config) {
|
||||
let sid = uci.add(data[0], section_type, config.id);
|
||||
delete config.id;
|
||||
Object.keys(config).forEach((k) => {
|
||||
uci.set(data[0], sid, k, config[k] ?? '');
|
||||
});
|
||||
imported_count++;
|
||||
if (config.type === 'file')
|
||||
type_file_count++;
|
||||
}
|
||||
}
|
||||
o.parseYaml = function(field, name, cfg) {
|
||||
let config = hm.HandleImport.prototype.parseYaml.call(this, field, name, cfg);
|
||||
|
||||
if (imported_count === 0)
|
||||
ui.addNotification(null, E('p', _('No valid %s found.').format(_('Provider'))));
|
||||
else {
|
||||
ui.addNotification(null, E('p', [
|
||||
_('Successfully imported %s %s of total %s.')
|
||||
.format(imported_count, _('Provider'), Object.keys(res).length),
|
||||
E('br'),
|
||||
type_file_count ? _("%s Provider of type '%s' need to be filled in manually.")
|
||||
.format(type_file_count, 'file') : ''
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
return hm.HandleImport.prototype.handleFn.call(this, textarea, imported_count);
|
||||
});
|
||||
}, o);
|
||||
return config ? parseProviderYaml.call(this, field, name, config) : config;
|
||||
};
|
||||
|
||||
return o.render();
|
||||
}
|
||||
|
||||
@@ -7,33 +7,24 @@
|
||||
'require fchomo as hm';
|
||||
|
||||
function parseRulesetYaml(field, name, cfg) {
|
||||
if (hm.isEmpty(cfg))
|
||||
return null;
|
||||
|
||||
if (!cfg.type)
|
||||
return null;
|
||||
|
||||
// key mapping
|
||||
const map_of_rule_provider = {
|
||||
//type: 'type',
|
||||
//behavior: 'behavior',
|
||||
//format: 'format',
|
||||
//url: 'url',
|
||||
"size-limit": 'size_limit',
|
||||
//interval: 'interval',
|
||||
//proxy: 'proxy',
|
||||
path: 'id',
|
||||
//payload: 'payload', // array: string
|
||||
};
|
||||
let config = Object.fromEntries(Object.entries(cfg).map(([key, value]) => [map_of_rule_provider[key] ?? key, value]));
|
||||
|
||||
// value rocessing
|
||||
config = Object.assign(config, {
|
||||
id: this.calcID(field, name),
|
||||
label: '%s %s'.format(name, _('(Imported)')),
|
||||
...(config.proxy ? {
|
||||
proxy: hm.preset_outbound.full.map(([key, label]) => key).includes(config.proxy) ? config.proxy : this.calcID(hm.glossary["proxy_group"].field, config.proxy)
|
||||
} : {}),
|
||||
let config = hm.removeBlankAttrs({
|
||||
id: cfg.hm_id,
|
||||
label: cfg.hm_label,
|
||||
type: cfg.type,
|
||||
format: cfg.format,
|
||||
behavior: cfg.behavior,
|
||||
...(cfg.type === 'inline' ? {
|
||||
payload: cfg.payload, // string: array
|
||||
} : {
|
||||
url: cfg.url,
|
||||
size_limit: cfg["size-limit"],
|
||||
interval: cfg.interval,
|
||||
proxy: cfg.proxy ? hm.preset_outbound.full.map(([key, label]) => key).includes(cfg.proxy) ? cfg.proxy : this.calcID(hm.glossary["proxy_group"].field, cfg.proxy) : null,
|
||||
})
|
||||
});
|
||||
|
||||
return config;
|
||||
@@ -147,7 +138,6 @@ return view.extend({
|
||||
s.hm_lowcase_only = false;
|
||||
/* Import mihomo config and Import rule-set links and Remove idle files start */
|
||||
s.handleYamlImport = function() {
|
||||
const section_type = this.sectiontype;
|
||||
const field = this.hm_field;
|
||||
const o = new hm.HandleImport(this.map, this, _('Import mihomo config'),
|
||||
_('Please type <code>%s</code> fields of mihomo config.</br>')
|
||||
@@ -166,6 +156,12 @@ return view.extend({
|
||||
' type: file\n' +
|
||||
' path: ./rule2.yaml\n' +
|
||||
' behavior: classical\n' +
|
||||
' google2:\n' +
|
||||
' type: http\n' +
|
||||
' path: ./rule3.yaml\n' +
|
||||
' url: "https://raw.githubusercontent.com/../Google.yaml"\n' +
|
||||
' proxy: proxy\n' +
|
||||
' behavior: classical\n' +
|
||||
' rule4:\n' +
|
||||
' type: inline\n' +
|
||||
' behavior: domain\n' +
|
||||
@@ -174,45 +170,11 @@ return view.extend({
|
||||
" - '*.*.microsoft.com'\n" +
|
||||
" - 'books.itunes.apple.com'\n" +
|
||||
' ...'
|
||||
o.handleFn = L.bind(function(textarea, save) {
|
||||
const content = textarea.getValue().trim();
|
||||
const command = `.["${field}"]`;
|
||||
return hm.yaml2json(content.replace(/(\s*payload:)/g, "$1 |-") /* payload to text */, command).then((res) => {
|
||||
//alert(JSON.stringify(res, null, 2));
|
||||
let imported_count = 0;
|
||||
let type_file_count = 0;
|
||||
if (!hm.isEmpty(res)) {
|
||||
for (let name in res) {
|
||||
let config = parseRulesetYaml.call(this, field, name, res[name]);
|
||||
//alert(JSON.stringify(config, null, 2));
|
||||
if (config) {
|
||||
let sid = uci.add(data[0], section_type, config.id);
|
||||
delete config.id;
|
||||
Object.keys(config).forEach((k) => {
|
||||
uci.set(data[0], sid, k, config[k] ?? '');
|
||||
});
|
||||
imported_count++;
|
||||
if (config.type === 'file')
|
||||
type_file_count++;
|
||||
}
|
||||
}
|
||||
o.parseYaml = function(field, name, cfg) {
|
||||
let config = hm.HandleImport.prototype.parseYaml.call(this, field, name, cfg);
|
||||
|
||||
if (imported_count === 0)
|
||||
ui.addNotification(null, E('p', _('No valid %s found.').format(_('rule-set'))));
|
||||
else {
|
||||
ui.addNotification(null, E('p', [
|
||||
_('Successfully imported %s %s of total %s.')
|
||||
.format(imported_count, _('rule-set'), Object.keys(res).length),
|
||||
E('br'),
|
||||
type_file_count ? _("%s rule-set of type '%s' need to be filled in manually.")
|
||||
.format(type_file_count, 'file') : ''
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
return hm.HandleImport.prototype.handleFn.call(this, textarea, imported_count);
|
||||
});
|
||||
}, o);
|
||||
return config ? parseRulesetYaml.call(this, field, name, config) : config;
|
||||
};
|
||||
|
||||
return o.render();
|
||||
}
|
||||
@@ -226,9 +188,10 @@ return view.extend({
|
||||
o.placeholder = 'http(s)://github.com/ACL4SSR/ACL4SSR/raw/refs/heads/master/Clash/Providers/BanAD.yaml?fmt=yaml&behav=classical&rawq=good%3Djob#BanAD\n' +
|
||||
'file:///example.txt?fmt=text&behav=domain&fill=LmNuCg#CN%20TLD\n' +
|
||||
'inline://LSAnLmhrJwoK?behav=domain#HK%20TLD\n';
|
||||
o.handleFn = L.bind(function(textarea, save) {
|
||||
o.handleFn = L.bind(function(textarea) {
|
||||
let input_links = textarea.getValue().trim().split('\n');
|
||||
let imported_count = 0;
|
||||
|
||||
if (input_links && input_links[0]) {
|
||||
/* Remove duplicate lines */
|
||||
input_links = input_links.reduce((pre, cur) =>
|
||||
@@ -237,11 +200,7 @@ return view.extend({
|
||||
input_links.forEach((l) => {
|
||||
let config = parseRulesetLink(section_type, l);
|
||||
if (config) {
|
||||
let sid = uci.add(data[0], section_type, config.id);
|
||||
config.id = null;
|
||||
Object.keys(config).forEach((k) => {
|
||||
uci.set(data[0], sid, k, config[k] || '');
|
||||
});
|
||||
this.write(config);
|
||||
imported_count++;
|
||||
}
|
||||
});
|
||||
@@ -253,7 +212,10 @@ return view.extend({
|
||||
.format(imported_count, _('rule-set'), input_links.length)));
|
||||
}
|
||||
|
||||
return hm.HandleImport.prototype.handleFn.call(this, textarea, imported_count);
|
||||
if (imported_count)
|
||||
return this.save();
|
||||
else
|
||||
return ui.hideModal();
|
||||
}, o);
|
||||
|
||||
return o.render();
|
||||
|
||||
@@ -2313,8 +2313,7 @@ msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/fchomo/global.js:235
|
||||
msgid ""
|
||||
"To check NAT Behavior you need to install <a href=\"%s\"><b>stuntman-client</"
|
||||
"b></a> first"
|
||||
"To check NAT Behavior you need to install <a href=\"%s\"><b>%s</b></a> first"
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/fchomo/global.js:484
|
||||
|
||||
@@ -2344,10 +2344,9 @@ msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/fchomo/global.js:235
|
||||
msgid ""
|
||||
"To check NAT Behavior you need to install <a href=\"%s\"><b>stuntman-client</"
|
||||
"b></a> first"
|
||||
"To check NAT Behavior you need to install <a href=\"%s\"><b>%s</b></a> first"
|
||||
msgstr ""
|
||||
"检测 NAT 行为需要先安装 <a href=\"%s\"><b>stuntman-client</b></a> first"
|
||||
"检测 NAT 行为需要先安装 <a href=\"%s\"><b>%s</b></a>"
|
||||
|
||||
#: htdocs/luci-static/resources/view/fchomo/global.js:484
|
||||
msgid ""
|
||||
|
||||
@@ -2344,10 +2344,9 @@ msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/fchomo/global.js:235
|
||||
msgid ""
|
||||
"To check NAT Behavior you need to install <a href=\"%s\"><b>stuntman-client</"
|
||||
"b></a> first"
|
||||
"To check NAT Behavior you need to install <a href=\"%s\"><b>%s</b></a> first"
|
||||
msgstr ""
|
||||
"檢測 NAT 行為需要先安裝 <a href=\"%s\"><b>stuntman-client</b></a> first"
|
||||
"檢測 NAT 行為需要先安裝 <a href=\"%s\"><b>%s</b></a>"
|
||||
|
||||
#: htdocs/luci-static/resources/view/fchomo/global.js:484
|
||||
msgid ""
|
||||
|
||||
@@ -275,6 +275,25 @@ return baseclass.extend({
|
||||
return true;
|
||||
},
|
||||
|
||||
validatePortRange(section_id, value) {
|
||||
if (section_id && value) {
|
||||
value = value.match(/^(\d+)?\:(\d+)?$/);
|
||||
if (value && (value[1] || value[2])) {
|
||||
if (!value[1])
|
||||
value[1] = 0;
|
||||
else if (!value[2])
|
||||
value[2] = 65535;
|
||||
|
||||
if (value[1] < value[2] && value[2] <= 65535)
|
||||
return true;
|
||||
}
|
||||
|
||||
return _('Expecting: %s').format( _('valid port range (port1:port2)'));
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
validateUniqueValue(uciconfig, ucisection, ucioption, section_id, value) {
|
||||
if (section_id) {
|
||||
if (!value)
|
||||
|
||||
@@ -59,25 +59,6 @@ function renderStatus(isRunning, version) {
|
||||
return renderHTML;
|
||||
}
|
||||
|
||||
function validatePortRange(section_id, value) {
|
||||
if (section_id && value) {
|
||||
value = value.match(/^(\d+)?\:(\d+)?$/);
|
||||
if (value && (value[1] || value[2])) {
|
||||
if (!value[1])
|
||||
value[1] = 0;
|
||||
else if (!value[2])
|
||||
value[2] = 65535;
|
||||
|
||||
if (value[1] < value[2] && value[2] <= 65535)
|
||||
return true;
|
||||
}
|
||||
|
||||
return _('Expecting: %s').format( _('valid port range (port1:port2)'));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
let stubValidator = {
|
||||
factory: validation,
|
||||
apply(type, value, args) {
|
||||
@@ -213,7 +194,7 @@ return view.extend({
|
||||
if (!value)
|
||||
return _('Expecting: %s').format(_('non-empty value'));
|
||||
|
||||
let ipv6_support = this.map.lookupOption('ipv6_support', section_id)[0].formvalue(section_id);
|
||||
let ipv6_support = this.section.formvalue(section_id, 'ipv6_support');
|
||||
try {
|
||||
let url = new URL(value.replace(/^.*:\/\//, 'http://'));
|
||||
if (stubValidator.apply('hostname', url.hostname))
|
||||
@@ -324,7 +305,6 @@ return view.extend({
|
||||
|
||||
ss = o.subsection;
|
||||
so = ss.option(form.Flag, 'tun_gso', _('Generic segmentation offload'));
|
||||
so.default = so.disabled;
|
||||
so.depends('homeproxy.config.proxy_mode', 'redirect_tun');
|
||||
so.depends('homeproxy.config.proxy_mode', 'tun');
|
||||
so.rmempty = false;
|
||||
@@ -368,7 +348,6 @@ return view.extend({
|
||||
|
||||
so = ss.option(form.Flag, 'bypass_cn_traffic', _('Bypass CN traffic'),
|
||||
_('Bypass mainland China traffic via firewall rules by default.'));
|
||||
so.default = so.disabled;
|
||||
so.rmempty = false;
|
||||
|
||||
so = ss.option(form.ListValue, 'domain_strategy', _('Domain strategy'),
|
||||
@@ -388,7 +367,6 @@ return view.extend({
|
||||
|
||||
this.value('nil', _('Disable'));
|
||||
this.value('direct-out', _('Direct'));
|
||||
this.value('block-out', _('Block'));
|
||||
uci.sections(data[0], 'routing_node', (res) => {
|
||||
if (res.enabled === '1')
|
||||
this.value(res['.name'], res.label);
|
||||
@@ -462,7 +440,7 @@ return view.extend({
|
||||
}
|
||||
so.validate = function(section_id, value) {
|
||||
if (section_id && value) {
|
||||
let node = this.map.lookupOption('node', section_id)[0].formvalue(section_id);
|
||||
let node = this.section.formvalue(section_id, 'node');
|
||||
|
||||
let conflict = false;
|
||||
uci.sections(data[0], 'routing_node', (res) => {
|
||||
@@ -516,7 +494,7 @@ return view.extend({
|
||||
so.placeholder = '180';
|
||||
so.validate = function(section_id, value) {
|
||||
if (section_id && value) {
|
||||
let idle_timeout = this.map.lookupOption('urltest_idle_timeout', section_id)[0].formvalue(section_id) || '1800';
|
||||
let idle_timeout = this.section.formvalue(section_id, 'idle_timeout') || '1800';
|
||||
if (parseInt(value) > parseInt(idle_timeout))
|
||||
return _('Test interval must be less or equal than idle timeout.');
|
||||
}
|
||||
@@ -542,7 +520,6 @@ return view.extend({
|
||||
|
||||
so = ss.option(form.Flag, 'urltest_interrupt_exist_connections', _('Interrupt existing connections'),
|
||||
_('Interrupt existing connections when the selected outbound has changed.'));
|
||||
so.default = so.disabled;
|
||||
so.depends('node', 'urltest');
|
||||
so.modalonly = true;
|
||||
/* Routing nodes end */
|
||||
@@ -562,10 +539,9 @@ return view.extend({
|
||||
ss.renderSectionAdd = L.bind(hp.renderSectionAdd, this, ss);
|
||||
|
||||
ss.tab('field_other', _('Other fields'));
|
||||
ss.tab('field_host', _('Host fields'));
|
||||
ss.tab('field_host', _('Host/IP fields'));
|
||||
ss.tab('field_port', _('Port fields'));
|
||||
ss.tab('field_source_ip', _('SRC-IP fields'));
|
||||
ss.tab('field_source_port', _('SRC-Port fields'));
|
||||
ss.tab('fields_process', _('Process fields'));
|
||||
|
||||
so = ss.taboption('field_other', form.Value, 'label', _('Label'));
|
||||
so.load = L.bind(hp.loadDefaultLabel, this, data[0]);
|
||||
@@ -624,75 +600,6 @@ return view.extend({
|
||||
so.value('udp', _('UDP'));
|
||||
so.value('', _('Both'));
|
||||
|
||||
so = ss.taboption('field_host', form.DynamicList, 'domain', _('Domain name'),
|
||||
_('Match full domain.'));
|
||||
so.datatype = 'hostname';
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_host', form.DynamicList, 'domain_suffix', _('Domain suffix'),
|
||||
_('Match domain suffix.'));
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_host', form.DynamicList, 'domain_keyword', _('Domain keyword'),
|
||||
_('Match domain using keyword.'));
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_host', form.DynamicList, 'domain_regex', _('Domain regex'),
|
||||
_('Match domain using regular expression.'));
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_source_ip', form.DynamicList, 'source_ip_cidr', _('Source IP CIDR'),
|
||||
_('Match source IP CIDR.'));
|
||||
so.datatype = 'or(cidr, ipaddr)';
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_source_ip', form.Flag, 'source_ip_is_private', _('Private source IP'),
|
||||
_('Match private source IP.'));
|
||||
so.default = so.disabled;
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_host', form.DynamicList, 'ip_cidr', _('IP CIDR'),
|
||||
_('Match IP CIDR.'));
|
||||
so.datatype = 'or(cidr, ipaddr)';
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_host', form.Flag, 'ip_is_private', _('Private IP'),
|
||||
_('Match private IP.'));
|
||||
so.default = so.disabled;
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_source_port', form.DynamicList, 'source_port', _('Source port'),
|
||||
_('Match source port.'));
|
||||
so.datatype = 'port';
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_source_port', form.DynamicList, 'source_port_range', _('Source port range'),
|
||||
_('Match source port range. Format as START:/:END/START:END.'));
|
||||
so.validate = validatePortRange;
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_port', form.DynamicList, 'port', _('Port'),
|
||||
_('Match port.'));
|
||||
so.datatype = 'port';
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_port', form.DynamicList, 'port_range', _('Port range'),
|
||||
_('Match port range. Format as START:/:END/START:END.'));
|
||||
so.validate = validatePortRange;
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_other', form.DynamicList, 'process_name', _('Process name'),
|
||||
_('Match process name.'));
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_other', form.DynamicList, 'process_path', _('Process path'),
|
||||
_('Match process path.'));
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_other', form.DynamicList, 'process_path_regex', _('Process path (regex)'),
|
||||
_('Match process path using regular expression.'));
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_other', form.DynamicList, 'user', _('User'),
|
||||
_('Match user name.'));
|
||||
so.modalonly = true;
|
||||
@@ -712,14 +619,12 @@ return view.extend({
|
||||
}
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_other', form.Flag, 'rule_set_ip_cidr_match_source', _('Match source IP via rule set'),
|
||||
so = ss.taboption('field_other', form.Flag, 'rule_set_ip_cidr_match_source', _('Rule set IP CIDR as source IP'),
|
||||
_('Make IP CIDR in rule set used to match the source IP.'));
|
||||
so.default = so.disabled;
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_other', form.Flag, 'invert', _('Invert'),
|
||||
_('Invert match result.'));
|
||||
so.default = so.disabled;
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_other', form.ListValue, 'outbound', _('Outbound'),
|
||||
@@ -739,6 +644,81 @@ return view.extend({
|
||||
}
|
||||
so.rmempty = false;
|
||||
so.editable = true;
|
||||
|
||||
so = ss.taboption('field_other', form.Value, 'override_address', _('Override address'),
|
||||
_('Override the connection destination address.'));
|
||||
so.datatype = 'ipaddr';
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_other', form.Value, 'override_port', _('Override port'),
|
||||
_('Override the connection destination port.'));
|
||||
so.datatype = 'port';
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_host', form.DynamicList, 'domain', _('Domain name'),
|
||||
_('Match full domain.'));
|
||||
so.datatype = 'hostname';
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_host', form.DynamicList, 'domain_suffix', _('Domain suffix'),
|
||||
_('Match domain suffix.'));
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_host', form.DynamicList, 'domain_keyword', _('Domain keyword'),
|
||||
_('Match domain using keyword.'));
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_host', form.DynamicList, 'domain_regex', _('Domain regex'),
|
||||
_('Match domain using regular expression.'));
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_host', form.DynamicList, 'source_ip_cidr', _('Source IP CIDR'),
|
||||
_('Match source IP CIDR.'));
|
||||
so.datatype = 'or(cidr, ipaddr)';
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_host', form.Flag, 'source_ip_is_private', _('Match private source IP'));
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_host', form.DynamicList, 'ip_cidr', _('IP CIDR'),
|
||||
_('Match IP CIDR.'));
|
||||
so.datatype = 'or(cidr, ipaddr)';
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_host', form.Flag, 'ip_is_private', _('Match private IP'));
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_port', form.DynamicList, 'source_port', _('Source port'),
|
||||
_('Match source port.'));
|
||||
so.datatype = 'port';
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_port', form.DynamicList, 'source_port_range', _('Source port range'),
|
||||
_('Match source port range. Format as START:/:END/START:END.'));
|
||||
so.validate = hp.validatePortRange;
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_port', form.DynamicList, 'port', _('Port'),
|
||||
_('Match port.'));
|
||||
so.datatype = 'port';
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_port', form.DynamicList, 'port_range', _('Port range'),
|
||||
_('Match port range. Format as START:/:END/START:END.'));
|
||||
so.validate = hp.validatePortRange;
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('fields_process', form.DynamicList, 'process_name', _('Process name'),
|
||||
_('Match process name.'));
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('fields_process', form.DynamicList, 'process_path', _('Process path'),
|
||||
_('Match process path.'));
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('fields_process', form.DynamicList, 'process_path_regex', _('Process path (regex)'),
|
||||
_('Match process path using regular expression.'));
|
||||
so.modalonly = true;
|
||||
/* Routing rules end */
|
||||
|
||||
/* DNS settings start */
|
||||
@@ -759,7 +739,6 @@ return view.extend({
|
||||
|
||||
this.value('default-dns', _('Default DNS (issued by WAN)'));
|
||||
this.value('system-dns', _('System DNS'));
|
||||
this.value('block-dns', _('Block DNS queries'));
|
||||
uci.sections(data[0], 'dns_server', (res) => {
|
||||
if (res.enabled === '1')
|
||||
this.value(res['.name'], res.label);
|
||||
@@ -771,15 +750,12 @@ return view.extend({
|
||||
so.rmempty = false;
|
||||
|
||||
so = ss.option(form.Flag, 'disable_cache', _('Disable DNS cache'));
|
||||
so.default = so.disabled;
|
||||
|
||||
so = ss.option(form.Flag, 'disable_cache_expire', _('Disable cache expire'));
|
||||
so.default = so.disabled;
|
||||
so.depends('disable_cache', '0');
|
||||
|
||||
so = ss.option(form.Flag, 'independent_cache', _('Independent cache per server'),
|
||||
_('Make each DNS server\'s cache independent for special purposes. If enabled, will slightly degrade performance.'));
|
||||
so.default = so.disabled;
|
||||
so.depends('disable_cache', '0');
|
||||
|
||||
so = ss.option(form.Value, 'client_subnet', _('EDNS Client subnet'),
|
||||
@@ -790,7 +766,6 @@ return view.extend({
|
||||
so = ss.option(form.Flag, 'cache_file_store_rdrc', _('Store RDRC'),
|
||||
_('Store rejected DNS response cache.<br/>' +
|
||||
'The check results of <code>Address filter DNS rule items</code> will be cached until expiration.'));
|
||||
so.default = so.disabled;
|
||||
|
||||
so = ss.option(form.Value, 'cache_file_rdrc_timeout', _('RDRC timeout'),
|
||||
_('Timeout of rejected DNS response cache in seconds. <code>604800 (7d)</code> is used by default.'));
|
||||
@@ -932,10 +907,9 @@ return view.extend({
|
||||
ss.renderSectionAdd = L.bind(hp.renderSectionAdd, this, ss);
|
||||
|
||||
ss.tab('field_other', _('Other fields'));
|
||||
ss.tab('field_host', _('Host fields'));
|
||||
ss.tab('field_host', _('Host/IP fields'));
|
||||
ss.tab('field_port', _('Port fields'));
|
||||
ss.tab('field_source_ip', _('SRC-IP fields'));
|
||||
ss.tab('field_source_port', _('SRC-Port fields'));
|
||||
ss.tab('fields_process', _('Process fields'));
|
||||
|
||||
so = ss.taboption('field_other', form.Value, 'label', _('Label'));
|
||||
so.load = L.bind(hp.loadDefaultLabel, this, data[0]);
|
||||
@@ -986,75 +960,6 @@ return view.extend({
|
||||
so.value('stun', _('STUN'));
|
||||
so.value('tls', _('TLS'));
|
||||
|
||||
so = ss.taboption('field_host', form.DynamicList, 'domain', _('Domain name'),
|
||||
_('Match full domain.'));
|
||||
so.datatype = 'hostname';
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_host', form.DynamicList, 'domain_suffix', _('Domain suffix'),
|
||||
_('Match domain suffix.'));
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_host', form.DynamicList, 'domain_keyword', _('Domain keyword'),
|
||||
_('Match domain using keyword.'));
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_host', form.DynamicList, 'domain_regex', _('Domain regex'),
|
||||
_('Match domain using regular expression.'));
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_port', form.DynamicList, 'port', _('Port'),
|
||||
_('Match port.'));
|
||||
so.datatype = 'port';
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_port', form.DynamicList, 'port_range', _('Port range'),
|
||||
_('Match port range. Format as START:/:END/START:END.'));
|
||||
so.validate = validatePortRange;
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_source_ip', form.DynamicList, 'source_ip_cidr', _('Source IP CIDR'),
|
||||
_('Match source IP CIDR.'));
|
||||
so.datatype = 'or(cidr, ipaddr)';
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_source_ip', form.Flag, 'source_ip_is_private', _('Private source IP'),
|
||||
_('Match private source IP.'));
|
||||
so.default = so.disabled;
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_other', form.DynamicList, 'ip_cidr', _('IP CIDR'),
|
||||
_('Match IP CIDR with query response.'));
|
||||
so.datatype = 'or(cidr, ipaddr)';
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_other', form.Flag, 'ip_is_private', _('Private IP'),
|
||||
_('Match private IP with query response.'));
|
||||
so.default = so.disabled;
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_source_port', form.DynamicList, 'source_port', _('Source port'),
|
||||
_('Match source port.'));
|
||||
so.datatype = 'port';
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_source_port', form.DynamicList, 'source_port_range', _('Source port range'),
|
||||
_('Match source port range. Format as START:/:END/START:END.'));
|
||||
so.validate = validatePortRange;
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_other', form.DynamicList, 'process_name', _('Process name'),
|
||||
_('Match process name.'));
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_other', form.DynamicList, 'process_path', _('Process path'),
|
||||
_('Match process path.'));
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_other', form.DynamicList, 'process_path_regex', _('Process path (regex)'),
|
||||
_('Match process path using regular expression.'));
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_other', form.DynamicList, 'user', _('User'),
|
||||
_('Match user name.'));
|
||||
so.modalonly = true;
|
||||
@@ -1076,17 +981,14 @@ return view.extend({
|
||||
|
||||
so = ss.taboption('field_other', form.Flag, 'rule_set_ip_cidr_match_source', _('Rule set IP CIDR as source IP'),
|
||||
_('Make IP CIDR in rule sets match the source IP.'));
|
||||
so.default = so.disabled;
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_other', form.Flag, 'rule_set_ip_cidr_accept_empty', _('Accept empty query response'),
|
||||
_('Make IP CIDR in rule-sets accept empty query response.'));
|
||||
so.default = so.disabled;
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_other', form.Flag, 'invert', _('Invert'),
|
||||
_('Invert match result.'));
|
||||
so.default = so.disabled;
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_other', form.MultiValue, 'outbound', _('Outbound'),
|
||||
@@ -1128,18 +1030,86 @@ return view.extend({
|
||||
|
||||
so = ss.taboption('field_other', form.Flag, 'dns_disable_cache', _('Disable dns cache'),
|
||||
_('Disable cache and save cache in this query.'));
|
||||
so.default = so.disabled;
|
||||
so.depends({'server': 'block-dns', '!reverse': true});
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_other', form.Value, 'rewrite_ttl', _('Rewrite TTL'),
|
||||
_('Rewrite TTL in DNS responses.'));
|
||||
so.datatype = 'uinteger';
|
||||
so.depends({'server': 'block-dns', '!reverse': true});
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_other', form.Value, 'client_subnet', _('EDNS Client subnet'),
|
||||
_('Append a <code>edns0-subnet</code> OPT extra record with the specified IP prefix to every query by default.<br/>' +
|
||||
'If value is an IP address instead of prefix, <code>/32</code> or <code>/128</code> will be appended automatically.'));
|
||||
so.datatype = 'or(cidr, ipaddr)';
|
||||
so.depends({'server': 'block-dns', '!reverse': true});
|
||||
|
||||
so = ss.taboption('field_host', form.DynamicList, 'domain', _('Domain name'),
|
||||
_('Match full domain.'));
|
||||
so.datatype = 'hostname';
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_host', form.DynamicList, 'domain_suffix', _('Domain suffix'),
|
||||
_('Match domain suffix.'));
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_host', form.DynamicList, 'domain_keyword', _('Domain keyword'),
|
||||
_('Match domain using keyword.'));
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_host', form.DynamicList, 'domain_regex', _('Domain regex'),
|
||||
_('Match domain using regular expression.'));
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_host', form.DynamicList, 'source_ip_cidr', _('Source IP CIDR'),
|
||||
_('Match source IP CIDR.'));
|
||||
so.datatype = 'or(cidr, ipaddr)';
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_host', form.Flag, 'source_ip_is_private', _('Match private source IP'));
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_host', form.DynamicList, 'ip_cidr', _('IP CIDR'),
|
||||
_('Match IP CIDR with query response. Current rule will be skipped if not match.'));
|
||||
so.datatype = 'or(cidr, ipaddr)';
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_host', form.Flag, 'ip_is_private', _('Match private IP'),
|
||||
_('Match private IP with query response.'));
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_port', form.DynamicList, 'source_port', _('Source port'),
|
||||
_('Match source port.'));
|
||||
so.datatype = 'port';
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_port', form.DynamicList, 'source_port_range', _('Source port range'),
|
||||
_('Match source port range. Format as START:/:END/START:END.'));
|
||||
so.validate = hp.validatePortRange;
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_port', form.DynamicList, 'port', _('Port'),
|
||||
_('Match port.'));
|
||||
so.datatype = 'port';
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('field_port', form.DynamicList, 'port_range', _('Port range'),
|
||||
_('Match port range. Format as START:/:END/START:END.'));
|
||||
so.validate = hp.validatePortRange;
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('fields_process', form.DynamicList, 'process_name', _('Process name'),
|
||||
_('Match process name.'));
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('fields_process', form.DynamicList, 'process_path', _('Process path'),
|
||||
_('Match process path.'));
|
||||
so.modalonly = true;
|
||||
|
||||
so = ss.taboption('fields_process', form.DynamicList, 'process_path_regex', _('Process path (regex)'),
|
||||
_('Match process path using regular expression.'));
|
||||
so.modalonly = true;
|
||||
/* DNS rules end */
|
||||
/* Custom routing settings end */
|
||||
|
||||
@@ -1331,8 +1301,7 @@ return view.extend({
|
||||
return callWriteDomainList('proxy_list', value);
|
||||
}
|
||||
so.remove = function(/* ... */) {
|
||||
let routing_mode = this.map.lookupOption('routing_mode', 'config')[0].formvalue('config');
|
||||
|
||||
let routing_mode = this.section.formvalue('config', 'routing_mode');
|
||||
if (routing_mode !== 'custom')
|
||||
return callWriteDomainList('proxy_list', '');
|
||||
return true;
|
||||
@@ -1364,8 +1333,7 @@ return view.extend({
|
||||
return callWriteDomainList('direct_list', value);
|
||||
}
|
||||
so.remove = function(/* ... */) {
|
||||
let routing_mode = this.map.lookupOption('routing_mode', 'config')[0].formvalue('config');
|
||||
|
||||
let routing_mode = this.section.formvalue('config', 'routing_mode');
|
||||
if (routing_mode !== 'custom')
|
||||
return callWriteDomainList('direct_list', '');
|
||||
return true;
|
||||
|
||||
@@ -271,6 +271,10 @@ function parseShareLink(uri, features) {
|
||||
config.http_path = params.get('path') ? decodeURIComponent(params.get('path')) : null;
|
||||
}
|
||||
break;
|
||||
case 'httpupgrade':
|
||||
config.httpupgrade_host = params.get('host') ? decodeURIComponent(params.get('host')) : null;
|
||||
config.http_path = params.get('path') ? decodeURIComponent(params.get('path')) : null;
|
||||
break;
|
||||
case 'ws':
|
||||
config.ws_host = params.get('host') ? decodeURIComponent(params.get('host')) : null;
|
||||
config.ws_path = params.get('path') ? decodeURIComponent(params.get('path')) : null;
|
||||
@@ -288,7 +292,7 @@ function parseShareLink(uri, features) {
|
||||
if (uri.includes('&'))
|
||||
return null;
|
||||
|
||||
/* https://github.com/2dust/v2rayN/wiki/%E5%88%86%E4%BA%AB%E9%93%BE%E6%8E%A5%E6%A0%BC%E5%BC%8F%E8%AF%B4%E6%98%8E(ver-2) */
|
||||
/* https://github.com/2dust/v2rayN/wiki/Description-of-VMess-share-link */
|
||||
uri = JSON.parse(hp.decodeBase64Str(uri[1]));
|
||||
|
||||
if (uri.v != '2')
|
||||
@@ -314,7 +318,8 @@ function parseShareLink(uri, features) {
|
||||
transport: (uri.net !== 'tcp') ? uri.net : null,
|
||||
tls: uri.tls === 'tls' ? '1' : '0',
|
||||
tls_sni: uri.sni || uri.host,
|
||||
tls_alpn: uri.alpn ? uri.alpn.split(',') : null
|
||||
tls_alpn: uri.alpn ? uri.alpn.split(',') : null,
|
||||
tls_utls: features.with_utls ? uri.fp : null
|
||||
};
|
||||
switch (uri.net) {
|
||||
case 'grpc':
|
||||
@@ -328,6 +333,10 @@ function parseShareLink(uri, features) {
|
||||
config.http_path = uri.path;
|
||||
}
|
||||
break;
|
||||
case 'httpupgrade':
|
||||
config.httpupgrade_host = uri.host;
|
||||
config.http_path = uri.path;
|
||||
break;
|
||||
case 'ws':
|
||||
config.ws_host = uri.host;
|
||||
config.ws_path = uri.path;
|
||||
@@ -439,12 +448,12 @@ function renderNodeSettings(section, data, features, main_node, routing_mode) {
|
||||
o.depends({'type': 'socks', 'socks_version': '5'});
|
||||
o.validate = function(section_id, value) {
|
||||
if (section_id) {
|
||||
let type = this.map.lookupOption('type', section_id)[0].formvalue(section_id);
|
||||
let type = this.section.formvalue(section_id, 'type');
|
||||
let required_type = [ 'shadowsocks', 'shadowtls', 'trojan' ];
|
||||
|
||||
if (required_type.includes(type)) {
|
||||
if (type === 'shadowsocks') {
|
||||
let encmode = this.map.lookupOption('shadowsocks_encrypt_method', section_id)[0].formvalue(section_id);
|
||||
let encmode = this.section.formvalue(section_id, 'shadowsocks_encrypt_method');
|
||||
if (encmode === 'none')
|
||||
return true;
|
||||
}
|
||||
@@ -458,16 +467,6 @@ function renderNodeSettings(section, data, features, main_node, routing_mode) {
|
||||
o.modalonly = true;
|
||||
|
||||
/* Direct config */
|
||||
o = s.option(form.Value, 'override_address', _('Override address'),
|
||||
_('Override the connection destination address.'));
|
||||
o.datatype = 'host';
|
||||
o.depends('type', 'direct');
|
||||
|
||||
o = s.option(form.Value, 'override_port', _('Override port'),
|
||||
_('Override the connection destination port.'));
|
||||
o.datatype = 'port';
|
||||
o.depends('type', 'direct');
|
||||
|
||||
o = s.option(form.ListValue, 'proxy_protocol', _('Proxy protocol'),
|
||||
_('Write proxy protocol in the connection header.'));
|
||||
o.value('', _('Disable'));
|
||||
@@ -477,6 +476,20 @@ function renderNodeSettings(section, data, features, main_node, routing_mode) {
|
||||
o.modalonly = true;
|
||||
|
||||
/* Hysteria (2) config start */
|
||||
o = s.option(form.DynamicList, 'hysteria_hopping_port', _('Hopping port'));
|
||||
o.depends('type', 'hysteria');
|
||||
o.depends('type', 'hysteria2');
|
||||
o.validate = hp.validatePortRange;
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.option(form.Value, 'hysteria_hop_interval', _('Hop interval'),
|
||||
_('Port hopping interval in seconds.'));
|
||||
o.datatype = 'uinteger';
|
||||
o.placeholder = '30';
|
||||
o.depends({'type': 'hysteria', 'hysteria_hopping_port': /[\s\S]/});
|
||||
o.depends({'type': 'hysteria2', 'hysteria_hopping_port': /[\s\S]/});
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.option(form.ListValue, 'hysteria_protocol', _('Protocol'));
|
||||
o.value('udp');
|
||||
/* WeChat-Video / FakeTCP are unsupported by sing-box currently
|
||||
@@ -832,12 +845,6 @@ function renderNodeSettings(section, data, features, main_node, routing_mode) {
|
||||
/* Transport config end */
|
||||
|
||||
/* Wireguard config start */
|
||||
o = s.option(form.Flag, 'wireguard_gso', _('Generic segmentation offload'));
|
||||
o.default = o.disabled;
|
||||
o.depends('type', 'wireguard');
|
||||
o.rmempty = false;
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.option(form.DynamicList, 'wireguard_local_address', _('Local address'),
|
||||
_('List of IP (v4 or v6) addresses prefixes to be assigned to the interface.'));
|
||||
o.datatype = 'cidr';
|
||||
@@ -877,6 +884,12 @@ function renderNodeSettings(section, data, features, main_node, routing_mode) {
|
||||
o.placeholder = '1408';
|
||||
o.depends('type', 'wireguard');
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.option(form.Value, 'wireguard_persistent_keepalive_interval', _('Persistent keepalive interval'),
|
||||
_('In seconds. Disabled by default.'));
|
||||
o.datatype = 'uinteger';
|
||||
o.depends('type', 'wireguard');
|
||||
o.modalonly = true;
|
||||
/* Wireguard config end */
|
||||
|
||||
/* Mux config start */
|
||||
@@ -1021,7 +1034,7 @@ function renderNodeSettings(section, data, features, main_node, routing_mode) {
|
||||
_('The path to the server certificate, in PEM format.'));
|
||||
o.value('/etc/homeproxy/certs/client_ca.pem');
|
||||
o.depends('tls_self_sign', '1');
|
||||
o.validate = L.bind(hp.validateCertificatePath, this);
|
||||
o.validate = hp.validateCertificatePath;
|
||||
o.rmempty = false;
|
||||
o.modalonly = true;
|
||||
|
||||
@@ -1040,11 +1053,6 @@ function renderNodeSettings(section, data, features, main_node, routing_mode) {
|
||||
o.default = o.disabled;
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.option(form.Flag, 'tls_ech_tls_disable_drs', _('Disable dynamic record sizing'));
|
||||
o.depends('tls_ech', '1');
|
||||
o.default = o.disabled;
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.option(form.Flag, 'tls_ech_enable_pqss', _('Enable PQ signature schemes'));
|
||||
o.depends('tls_ech', '1');
|
||||
o.default = o.disabled;
|
||||
|
||||
@@ -195,12 +195,12 @@ return view.extend({
|
||||
}
|
||||
o.validate = function(section_id, value) {
|
||||
if (section_id) {
|
||||
let type = this.map.lookupOption('type', section_id)[0].formvalue(section_id);
|
||||
let type = this.section.formvalue(section_id, 'type');
|
||||
let required_type = [ 'http', 'mixed', 'naive', 'socks', 'shadowsocks' ];
|
||||
|
||||
if (required_type.includes(type)) {
|
||||
if (type === 'shadowsocks') {
|
||||
let encmode = this.map.lookupOption('shadowsocks_encrypt_method', section_id)[0].formvalue(section_id);
|
||||
let encmode = this.section.formvalue(section_id, 'shadowsocks_encrypt_method');
|
||||
if (encmode === 'none')
|
||||
return true;
|
||||
else if (encmode === '2022-blake3-aes-128-gcm')
|
||||
@@ -745,7 +745,7 @@ return view.extend({
|
||||
o.depends({'tls': '1', 'tls_acme': '0', 'tls_reality': '0'});
|
||||
o.depends({'tls': '1', 'tls_acme': null, 'tls_reality': '0'});
|
||||
o.depends({'tls': '1', 'tls_acme': null, 'tls_reality': null});
|
||||
o.validate = L.bind(hp.validateCertificatePath, this);
|
||||
o.validate = hp.validateCertificatePath;
|
||||
o.rmempty = false;
|
||||
o.modalonly = true;
|
||||
|
||||
@@ -764,7 +764,7 @@ return view.extend({
|
||||
o.depends({'tls': '1', 'tls_acme': '0', 'tls_reality': null});
|
||||
o.depends({'tls': '1', 'tls_acme': null, 'tls_reality': '0'});
|
||||
o.depends({'tls': '1', 'tls_acme': null, 'tls_reality': null});
|
||||
o.validate = L.bind(hp.validateCertificatePath, this);
|
||||
o.validate = hp.validateCertificatePath;
|
||||
o.rmempty = false;
|
||||
o.modalonly = true;
|
||||
|
||||
@@ -802,16 +802,6 @@ return view.extend({
|
||||
o.depends({'network': 'tcp', '!reverse': true});
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.option(form.Flag, 'sniff_override', _('Override destination'),
|
||||
_('Override the connection destination address with the sniffed domain.'));
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.option(form.ListValue, 'domain_strategy', _('Domain strategy'),
|
||||
_('If set, the requested domain name will be resolved to IP before routing.'));
|
||||
for (let i in hp.dns_strategy)
|
||||
o.value(i, hp.dns_strategy[i])
|
||||
o.modalonly = true;
|
||||
|
||||
o = s.option(form.ListValue, 'network', _('Network'));
|
||||
o.value('tcp', _('TCP'));
|
||||
o.value('udp', _('UDP'));
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -32,8 +32,11 @@
|
||||
5.154.132.0/23
|
||||
5.154.136.0/22
|
||||
5.154.140.0/23
|
||||
5.175.237.0/24
|
||||
5.175.239.0/24
|
||||
5.180.98.0/23
|
||||
5.181.219.0/24
|
||||
5.231.96.0/24
|
||||
8.25.82.0/24
|
||||
8.38.121.0/24
|
||||
8.45.52.0/24
|
||||
@@ -45,8 +48,11 @@
|
||||
8.136.0.0/13
|
||||
8.144.0.0/14
|
||||
8.148.0.0/19
|
||||
8.148.32.0/21
|
||||
8.148.40.0/22
|
||||
8.148.32.0/22
|
||||
8.148.36.0/23
|
||||
8.148.38.0/24
|
||||
8.148.41.0/24
|
||||
8.148.42.0/23
|
||||
8.148.64.0/18
|
||||
8.148.128.0/17
|
||||
8.149.0.0/16
|
||||
@@ -68,7 +74,7 @@
|
||||
16.2.142.0/23
|
||||
23.0.0.0/24
|
||||
23.3.99.0/24
|
||||
23.26.132.0/24
|
||||
23.36.65.0/24
|
||||
23.48.160.0/24
|
||||
23.53.220.0/24
|
||||
23.54.75.0/24
|
||||
@@ -77,7 +83,6 @@
|
||||
23.61.203.0/24
|
||||
23.63.29.0/24
|
||||
23.63.98.0/23
|
||||
23.184.136.0/24
|
||||
23.216.52.0/23
|
||||
23.225.71.0/24
|
||||
23.247.128.0/24
|
||||
@@ -160,7 +165,6 @@
|
||||
36.255.128.0/22
|
||||
36.255.164.0/24
|
||||
36.255.192.0/24
|
||||
38.12.36.0/23
|
||||
38.12.39.0/24
|
||||
38.46.5.0/24
|
||||
38.46.6.0/24
|
||||
@@ -170,7 +174,7 @@
|
||||
38.47.122.0/23
|
||||
38.55.135.0/24
|
||||
38.71.124.0/24
|
||||
38.71.126.0/24
|
||||
38.71.126.0/23
|
||||
38.77.248.0/24
|
||||
38.95.118.0/23
|
||||
38.95.121.0/24
|
||||
@@ -180,7 +184,7 @@
|
||||
38.105.26.0/23
|
||||
38.105.28.0/22
|
||||
38.111.220.0/23
|
||||
38.134.56.0/23
|
||||
38.134.56.0/22
|
||||
38.147.168.0/23
|
||||
38.147.174.0/23
|
||||
38.196.176.0/24
|
||||
@@ -256,10 +260,6 @@
|
||||
42.242.0.0/15
|
||||
42.244.0.0/14
|
||||
42.248.0.0/15
|
||||
43.16.0.0/12
|
||||
43.32.0.0/11
|
||||
43.64.0.0/14
|
||||
43.68.0.0/15
|
||||
43.136.0.0/13
|
||||
43.144.0.0/15
|
||||
43.192.0.0/16
|
||||
@@ -320,7 +320,6 @@
|
||||
43.231.160.0/21
|
||||
43.231.168.0/23
|
||||
43.231.170.0/24
|
||||
43.231.184.0/24
|
||||
43.231.186.0/24
|
||||
43.239.120.0/22
|
||||
43.240.0.0/22
|
||||
@@ -399,6 +398,7 @@
|
||||
43.250.236.0/22
|
||||
43.250.244.0/22
|
||||
43.251.4.0/22
|
||||
43.251.8.0/24
|
||||
43.251.16.0/23
|
||||
43.251.36.0/22
|
||||
43.251.100.0/22
|
||||
@@ -444,7 +444,6 @@
|
||||
45.12.88.0/24
|
||||
45.12.90.0/24
|
||||
45.40.192.0/18
|
||||
45.41.8.0/23
|
||||
45.61.200.0/23
|
||||
45.61.224.0/21
|
||||
45.64.74.0/23
|
||||
@@ -479,7 +478,6 @@
|
||||
45.119.60.0/22
|
||||
45.119.68.0/22
|
||||
45.119.104.0/23
|
||||
45.119.106.0/24
|
||||
45.119.116.0/22
|
||||
45.120.100.0/22
|
||||
45.120.164.0/22
|
||||
@@ -511,18 +509,15 @@
|
||||
45.150.236.0/23
|
||||
45.151.47.0/24
|
||||
45.153.128.0/22
|
||||
45.157.70.0/23
|
||||
45.157.88.0/24
|
||||
45.192.64.0/19
|
||||
45.195.6.0/24
|
||||
45.199.167.0/24
|
||||
45.202.209.0/24
|
||||
45.202.210.0/23
|
||||
45.202.212.0/24
|
||||
45.204.112.0/20
|
||||
45.248.8.0/22
|
||||
45.248.108.0/24
|
||||
45.248.204.0/22
|
||||
45.249.208.0/23
|
||||
45.249.212.0/22
|
||||
45.250.32.0/21
|
||||
45.250.40.0/22
|
||||
@@ -532,11 +527,11 @@
|
||||
45.251.8.0/22
|
||||
45.251.20.0/22
|
||||
45.251.88.0/21
|
||||
45.251.96.0/21
|
||||
45.251.100.0/22
|
||||
45.251.120.0/22
|
||||
45.251.138.0/23
|
||||
45.251.242.0/23
|
||||
45.252.0.0/22
|
||||
45.252.48.0/22
|
||||
45.252.104.0/22
|
||||
45.253.24.0/22
|
||||
45.253.32.0/24
|
||||
@@ -588,8 +583,8 @@
|
||||
47.246.50.0/24
|
||||
47.246.57.0/24
|
||||
47.246.58.0/24
|
||||
47.246.61.0/24
|
||||
47.246.62.0/23
|
||||
47.246.60.0/23
|
||||
47.246.63.0/24
|
||||
49.4.0.0/18
|
||||
49.4.64.0/19
|
||||
49.4.96.0/21
|
||||
@@ -654,6 +649,7 @@
|
||||
54.222.88.0/22
|
||||
54.222.96.0/23
|
||||
54.222.100.0/22
|
||||
54.222.104.0/21
|
||||
54.222.112.0/22
|
||||
54.222.116.0/23
|
||||
54.222.128.0/17
|
||||
@@ -667,8 +663,9 @@
|
||||
58.68.234.0/24
|
||||
58.68.236.0/24
|
||||
58.68.247.0/24
|
||||
58.82.0.0/17
|
||||
58.83.0.0/16
|
||||
58.82.0.0/23
|
||||
58.83.17.0/24
|
||||
58.83.128.0/17
|
||||
58.87.64.0/18
|
||||
58.99.128.0/17
|
||||
58.100.0.0/15
|
||||
@@ -689,7 +686,6 @@
|
||||
59.81.40.0/23
|
||||
59.81.46.0/24
|
||||
59.81.64.0/23
|
||||
59.81.72.0/23
|
||||
59.81.82.0/23
|
||||
59.81.94.0/23
|
||||
59.81.102.0/23
|
||||
@@ -770,14 +766,12 @@
|
||||
59.153.164.0/22
|
||||
59.153.168.0/24
|
||||
59.172.0.0/14
|
||||
59.191.0.0/17
|
||||
59.252.0.0/16
|
||||
60.0.0.0/11
|
||||
60.63.0.0/16
|
||||
60.160.0.0/11
|
||||
60.194.0.0/15
|
||||
60.200.0.0/18
|
||||
60.200.128.0/19
|
||||
60.200.0.0/24
|
||||
60.204.0.0/14
|
||||
60.208.0.0/12
|
||||
60.232.0.0/16
|
||||
@@ -823,17 +817,17 @@
|
||||
64.188.38.0/23
|
||||
64.188.40.0/22
|
||||
64.188.44.0/23
|
||||
65.49.130.0/23
|
||||
66.102.240.0/21
|
||||
66.102.248.0/22
|
||||
66.102.252.0/24
|
||||
66.102.254.0/23
|
||||
67.229.241.0/24
|
||||
68.79.0.0/18
|
||||
69.163.104.0/24
|
||||
69.163.106.0/24
|
||||
69.163.123.0/24
|
||||
69.165.78.0/23
|
||||
69.172.70.0/24
|
||||
69.194.0.0/23
|
||||
69.230.192.0/18
|
||||
69.231.128.0/18
|
||||
69.234.192.0/18
|
||||
@@ -854,10 +848,13 @@
|
||||
81.173.20.0/22
|
||||
81.173.28.0/24
|
||||
82.156.0.0/15
|
||||
84.54.2.0/23
|
||||
85.237.205.0/24
|
||||
87.254.207.0/24
|
||||
88.221.162.0/23
|
||||
89.106.207.0/24
|
||||
89.144.38.0/24
|
||||
92.118.189.0/24
|
||||
92.123.94.0/23
|
||||
93.177.76.0/23
|
||||
93.183.14.0/24
|
||||
@@ -886,7 +883,7 @@
|
||||
101.52.112.0/21
|
||||
101.52.124.0/22
|
||||
101.52.128.0/20
|
||||
101.52.200.0/21
|
||||
101.52.204.0/22
|
||||
101.52.212.0/22
|
||||
101.52.216.0/21
|
||||
101.52.232.0/23
|
||||
@@ -908,7 +905,10 @@
|
||||
101.104.144.0/20
|
||||
101.104.160.0/20
|
||||
101.106.0.0/19
|
||||
101.124.0.0/16
|
||||
101.124.0.0/20
|
||||
101.124.19.0/24
|
||||
101.124.22.0/24
|
||||
101.124.62.0/24
|
||||
101.125.0.0/22
|
||||
101.125.4.0/23
|
||||
101.125.6.0/24
|
||||
@@ -928,7 +928,6 @@
|
||||
101.128.0.0/22
|
||||
101.129.0.0/16
|
||||
101.132.0.0/15
|
||||
101.144.0.0/12
|
||||
101.197.0.0/16
|
||||
101.198.0.0/22
|
||||
101.198.160.0/19
|
||||
@@ -1038,6 +1037,7 @@
|
||||
103.27.24.0/22
|
||||
103.27.240.0/22
|
||||
103.28.8.0/24
|
||||
103.28.54.0/24
|
||||
103.28.204.0/22
|
||||
103.28.212.0/22
|
||||
103.29.16.0/22
|
||||
@@ -1058,7 +1058,8 @@
|
||||
103.36.132.0/22
|
||||
103.36.136.0/22
|
||||
103.36.164.0/22
|
||||
103.36.168.0/21
|
||||
103.36.168.0/23
|
||||
103.36.172.0/22
|
||||
103.36.192.0/20
|
||||
103.36.208.0/22
|
||||
103.36.220.0/22
|
||||
@@ -1096,7 +1097,6 @@
|
||||
103.41.232.0/23
|
||||
103.42.8.0/22
|
||||
103.42.76.0/22
|
||||
103.43.133.0/24
|
||||
103.43.134.0/23
|
||||
103.43.184.0/21
|
||||
103.44.56.0/22
|
||||
@@ -1137,7 +1137,6 @@
|
||||
103.53.124.0/22
|
||||
103.53.208.0/24
|
||||
103.53.211.0/24
|
||||
103.54.48.0/22
|
||||
103.55.172.0/22
|
||||
103.55.228.0/22
|
||||
103.56.32.0/22
|
||||
@@ -1244,6 +1243,7 @@
|
||||
103.87.132.0/22
|
||||
103.88.32.0/21
|
||||
103.88.64.0/22
|
||||
103.88.96.0/22
|
||||
103.89.184.0/21
|
||||
103.89.192.0/19
|
||||
103.89.224.0/21
|
||||
@@ -1285,7 +1285,6 @@
|
||||
103.99.104.0/22
|
||||
103.99.178.0/24
|
||||
103.100.64.0/22
|
||||
103.100.158.0/23
|
||||
103.101.124.0/23
|
||||
103.101.180.0/22
|
||||
103.102.196.0/22
|
||||
@@ -1315,7 +1314,6 @@
|
||||
103.109.106.0/23
|
||||
103.110.132.0/22
|
||||
103.110.136.0/22
|
||||
103.110.156.0/22
|
||||
103.111.64.0/24
|
||||
103.112.68.0/22
|
||||
103.112.172.0/22
|
||||
@@ -1323,7 +1321,7 @@
|
||||
103.113.4.0/22
|
||||
103.114.72.0/22
|
||||
103.114.100.0/22
|
||||
103.114.156.0/22
|
||||
103.114.158.0/23
|
||||
103.114.212.0/23
|
||||
103.114.236.0/22
|
||||
103.115.40.0/21
|
||||
@@ -1362,8 +1360,10 @@
|
||||
103.121.250.0/24
|
||||
103.121.252.0/22
|
||||
103.122.48.0/22
|
||||
103.122.179.0/24
|
||||
103.122.243.0/24
|
||||
103.123.4.0/23
|
||||
103.125.236.0/22
|
||||
103.126.1.0/24
|
||||
103.126.18.0/23
|
||||
103.126.101.0/24
|
||||
@@ -1386,7 +1386,6 @@
|
||||
103.137.60.0/24
|
||||
103.138.156.0/23
|
||||
103.139.136.0/23
|
||||
103.139.172.0/23
|
||||
103.139.212.0/23
|
||||
103.140.14.0/23
|
||||
103.140.126.0/23
|
||||
@@ -1399,17 +1398,13 @@
|
||||
103.142.234.0/23
|
||||
103.143.16.0/22
|
||||
103.143.92.0/23
|
||||
103.143.230.0/24
|
||||
103.143.238.0/24
|
||||
103.144.28.0/24
|
||||
103.143.231.0/24
|
||||
103.144.52.0/23
|
||||
103.144.66.0/23
|
||||
103.144.70.0/24
|
||||
103.144.148.0/23
|
||||
103.144.158.0/23
|
||||
103.144.245.0/24
|
||||
103.145.42.0/23
|
||||
103.145.60.0/23
|
||||
103.145.90.0/24
|
||||
103.145.92.0/24
|
||||
103.145.106.0/23
|
||||
@@ -1464,9 +1459,10 @@
|
||||
103.174.94.0/23
|
||||
103.175.197.0/24
|
||||
103.177.28.0/23
|
||||
103.177.44.0/24
|
||||
103.177.80.0/23
|
||||
103.179.78.0/23
|
||||
103.180.108.0/24
|
||||
103.180.109.0/24
|
||||
103.181.234.0/24
|
||||
103.183.66.0/23
|
||||
103.183.122.0/23
|
||||
@@ -1562,7 +1558,7 @@
|
||||
103.219.176.0/22
|
||||
103.219.184.0/22
|
||||
103.220.52.0/22
|
||||
103.220.56.0/21
|
||||
103.220.56.0/22
|
||||
103.220.64.0/22
|
||||
103.220.92.0/22
|
||||
103.220.124.0/22
|
||||
@@ -1577,8 +1573,8 @@
|
||||
103.222.216.0/22
|
||||
103.223.132.0/22
|
||||
103.224.80.0/22
|
||||
103.224.220.0/22
|
||||
103.224.232.0/22
|
||||
103.225.86.0/23
|
||||
103.226.57.0/24
|
||||
103.226.124.0/22
|
||||
103.227.76.0/22
|
||||
@@ -1617,10 +1613,13 @@
|
||||
103.234.56.0/22
|
||||
103.234.128.0/22
|
||||
103.235.85.0/24
|
||||
103.235.102.0/23
|
||||
103.235.136.0/22
|
||||
103.235.144.0/24
|
||||
103.235.220.0/22
|
||||
103.235.224.0/19
|
||||
103.235.224.0/20
|
||||
103.235.244.0/22
|
||||
103.235.248.0/21
|
||||
103.236.120.0/22
|
||||
103.236.244.0/22
|
||||
103.236.248.0/21
|
||||
@@ -1641,7 +1640,7 @@
|
||||
103.238.132.0/22
|
||||
103.238.144.0/22
|
||||
103.238.160.0/22
|
||||
103.238.180.0/22
|
||||
103.238.180.0/23
|
||||
103.238.184.0/23
|
||||
103.238.188.0/22
|
||||
103.238.204.0/22
|
||||
@@ -1722,12 +1721,9 @@
|
||||
104.233.144.0/21
|
||||
104.233.157.0/24
|
||||
104.233.159.0/24
|
||||
104.233.224.0/20
|
||||
104.233.240.0/22
|
||||
104.245.96.0/21
|
||||
106.0.4.0/22
|
||||
106.2.37.0/24
|
||||
106.2.38.0/23
|
||||
106.2.40.0/23
|
||||
106.2.42.0/24
|
||||
106.2.45.0/24
|
||||
@@ -1797,14 +1793,11 @@
|
||||
106.228.0.0/15
|
||||
106.230.0.0/16
|
||||
107.148.84.0/23
|
||||
107.148.92.0/23
|
||||
107.148.150.0/23
|
||||
107.148.160.0/22
|
||||
107.149.198.0/23
|
||||
107.149.208.0/23
|
||||
107.151.208.0/20
|
||||
107.190.229.0/24
|
||||
109.176.254.0/23
|
||||
109.206.244.0/22
|
||||
109.244.0.0/16
|
||||
110.6.0.0/15
|
||||
@@ -1826,10 +1819,9 @@
|
||||
110.41.216.0/21
|
||||
110.41.224.0/19
|
||||
110.42.0.0/15
|
||||
110.44.144.0/20
|
||||
110.51.0.0/16
|
||||
110.52.0.0/15
|
||||
110.56.0.0/13
|
||||
110.56.0.0/16
|
||||
110.64.0.0/15
|
||||
110.72.0.0/15
|
||||
110.75.0.0/16
|
||||
@@ -1854,7 +1846,7 @@
|
||||
110.100.200.0/21
|
||||
110.100.208.0/20
|
||||
110.100.224.0/20
|
||||
110.112.0.0/15
|
||||
110.113.0.0/16
|
||||
110.114.0.0/16
|
||||
110.116.0.0/19
|
||||
110.116.48.0/20
|
||||
@@ -1870,7 +1862,6 @@
|
||||
110.152.0.0/14
|
||||
110.156.0.0/15
|
||||
110.166.0.0/15
|
||||
110.172.200.0/21
|
||||
110.173.8.0/21
|
||||
110.173.16.0/20
|
||||
110.173.32.0/20
|
||||
@@ -1909,6 +1900,7 @@
|
||||
111.142.0.0/15
|
||||
111.144.0.0/14
|
||||
111.148.0.0/16
|
||||
111.149.0.0/24
|
||||
111.160.0.0/13
|
||||
111.170.0.0/16
|
||||
111.172.0.0/14
|
||||
@@ -1919,6 +1911,7 @@
|
||||
111.208.254.0/24
|
||||
111.210.0.0/20
|
||||
111.210.16.0/24
|
||||
111.211.192.0/18
|
||||
111.212.0.0/14
|
||||
111.221.28.0/24
|
||||
111.221.128.0/17
|
||||
@@ -1964,7 +1957,6 @@
|
||||
113.31.96.0/19
|
||||
113.31.144.0/20
|
||||
113.31.160.0/19
|
||||
113.31.192.0/18
|
||||
113.44.0.0/16
|
||||
113.45.0.0/18
|
||||
113.45.64.0/19
|
||||
@@ -1979,7 +1971,10 @@
|
||||
113.46.240.0/21
|
||||
113.47.0.0/18
|
||||
113.47.64.0/19
|
||||
113.47.96.0/21
|
||||
113.47.104.0/22
|
||||
113.47.112.0/20
|
||||
113.47.128.0/18
|
||||
113.47.204.0/22
|
||||
113.47.220.0/22
|
||||
113.47.234.0/23
|
||||
@@ -2045,7 +2040,14 @@
|
||||
114.67.60.0/23
|
||||
114.67.62.0/24
|
||||
114.67.64.0/18
|
||||
114.67.128.0/17
|
||||
114.67.131.0/24
|
||||
114.67.136.0/24
|
||||
114.67.150.0/24
|
||||
114.67.152.0/22
|
||||
114.67.156.0/24
|
||||
114.67.159.0/24
|
||||
114.67.160.0/19
|
||||
114.67.192.0/18
|
||||
114.80.0.0/12
|
||||
114.96.0.0/13
|
||||
114.104.0.0/14
|
||||
@@ -2088,7 +2090,6 @@
|
||||
114.119.36.0/24
|
||||
114.119.117.0/24
|
||||
114.119.119.0/24
|
||||
114.119.204.0/22
|
||||
114.132.0.0/16
|
||||
114.135.0.0/16
|
||||
114.138.0.0/15
|
||||
@@ -2121,6 +2122,7 @@
|
||||
115.174.64.0/19
|
||||
115.175.0.0/18
|
||||
115.175.64.0/19
|
||||
115.175.120.0/21
|
||||
115.182.0.0/15
|
||||
115.190.0.0/17
|
||||
115.190.128.0/19
|
||||
@@ -2138,8 +2140,8 @@
|
||||
116.56.0.0/15
|
||||
116.62.0.0/15
|
||||
116.66.36.0/24
|
||||
116.66.48.0/22
|
||||
116.66.52.0/23
|
||||
116.66.48.0/23
|
||||
116.66.53.0/24
|
||||
116.66.98.0/24
|
||||
116.66.120.0/22
|
||||
116.68.136.0/21
|
||||
@@ -2147,7 +2149,12 @@
|
||||
116.70.64.0/18
|
||||
116.76.0.0/15
|
||||
116.78.0.0/16
|
||||
116.85.0.0/16
|
||||
116.85.0.0/22
|
||||
116.85.13.0/24
|
||||
116.85.14.0/23
|
||||
116.85.16.0/22
|
||||
116.85.64.0/20
|
||||
116.85.240.0/20
|
||||
116.89.240.0/22
|
||||
116.90.80.0/20
|
||||
116.90.192.0/20
|
||||
@@ -2256,7 +2263,6 @@
|
||||
117.72.32.0/20
|
||||
117.72.48.0/21
|
||||
117.72.64.0/18
|
||||
117.72.248.0/22
|
||||
117.72.255.0/24
|
||||
117.73.0.0/20
|
||||
117.73.16.0/21
|
||||
@@ -2268,7 +2274,6 @@
|
||||
117.79.80.0/20
|
||||
117.79.128.0/21
|
||||
117.79.144.0/20
|
||||
117.79.160.0/21
|
||||
117.79.224.0/20
|
||||
117.79.241.0/24
|
||||
117.79.242.0/24
|
||||
@@ -2281,7 +2286,7 @@
|
||||
117.124.98.0/24
|
||||
117.124.231.0/24
|
||||
117.124.232.0/22
|
||||
117.124.237.0/24
|
||||
117.124.236.0/23
|
||||
117.126.0.0/16
|
||||
117.128.0.0/10
|
||||
118.24.0.0/15
|
||||
@@ -2302,7 +2307,7 @@
|
||||
118.26.200.0/21
|
||||
118.26.208.0/20
|
||||
118.26.224.0/19
|
||||
118.30.0.0/15
|
||||
118.31.0.0/16
|
||||
118.64.0.0/21
|
||||
118.64.248.0/21
|
||||
118.66.112.0/23
|
||||
@@ -2367,12 +2372,11 @@
|
||||
118.186.80.0/20
|
||||
118.186.96.0/20
|
||||
118.186.112.0/21
|
||||
118.186.128.0/18
|
||||
118.186.160.0/19
|
||||
118.186.208.0/21
|
||||
118.186.240.0/21
|
||||
118.187.0.0/18
|
||||
118.187.64.0/19
|
||||
118.187.254.0/23
|
||||
118.188.18.0/23
|
||||
118.188.20.0/22
|
||||
118.188.24.0/23
|
||||
@@ -2403,9 +2407,8 @@
|
||||
118.193.188.0/22
|
||||
118.194.32.0/19
|
||||
118.194.128.0/21
|
||||
118.194.164.0/22
|
||||
118.194.240.0/21
|
||||
118.195.0.0/16
|
||||
118.195.128.0/17
|
||||
118.196.0.0/19
|
||||
118.196.32.0/20
|
||||
118.199.0.0/16
|
||||
@@ -2775,7 +2778,7 @@
|
||||
123.58.188.0/22
|
||||
123.58.224.0/19
|
||||
123.59.0.0/16
|
||||
123.60.0.0/15
|
||||
123.60.0.0/16
|
||||
123.64.0.0/15
|
||||
123.66.0.0/16
|
||||
123.77.0.0/19
|
||||
@@ -2974,7 +2977,7 @@
|
||||
139.148.0.0/16
|
||||
139.155.0.0/16
|
||||
139.159.0.0/19
|
||||
139.159.32.0/20
|
||||
139.159.32.0/22
|
||||
139.159.96.0/20
|
||||
139.159.112.0/22
|
||||
139.159.132.0/22
|
||||
@@ -2991,7 +2994,8 @@
|
||||
139.208.0.0/13
|
||||
139.217.0.0/16
|
||||
139.219.0.0/16
|
||||
139.220.128.0/17
|
||||
139.220.192.0/22
|
||||
139.220.240.0/22
|
||||
139.224.0.0/16
|
||||
139.226.0.0/15
|
||||
140.75.0.0/16
|
||||
@@ -3014,7 +3018,6 @@
|
||||
140.249.0.0/16
|
||||
140.250.0.0/16
|
||||
140.255.0.0/16
|
||||
141.11.50.0/23
|
||||
143.64.0.0/16
|
||||
143.92.44.0/22
|
||||
144.0.0.0/16
|
||||
@@ -3042,7 +3045,7 @@
|
||||
146.56.192.0/18
|
||||
146.196.56.0/22
|
||||
146.196.68.0/22
|
||||
146.196.80.0/23
|
||||
146.196.80.0/22
|
||||
146.196.112.0/21
|
||||
146.222.79.0/24
|
||||
146.222.81.0/24
|
||||
@@ -3057,18 +3060,6 @@
|
||||
149.87.239.0/24
|
||||
149.87.240.0/23
|
||||
149.87.242.0/24
|
||||
149.115.227.0/24
|
||||
149.115.228.0/24
|
||||
149.115.233.0/24
|
||||
149.115.234.0/23
|
||||
149.115.239.0/24
|
||||
149.115.240.0/23
|
||||
149.115.243.0/24
|
||||
149.115.244.0/24
|
||||
149.115.246.0/24
|
||||
149.115.248.0/24
|
||||
149.115.255.0/24
|
||||
149.134.158.0/24
|
||||
150.129.80.0/22
|
||||
150.129.136.0/22
|
||||
150.129.192.0/22
|
||||
@@ -3102,9 +3093,7 @@
|
||||
154.8.48.0/20
|
||||
154.8.128.0/17
|
||||
154.9.244.0/22
|
||||
154.19.64.0/22
|
||||
154.19.72.0/21
|
||||
154.19.80.0/22
|
||||
154.19.88.0/22
|
||||
154.19.100.0/22
|
||||
154.19.112.0/20
|
||||
@@ -3117,36 +3106,30 @@
|
||||
154.83.28.0/24
|
||||
154.89.32.0/20
|
||||
154.91.158.0/23
|
||||
154.195.64.0/19
|
||||
154.197.137.0/24
|
||||
154.197.153.0/24
|
||||
154.197.156.0/24
|
||||
154.197.163.0/24
|
||||
154.197.168.0/24
|
||||
154.197.172.0/24
|
||||
154.197.189.0/24
|
||||
154.197.192.0/24
|
||||
154.197.206.0/24
|
||||
154.197.208.0/24
|
||||
154.197.212.0/24
|
||||
154.197.224.0/24
|
||||
154.197.232.0/24
|
||||
154.197.240.0/24
|
||||
154.205.64.0/20
|
||||
154.205.80.0/22
|
||||
154.205.84.0/23
|
||||
154.205.87.0/24
|
||||
154.205.88.0/21
|
||||
154.205.96.0/20
|
||||
154.205.112.0/21
|
||||
154.205.124.0/22
|
||||
154.197.248.0/24
|
||||
154.208.140.0/22
|
||||
154.208.144.0/20
|
||||
154.208.160.0/21
|
||||
154.208.172.0/23
|
||||
154.213.4.0/23
|
||||
154.223.96.0/19
|
||||
155.102.0.0/22
|
||||
155.102.4.0/23
|
||||
155.102.9.0/24
|
||||
155.102.10.0/24
|
||||
155.102.10.0/23
|
||||
155.102.12.0/22
|
||||
155.102.16.0/22
|
||||
155.102.20.0/24
|
||||
@@ -3155,11 +3138,7 @@
|
||||
155.102.27.0/24
|
||||
155.102.28.0/23
|
||||
155.102.30.0/24
|
||||
155.102.32.0/23
|
||||
155.102.34.0/24
|
||||
155.102.36.0/24
|
||||
155.102.38.0/23
|
||||
155.102.40.0/21
|
||||
155.102.32.0/20
|
||||
155.102.49.0/24
|
||||
155.102.50.0/23
|
||||
155.102.52.0/22
|
||||
@@ -3185,21 +3164,15 @@
|
||||
156.224.200.0/24
|
||||
156.224.224.0/24
|
||||
156.224.232.0/24
|
||||
156.227.40.0/21
|
||||
156.227.48.0/20
|
||||
156.230.11.0/24
|
||||
156.230.12.0/23
|
||||
156.232.9.0/24
|
||||
156.232.10.0/23
|
||||
156.236.119.0/24
|
||||
156.237.104.0/23
|
||||
156.239.0.0/20
|
||||
156.239.64.0/18
|
||||
156.239.224.0/19
|
||||
156.242.5.0/24
|
||||
156.242.6.0/24
|
||||
156.247.8.0/22
|
||||
156.247.12.0/23
|
||||
156.247.12.0/24
|
||||
156.247.14.0/24
|
||||
156.255.2.0/23
|
||||
157.0.0.0/16
|
||||
@@ -3220,33 +3193,36 @@
|
||||
158.26.192.0/24
|
||||
158.26.194.0/24
|
||||
158.140.252.0/23
|
||||
158.140.254.0/24
|
||||
159.27.0.0/16
|
||||
159.75.0.0/16
|
||||
159.226.0.0/16
|
||||
160.19.76.0/23
|
||||
160.19.208.0/21
|
||||
160.19.208.0/22
|
||||
160.20.18.0/23
|
||||
160.22.188.0/24
|
||||
160.22.244.0/23
|
||||
160.25.20.0/23
|
||||
160.30.230.0/23
|
||||
160.83.110.0/24
|
||||
160.191.0.0/24
|
||||
160.191.194.0/23
|
||||
160.202.212.0/22
|
||||
160.202.224.0/19
|
||||
160.250.10.0/24
|
||||
160.250.14.0/23
|
||||
160.250.18.0/24
|
||||
161.163.0.0/21
|
||||
161.163.28.0/23
|
||||
161.189.0.0/16
|
||||
161.207.0.0/16
|
||||
162.0.152.0/24
|
||||
162.14.0.0/16
|
||||
162.105.0.0/16
|
||||
162.248.72.0/21
|
||||
163.0.0.0/16
|
||||
163.5.166.0/24
|
||||
163.47.4.0/22
|
||||
163.53.44.0/24
|
||||
163.53.46.0/23
|
||||
163.53.60.0/22
|
||||
163.53.88.0/21
|
||||
@@ -3322,13 +3298,16 @@
|
||||
163.181.204.0/22
|
||||
163.181.209.0/24
|
||||
163.181.210.0/23
|
||||
163.181.212.0/22
|
||||
163.181.212.0/23
|
||||
163.181.214.0/24
|
||||
163.181.216.0/21
|
||||
163.181.224.0/23
|
||||
163.181.228.0/22
|
||||
163.181.232.0/24
|
||||
163.181.234.0/24
|
||||
163.181.236.0/22
|
||||
163.181.241.0/24
|
||||
163.181.242.0/23
|
||||
163.181.244.0/23
|
||||
163.181.246.0/24
|
||||
163.181.248.0/22
|
||||
@@ -3338,7 +3317,6 @@
|
||||
164.155.133.0/24
|
||||
166.111.0.0/16
|
||||
167.139.0.0/16
|
||||
167.189.0.0/16
|
||||
167.220.244.0/22
|
||||
168.159.144.0/21
|
||||
168.159.152.0/22
|
||||
@@ -3379,8 +3357,7 @@
|
||||
175.46.0.0/15
|
||||
175.102.0.0/19
|
||||
175.102.32.0/22
|
||||
175.102.128.0/20
|
||||
175.102.144.0/21
|
||||
175.102.128.0/21
|
||||
175.102.178.0/23
|
||||
175.102.180.0/22
|
||||
175.102.184.0/24
|
||||
@@ -3524,8 +3501,6 @@
|
||||
185.227.152.0/22
|
||||
185.232.92.0/23
|
||||
185.234.212.0/24
|
||||
185.238.248.0/24
|
||||
185.238.250.0/24
|
||||
185.239.84.0/22
|
||||
185.242.232.0/22
|
||||
185.243.240.0/22
|
||||
@@ -3547,9 +3522,9 @@
|
||||
192.232.97.0/24
|
||||
193.9.44.0/24
|
||||
193.9.46.0/24
|
||||
193.22.152.0/24
|
||||
193.112.0.0/16
|
||||
193.119.10.0/24
|
||||
193.119.13.0/24
|
||||
193.119.20.0/24
|
||||
193.119.30.0/24
|
||||
194.15.39.0/24
|
||||
@@ -3566,7 +3541,6 @@
|
||||
194.169.180.0/23
|
||||
196.50.192.0/18
|
||||
198.175.100.0/22
|
||||
198.187.64.0/18
|
||||
198.208.17.0/24
|
||||
198.208.19.0/24
|
||||
198.208.30.0/24
|
||||
@@ -3628,8 +3602,11 @@
|
||||
202.60.112.0/20
|
||||
202.60.132.0/22
|
||||
202.61.88.0/22
|
||||
202.61.128.0/21
|
||||
202.61.136.0/22
|
||||
202.61.128.0/23
|
||||
202.61.131.0/24
|
||||
202.61.132.0/22
|
||||
202.61.136.0/24
|
||||
202.61.138.0/23
|
||||
202.61.140.0/24
|
||||
202.61.142.0/23
|
||||
202.61.144.0/21
|
||||
@@ -3656,7 +3633,7 @@
|
||||
202.81.176.0/20
|
||||
202.84.17.0/24
|
||||
202.85.208.0/20
|
||||
202.89.96.0/22
|
||||
202.89.96.0/24
|
||||
202.89.232.0/21
|
||||
202.90.20.0/22
|
||||
202.90.96.0/20
|
||||
@@ -3942,7 +3919,7 @@
|
||||
203.107.108.0/23
|
||||
203.110.160.0/19
|
||||
203.110.208.0/20
|
||||
203.110.232.0/23
|
||||
203.110.232.0/24
|
||||
203.114.244.0/22
|
||||
203.118.248.0/22
|
||||
203.119.25.0/24
|
||||
@@ -3994,7 +3971,6 @@
|
||||
203.168.8.0/24
|
||||
203.168.16.0/23
|
||||
203.168.18.0/24
|
||||
203.170.58.0/23
|
||||
203.174.4.0/24
|
||||
203.174.96.0/19
|
||||
203.175.128.0/19
|
||||
@@ -4053,7 +4029,6 @@
|
||||
206.54.1.128/25
|
||||
206.237.8.0/23
|
||||
206.237.16.0/20
|
||||
206.237.112.0/20
|
||||
207.226.153.0/24
|
||||
207.226.154.0/24
|
||||
210.2.0.0/23
|
||||
@@ -4300,17 +4275,13 @@
|
||||
211.160.204.0/23
|
||||
211.160.240.0/20
|
||||
211.161.0.0/20
|
||||
211.161.20.0/22
|
||||
211.161.24.0/22
|
||||
211.161.32.0/20
|
||||
211.161.52.0/22
|
||||
211.161.56.0/21
|
||||
211.161.80.0/20
|
||||
211.161.60.0/22
|
||||
211.161.97.0/24
|
||||
211.161.101.0/24
|
||||
211.161.102.0/23
|
||||
211.161.120.0/21
|
||||
211.161.128.0/20
|
||||
211.161.192.0/22
|
||||
211.161.203.0/24
|
||||
211.161.209.0/24
|
||||
@@ -4321,7 +4292,6 @@
|
||||
211.162.112.0/20
|
||||
211.162.192.0/22
|
||||
211.162.200.0/22
|
||||
211.162.240.0/20
|
||||
211.165.0.0/16
|
||||
211.166.0.0/16
|
||||
211.167.64.0/18
|
||||
@@ -4354,7 +4324,7 @@
|
||||
218.98.111.0/24
|
||||
218.98.112.0/20
|
||||
218.98.160.0/24
|
||||
218.98.176.0/20
|
||||
218.98.176.0/21
|
||||
218.98.192.0/24
|
||||
218.100.88.0/21
|
||||
218.104.0.0/15
|
||||
@@ -4373,7 +4343,6 @@
|
||||
218.240.184.0/24
|
||||
218.240.255.0/24
|
||||
218.241.16.0/21
|
||||
218.241.24.0/22
|
||||
218.241.96.0/19
|
||||
218.241.128.0/17
|
||||
218.242.0.0/16
|
||||
@@ -4391,6 +4360,7 @@
|
||||
218.245.0.0/17
|
||||
218.246.0.0/19
|
||||
218.246.32.0/22
|
||||
218.246.48.0/22
|
||||
218.246.59.0/24
|
||||
218.246.64.0/18
|
||||
218.246.160.0/19
|
||||
@@ -4434,7 +4404,8 @@
|
||||
219.235.0.0/20
|
||||
219.235.32.0/19
|
||||
219.235.64.0/18
|
||||
219.235.128.0/19
|
||||
219.235.128.0/20
|
||||
219.235.144.0/21
|
||||
219.235.192.0/23
|
||||
219.235.207.0/24
|
||||
219.235.224.0/22
|
||||
@@ -4451,21 +4422,12 @@
|
||||
220.101.192.0/18
|
||||
220.112.0.0/18
|
||||
220.112.192.0/20
|
||||
220.112.224.0/19
|
||||
220.113.0.0/18
|
||||
220.113.0.0/19
|
||||
220.113.32.0/20
|
||||
220.113.96.0/21
|
||||
220.113.128.0/21
|
||||
220.113.136.0/22
|
||||
220.113.144.0/20
|
||||
220.113.168.0/21
|
||||
220.113.180.0/22
|
||||
220.113.184.0/22
|
||||
220.113.150.0/23
|
||||
220.113.152.0/21
|
||||
220.114.250.0/23
|
||||
220.115.8.0/21
|
||||
220.115.16.0/20
|
||||
220.115.228.0/22
|
||||
220.115.232.0/21
|
||||
220.115.240.0/21
|
||||
220.152.128.0/17
|
||||
220.160.0.0/12
|
||||
220.176.0.0/14
|
||||
@@ -4540,8 +4502,7 @@
|
||||
221.133.232.0/22
|
||||
221.133.244.0/23
|
||||
221.137.0.0/16
|
||||
221.172.0.0/16
|
||||
221.174.0.0/16
|
||||
221.174.0.0/17
|
||||
221.176.0.0/13
|
||||
221.192.0.0/14
|
||||
221.196.0.0/15
|
||||
|
||||
@@ -1 +1 @@
|
||||
20250127031156
|
||||
20250311032050
|
||||
|
||||
@@ -28,11 +28,10 @@
|
||||
2400:5a60:100::/48
|
||||
2400:5f60::/32
|
||||
2400:6000::/32
|
||||
2400:6460::/40
|
||||
2400:6460::/39
|
||||
2400:6600::/32
|
||||
2400:6e60:1301::/48
|
||||
2400:7100::/32
|
||||
2400:73e0::/32
|
||||
2400:75aa::/32
|
||||
2400:7bc0:20::/43
|
||||
2400:7fc0::/40
|
||||
@@ -109,7 +108,6 @@
|
||||
2400:da00::/32
|
||||
2400:dd00::/28
|
||||
2400:ebc0::/32
|
||||
2400:ed60::/48
|
||||
2400:ee00::/32
|
||||
2400:f6e0::/32
|
||||
2400:f720::/32
|
||||
@@ -119,6 +117,7 @@
|
||||
2401:800::/32
|
||||
2401:ba0::/32
|
||||
2401:1160::/32
|
||||
2401:11a0:10::/44
|
||||
2401:11a0:1500::/40
|
||||
2401:11a0:d150::/48
|
||||
2401:11a0:d152::/48
|
||||
@@ -150,6 +149,7 @@
|
||||
2401:4780::/32
|
||||
2401:4880::/32
|
||||
2401:4a80::/32
|
||||
2401:5c20:10::/48
|
||||
2401:70e0::/32
|
||||
2401:71c0::/48
|
||||
2401:7700::/32
|
||||
@@ -195,6 +195,7 @@
|
||||
2402:1440::/32
|
||||
2402:2000::/32
|
||||
2402:3180::/48
|
||||
2402:3180:2::/47
|
||||
2402:3180:8000::/33
|
||||
2402:3c00::/32
|
||||
2402:3f80:1400::/40
|
||||
@@ -220,6 +221,7 @@
|
||||
2402:b8c0:6::/48
|
||||
2402:b8c0:86::/48
|
||||
2402:b8c0:106::/48
|
||||
2402:b8c0:186::/48
|
||||
2402:d340::/32
|
||||
2402:db40:5100::/48
|
||||
2402:db40:5900::/48
|
||||
@@ -313,6 +315,8 @@
|
||||
2404:2280:160::/48
|
||||
2404:2280:170::/48
|
||||
2404:2280:177::/48
|
||||
2404:2280:17a::/47
|
||||
2404:2280:17c::/48
|
||||
2404:2280:17e::/47
|
||||
2404:2280:180::/47
|
||||
2404:2280:183::/48
|
||||
@@ -340,7 +344,7 @@
|
||||
2404:2280:1d0::/47
|
||||
2404:2280:1d3::/48
|
||||
2404:2280:1d4::/48
|
||||
2404:2280:1d6::/48
|
||||
2404:2280:1d6::/47
|
||||
2404:2280:1d8::/45
|
||||
2404:2280:1e0::/45
|
||||
2404:2280:1e8::/46
|
||||
@@ -349,6 +353,7 @@
|
||||
2404:2280:1f0::/45
|
||||
2404:2280:1f8::/46
|
||||
2404:3700::/48
|
||||
2404:4dc0::/32
|
||||
2404:6380::/48
|
||||
2404:6380:1000::/48
|
||||
2404:6380:8001::/48
|
||||
@@ -386,9 +391,6 @@
|
||||
2404:e280::/47
|
||||
2404:e8c0::/32
|
||||
2404:f4c0:f000::/44
|
||||
2404:f4c0:fa00::/48
|
||||
2404:f4c0:fa02::/48
|
||||
2404:f4c0:fa0b::/48
|
||||
2405:80:1::/48
|
||||
2405:80:13::/48
|
||||
2405:6c0:2::/48
|
||||
@@ -425,28 +427,26 @@
|
||||
2406:280::/32
|
||||
2406:840:9000::/44
|
||||
2406:840:9961::/48
|
||||
2406:840:9962::/47
|
||||
2406:840:9962::/48
|
||||
2406:840:996c::/48
|
||||
2406:840:e031::/48
|
||||
2406:840:e033::/48
|
||||
2406:840:e03f::/48
|
||||
2406:840:e080::/44
|
||||
2406:840:e0cf::/48
|
||||
2406:840:e0e0::/47
|
||||
2406:840:e0e1::/48
|
||||
2406:840:e0e2::/48
|
||||
2406:840:e0e4::/47
|
||||
2406:840:e0e5::/48
|
||||
2406:840:e0e8::/48
|
||||
2406:840:e10f::/48
|
||||
2406:840:e14f::/48
|
||||
2406:840:e20f::/48
|
||||
2406:840:e230::/44
|
||||
2406:840:e36f::/48
|
||||
2406:840:e500::/47
|
||||
2406:840:e621::/48
|
||||
2406:840:e666::/47
|
||||
2406:840:e720::/44
|
||||
2406:840:e80f::/48
|
||||
2406:840:eab0::/48
|
||||
2406:840:eab4::/48
|
||||
2406:840:eabb::/48
|
||||
2406:840:eabc::/47
|
||||
2406:840:eabe::/48
|
||||
@@ -465,15 +465,13 @@
|
||||
2406:840:f380::/44
|
||||
2406:840:fc80::/44
|
||||
2406:840:fcd0::/48
|
||||
2406:840:fd00::/47
|
||||
2406:840:fd03::/48
|
||||
2406:840:fd1f::/48
|
||||
2406:840:fd40::/42
|
||||
2406:840:fd80::/42
|
||||
2406:840:fdc0::/44
|
||||
2406:840:fdd1::/48
|
||||
2406:840:fe27::/48
|
||||
2406:840:fe90::/46
|
||||
2406:840:fe94::/48
|
||||
2406:840:fe96::/47
|
||||
2406:840:fe98::/46
|
||||
2406:840:feab::/48
|
||||
@@ -521,6 +519,7 @@
|
||||
2407:2840::/48
|
||||
2407:3740::/48
|
||||
2407:37c0::/32
|
||||
2407:4980::/32
|
||||
2407:5380::/32
|
||||
2407:6c40:1100::/48
|
||||
2407:6c40:1210::/48
|
||||
@@ -1160,7 +1159,6 @@
|
||||
240c:c000::/20
|
||||
240d:4000::/21
|
||||
240e::/20
|
||||
2601:1d08:4000::/44
|
||||
2602:2e0:ff::/48
|
||||
2602:f7ee:ee::/48
|
||||
2602:f9ba:a8::/48
|
||||
@@ -1173,8 +1171,8 @@
|
||||
2602:feda:1bf::/48
|
||||
2602:feda:1d1::/48
|
||||
2602:feda:1df::/48
|
||||
2602:feda:2d0::/47
|
||||
2602:feda:2f0::/48
|
||||
2602:feda:2d0::/44
|
||||
2602:feda:2f0::/44
|
||||
2602:feda:bd0::/48
|
||||
2602:feda:d80::/48
|
||||
2605:9d80:8001::/48
|
||||
@@ -1218,25 +1216,23 @@
|
||||
2a04:f580:9270::/48
|
||||
2a04:f580:9280::/48
|
||||
2a04:f580:9290::/48
|
||||
2a05:dfc7:4000::/34
|
||||
2a06:1281::/36
|
||||
2a05:b900::/29
|
||||
2a06:1281:b100::/40
|
||||
2a06:1281:b200::/39
|
||||
2a06:1281:b400::/38
|
||||
2a06:1281:b800::/39
|
||||
2a06:3601::/32
|
||||
2a06:3602::/31
|
||||
2a06:9f81:4600::/44
|
||||
2a06:3603::/32
|
||||
2a06:3607::/32
|
||||
2a06:9f81:4600::/43
|
||||
2a06:9f81:4620::/44
|
||||
2a06:9f81:4640::/43
|
||||
2a06:9f81:4660::/44
|
||||
2a06:9f81:5100::/40
|
||||
2a06:9f81:5400::/40
|
||||
2a06:9f81:6100::/40
|
||||
2a06:9f81:640b::/48
|
||||
2a06:9f81:6455::/48
|
||||
2a06:9f81:6488::/48
|
||||
2a06:9f81:649f::/48
|
||||
2a06:9f81:64a1::/48
|
||||
2a06:a005:260::/43
|
||||
2a06:a005:280::/43
|
||||
@@ -1247,31 +1243,23 @@
|
||||
2a06:a005:a13::/48
|
||||
2a06:a005:e80::/43
|
||||
2a06:a005:1c40::/44
|
||||
2a09:54c6:2000::/36
|
||||
2a09:54c6:4000::/35
|
||||
2a09:54c6:4000::/36
|
||||
2a09:54c6:c100::/40
|
||||
2a09:54c6:c200::/39
|
||||
2a09:54c6:c400::/39
|
||||
2a09:54c6:c200::/40
|
||||
2a09:54c6:c400::/40
|
||||
2a09:b280:ff83::/48
|
||||
2a09:b280:ff84::/47
|
||||
2a0a:2840:20::/43
|
||||
2a0a:2845:aab8::/46
|
||||
2a0a:6040:3410::/48
|
||||
2a0a:6040:3430::/48
|
||||
2a0a:6040:34ff::/48
|
||||
2a0a:6040:ec00::/40
|
||||
2a0a:6044:6600::/40
|
||||
2a0a:6044:6e00::/47
|
||||
2a0a:6044:6e02::/48
|
||||
2a0a:6044:b800::/40
|
||||
2a0b:b87:ffb5::/48
|
||||
2a0b:2542::/48
|
||||
2a0b:4340:a6::/48
|
||||
2a0b:4e07:b8::/47
|
||||
2a0c:9a40:84e0::/48
|
||||
2a0c:9a46:800::/43
|
||||
2a0c:b641:571::/48
|
||||
2a0e:8f02:2182::/47
|
||||
2a0e:8f02:f055::/48
|
||||
2a0e:8f02:f067::/48
|
||||
2a0e:97c0:550::/44
|
||||
2a0e:97c0:83f::/48
|
||||
@@ -1281,7 +1269,6 @@
|
||||
2a0e:aa06:490::/44
|
||||
2a0e:aa06:500::/44
|
||||
2a0e:aa06:520::/48
|
||||
2a0e:aa06:525::/48
|
||||
2a0e:aa06:541::/48
|
||||
2a0e:aa07:e01b::/48
|
||||
2a0e:aa07:e024::/47
|
||||
@@ -1289,48 +1276,55 @@
|
||||
2a0e:aa07:e035::/48
|
||||
2a0e:aa07:e039::/48
|
||||
2a0e:aa07:e044::/48
|
||||
2a0e:aa07:e150::/44
|
||||
2a0e:aa07:e151::/48
|
||||
2a0e:aa07:e16a::/48
|
||||
2a0e:aa07:e1a0::/43
|
||||
2a0e:aa07:e1e1::/48
|
||||
2a0e:aa07:e1e2::/47
|
||||
2a0e:aa07:e1e4::/48
|
||||
2a0e:aa07:e200::/44
|
||||
2a0e:aa07:f0d0::/47
|
||||
2a0e:aa07:e1e2::/48
|
||||
2a0e:aa07:e1e4::/47
|
||||
2a0e:aa07:e200::/43
|
||||
2a0e:aa07:e220::/44
|
||||
2a0e:aa07:f0d1::/48
|
||||
2a0e:aa07:f0d2::/48
|
||||
2a0e:aa07:f0d4::/47
|
||||
2a0e:aa07:f0d5::/48
|
||||
2a0e:aa07:f0d8::/48
|
||||
2a0e:aa07:f0de::/48
|
||||
2a0e:b107:12b::/48
|
||||
2a0e:b107:272::/48
|
||||
2a0e:b107:740::/44
|
||||
2a0e:b107:c10::/48
|
||||
2a0e:b107:dce::/48
|
||||
2a0e:b107:16b0::/44
|
||||
2a0e:b107:16c0::/44
|
||||
2a0e:b107:178d::/48
|
||||
2a0e:b107:178c::/47
|
||||
2a0f:1f80::/29
|
||||
2a0f:5707:ac00::/47
|
||||
2a0f:7803:dd00::/42
|
||||
2a0f:7803:f5d0::/44
|
||||
2a0f:7803:f5e0::/43
|
||||
2a0f:7803:f680::/43
|
||||
2a0f:7803:f6a0::/44
|
||||
2a0f:7803:f7c0::/44
|
||||
2a0f:7803:f7e0::/43
|
||||
2a0f:7803:f7c0::/42
|
||||
2a0f:7803:f800::/43
|
||||
2a0f:7803:f840::/44
|
||||
2a0f:7803:f860::/44
|
||||
2a0f:7803:f8b0::/44
|
||||
2a0f:7803:f970::/44
|
||||
2a0f:7803:fe22::/48
|
||||
2a0f:7803:fa21::/48
|
||||
2a0f:7803:fa22::/47
|
||||
2a0f:7803:fa24::/46
|
||||
2a0f:7803:faf3::/48
|
||||
2a0f:7803:faf7::/48
|
||||
2a0f:7803:fe81::/48
|
||||
2a0f:7803:fe82::/48
|
||||
2a0f:7803:fe84::/48
|
||||
2a0f:7804:da00::/40
|
||||
2a0f:7807::/32
|
||||
2a0f:7d07::/32
|
||||
2a0f:85c1:816::/48
|
||||
2a0f:85c1:8f4::/48
|
||||
2a0f:85c1:b3a::/48
|
||||
2a0f:9400:6110::/48
|
||||
2a0f:9400:7700::/48
|
||||
2a0f:ac00::/29
|
||||
2a10:2f00:15a::/48
|
||||
2a10:cc40:190::/48
|
||||
2a12:4ac0::/29
|
||||
2a12:f8c0:1000::/40
|
||||
2a12:f8c3::/36
|
||||
2a13:1800::/48
|
||||
@@ -1338,44 +1332,37 @@
|
||||
2a13:1800:80::/44
|
||||
2a13:1800:300::/44
|
||||
2a13:1801:180::/43
|
||||
2a13:a5c3:ff10::/44
|
||||
2a13:a5c3:ff21::/48
|
||||
2a13:a5c3:ff50::/44
|
||||
2a13:a5c7:1800::/40
|
||||
2a13:a5c7:2100::/48
|
||||
2a13:a5c7:2102::/48
|
||||
2a13:a5c7:2109::/48
|
||||
2a13:a5c7:2110::/48
|
||||
2a13:a5c7:2117::/48
|
||||
2a13:a5c7:2118::/48
|
||||
2a13:a5c7:2121::/48
|
||||
2a13:a5c7:2200::/40
|
||||
2a13:a5c7:2801::/48
|
||||
2a13:a5c7:2803::/48
|
||||
2a13:aac4:f000::/44
|
||||
2a14:7c0:4a01::/48
|
||||
2a14:4c41::/32
|
||||
2a14:67c1:20::/44
|
||||
2a14:67c1:70::/46
|
||||
2a14:67c1:510::/44
|
||||
2a14:67c1:520::/44
|
||||
2a14:67c1:610::/44
|
||||
2a14:67c1:701::/48
|
||||
2a14:67c1:703::/48
|
||||
2a14:67c1:704::/48
|
||||
2a14:67c1:800::/48
|
||||
2a14:67c1:802::/48
|
||||
2a14:67c1:804::/48
|
||||
2a14:67c1:80b::/48
|
||||
2a14:67c1:1000::/37
|
||||
2a14:67c1:a010::/44
|
||||
2a14:67c1:a020::/47
|
||||
2a14:67c1:a024::/48
|
||||
2a14:67c1:a02a::/48
|
||||
2a14:67c1:a02f::/48
|
||||
2a14:7580:9200::/40
|
||||
2a14:67c1:b066::/48
|
||||
2a14:67c1:b100::/47
|
||||
2a14:67c5:1000::/36
|
||||
2a14:7580:9202::/47
|
||||
2a14:7580:9204::/46
|
||||
2a14:7580:9400::/39
|
||||
2a14:7580:c000::/35
|
||||
2a14:7580:d000::/36
|
||||
2a14:7580:e200::/40
|
||||
2a14:7581:9010::/44
|
||||
2a14:7584:9000::/36
|
||||
2c0f:f7a8:8011::/48
|
||||
2c0f:f7a8:8050::/48
|
||||
2c0f:f7a8:805f::/48
|
||||
|
||||
@@ -1 +1 @@
|
||||
20250127031156
|
||||
20250311032050
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
||||
202501262210
|
||||
202503102212
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
||||
202501262210
|
||||
202503102212
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#!/usr/bin/utpl
|
||||
#!/usr/bin/utpl -S
|
||||
|
||||
{%-
|
||||
'use strict';
|
||||
|
||||
import { readfile } from 'fs';
|
||||
import { cursor } from 'uci';
|
||||
import { isEmpty } from '/etc/homeproxy/scripts/homeproxy.uc';
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*
|
||||
* Copyright (C) 2023-2024 ImmortalWrt.org
|
||||
* Copyright (C) 2023-2025 ImmortalWrt.org
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
|
||||
const ubus = connect();
|
||||
|
||||
const features = ubus.call('luci.homeproxy', 'singbox_get_features') || {};
|
||||
/* const features = ubus.call('luci.homeproxy', 'singbox_get_features') || {}; */
|
||||
|
||||
/* UCI config start */
|
||||
const uci = cursor();
|
||||
@@ -153,6 +153,36 @@ function parse_dnsquery(strquery) {
|
||||
|
||||
}
|
||||
|
||||
function generate_endpoint(node) {
|
||||
if (type(node) !== 'object' || isEmpty(node))
|
||||
return null;
|
||||
|
||||
const endpoint = {
|
||||
type: node.type,
|
||||
tag: 'cfg-' + node['.name'] + '-out',
|
||||
address: node.wireguard_local_address,
|
||||
mtu: strToInt(node.wireguard_mtu),
|
||||
private_key: node.wireguard_private_key,
|
||||
peers: (node.type === 'wireguard') ? [
|
||||
{
|
||||
address: node.address,
|
||||
port: strToInt(node.port),
|
||||
allowed_ips: [
|
||||
'0.0.0.0/0',
|
||||
'::/0'
|
||||
],
|
||||
persistent_keepalive_interval: strToInt(node.wireguard_persistent_keepalive_interval),
|
||||
public_key: node.wireguard_peer_public_key,
|
||||
pre_shared_key: node.wireguard_pre_shared_key,
|
||||
reserved: parse_port(node.wireguard_reserved),
|
||||
}
|
||||
] : null,
|
||||
system: (node.type === 'wireguard') ? false : null,
|
||||
};
|
||||
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
function generate_outbound(node) {
|
||||
if (type(node) !== 'object' || isEmpty(node))
|
||||
return null;
|
||||
@@ -164,6 +194,8 @@ function generate_outbound(node) {
|
||||
|
||||
server: node.address,
|
||||
server_port: strToInt(node.port),
|
||||
/* Hysteria(2) */
|
||||
server_ports: node.hysteria_hopping_port,
|
||||
|
||||
username: (node.type !== 'ssh') ? node.username : null,
|
||||
user: (node.type === 'ssh') ? node.username : null,
|
||||
@@ -174,6 +206,7 @@ function generate_outbound(node) {
|
||||
override_port: strToInt(node.override_port),
|
||||
proxy_protocol: strToInt(node.proxy_protocol),
|
||||
/* Hysteria (2) */
|
||||
hop_interval: node.hysteria_hop_interval ? (node.hysteria_hop_interval + 's') : null,
|
||||
up_mbps: strToInt(node.hysteria_up_mbps),
|
||||
down_mbps: strToInt(node.hysteria_down_mbps),
|
||||
obfs: node.hysteria_obfs_type ? {
|
||||
@@ -211,14 +244,6 @@ function generate_outbound(node) {
|
||||
global_padding: node.vmess_global_padding ? (node.vmess_global_padding === '1') : null,
|
||||
authenticated_length: node.vmess_authenticated_length ? (node.vmess_authenticated_length === '1') : null,
|
||||
packet_encoding: node.packet_encoding,
|
||||
/* WireGuard */
|
||||
gso: (node.wireguard_gso === '1') || null,
|
||||
local_address: node.wireguard_local_address,
|
||||
private_key: node.wireguard_private_key,
|
||||
peer_public_key: node.wireguard_peer_public_key,
|
||||
pre_shared_key: node.wireguard_pre_shared_key,
|
||||
reserved: parse_port(node.wireguard_reserved),
|
||||
mtu: strToInt(node.wireguard_mtu),
|
||||
|
||||
multiplex: (node.multiplex === '1') ? {
|
||||
enabled: true,
|
||||
@@ -244,7 +269,6 @@ function generate_outbound(node) {
|
||||
certificate_path: node.tls_cert_path,
|
||||
ech: (node.tls_ech === '1') ? {
|
||||
enabled: true,
|
||||
dynamic_record_sizing_disabled: (node.tls_ech_tls_disable_drs === '1'),
|
||||
pq_signature_schemes_enabled: (node.tls_ech_enable_pqss === '1'),
|
||||
config: node.tls_ech_config,
|
||||
config_path: node.tls_ech_config_path
|
||||
@@ -299,9 +323,12 @@ function get_outbound(cfg) {
|
||||
push(outbounds, get_outbound(i));
|
||||
return outbounds;
|
||||
} else {
|
||||
if (cfg in ['direct-out', 'block-out']) {
|
||||
switch (cfg) {
|
||||
case 'block-out':
|
||||
return null;
|
||||
case 'direct-out':
|
||||
return cfg;
|
||||
} else {
|
||||
default:
|
||||
const node = uci.get(uciconfig, cfg, 'node');
|
||||
if (isEmpty(node))
|
||||
die(sprintf("%s's node is missing, please check your configuration.", cfg));
|
||||
@@ -317,10 +344,15 @@ function get_resolver(cfg) {
|
||||
if (isEmpty(cfg))
|
||||
return null;
|
||||
|
||||
if (cfg in ['default-dns', 'system-dns', 'block-dns'])
|
||||
switch (cfg) {
|
||||
case 'block-dns':
|
||||
return null;
|
||||
case 'default-dns':
|
||||
case 'system-dns':
|
||||
return cfg;
|
||||
else
|
||||
default:
|
||||
return 'cfg-' + cfg + '-dns';
|
||||
}
|
||||
}
|
||||
|
||||
function get_ruleset(cfg) {
|
||||
@@ -357,10 +389,6 @@ config.dns = {
|
||||
tag: 'system-dns',
|
||||
address: 'local',
|
||||
detour: 'direct-out'
|
||||
},
|
||||
{
|
||||
tag: 'block-dns',
|
||||
address: 'rcode://name_error'
|
||||
}
|
||||
],
|
||||
rules: [],
|
||||
@@ -386,12 +414,14 @@ if (!isEmpty(main_node)) {
|
||||
/* Avoid DNS loop */
|
||||
push(config.dns.rules, {
|
||||
outbound: 'any',
|
||||
action: 'route',
|
||||
server: 'default-dns'
|
||||
});
|
||||
|
||||
if (length(direct_domain_list))
|
||||
push(config.dns.rules, {
|
||||
rule_set: 'direct-domain',
|
||||
action: 'route',
|
||||
server: (routing_mode === 'bypass_mainland_china' ) ? 'china-dns' : 'default-dns'
|
||||
});
|
||||
|
||||
@@ -400,7 +430,7 @@ if (!isEmpty(main_node)) {
|
||||
push(config.dns.rules, {
|
||||
rule_set: (routing_mode !== 'gfwlist') ? 'proxy-domain' : null,
|
||||
query_type: [64, 65],
|
||||
server: 'block-dns'
|
||||
action: 'reject'
|
||||
});
|
||||
|
||||
if (routing_mode === 'bypass_mainland_china') {
|
||||
@@ -414,11 +444,13 @@ if (!isEmpty(main_node)) {
|
||||
if (length(proxy_domain_list))
|
||||
push(config.dns.rules, {
|
||||
rule_set: 'proxy-domain',
|
||||
action: 'route',
|
||||
server: 'main-dns'
|
||||
});
|
||||
|
||||
push(config.dns.rules, {
|
||||
rule_set: 'geosite-cn',
|
||||
action: 'route',
|
||||
server: 'china-dns'
|
||||
});
|
||||
push(config.dns.rules, {
|
||||
@@ -433,6 +465,7 @@ if (!isEmpty(main_node)) {
|
||||
rule_set: 'geoip-cn'
|
||||
}
|
||||
],
|
||||
action: 'route',
|
||||
server: 'china-dns'
|
||||
});
|
||||
}
|
||||
@@ -484,10 +517,12 @@ if (!isEmpty(main_node)) {
|
||||
rule_set_ip_cidr_match_source: (cfg.rule_set_ip_cidr_match_source === '1') || null,
|
||||
invert: (cfg.invert === '1') || null,
|
||||
outbound: get_outbound(cfg.outbound),
|
||||
action: (cfg.server === 'block-dns') ? 'reject' : 'route',
|
||||
server: get_resolver(cfg.server),
|
||||
disable_cache: (cfg.dns_disable_cache === '1') || null,
|
||||
rewrite_ttl: strToInt(cfg.rewrite_ttl),
|
||||
client_subnet: cfg.client_subnet
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -516,7 +551,6 @@ push(config.inbounds, {
|
||||
udp_timeout: udp_timeout ? (udp_timeout + 's') : null,
|
||||
sniff: true,
|
||||
sniff_override_destination: (sniff_override === '1'),
|
||||
domain_strategy: domain_strategy,
|
||||
set_system_proxy: false
|
||||
});
|
||||
|
||||
@@ -528,8 +562,7 @@ if (match(proxy_mode, /redirect/))
|
||||
listen: '::',
|
||||
listen_port: int(redirect_port),
|
||||
sniff: true,
|
||||
sniff_override_destination: (sniff_override === '1'),
|
||||
domain_strategy: domain_strategy,
|
||||
sniff_override_destination: (sniff_override === '1')
|
||||
});
|
||||
if (match(proxy_mode, /tproxy/))
|
||||
push(config.inbounds, {
|
||||
@@ -541,8 +574,7 @@ if (match(proxy_mode, /tproxy/))
|
||||
network: 'udp',
|
||||
udp_timeout: udp_timeout ? (udp_timeout + 's') : null,
|
||||
sniff: true,
|
||||
sniff_override_destination: (sniff_override === '1'),
|
||||
domain_strategy: domain_strategy,
|
||||
sniff_override_destination: (sniff_override === '1')
|
||||
});
|
||||
if (match(proxy_mode, /tun/))
|
||||
push(config.inbounds, {
|
||||
@@ -550,7 +582,6 @@ if (match(proxy_mode, /tun/))
|
||||
tag: 'tun-in',
|
||||
|
||||
interface_name: tun_name,
|
||||
/* inet4_address and inet6_address are deprecated in sing-box 1.10.0 */
|
||||
address: (ipv6_support === '1') ? [tun_addr4, tun_addr6] : [tun_addr4],
|
||||
mtu: strToInt(tun_mtu),
|
||||
gso: (tun_gso === '1'),
|
||||
@@ -559,26 +590,19 @@ if (match(proxy_mode, /tun/))
|
||||
udp_timeout: udp_timeout ? (udp_timeout + 's') : null,
|
||||
stack: tcpip_stack,
|
||||
sniff: true,
|
||||
sniff_override_destination: (sniff_override === '1'),
|
||||
domain_strategy: domain_strategy,
|
||||
sniff_override_destination: (sniff_override === '1')
|
||||
});
|
||||
/* Inbound end */
|
||||
|
||||
/* Outbound start */
|
||||
config.endpoints = [];
|
||||
|
||||
/* Default outbounds */
|
||||
config.outbounds = [
|
||||
{
|
||||
type: 'direct',
|
||||
tag: 'direct-out',
|
||||
routing_mark: strToInt(self_mark)
|
||||
},
|
||||
{
|
||||
type: 'block',
|
||||
tag: 'block-out'
|
||||
},
|
||||
{
|
||||
type: 'dns',
|
||||
tag: 'dns-out'
|
||||
}
|
||||
];
|
||||
|
||||
@@ -602,9 +626,14 @@ if (!isEmpty(main_node)) {
|
||||
urltest_nodes = main_urltest_nodes;
|
||||
} else {
|
||||
const main_node_cfg = uci.get_all(uciconfig, main_node) || {};
|
||||
push(config.outbounds, generate_outbound(main_node_cfg));
|
||||
config.outbounds[length(config.outbounds)-1].domain_strategy = (ipv6_support !== '1') ? 'prefer_ipv4' : null;
|
||||
config.outbounds[length(config.outbounds)-1].tag = 'main-out';
|
||||
if (main_node_cfg.type === 'wireguard') {
|
||||
push(config.endpoints, generate_endpoint(main_node_cfg));
|
||||
config.endpoints[length(config.endpoints)-1].tag = 'main-out';
|
||||
} else {
|
||||
push(config.outbounds, generate_outbound(main_node_cfg));
|
||||
config.outbounds[length(config.outbounds)-1].domain_strategy = (ipv6_support !== '1') ? 'prefer_ipv4' : null;
|
||||
config.outbounds[length(config.outbounds)-1].tag = 'main-out';
|
||||
}
|
||||
}
|
||||
|
||||
if (main_udp_node === 'urltest') {
|
||||
@@ -623,14 +652,26 @@ if (!isEmpty(main_node)) {
|
||||
urltest_nodes = [...urltest_nodes, ...filter(main_udp_urltest_nodes, (l) => !~index(urltest_nodes, l))];
|
||||
} else if (dedicated_udp_node) {
|
||||
const main_udp_node_cfg = uci.get_all(uciconfig, main_udp_node) || {};
|
||||
push(config.outbounds, generate_outbound(main_udp_node_cfg));
|
||||
config.outbounds[length(config.outbounds)-1].domain_strategy = (ipv6_support !== '1') ? 'prefer_ipv4' : null;
|
||||
config.outbounds[length(config.outbounds)-1].tag = 'main-udp-out';
|
||||
if (main_node_cfg.type === 'wireguard') {
|
||||
push(config.endpoints, generate_endpoint(main_udp_node_cfg));
|
||||
config.endpoints[length(config.endpoints)-1].tag = 'main-udp-out';
|
||||
} else {
|
||||
push(config.outbounds, generate_outbound(main_udp_node_cfg));
|
||||
config.outbounds[length(config.outbounds)-1].domain_strategy = (ipv6_support !== '1') ? 'prefer_ipv4' : null;
|
||||
config.outbounds[length(config.outbounds)-1].tag = 'main-udp-out';
|
||||
}
|
||||
}
|
||||
|
||||
for (let i in urltest_nodes) {
|
||||
push(config.outbounds, generate_outbound(uci.get_all(uciconfig, i)));
|
||||
config.outbounds[length(config.outbounds)-1].domain_strategy = (ipv6_support !== '1') ? 'prefer_ipv4' : null;
|
||||
const urltest_node = uci.get_all(uciconfig, i) || {};
|
||||
if (urltest_node.type === 'wireguard') {
|
||||
push(config.endpoints, generate_endpoint(urltest_node));
|
||||
config.endpoints[length(config.endpoints)-1].tag = 'cfg-' + i + '-out';
|
||||
} else {
|
||||
push(config.outbounds, generate_outbound(urltest_node));
|
||||
config.outbounds[length(config.outbounds)-1].domain_strategy = (ipv6_support !== '1') ? 'prefer_ipv4' : null;
|
||||
config.outbounds[length(config.outbounds)-1].tag = 'cfg-' + i + '-out';
|
||||
}
|
||||
}
|
||||
} else if (!isEmpty(default_outbound)) {
|
||||
let urltest_nodes = [],
|
||||
@@ -654,17 +695,29 @@ if (!isEmpty(main_node)) {
|
||||
urltest_nodes = [...urltest_nodes, ...filter(cfg.urltest_nodes, (l) => !~index(urltest_nodes, l))];
|
||||
} else {
|
||||
const outbound = uci.get_all(uciconfig, cfg.node) || {};
|
||||
push(config.outbounds, generate_outbound(outbound));
|
||||
config.outbounds[length(config.outbounds)-1].domain_strategy = cfg.domain_strategy;
|
||||
config.outbounds[length(config.outbounds)-1].bind_interface = cfg.bind_interface;
|
||||
config.outbounds[length(config.outbounds)-1].detour = get_outbound(cfg.outbound);
|
||||
if (outbound.type === 'wireguard') {
|
||||
push(config.endpoints, generate_endpoint(outbound));
|
||||
} else {
|
||||
push(config.outbounds, generate_outbound(outbound));
|
||||
config.outbounds[length(config.outbounds)-1].domain_strategy = cfg.domain_strategy;
|
||||
config.outbounds[length(config.outbounds)-1].bind_interface = cfg.bind_interface;
|
||||
config.outbounds[length(config.outbounds)-1].detour = get_outbound(cfg.outbound);
|
||||
}
|
||||
push(routing_nodes, cfg.node);
|
||||
}
|
||||
});
|
||||
|
||||
for (let i in filter(urltest_nodes, (l) => !~index(routing_nodes, l)))
|
||||
push(config.outbounds, generate_outbound(uci.get_all(uciconfig, i)));
|
||||
for (let i in filter(urltest_nodes, (l) => !~index(routing_nodes, l))) {
|
||||
const urltest_node = uci.get_all(uciconfig, i) || {};
|
||||
if (urltest_node.type === 'wireguard')
|
||||
push(config.endpoints, generate_endpoint(urltest_node));
|
||||
else
|
||||
push(config.outbounds, generate_outbound(urltest_node));
|
||||
}
|
||||
}
|
||||
|
||||
if (isEmpty(config.endpoints))
|
||||
config.endpoints = null;
|
||||
/* Outbound end */
|
||||
|
||||
/* Routing rules start */
|
||||
@@ -673,8 +726,14 @@ config.route = {
|
||||
rules: [
|
||||
{
|
||||
inbound: 'dns-in',
|
||||
outbound: 'dns-out'
|
||||
action: 'hijack-dns'
|
||||
}
|
||||
/*
|
||||
* leave for sing-box 1.13.0
|
||||
* {
|
||||
* action: 'sniff'
|
||||
* }
|
||||
*/
|
||||
],
|
||||
rule_set: [],
|
||||
auto_detect_interface: isEmpty(default_interface) ? true : null,
|
||||
@@ -687,6 +746,7 @@ if (!isEmpty(main_node)) {
|
||||
if (length(direct_domain_list))
|
||||
push(config.route.rules, {
|
||||
rule_set: 'direct-domain',
|
||||
action: 'route',
|
||||
outbound: 'direct-out'
|
||||
});
|
||||
|
||||
@@ -694,6 +754,7 @@ if (!isEmpty(main_node)) {
|
||||
if (dedicated_udp_node)
|
||||
push(config.route.rules, {
|
||||
network: 'udp',
|
||||
action: 'route',
|
||||
outbound: 'main-udp-out'
|
||||
});
|
||||
|
||||
@@ -751,6 +812,12 @@ if (!isEmpty(main_node)) {
|
||||
if (isEmpty(config.route.rule_set))
|
||||
config.route.rule_set = null;
|
||||
} else if (!isEmpty(default_outbound)) {
|
||||
if (domain_strategy)
|
||||
push(config.route.rules, {
|
||||
action: 'resolve',
|
||||
strategy: domain_strategy
|
||||
});
|
||||
|
||||
uci.foreach(uciconfig, uciroutingrule, (cfg) => {
|
||||
if (cfg.enabled !== '1')
|
||||
return null;
|
||||
@@ -776,11 +843,13 @@ if (!isEmpty(main_node)) {
|
||||
process_path_regex: cfg.process_path_regex,
|
||||
user: cfg.user,
|
||||
rule_set: get_ruleset(cfg.rule_set),
|
||||
/* rule_set_ipcidr_match_source is deprecated in sing-box 1.10.0 */
|
||||
rule_set_ip_cidr_match_source: (cfg.rule_set_ip_cidr_match_source === '1') || null,
|
||||
rule_set_ip_cidr_accept_empty: (cfg.rule_set_ip_cidr_accept_empty === '1') || null,
|
||||
invert: (cfg.invert === '1') || null,
|
||||
outbound: get_outbound(cfg.outbound)
|
||||
action: (cfg.outbound === 'block-out') ? 'reject' : 'route',
|
||||
override_address: cfg.override_address,
|
||||
override_port: strToInt(cfg.override_port),
|
||||
outbound: get_outbound(cfg.outbound),
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -49,9 +49,6 @@ uci.foreach(uciconfig, uciserver, (cfg) => {
|
||||
tcp_multi_path: strToBool(cfg.tcp_multi_path),
|
||||
udp_fragment: strToBool(cfg.udp_fragment),
|
||||
udp_timeout: cfg.udp_timeout ? (cfg.udp_timeout + 's') : null,
|
||||
sniff: true,
|
||||
sniff_override_destination: (cfg.sniff_override === '1'),
|
||||
domain_strategy: cfg.domain_strategy,
|
||||
network: cfg.network,
|
||||
|
||||
/* Hysteria */
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/ucode
|
||||
/*
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*
|
||||
* Copyright (C) 2025 ImmortalWrt.org
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { cursor } from 'uci';
|
||||
import { isEmpty } from 'homeproxy';
|
||||
|
||||
const uci = cursor();
|
||||
|
||||
const uciconfig = 'homeproxy';
|
||||
uci.load(uciconfig);
|
||||
|
||||
const uciinfra = 'infra',
|
||||
ucimain = 'config',
|
||||
ucinode = 'node',
|
||||
ucidns = 'dns',
|
||||
ucidnsrule = 'dns_rule',
|
||||
ucirouting = 'routing',
|
||||
uciroutingnode = 'routing_node',
|
||||
uciroutingrule = 'routing_rule',
|
||||
uciserver = 'server';
|
||||
|
||||
/* chinadns-ng has been removed */
|
||||
if (uci.get(uciconfig, uciinfra, 'china_dns_port'))
|
||||
uci.delete(uciconfig, uciinfra, 'china_dns_port');
|
||||
|
||||
/* chinadns server now only accepts single server */
|
||||
const china_dns_server = uci.get(uciconfig, ucimain, 'china_dns_server');
|
||||
if (china_dns_server === 'wan_114')
|
||||
uci.set(uciconfig, ucimain, 'china_dns_server', '114.114.114.114');
|
||||
else if (match(china_dns_server, /,/))
|
||||
uci.set(uciconfig, ucimain, 'china_dns_server', split(china_dns_server, ',')[0]);
|
||||
else if (match(china_dns_server, / /))
|
||||
uci.set(uciconfig, ucimain, 'china_dns_server', split(china_dns_server, ' ')[0]);
|
||||
|
||||
/* empty value defaults to all ports now */
|
||||
if (uci.get(uciconfig, ucimain, 'routing_port') === 'all')
|
||||
uci.delete(uciconfig, ucimain, 'routing_port');
|
||||
|
||||
/* experimental section was removed */
|
||||
if (uci.get(uciconfig, 'experimental'))
|
||||
uci.delete(uciconfig, 'experimental');
|
||||
|
||||
/* block-dns was removed from built-in dns servers */
|
||||
if (uci.get(uciconfig, ucidns, 'default_server') === 'block-dns')
|
||||
uci.set(uciconfig, ucidns, 'default_server', 'default-dns');
|
||||
|
||||
/* block-out was removed from built-in outbounds */
|
||||
if (uci.get(uciconfig, ucirouting, 'default_outbound') === 'block-out')
|
||||
uci.set(uciconfig, ucirouting, 'default_outbound', 'nil');
|
||||
|
||||
|
||||
/* DNS rules options */
|
||||
uci.foreach(uciconfig, ucidnsrule, (cfg) => {
|
||||
/* rule_set_ipcidr_match_source was renamed in sb 1.10 */
|
||||
if (cfg.rule_set_ipcidr_match_source === '1')
|
||||
uci.rename(uciconfig, cfg, 'rule_set_ipcidr_match_source', 'rule_set_ip_cidr_match_source');
|
||||
});
|
||||
|
||||
/* nodes options */
|
||||
uci.foreach(uciconfig, ucinode, (cfg) => {
|
||||
/* tls_ech_tls_disable_drs is useless and deprecated in sb 1.12 */
|
||||
if (!isEmpty(cfg.tls_ech_tls_disable_drs))
|
||||
uci.delete(uciconfig, cfg, 'tls_ech_tls_disable_drs');
|
||||
|
||||
/* wireguard_gso was deprecated in sb 1.11 */
|
||||
if (!isEmpty(cfg.wireguard_gso))
|
||||
uci.delete(uciconfig, cfg, 'wireguard_gso');
|
||||
});
|
||||
|
||||
/* routing rules options */
|
||||
uci.foreach(uciconfig, uciroutingrule, (cfg) => {
|
||||
/* rule_set_ipcidr_match_source was renamed in sb 1.10 */
|
||||
if (cfg.rule_set_ipcidr_match_source === '1')
|
||||
uci.rename(uciconfig, cfg, 'rule_set_ipcidr_match_source', 'rule_set_ip_cidr_match_source');
|
||||
});
|
||||
|
||||
/* server options */
|
||||
uci.foreach(uciconfig, uciserver, (cfg) => {
|
||||
/* sniff_override was deprecated in sb 1.11 */
|
||||
if (!isEmpty(cfg.sniff_override))
|
||||
uci.delete(uciconfig, cfg, 'sniff_override');
|
||||
|
||||
/* domain_strategy is now pointless without sniff override */
|
||||
if (!isEmpty(cfg.domain_strategy))
|
||||
uci.delete(uciconfig, cfg, 'domain_strategy');
|
||||
});
|
||||
|
||||
if (!isEmpty(uci.changes(uciconfig)))
|
||||
uci.commit(uciconfig);
|
||||
@@ -342,6 +342,10 @@ function parse_uri(uri) {
|
||||
config.http_path = params.path ? urldecode(params.path) : null;
|
||||
}
|
||||
break;
|
||||
case 'httpupgrade':
|
||||
config.httpupgrade_host = params.host ? urldecode(params.host) : null;
|
||||
config.http_path = params.path ? urldecode(params.path) : null;
|
||||
break;
|
||||
case 'ws':
|
||||
config.ws_host = params.host ? urldecode(params.host) : null;
|
||||
config.ws_path = params.path ? urldecode(params.path) : null;
|
||||
@@ -361,7 +365,7 @@ function parse_uri(uri) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/* https://github.com/2dust/v2rayN/wiki/%E5%88%86%E4%BA%AB%E9%93%BE%E6%8E%A5%E6%A0%BC%E5%BC%8F%E8%AF%B4%E6%98%8E(ver-2) */
|
||||
/* https://github.com/2dust/v2rayN/wiki/Description-of-VMess-share-link */
|
||||
try {
|
||||
uri = json(decodeBase64Str(uri[1])) || {};
|
||||
} catch(e) {
|
||||
@@ -403,7 +407,8 @@ function parse_uri(uri) {
|
||||
transport: (uri.net !== 'tcp') ? uri.net : null,
|
||||
tls: (uri.tls === 'tls') ? '1' : '0',
|
||||
tls_sni: uri.sni || uri.host,
|
||||
tls_alpn: uri.alpn ? split(uri.alpn, ',') : null
|
||||
tls_alpn: uri.alpn ? split(uri.alpn, ',') : null,
|
||||
tls_utls: sing_features.with_utls ? uri.fp : null
|
||||
};
|
||||
switch (uri.net) {
|
||||
case 'grpc':
|
||||
@@ -417,6 +422,10 @@ function parse_uri(uri) {
|
||||
config.http_path = uri.path;
|
||||
}
|
||||
break;
|
||||
case 'httpupgrade':
|
||||
config.httpupgrade_host = uri.host;
|
||||
config.http_path = uri.path;
|
||||
break;
|
||||
case 'ws':
|
||||
config.ws_host = uri.host;
|
||||
config.ws_path = uri.path;
|
||||
|
||||
@@ -1,26 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
china_dns_server="$(uci -q get "homeproxy.config.china_dns_server")"
|
||||
if [ "$china_dns_server" = "wan_114" ]; then
|
||||
uci -q delete "homeproxy.config.china_dns_server"
|
||||
uci -q set "homeproxy.config.china_dns_server"="114.114.114.114"
|
||||
elif echo "$china_dns_server" | grep -q ","; then
|
||||
uci -q delete "homeproxy.config.china_dns_server"
|
||||
uci -q set "homeproxy.config.china_dns_server"="${china_dns_server%%,*}"
|
||||
elif echo "$china_dns_server" | grep -q " "; then
|
||||
uci -q delete "homeproxy.config.china_dns_server"
|
||||
uci -q set "homeproxy.config.china_dns_server"="${china_dns_server%% *}"
|
||||
fi
|
||||
|
||||
if [ "$(uci -q get homeproxy.config.routing_port)" = "all" ]; then
|
||||
uci -q delete "homeproxy.config.routing_port"
|
||||
fi
|
||||
|
||||
[ -z "$(uci -q show homeproxy.experimental)" ] || uci -q delete "homeproxy.experimental"
|
||||
|
||||
[ -z "$(uci -q changes "homeproxy")" ] || uci -q commit "homeproxy"
|
||||
|
||||
sed -i "s/rule_set_ipcidr_match_source/rule_set_ip_cidr_match_source/g" "/etc/config/homeproxy"
|
||||
sed -i "/china_dns_port/d" "/etc/config/homeproxy"
|
||||
ucode "/etc/homeproxy/scripts/migrate_config.uc"
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -495,8 +495,6 @@ if DEFAULT_TAG == "none_noip" then table.insert(config_lines, "noip-as-chnip") e
|
||||
if DEFAULT_TAG == nil or DEFAULT_TAG == "smart" or DEFAULT_TAG == "none_noip" then DEFAULT_TAG = "none" end
|
||||
|
||||
table.insert(config_lines, "default-tag " .. DEFAULT_TAG)
|
||||
table.insert(config_lines, "cache 4096")
|
||||
table.insert(config_lines, "cache-stale 3600")
|
||||
|
||||
if DEFAULT_TAG == "none" then
|
||||
table.insert(config_lines, "verdict-cache 5000")
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=sing-box
|
||||
PKG_VERSION:=1.11.4
|
||||
PKG_VERSION:=1.11.5
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/SagerNet/sing-box/tar.gz/v$(PKG_VERSION)?
|
||||
PKG_HASH:=633e40e2a64937069ada9d8d96dec1a5d735095d44eb3cbea105ee05f4616fc6
|
||||
PKG_HASH:=ac95287a65ae9297aa0b9f25934ead8468bf7ef3f9045e483659ddc400e758a1
|
||||
|
||||
PKG_LICENSE:=GPL-3.0-or-later
|
||||
PKG_LICENSE_FILES:=LICENSE
|
||||
|
||||
@@ -11,6 +11,7 @@ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
import math
|
||||
import re
|
||||
import time
|
||||
|
||||
from youtube_dl.compat import compat_str as str
|
||||
from youtube_dl.jsinterp import JS_Undefined, JSInterpreter
|
||||
@@ -208,6 +209,34 @@ class TestJSInterpreter(unittest.TestCase):
|
||||
self._test(jsi, 86000, args=['12/31/1969 18:01:26 MDT'])
|
||||
# epoch 0
|
||||
self._test(jsi, 0, args=['1 January 1970 00:00:00 UTC'])
|
||||
# undefined
|
||||
self._test(jsi, NaN, args=[JS_Undefined])
|
||||
# y,m,d, ... - may fail with older dates lacking DST data
|
||||
jsi = JSInterpreter(
|
||||
'function f() { return new Date(%s); }'
|
||||
% ('2024, 5, 29, 2, 52, 12, 42',))
|
||||
self._test(jsi, (
|
||||
1719625932042 # UK value
|
||||
+ (
|
||||
+ 3600 # back to GMT
|
||||
+ (time.altzone if time.daylight # host's DST
|
||||
else time.timezone)
|
||||
) * 1000))
|
||||
# no arg
|
||||
self.assertAlmostEqual(JSInterpreter(
|
||||
'function f() { return new Date() - 0; }').call_function('f'),
|
||||
time.time() * 1000, delta=100)
|
||||
# Date.now()
|
||||
self.assertAlmostEqual(JSInterpreter(
|
||||
'function f() { return Date.now(); }').call_function('f'),
|
||||
time.time() * 1000, delta=100)
|
||||
# Date.parse()
|
||||
jsi = JSInterpreter('function f(dt) { return Date.parse(dt); }')
|
||||
self._test(jsi, 0, args=['1 January 1970 00:00:00 UTC'])
|
||||
# Date.UTC()
|
||||
jsi = JSInterpreter('function f() { return Date.UTC(%s); }'
|
||||
% ('1970, 0, 1, 0, 0, 0, 0',))
|
||||
self._test(jsi, 0)
|
||||
|
||||
def test_call(self):
|
||||
jsi = JSInterpreter('''
|
||||
@@ -463,6 +492,14 @@ class TestJSInterpreter(unittest.TestCase):
|
||||
self._test('function f(){return NaN << 42}', 0)
|
||||
self._test('function f(){return "21.9" << 1}', 42)
|
||||
self._test('function f(){return 21 << 4294967297}', 42)
|
||||
self._test('function f(){return true << "5";}', 32)
|
||||
self._test('function f(){return true << true;}', 2)
|
||||
self._test('function f(){return "19" & "21.9";}', 17)
|
||||
self._test('function f(){return "19" & false;}', 0)
|
||||
self._test('function f(){return "11.0" >> "2.1";}', 2)
|
||||
self._test('function f(){return 5 ^ 9;}', 12)
|
||||
self._test('function f(){return 0.0 << NaN}', 0)
|
||||
self._test('function f(){return null << undefined}', 0)
|
||||
|
||||
def test_negative(self):
|
||||
self._test('function f(){return 2 * -2.0 ;}', -4)
|
||||
|
||||
@@ -223,6 +223,18 @@ _NSIG_TESTS = [
|
||||
'https://www.youtube.com/s/player/9c6dfc4a/player_ias.vflset/en_US/base.js',
|
||||
'jbu7ylIosQHyJyJV', 'uwI0ESiynAmhNg',
|
||||
),
|
||||
(
|
||||
'https://www.youtube.com/s/player/f6e09c70/player_ias.vflset/en_US/base.js',
|
||||
'W9HJZKktxuYoDTqW', 'jHbbkcaxm54',
|
||||
),
|
||||
(
|
||||
'https://www.youtube.com/s/player/f6e09c70/player_ias_tce.vflset/en_US/base.js',
|
||||
'W9HJZKktxuYoDTqW', 'jHbbkcaxm54',
|
||||
),
|
||||
(
|
||||
'https://www.youtube.com/s/player/91201489/player_ias_tce.vflset/en_US/base.js',
|
||||
'W9HJZKktxuYoDTqW', 'U48vOZHaeYS6vO',
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
@@ -284,7 +296,7 @@ def t_factory(name, sig_func, url_pattern):
|
||||
|
||||
|
||||
def signature(jscode, sig_input):
|
||||
func = YoutubeIE(FakeYDL())._parse_sig_js(jscode)
|
||||
func = YoutubeIE(FakeYDL({'cachedir': False}))._parse_sig_js(jscode)
|
||||
src_sig = (
|
||||
compat_str(string.printable[:sig_input])
|
||||
if isinstance(sig_input, int) else sig_input)
|
||||
@@ -292,9 +304,10 @@ def signature(jscode, sig_input):
|
||||
|
||||
|
||||
def n_sig(jscode, sig_input):
|
||||
funcname = YoutubeIE(FakeYDL())._extract_n_function_name(jscode)
|
||||
return JSInterpreter(jscode).call_function(
|
||||
funcname, sig_input, _ytdl_do_not_return=sig_input)
|
||||
ie = YoutubeIE(FakeYDL({'cachedir': False}))
|
||||
jsi = JSInterpreter(jscode)
|
||||
jsi, _, func_code = ie._extract_n_function_code_jsi(sig_input, jsi)
|
||||
return ie._extract_n_function_from_code(jsi, func_code)(sig_input)
|
||||
|
||||
|
||||
make_sig_test = t_factory(
|
||||
|
||||
@@ -18,7 +18,7 @@ from .compat import (
|
||||
compat_getpass,
|
||||
compat_register_utf8,
|
||||
compat_shlex_split,
|
||||
workaround_optparse_bug9161,
|
||||
_workaround_optparse_bug9161,
|
||||
)
|
||||
from .utils import (
|
||||
_UnsafeExtensionError,
|
||||
@@ -50,7 +50,7 @@ def _real_main(argv=None):
|
||||
# Compatibility fix for Windows
|
||||
compat_register_utf8()
|
||||
|
||||
workaround_optparse_bug9161()
|
||||
_workaround_optparse_bug9161()
|
||||
|
||||
setproctitle('youtube-dl')
|
||||
|
||||
|
||||
+138
-47
@@ -16,7 +16,6 @@ import os
|
||||
import platform
|
||||
import re
|
||||
import shlex
|
||||
import shutil
|
||||
import socket
|
||||
import struct
|
||||
import subprocess
|
||||
@@ -24,11 +23,15 @@ import sys
|
||||
import types
|
||||
import xml.etree.ElementTree
|
||||
|
||||
_IDENTITY = lambda x: x
|
||||
|
||||
# naming convention
|
||||
# 'compat_' + Python3_name.replace('.', '_')
|
||||
# other aliases exist for convenience and/or legacy
|
||||
# wrap disposable test values in type() to reclaim storage
|
||||
|
||||
# deal with critical unicode/str things first
|
||||
# deal with critical unicode/str things first:
|
||||
# compat_str, compat_basestring, compat_chr
|
||||
try:
|
||||
# Python 2
|
||||
compat_str, compat_basestring, compat_chr = (
|
||||
@@ -39,18 +42,23 @@ except NameError:
|
||||
str, (str, bytes), chr
|
||||
)
|
||||
|
||||
# casefold
|
||||
|
||||
# compat_casefold
|
||||
try:
|
||||
compat_str.casefold
|
||||
compat_casefold = lambda s: s.casefold()
|
||||
except AttributeError:
|
||||
from .casefold import _casefold as compat_casefold
|
||||
|
||||
|
||||
# compat_collections_abc
|
||||
try:
|
||||
import collections.abc as compat_collections_abc
|
||||
except ImportError:
|
||||
import collections as compat_collections_abc
|
||||
|
||||
|
||||
# compat_urllib_request
|
||||
try:
|
||||
import urllib.request as compat_urllib_request
|
||||
except ImportError: # Python 2
|
||||
@@ -79,11 +87,15 @@ except TypeError:
|
||||
_add_init_method_arg(compat_urllib_request.Request)
|
||||
del _add_init_method_arg
|
||||
|
||||
|
||||
# compat_urllib_error
|
||||
try:
|
||||
import urllib.error as compat_urllib_error
|
||||
except ImportError: # Python 2
|
||||
import urllib2 as compat_urllib_error
|
||||
|
||||
|
||||
# compat_urllib_parse
|
||||
try:
|
||||
import urllib.parse as compat_urllib_parse
|
||||
except ImportError: # Python 2
|
||||
@@ -98,17 +110,23 @@ except ImportError: # Python 2
|
||||
compat_urlparse = compat_urllib_parse
|
||||
compat_urllib_parse_urlparse = compat_urllib_parse.urlparse
|
||||
|
||||
|
||||
# compat_urllib_response
|
||||
try:
|
||||
import urllib.response as compat_urllib_response
|
||||
except ImportError: # Python 2
|
||||
import urllib as compat_urllib_response
|
||||
|
||||
|
||||
# compat_urllib_response.addinfourl
|
||||
try:
|
||||
compat_urllib_response.addinfourl.status
|
||||
except AttributeError:
|
||||
# .getcode() is deprecated in Py 3.
|
||||
compat_urllib_response.addinfourl.status = property(lambda self: self.getcode())
|
||||
|
||||
|
||||
# compat_http_cookiejar
|
||||
try:
|
||||
import http.cookiejar as compat_cookiejar
|
||||
except ImportError: # Python 2
|
||||
@@ -127,12 +145,16 @@ else:
|
||||
compat_cookiejar_Cookie = compat_cookiejar.Cookie
|
||||
compat_http_cookiejar_Cookie = compat_cookiejar_Cookie
|
||||
|
||||
|
||||
# compat_http_cookies
|
||||
try:
|
||||
import http.cookies as compat_cookies
|
||||
except ImportError: # Python 2
|
||||
import Cookie as compat_cookies
|
||||
compat_http_cookies = compat_cookies
|
||||
|
||||
|
||||
# compat_http_cookies_SimpleCookie
|
||||
if sys.version_info[0] == 2 or sys.version_info < (3, 3):
|
||||
class compat_cookies_SimpleCookie(compat_cookies.SimpleCookie):
|
||||
def load(self, rawdata):
|
||||
@@ -155,11 +177,15 @@ else:
|
||||
compat_cookies_SimpleCookie = compat_cookies.SimpleCookie
|
||||
compat_http_cookies_SimpleCookie = compat_cookies_SimpleCookie
|
||||
|
||||
|
||||
# compat_html_entities, probably useless now
|
||||
try:
|
||||
import html.entities as compat_html_entities
|
||||
except ImportError: # Python 2
|
||||
import htmlentitydefs as compat_html_entities
|
||||
|
||||
|
||||
# compat_html_entities_html5
|
||||
try: # Python >= 3.3
|
||||
compat_html_entities_html5 = compat_html_entities.html5
|
||||
except AttributeError:
|
||||
@@ -2408,18 +2434,24 @@ except AttributeError:
|
||||
# Py < 3.1
|
||||
compat_http_client.HTTPResponse.getcode = lambda self: self.status
|
||||
|
||||
|
||||
# compat_urllib_HTTPError
|
||||
try:
|
||||
from urllib.error import HTTPError as compat_HTTPError
|
||||
except ImportError: # Python 2
|
||||
from urllib2 import HTTPError as compat_HTTPError
|
||||
compat_urllib_HTTPError = compat_HTTPError
|
||||
|
||||
|
||||
# compat_urllib_request_urlretrieve
|
||||
try:
|
||||
from urllib.request import urlretrieve as compat_urlretrieve
|
||||
except ImportError: # Python 2
|
||||
from urllib import urlretrieve as compat_urlretrieve
|
||||
compat_urllib_request_urlretrieve = compat_urlretrieve
|
||||
|
||||
|
||||
# compat_html_parser_HTMLParser, compat_html_parser_HTMLParseError
|
||||
try:
|
||||
from HTMLParser import (
|
||||
HTMLParser as compat_HTMLParser,
|
||||
@@ -2432,22 +2464,33 @@ except ImportError: # Python 3
|
||||
# HTMLParseError was deprecated in Python 3.3 and removed in
|
||||
# Python 3.5. Introducing dummy exception for Python >3.5 for compatible
|
||||
# and uniform cross-version exception handling
|
||||
|
||||
class compat_HTMLParseError(Exception):
|
||||
pass
|
||||
|
||||
compat_html_parser_HTMLParser = compat_HTMLParser
|
||||
compat_html_parser_HTMLParseError = compat_HTMLParseError
|
||||
|
||||
|
||||
# compat_subprocess_get_DEVNULL
|
||||
try:
|
||||
_DEVNULL = subprocess.DEVNULL
|
||||
compat_subprocess_get_DEVNULL = lambda: _DEVNULL
|
||||
except AttributeError:
|
||||
compat_subprocess_get_DEVNULL = lambda: open(os.path.devnull, 'w')
|
||||
|
||||
|
||||
# compat_http_server
|
||||
try:
|
||||
import http.server as compat_http_server
|
||||
except ImportError:
|
||||
import BaseHTTPServer as compat_http_server
|
||||
|
||||
|
||||
# compat_urllib_parse_unquote_to_bytes,
|
||||
# compat_urllib_parse_unquote, compat_urllib_parse_unquote_plus,
|
||||
# compat_urllib_parse_urlencode,
|
||||
# compat_urllib_parse_parse_qs
|
||||
try:
|
||||
from urllib.parse import unquote_to_bytes as compat_urllib_parse_unquote_to_bytes
|
||||
from urllib.parse import unquote as compat_urllib_parse_unquote
|
||||
@@ -2598,6 +2641,8 @@ except ImportError: # Python 2
|
||||
|
||||
compat_urllib_parse_parse_qs = compat_parse_qs
|
||||
|
||||
|
||||
# compat_urllib_request_DataHandler
|
||||
try:
|
||||
from urllib.request import DataHandler as compat_urllib_request_DataHandler
|
||||
except ImportError: # Python < 3.4
|
||||
@@ -2632,16 +2677,20 @@ except ImportError: # Python < 3.4
|
||||
|
||||
return compat_urllib_response.addinfourl(io.BytesIO(data), headers, url)
|
||||
|
||||
|
||||
# compat_xml_etree_ElementTree_ParseError
|
||||
try:
|
||||
from xml.etree.ElementTree import ParseError as compat_xml_parse_error
|
||||
except ImportError: # Python 2.6
|
||||
from xml.parsers.expat import ExpatError as compat_xml_parse_error
|
||||
compat_xml_etree_ElementTree_ParseError = compat_xml_parse_error
|
||||
|
||||
etree = xml.etree.ElementTree
|
||||
|
||||
# compat_xml_etree_ElementTree_Element
|
||||
_etree = xml.etree.ElementTree
|
||||
|
||||
|
||||
class _TreeBuilder(etree.TreeBuilder):
|
||||
class _TreeBuilder(_etree.TreeBuilder):
|
||||
def doctype(self, name, pubid, system):
|
||||
pass
|
||||
|
||||
@@ -2650,7 +2699,7 @@ try:
|
||||
# xml.etree.ElementTree.Element is a method in Python <=2.6 and
|
||||
# the following will crash with:
|
||||
# TypeError: isinstance() arg 2 must be a class, type, or tuple of classes and types
|
||||
isinstance(None, etree.Element)
|
||||
isinstance(None, _etree.Element)
|
||||
from xml.etree.ElementTree import Element as compat_etree_Element
|
||||
except TypeError: # Python <=2.6
|
||||
from xml.etree.ElementTree import _ElementInterface as compat_etree_Element
|
||||
@@ -2658,12 +2707,12 @@ compat_xml_etree_ElementTree_Element = compat_etree_Element
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
def compat_etree_fromstring(text):
|
||||
return etree.XML(text, parser=etree.XMLParser(target=_TreeBuilder()))
|
||||
return _etree.XML(text, parser=_etree.XMLParser(target=_TreeBuilder()))
|
||||
else:
|
||||
# python 2.x tries to encode unicode strings with ascii (see the
|
||||
# XMLParser._fixtext method)
|
||||
try:
|
||||
_etree_iter = etree.Element.iter
|
||||
_etree_iter = _etree.Element.iter
|
||||
except AttributeError: # Python <=2.6
|
||||
def _etree_iter(root):
|
||||
for el in root.findall('*'):
|
||||
@@ -2675,27 +2724,29 @@ else:
|
||||
# 2.7 source
|
||||
def _XML(text, parser=None):
|
||||
if not parser:
|
||||
parser = etree.XMLParser(target=_TreeBuilder())
|
||||
parser = _etree.XMLParser(target=_TreeBuilder())
|
||||
parser.feed(text)
|
||||
return parser.close()
|
||||
|
||||
def _element_factory(*args, **kwargs):
|
||||
el = etree.Element(*args, **kwargs)
|
||||
el = _etree.Element(*args, **kwargs)
|
||||
for k, v in el.items():
|
||||
if isinstance(v, bytes):
|
||||
el.set(k, v.decode('utf-8'))
|
||||
return el
|
||||
|
||||
def compat_etree_fromstring(text):
|
||||
doc = _XML(text, parser=etree.XMLParser(target=_TreeBuilder(element_factory=_element_factory)))
|
||||
doc = _XML(text, parser=_etree.XMLParser(target=_TreeBuilder(element_factory=_element_factory)))
|
||||
for el in _etree_iter(doc):
|
||||
if el.text is not None and isinstance(el.text, bytes):
|
||||
el.text = el.text.decode('utf-8')
|
||||
return doc
|
||||
|
||||
if hasattr(etree, 'register_namespace'):
|
||||
compat_etree_register_namespace = etree.register_namespace
|
||||
else:
|
||||
|
||||
# compat_xml_etree_register_namespace
|
||||
try:
|
||||
compat_etree_register_namespace = _etree.register_namespace
|
||||
except AttributeError:
|
||||
def compat_etree_register_namespace(prefix, uri):
|
||||
"""Register a namespace prefix.
|
||||
The registry is global, and any existing mapping for either the
|
||||
@@ -2704,14 +2755,16 @@ else:
|
||||
attributes in this namespace will be serialized with prefix if possible.
|
||||
ValueError is raised if prefix is reserved or is invalid.
|
||||
"""
|
||||
if re.match(r"ns\d+$", prefix):
|
||||
raise ValueError("Prefix format reserved for internal use")
|
||||
for k, v in list(etree._namespace_map.items()):
|
||||
if re.match(r'ns\d+$', prefix):
|
||||
raise ValueError('Prefix format reserved for internal use')
|
||||
for k, v in list(_etree._namespace_map.items()):
|
||||
if k == uri or v == prefix:
|
||||
del etree._namespace_map[k]
|
||||
etree._namespace_map[uri] = prefix
|
||||
del _etree._namespace_map[k]
|
||||
_etree._namespace_map[uri] = prefix
|
||||
compat_xml_etree_register_namespace = compat_etree_register_namespace
|
||||
|
||||
|
||||
# compat_xpath, compat_etree_iterfind
|
||||
if sys.version_info < (2, 7):
|
||||
# Here comes the crazy part: In 2.6, if the xpath is a unicode,
|
||||
# .//node does not match if a node is a direct child of . !
|
||||
@@ -2898,7 +2951,6 @@ if sys.version_info < (2, 7):
|
||||
def __init__(self, root):
|
||||
self.root = root
|
||||
|
||||
##
|
||||
# Generate all matching objects.
|
||||
|
||||
def compat_etree_iterfind(elem, path, namespaces=None):
|
||||
@@ -2933,13 +2985,15 @@ if sys.version_info < (2, 7):
|
||||
|
||||
|
||||
else:
|
||||
compat_xpath = lambda xpath: xpath
|
||||
compat_etree_iterfind = lambda element, match: element.iterfind(match)
|
||||
compat_xpath = _IDENTITY
|
||||
|
||||
|
||||
# compat_os_name
|
||||
compat_os_name = os._name if os.name == 'java' else os.name
|
||||
|
||||
|
||||
# compat_shlex_quote
|
||||
if compat_os_name == 'nt':
|
||||
def compat_shlex_quote(s):
|
||||
return s if re.match(r'^[-_\w./]+$', s) else '"%s"' % s.replace('"', '\\"')
|
||||
@@ -2954,6 +3008,7 @@ else:
|
||||
return "'" + s.replace("'", "'\"'\"'") + "'"
|
||||
|
||||
|
||||
# compat_shlex.split
|
||||
try:
|
||||
args = shlex.split('中文')
|
||||
assert (isinstance(args, list)
|
||||
@@ -2969,6 +3024,7 @@ except (AssertionError, UnicodeEncodeError):
|
||||
return list(map(lambda s: s.decode('utf-8'), shlex.split(s, comments, posix)))
|
||||
|
||||
|
||||
# compat_ord
|
||||
def compat_ord(c):
|
||||
if isinstance(c, int):
|
||||
return c
|
||||
@@ -2976,6 +3032,7 @@ def compat_ord(c):
|
||||
return ord(c)
|
||||
|
||||
|
||||
# compat_getenv, compat_os_path_expanduser, compat_setenv
|
||||
if sys.version_info >= (3, 0):
|
||||
compat_getenv = os.getenv
|
||||
compat_expanduser = os.path.expanduser
|
||||
@@ -3063,6 +3120,7 @@ else:
|
||||
compat_os_path_expanduser = compat_expanduser
|
||||
|
||||
|
||||
# compat_os_path_realpath
|
||||
if compat_os_name == 'nt' and sys.version_info < (3, 8):
|
||||
# os.path.realpath on Windows does not follow symbolic links
|
||||
# prior to Python 3.8 (see https://bugs.python.org/issue9949)
|
||||
@@ -3076,6 +3134,7 @@ else:
|
||||
compat_os_path_realpath = compat_realpath
|
||||
|
||||
|
||||
# compat_print
|
||||
if sys.version_info < (3, 0):
|
||||
def compat_print(s):
|
||||
from .utils import preferredencoding
|
||||
@@ -3086,6 +3145,7 @@ else:
|
||||
print(s)
|
||||
|
||||
|
||||
# compat_getpass_getpass
|
||||
if sys.version_info < (3, 0) and sys.platform == 'win32':
|
||||
def compat_getpass(prompt, *args, **kwargs):
|
||||
if isinstance(prompt, compat_str):
|
||||
@@ -3098,22 +3158,22 @@ else:
|
||||
compat_getpass_getpass = compat_getpass
|
||||
|
||||
|
||||
# compat_input
|
||||
try:
|
||||
compat_input = raw_input
|
||||
except NameError: # Python 3
|
||||
compat_input = input
|
||||
|
||||
|
||||
# compat_kwargs
|
||||
# Python < 2.6.5 require kwargs to be bytes
|
||||
try:
|
||||
def _testfunc(x):
|
||||
pass
|
||||
_testfunc(**{'x': 0})
|
||||
(lambda x: x)(**{'x': 0})
|
||||
except TypeError:
|
||||
def compat_kwargs(kwargs):
|
||||
return dict((bytes(k), v) for k, v in kwargs.items())
|
||||
else:
|
||||
compat_kwargs = lambda kwargs: kwargs
|
||||
compat_kwargs = _IDENTITY
|
||||
|
||||
|
||||
# compat_numeric_types
|
||||
@@ -3132,6 +3192,8 @@ except NameError: # Python 3
|
||||
# compat_int
|
||||
compat_int = compat_integer_types[-1]
|
||||
|
||||
|
||||
# compat_socket_create_connection
|
||||
if sys.version_info < (2, 7):
|
||||
def compat_socket_create_connection(address, timeout, source_address=None):
|
||||
host, port = address
|
||||
@@ -3158,6 +3220,7 @@ else:
|
||||
compat_socket_create_connection = socket.create_connection
|
||||
|
||||
|
||||
# compat_contextlib_suppress
|
||||
try:
|
||||
from contextlib import suppress as compat_contextlib_suppress
|
||||
except ImportError:
|
||||
@@ -3200,12 +3263,12 @@ except AttributeError:
|
||||
# repeated .close() is OK, but just in case
|
||||
with compat_contextlib_suppress(EnvironmentError):
|
||||
f.close()
|
||||
popen.wait()
|
||||
popen.wait()
|
||||
|
||||
|
||||
# Fix https://github.com/ytdl-org/youtube-dl/issues/4223
|
||||
# See http://bugs.python.org/issue9161 for what is broken
|
||||
def workaround_optparse_bug9161():
|
||||
def _workaround_optparse_bug9161():
|
||||
op = optparse.OptionParser()
|
||||
og = optparse.OptionGroup(op, 'foo')
|
||||
try:
|
||||
@@ -3224,9 +3287,10 @@ def workaround_optparse_bug9161():
|
||||
optparse.OptionGroup.add_option = _compat_add_option
|
||||
|
||||
|
||||
if hasattr(shutil, 'get_terminal_size'): # Python >= 3.3
|
||||
compat_get_terminal_size = shutil.get_terminal_size
|
||||
else:
|
||||
# compat_shutil_get_terminal_size
|
||||
try:
|
||||
from shutil import get_terminal_size as compat_get_terminal_size # Python >= 3.3
|
||||
except ImportError:
|
||||
_terminal_size = collections.namedtuple('terminal_size', ['columns', 'lines'])
|
||||
|
||||
def compat_get_terminal_size(fallback=(80, 24)):
|
||||
@@ -3256,27 +3320,33 @@ else:
|
||||
columns = _columns
|
||||
if lines is None or lines <= 0:
|
||||
lines = _lines
|
||||
|
||||
return _terminal_size(columns, lines)
|
||||
|
||||
compat_shutil_get_terminal_size = compat_get_terminal_size
|
||||
|
||||
|
||||
# compat_itertools_count
|
||||
try:
|
||||
itertools.count(start=0, step=1)
|
||||
type(itertools.count(start=0, step=1))
|
||||
compat_itertools_count = itertools.count
|
||||
except TypeError: # Python 2.6
|
||||
except TypeError: # Python 2.6 lacks step
|
||||
def compat_itertools_count(start=0, step=1):
|
||||
while True:
|
||||
yield start
|
||||
start += step
|
||||
|
||||
|
||||
# compat_tokenize_tokenize
|
||||
if sys.version_info >= (3, 0):
|
||||
from tokenize import tokenize as compat_tokenize_tokenize
|
||||
else:
|
||||
from tokenize import generate_tokens as compat_tokenize_tokenize
|
||||
|
||||
|
||||
# compat_struct_pack, compat_struct_unpack, compat_Struct
|
||||
try:
|
||||
struct.pack('!I', 0)
|
||||
type(struct.pack('!I', 0))
|
||||
except TypeError:
|
||||
# In Python 2.6 and 2.7.x < 2.7.7, struct requires a bytes argument
|
||||
# See https://bugs.python.org/issue19099
|
||||
@@ -3308,8 +3378,10 @@ else:
|
||||
compat_Struct = struct.Struct
|
||||
|
||||
|
||||
# compat_map/filter() returning an iterator, supposedly the
|
||||
# same versioning as for zip below
|
||||
# builtins returning an iterator
|
||||
|
||||
# compat_map, compat_filter
|
||||
# supposedly the same versioning as for zip below
|
||||
try:
|
||||
from future_builtins import map as compat_map
|
||||
except ImportError:
|
||||
@@ -3326,6 +3398,7 @@ except ImportError:
|
||||
except ImportError:
|
||||
compat_filter = filter
|
||||
|
||||
# compat_zip
|
||||
try:
|
||||
from future_builtins import zip as compat_zip
|
||||
except ImportError: # not 2.6+ or is 3.x
|
||||
@@ -3335,6 +3408,7 @@ except ImportError: # not 2.6+ or is 3.x
|
||||
compat_zip = zip
|
||||
|
||||
|
||||
# compat_itertools_zip_longest
|
||||
# method renamed between Py2/3
|
||||
try:
|
||||
from itertools import zip_longest as compat_itertools_zip_longest
|
||||
@@ -3342,7 +3416,8 @@ except ImportError:
|
||||
from itertools import izip_longest as compat_itertools_zip_longest
|
||||
|
||||
|
||||
# new class in collections
|
||||
# compat_collections_chain_map
|
||||
# collections.ChainMap: new class
|
||||
try:
|
||||
from collections import ChainMap as compat_collections_chain_map
|
||||
# Py3.3's ChainMap is deficient
|
||||
@@ -3398,19 +3473,22 @@ except ImportError:
|
||||
def new_child(self, m=None, **kwargs):
|
||||
m = m or {}
|
||||
m.update(kwargs)
|
||||
return compat_collections_chain_map(m, *self.maps)
|
||||
# support inheritance !
|
||||
return type(self)(m, *self.maps)
|
||||
|
||||
@property
|
||||
def parents(self):
|
||||
return compat_collections_chain_map(*(self.maps[1:]))
|
||||
return type(self)(*(self.maps[1:]))
|
||||
|
||||
|
||||
# compat_re_Pattern, compat_re_Match
|
||||
# Pythons disagree on the type of a pattern (RegexObject, _sre.SRE_Pattern, Pattern, ...?)
|
||||
compat_re_Pattern = type(re.compile(''))
|
||||
# and on the type of a match
|
||||
compat_re_Match = type(re.match('a', 'a'))
|
||||
|
||||
|
||||
# compat_base64_b64decode
|
||||
if sys.version_info < (3, 3):
|
||||
def compat_b64decode(s, *args, **kwargs):
|
||||
if isinstance(s, compat_str):
|
||||
@@ -3422,6 +3500,7 @@ else:
|
||||
compat_base64_b64decode = compat_b64decode
|
||||
|
||||
|
||||
# compat_ctypes_WINFUNCTYPE
|
||||
if platform.python_implementation() == 'PyPy' and sys.pypy_version_info < (5, 4, 0):
|
||||
# PyPy2 prior to version 5.4.0 expects byte strings as Windows function
|
||||
# names, see the original PyPy issue [1] and the youtube-dl one [2].
|
||||
@@ -3440,6 +3519,7 @@ else:
|
||||
return ctypes.WINFUNCTYPE(*args, **kwargs)
|
||||
|
||||
|
||||
# compat_open
|
||||
if sys.version_info < (3, 0):
|
||||
# open(file, mode='r', buffering=- 1, encoding=None, errors=None, newline=None, closefd=True) not: opener=None
|
||||
def compat_open(file_, *args, **kwargs):
|
||||
@@ -3467,18 +3547,28 @@ except AttributeError:
|
||||
def compat_datetime_timedelta_total_seconds(td):
|
||||
return (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6
|
||||
|
||||
|
||||
# optional decompression packages
|
||||
# compat_brotli
|
||||
# PyPi brotli package implements 'br' Content-Encoding
|
||||
try:
|
||||
import brotli as compat_brotli
|
||||
except ImportError:
|
||||
compat_brotli = None
|
||||
# compat_ncompress
|
||||
# PyPi ncompress package implements 'compress' Content-Encoding
|
||||
try:
|
||||
import ncompress as compat_ncompress
|
||||
except ImportError:
|
||||
compat_ncompress = None
|
||||
|
||||
# compat_zstandard
|
||||
# PyPi zstandard package implements 'zstd' Content-Encoding (RFC 8878 7.2)
|
||||
try:
|
||||
import zstandard as compat_zstandard
|
||||
except ImportError:
|
||||
compat_zstandard = None
|
||||
|
||||
|
||||
legacy = [
|
||||
'compat_HTMLParseError',
|
||||
@@ -3495,6 +3585,7 @@ legacy = [
|
||||
'compat_getpass',
|
||||
'compat_parse_qs',
|
||||
'compat_realpath',
|
||||
'compat_shlex_split',
|
||||
'compat_urllib_parse_parse_qs',
|
||||
'compat_urllib_parse_unquote',
|
||||
'compat_urllib_parse_unquote_plus',
|
||||
@@ -3508,8 +3599,6 @@ legacy = [
|
||||
|
||||
|
||||
__all__ = [
|
||||
'compat_html_parser_HTMLParseError',
|
||||
'compat_html_parser_HTMLParser',
|
||||
'compat_Struct',
|
||||
'compat_base64_b64decode',
|
||||
'compat_basestring',
|
||||
@@ -3518,13 +3607,9 @@ __all__ = [
|
||||
'compat_chr',
|
||||
'compat_collections_abc',
|
||||
'compat_collections_chain_map',
|
||||
'compat_datetime_timedelta_total_seconds',
|
||||
'compat_http_cookiejar',
|
||||
'compat_http_cookiejar_Cookie',
|
||||
'compat_http_cookies',
|
||||
'compat_http_cookies_SimpleCookie',
|
||||
'compat_contextlib_suppress',
|
||||
'compat_ctypes_WINFUNCTYPE',
|
||||
'compat_datetime_timedelta_total_seconds',
|
||||
'compat_etree_fromstring',
|
||||
'compat_etree_iterfind',
|
||||
'compat_filter',
|
||||
@@ -3533,6 +3618,12 @@ __all__ = [
|
||||
'compat_getpass_getpass',
|
||||
'compat_html_entities',
|
||||
'compat_html_entities_html5',
|
||||
'compat_html_parser_HTMLParseError',
|
||||
'compat_html_parser_HTMLParser',
|
||||
'compat_http_cookiejar',
|
||||
'compat_http_cookiejar_Cookie',
|
||||
'compat_http_cookies',
|
||||
'compat_http_cookies_SimpleCookie',
|
||||
'compat_http_client',
|
||||
'compat_http_server',
|
||||
'compat_input',
|
||||
@@ -3555,7 +3646,7 @@ __all__ = [
|
||||
'compat_register_utf8',
|
||||
'compat_setenv',
|
||||
'compat_shlex_quote',
|
||||
'compat_shlex_split',
|
||||
'compat_shutil_get_terminal_size',
|
||||
'compat_socket_create_connection',
|
||||
'compat_str',
|
||||
'compat_struct_pack',
|
||||
@@ -3575,5 +3666,5 @@ __all__ = [
|
||||
'compat_xml_etree_register_namespace',
|
||||
'compat_xpath',
|
||||
'compat_zip',
|
||||
'workaround_optparse_bug9161',
|
||||
'compat_zstandard',
|
||||
]
|
||||
|
||||
@@ -122,7 +122,8 @@ class YoutubeBaseInfoExtractor(InfoExtractor):
|
||||
'INNERTUBE_CONTEXT': {
|
||||
'client': {
|
||||
'clientName': 'TVHTML5',
|
||||
'clientVersion': '7.20241201.18.00',
|
||||
'clientVersion': '7.20250120.19.00',
|
||||
'userAgent': 'Mozilla/5.0 (ChromiumStylePlatform) Cobalt/Version',
|
||||
},
|
||||
},
|
||||
'INNERTUBE_CONTEXT_CLIENT_NAME': 7,
|
||||
@@ -1851,12 +1852,22 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
||||
|
||||
if func_code:
|
||||
return jsi, player_id, func_code
|
||||
return self._extract_n_function_code_jsi(video_id, jsi, player_id)
|
||||
|
||||
func_name = self._extract_n_function_name(jscode)
|
||||
def _extract_n_function_code_jsi(self, video_id, jsi, player_id=None):
|
||||
|
||||
var_ay = self._search_regex(
|
||||
r'(?:[;\s]|^)\s*(var\s*[\w$]+\s*=\s*"[^"]+"\s*\.\s*split\("\{"\))(?=\s*[,;])',
|
||||
jsi.code, 'useful values', default='')
|
||||
|
||||
func_name = self._extract_n_function_name(jsi.code)
|
||||
|
||||
func_code = jsi.extract_function_code(func_name)
|
||||
if var_ay:
|
||||
func_code = (func_code[0], ';\n'.join((var_ay, func_code[1])))
|
||||
|
||||
self.cache.store('youtube-nsig', player_id, func_code)
|
||||
if player_id:
|
||||
self.cache.store('youtube-nsig', player_id, func_code)
|
||||
return jsi, player_id, func_code
|
||||
|
||||
def _extract_n_function_from_code(self, jsi, func_code):
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
# coding: utf-8
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import calendar
|
||||
import itertools
|
||||
import json
|
||||
import operator
|
||||
import re
|
||||
import time
|
||||
|
||||
from functools import update_wrapper, wraps
|
||||
|
||||
@@ -12,8 +14,10 @@ from .utils import (
|
||||
error_to_compat_str,
|
||||
ExtractorError,
|
||||
float_or_none,
|
||||
int_or_none,
|
||||
js_to_json,
|
||||
remove_quotes,
|
||||
str_or_none,
|
||||
unified_timestamp,
|
||||
variadic,
|
||||
write_string,
|
||||
@@ -150,6 +154,7 @@ def _js_to_primitive(v):
|
||||
)
|
||||
|
||||
|
||||
# more exact: yt-dlp/yt-dlp#12110
|
||||
def _js_toString(v):
|
||||
return (
|
||||
'undefined' if v is JS_Undefined
|
||||
@@ -158,7 +163,7 @@ def _js_toString(v):
|
||||
else 'null' if v is None
|
||||
# bool <= int: do this first
|
||||
else ('false', 'true')[v] if isinstance(v, bool)
|
||||
else '{0:.7f}'.format(v).rstrip('.0') if isinstance(v, compat_numeric_types)
|
||||
else re.sub(r'(?<=\d)\.?0*$', '', '{0:.7f}'.format(v)) if isinstance(v, compat_numeric_types)
|
||||
else _js_to_primitive(v))
|
||||
|
||||
|
||||
@@ -404,6 +409,7 @@ class JSInterpreter(object):
|
||||
class Exception(ExtractorError):
|
||||
def __init__(self, msg, *args, **kwargs):
|
||||
expr = kwargs.pop('expr', None)
|
||||
msg = str_or_none(msg, default='"None"')
|
||||
if expr is not None:
|
||||
msg = '{0} in: {1!r:.100}'.format(msg.rstrip(), expr)
|
||||
super(JSInterpreter.Exception, self).__init__(msg, *args, **kwargs)
|
||||
@@ -431,6 +437,7 @@ class JSInterpreter(object):
|
||||
flags, _ = self.regex_flags(flags)
|
||||
# First, avoid https://github.com/python/cpython/issues/74534
|
||||
self.__self = None
|
||||
pattern_txt = str_or_none(pattern_txt) or '(?:)'
|
||||
self.__pattern_txt = pattern_txt.replace('[[', r'[\[')
|
||||
self.__flags = flags
|
||||
|
||||
@@ -475,6 +482,73 @@ class JSInterpreter(object):
|
||||
flags |= cls.RE_FLAGS[ch]
|
||||
return flags, expr[idx + 1:]
|
||||
|
||||
class JS_Date(object):
|
||||
_t = None
|
||||
|
||||
@staticmethod
|
||||
def __ymd_etc(*args, **kw_is_utc):
|
||||
# args: year, monthIndex, day, hours, minutes, seconds, milliseconds
|
||||
is_utc = kw_is_utc.get('is_utc', False)
|
||||
|
||||
args = list(args[:7])
|
||||
args += [0] * (9 - len(args))
|
||||
args[1] += 1 # month 0..11 -> 1..12
|
||||
ms = args[6]
|
||||
for i in range(6, 9):
|
||||
args[i] = -1 # don't know
|
||||
if is_utc:
|
||||
args[-1] = 1
|
||||
# TODO: [MDN] When a segment overflows or underflows its expected
|
||||
# range, it usually "carries over to" or "borrows from" the higher segment.
|
||||
try:
|
||||
mktime = calendar.timegm if is_utc else time.mktime
|
||||
return mktime(time.struct_time(args)) * 1000 + ms
|
||||
except (OverflowError, ValueError):
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def UTC(cls, *args):
|
||||
t = cls.__ymd_etc(*args, is_utc=True)
|
||||
return _NaN if t is None else t
|
||||
|
||||
@staticmethod
|
||||
def parse(date_str, **kw_is_raw):
|
||||
is_raw = kw_is_raw.get('is_raw', False)
|
||||
|
||||
t = unified_timestamp(str_or_none(date_str), False)
|
||||
return int(t * 1000) if t is not None else t if is_raw else _NaN
|
||||
|
||||
@staticmethod
|
||||
def now(**kw_is_raw):
|
||||
is_raw = kw_is_raw.get('is_raw', False)
|
||||
|
||||
t = time.time()
|
||||
return int(t * 1000) if t is not None else t if is_raw else _NaN
|
||||
|
||||
def __init__(self, *args):
|
||||
if not args:
|
||||
args = [self.now(is_raw=True)]
|
||||
if len(args) == 1:
|
||||
if isinstance(args[0], JSInterpreter.JS_Date):
|
||||
self._t = int_or_none(args[0].valueOf(), default=None)
|
||||
else:
|
||||
arg_type = _js_typeof(args[0])
|
||||
if arg_type == 'string':
|
||||
self._t = self.parse(args[0], is_raw=True)
|
||||
elif arg_type == 'number':
|
||||
self._t = int(args[0])
|
||||
else:
|
||||
self._t = self.__ymd_etc(*args)
|
||||
|
||||
def toString(self):
|
||||
try:
|
||||
return time.strftime('%a %b %0d %Y %H:%M:%S %Z%z', self._t).rstrip()
|
||||
except TypeError:
|
||||
return "Invalid Date"
|
||||
|
||||
def valueOf(self):
|
||||
return _NaN if self._t is None else self._t
|
||||
|
||||
@classmethod
|
||||
def __op_chars(cls):
|
||||
op_chars = set(';,[')
|
||||
@@ -599,14 +673,15 @@ class JSInterpreter(object):
|
||||
except Exception as e:
|
||||
raise self.Exception('Failed to evaluate {left_val!r:.50} {op} {right_val!r:.50}'.format(**locals()), expr, cause=e)
|
||||
|
||||
def _index(self, obj, idx, allow_undefined=True):
|
||||
def _index(self, obj, idx, allow_undefined=None):
|
||||
if idx == 'length' and isinstance(obj, list):
|
||||
return len(obj)
|
||||
try:
|
||||
return obj[int(idx)] if isinstance(obj, list) else obj[compat_str(idx)]
|
||||
except (TypeError, KeyError, IndexError) as e:
|
||||
if allow_undefined:
|
||||
# when is not allowed?
|
||||
# allow_undefined is None gives correct behaviour
|
||||
if allow_undefined or (
|
||||
allow_undefined is None and not isinstance(e, TypeError)):
|
||||
return JS_Undefined
|
||||
raise self.Exception('Cannot get index {idx!r:.100}'.format(**locals()), expr=repr(obj), cause=e)
|
||||
|
||||
@@ -715,7 +790,7 @@ class JSInterpreter(object):
|
||||
|
||||
new_kw, _, obj = expr.partition('new ')
|
||||
if not new_kw:
|
||||
for klass, konstr in (('Date', lambda x: int(unified_timestamp(x, False) * 1000)),
|
||||
for klass, konstr in (('Date', lambda *x: self.JS_Date(*x).valueOf()),
|
||||
('RegExp', self.JS_RegExp),
|
||||
('Error', self.Exception)):
|
||||
if not obj.startswith(klass + '('):
|
||||
@@ -1034,6 +1109,7 @@ class JSInterpreter(object):
|
||||
'String': compat_str,
|
||||
'Math': float,
|
||||
'Array': list,
|
||||
'Date': self.JS_Date,
|
||||
}
|
||||
obj = local_vars.get(variable)
|
||||
if obj in (JS_Undefined, None):
|
||||
@@ -1086,6 +1162,8 @@ class JSInterpreter(object):
|
||||
assertion(len(argvals) == 2, 'takes two arguments')
|
||||
return argvals[0] ** argvals[1]
|
||||
raise self.Exception('Unsupported Math method ' + member, expr=expr)
|
||||
elif obj is self.JS_Date:
|
||||
return getattr(obj, member)(*argvals)
|
||||
|
||||
if member == 'split':
|
||||
assertion(len(argvals) <= 2, 'takes at most two arguments')
|
||||
|
||||
Reference in New Issue
Block a user