mirror of
https://github.com/bolucat/Archive.git
synced 2025-09-26 20:21:35 +08:00
Update On Mon Mar 3 19:34:41 CET 2025
This commit is contained in:
1
.github/update.log
vendored
1
.github/update.log
vendored
@@ -930,3 +930,4 @@ Update On Thu Feb 27 19:34:57 CET 2025
|
||||
Update On Fri Feb 28 19:34:20 CET 2025
|
||||
Update On Sat Mar 1 19:31:26 CET 2025
|
||||
Update On Sun Mar 2 19:33:02 CET 2025
|
||||
Update On Mon Mar 3 19:34:32 CET 2025
|
||||
|
@@ -46,8 +46,8 @@ subprojects {
|
||||
minSdk = 21
|
||||
targetSdk = 35
|
||||
|
||||
versionName = "2.11.6"
|
||||
versionCode = 211006
|
||||
versionName = "2.11.7"
|
||||
versionCode = 211007
|
||||
|
||||
resValue("string", "release_name", "v$versionName")
|
||||
resValue("integer", "release_code", "$versionCode")
|
||||
|
@@ -1,17 +1,17 @@
|
||||
[Unit]
|
||||
Description=mihomo Daemon, Another Clash Kernel.
|
||||
After=network.target NetworkManager.service systemd-networkd.service iwd.service
|
||||
Documentation=https://wiki.metacubex.one
|
||||
After=network.target nss-lookup.target network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
LimitNPROC=500
|
||||
LimitNOFILE=1000000
|
||||
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_TIME CAP_SYS_PTRACE CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE
|
||||
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_TIME CAP_SYS_PTRACE CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE
|
||||
Restart=always
|
||||
ExecStartPre=/usr/bin/sleep 2s
|
||||
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
|
||||
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
|
||||
ExecStart=/usr/bin/mihomo -d /etc/mihomo
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
LimitNOFILE=infinity
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
17
clash-meta-android/core/src/foss/golang/clash/.github/mihomo@.service
vendored
Normal file
17
clash-meta-android/core/src/foss/golang/clash/.github/mihomo@.service
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
[Unit]
|
||||
Description=mihomo Daemon, Another Clash Kernel.
|
||||
Documentation=https://wiki.metacubex.one
|
||||
After=network.target nss-lookup.target network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
|
||||
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
|
||||
ExecStart=/usr/bin/mihomo -d /etc/mihomo
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
LimitNOFILE=infinity
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
@@ -276,17 +276,20 @@ jobs:
|
||||
|
||||
mkdir -p mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/DEBIAN
|
||||
mkdir -p mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/usr/bin
|
||||
mkdir -p mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/etc/mihomo
|
||||
mkdir -p mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/etc/systemd/system/
|
||||
mkdir -p mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/usr/share/licenses/mihomo
|
||||
mkdir -p mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/etc/mihomo
|
||||
mkdir -p mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/lib/systemd/system
|
||||
|
||||
cp mihomo mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/usr/bin/mihomo
|
||||
cp mihomo mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/usr/bin/
|
||||
cp LICENSE mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/usr/share/licenses/mihomo/
|
||||
cp .github/mihomo.service mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/etc/systemd/system/
|
||||
cp .github/{mihomo.service,mihomo@.service} mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/lib/systemd/system/
|
||||
|
||||
cat > mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/etc/mihomo/config.yaml <<EOF
|
||||
mixed-port: 7890
|
||||
external-controller: 127.0.0.1:9090
|
||||
EOF
|
||||
|
||||
cat > mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/DEBIAN/conffiles <<EOF
|
||||
/etc/mihomo/config.yaml
|
||||
EOF
|
||||
|
||||
cat > mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/DEBIAN/control <<EOF
|
||||
|
@@ -42,6 +42,7 @@ type AnyTLSOption struct {
|
||||
UDP bool `proxy:"udp,omitempty"`
|
||||
IdleSessionCheckInterval int `proxy:"idle-session-check-interval,omitempty"`
|
||||
IdleSessionTimeout int `proxy:"idle-session-timeout,omitempty"`
|
||||
MinIdleSession int `proxy:"min-idle-session,omitempty"`
|
||||
}
|
||||
|
||||
func (t *AnyTLS) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) {
|
||||
@@ -98,6 +99,7 @@ func NewAnyTLS(option AnyTLSOption) (*AnyTLS, error) {
|
||||
Dialer: singDialer,
|
||||
IdleSessionCheckInterval: time.Duration(option.IdleSessionCheckInterval) * time.Second,
|
||||
IdleSessionTimeout: time.Duration(option.IdleSessionTimeout) * time.Second,
|
||||
MinIdleSession: option.MinIdleSession,
|
||||
}
|
||||
tlsConfig := &vmess.TLSConfig{
|
||||
Host: option.SNI,
|
||||
|
@@ -459,6 +459,11 @@ func NewVmess(option VmessOption) (*Vmess, error) {
|
||||
option: &option,
|
||||
}
|
||||
|
||||
v.realityConfig, err = v.option.RealityOpts.Parse()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch option.Network {
|
||||
case "h2":
|
||||
if len(option.HTTP2Opts.Host) == 0 {
|
||||
@@ -507,11 +512,6 @@ func NewVmess(option VmessOption) (*Vmess, error) {
|
||||
v.transport = gun.NewHTTP2Client(dialFn, tlsConfig, v.option.ClientFingerprint, v.realityConfig)
|
||||
}
|
||||
|
||||
v.realityConfig, err = v.option.RealityOpts.Parse()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
|
@@ -139,10 +139,13 @@ func (ranges IntRanges[T]) Range(f func(t T) bool) {
|
||||
}
|
||||
|
||||
for _, r := range ranges {
|
||||
for i := r.Start(); i <= r.End(); i++ {
|
||||
for i := r.Start(); i <= r.End() && i >= r.Start(); i++ {
|
||||
if !f(i) {
|
||||
return
|
||||
}
|
||||
if i+1 < i { // integer overflow
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -28,6 +28,7 @@ const (
|
||||
VLESS
|
||||
REDIR
|
||||
TPROXY
|
||||
TROJAN
|
||||
TUNNEL
|
||||
TUN
|
||||
TUIC
|
||||
@@ -77,6 +78,8 @@ func (t Type) String() string {
|
||||
return "Redir"
|
||||
case TPROXY:
|
||||
return "TProxy"
|
||||
case TROJAN:
|
||||
return "Trojan"
|
||||
case TUNNEL:
|
||||
return "Tunnel"
|
||||
case TUN:
|
||||
@@ -115,6 +118,8 @@ func ParseType(t string) (*Type, error) {
|
||||
res = REDIR
|
||||
case "TPROXY":
|
||||
res = TPROXY
|
||||
case "TROJAN":
|
||||
res = TROJAN
|
||||
case "TUNNEL":
|
||||
res = TUNNEL
|
||||
case "TUN":
|
||||
|
@@ -874,6 +874,7 @@ proxies: # socks5
|
||||
udp: true
|
||||
# idle-session-check-interval: 30 # seconds
|
||||
# idle-session-timeout: 30 # seconds
|
||||
# min-idle-session: 0
|
||||
# sni: "example.com"
|
||||
# alpn:
|
||||
# - h2
|
||||
@@ -1095,7 +1096,7 @@ sub-rules:
|
||||
listeners:
|
||||
- name: socks5-in-1
|
||||
type: socks
|
||||
port: 10808
|
||||
port: 10808 # 支持使用ports格式,例如200,302 or 200,204,401-429,501-503
|
||||
#listen: 0.0.0.0 # 默认监听 0.0.0.0
|
||||
# rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules
|
||||
# proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理
|
||||
@@ -1103,20 +1104,26 @@ listeners:
|
||||
# users: # 如果不填写users项,则遵从全局authentication设置,如果填写会忽略全局设置, 如想跳过该入站的验证可填写 users: []
|
||||
# - username: aaa
|
||||
# password: aaa
|
||||
# 下面两项如果填写则开启 tls(需要同时填写)
|
||||
# certificate: ./server.crt
|
||||
# private-key: ./server.key
|
||||
|
||||
- name: http-in-1
|
||||
type: http
|
||||
port: 10809
|
||||
port: 10809 # 支持使用ports格式,例如200,302 or 200,204,401-429,501-503
|
||||
listen: 0.0.0.0
|
||||
# rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules
|
||||
# proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时,这里的 proxy 名称必须合法,否则会出错)
|
||||
# users: # 如果不填写users项,则遵从全局authentication设置,如果填写会忽略全局设置, 如想跳过该入站的验证可填写 users: []
|
||||
# - username: aaa
|
||||
# password: aaa
|
||||
# 下面两项如果填写则开启 tls(需要同时填写)
|
||||
# certificate: ./server.crt
|
||||
# private-key: ./server.key
|
||||
|
||||
- name: mixed-in-1
|
||||
type: mixed # HTTP(S) 和 SOCKS 代理混合
|
||||
port: 10810
|
||||
port: 10810 # 支持使用ports格式,例如200,302 or 200,204,401-429,501-503
|
||||
listen: 0.0.0.0
|
||||
# rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules
|
||||
# proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时,这里的 proxy 名称必须合法,否则会出错)
|
||||
@@ -1124,17 +1131,20 @@ listeners:
|
||||
# users: # 如果不填写users项,则遵从全局authentication设置,如果填写会忽略全局设置, 如想跳过该入站的验证可填写 users: []
|
||||
# - username: aaa
|
||||
# password: aaa
|
||||
# 下面两项如果填写则开启 tls(需要同时填写)
|
||||
# certificate: ./server.crt
|
||||
# private-key: ./server.key
|
||||
|
||||
- name: reidr-in-1
|
||||
type: redir
|
||||
port: 10811
|
||||
port: 10811 # 支持使用ports格式,例如200,302 or 200,204,401-429,501-503
|
||||
listen: 0.0.0.0
|
||||
# rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules
|
||||
# proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时,这里的 proxy 名称必须合法,否则会出错)
|
||||
|
||||
- name: tproxy-in-1
|
||||
type: tproxy
|
||||
port: 10812
|
||||
port: 10812 # 支持使用ports格式,例如200,302 or 200,204,401-429,501-503
|
||||
listen: 0.0.0.0
|
||||
# rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules
|
||||
# proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时,这里的 proxy 名称必须合法,否则会出错)
|
||||
@@ -1142,7 +1152,7 @@ listeners:
|
||||
|
||||
- name: shadowsocks-in-1
|
||||
type: shadowsocks
|
||||
port: 10813
|
||||
port: 10813 # 支持使用ports格式,例如200,302 or 200,204,401-429,501-503
|
||||
listen: 0.0.0.0
|
||||
# rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules
|
||||
# proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时,这里的 proxy 名称必须合法,否则会出错)
|
||||
@@ -1151,7 +1161,7 @@ listeners:
|
||||
|
||||
- name: vmess-in-1
|
||||
type: vmess
|
||||
port: 10814
|
||||
port: 10814 # 支持使用ports格式,例如200,302 or 200,204,401-429,501-503
|
||||
listen: 0.0.0.0
|
||||
# rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules
|
||||
# proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时,这里的 proxy 名称必须合法,否则会出错)
|
||||
@@ -1160,6 +1170,7 @@ listeners:
|
||||
uuid: 9d0cb9d0-964f-4ef6-897d-6c6b3ccf9e68
|
||||
alterId: 1
|
||||
# ws-path: "/" # 如果不为空则开启 websocket 传输层
|
||||
# grpc-service-name: "GunService" # 如果不为空则开启 grpc 传输层
|
||||
# 下面两项如果填写则开启 tls(需要同时填写)
|
||||
# certificate: ./server.crt
|
||||
# private-key: ./server.key
|
||||
@@ -1174,7 +1185,7 @@ listeners:
|
||||
|
||||
- name: tuic-in-1
|
||||
type: tuic
|
||||
port: 10815
|
||||
port: 10815 # 支持使用ports格式,例如200,302 or 200,204,401-429,501-503
|
||||
listen: 0.0.0.0
|
||||
# rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules
|
||||
# proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时,这里的 proxy 名称必须合法,否则会出错)
|
||||
@@ -1194,7 +1205,7 @@ listeners:
|
||||
|
||||
- name: tunnel-in-1
|
||||
type: tunnel
|
||||
port: 10816
|
||||
port: 10816 # 支持使用ports格式,例如200,302 or 200,204,401-429,501-503
|
||||
listen: 0.0.0.0
|
||||
# rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules
|
||||
# proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时,这里的 proxy 名称必须合法,否则会出错)
|
||||
@@ -1203,7 +1214,7 @@ listeners:
|
||||
|
||||
- name: vless-in-1
|
||||
type: vless
|
||||
port: 10817
|
||||
port: 10817 # 支持使用ports格式,例如200,302 or 200,204,401-429,501-503
|
||||
listen: 0.0.0.0
|
||||
# rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules
|
||||
# proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时,这里的 proxy 名称必须合法,否则会出错)
|
||||
@@ -1212,6 +1223,7 @@ listeners:
|
||||
uuid: 9d0cb9d0-964f-4ef6-897d-6c6b3ccf9e68
|
||||
flow: xtls-rprx-vision
|
||||
# ws-path: "/" # 如果不为空则开启 websocket 传输层
|
||||
# grpc-service-name: "GunService" # 如果不为空则开启 grpc 传输层
|
||||
# 下面两项如果填写则开启 tls(需要同时填写)
|
||||
# certificate: ./server.crt
|
||||
# private-key: ./server.key
|
||||
@@ -1227,7 +1239,7 @@ listeners:
|
||||
|
||||
- name: anytls-in-1
|
||||
type: anytls
|
||||
port: 10818
|
||||
port: 10818 # 支持使用ports格式,例如200,302 or 200,204,401-429,501-503
|
||||
listen: 0.0.0.0
|
||||
users:
|
||||
username1: password1
|
||||
@@ -1237,6 +1249,34 @@ listeners:
|
||||
private-key: ./server.key
|
||||
# padding-scheme: "" # https://github.com/anytls/anytls-go/blob/main/docs/protocol.md#cmdupdatepaddingscheme
|
||||
|
||||
- name: trojan-in-1
|
||||
type: trojan
|
||||
port: 10819 # 支持使用ports格式,例如200,302 or 200,204,401-429,501-503
|
||||
listen: 0.0.0.0
|
||||
# rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules
|
||||
# proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时,这里的 proxy 名称必须合法,否则会出错)
|
||||
users:
|
||||
- username: 1
|
||||
password: 9d0cb9d0-964f-4ef6-897d-6c6b3ccf9e68
|
||||
# ws-path: "/" # 如果不为空则开启 websocket 传输层
|
||||
# grpc-service-name: "GunService" # 如果不为空则开启 grpc 传输层
|
||||
# 下面两项如果填写则开启 tls(需要同时填写)
|
||||
certificate: ./server.crt
|
||||
private-key: ./server.key
|
||||
# 如果填写reality-config则开启reality(注意不可与certificate和private-key同时填写)
|
||||
# reality-config:
|
||||
# dest: test.com:443
|
||||
# private-key: jNXHt1yRo0vDuchQlIP6Z0ZvjT3KtzVI-T4E7RoLJS0 # 可由 mihomo generate reality-keypair 命令生成
|
||||
# short-id:
|
||||
# - 0123456789abcdef
|
||||
# server-names:
|
||||
# - test.com
|
||||
# ss-option: # like trojan-go's `shadowsocks` config
|
||||
# enabled: false
|
||||
# method: aes-128-gcm # aes-128-gcm/aes-256-gcm/chacha20-ietf-poly1305
|
||||
# password: "example"
|
||||
### 注意,对于trojan listener, 至少需要填写 “certificate和private-key” 或 “reality-config” 或 “ss-option” 的其中一项 ###
|
||||
|
||||
- name: tun-in-1
|
||||
type: tun
|
||||
# rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules
|
||||
|
@@ -28,7 +28,7 @@ require (
|
||||
github.com/metacubex/sing-shadowsocks v0.2.8
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.2
|
||||
github.com/metacubex/sing-tun v0.4.5
|
||||
github.com/metacubex/sing-vmess v0.1.14-0.20250203033000-f61322b3dbe3
|
||||
github.com/metacubex/sing-vmess v0.1.14-0.20250228002636-abc39e113b82
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20241126021510-0827d417b589
|
||||
github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422
|
||||
github.com/metacubex/utls v1.6.6
|
||||
@@ -41,9 +41,10 @@ require (
|
||||
github.com/sagernet/cors v1.2.1
|
||||
github.com/sagernet/fswatch v0.1.1
|
||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a
|
||||
github.com/sagernet/sing v0.5.1
|
||||
github.com/sagernet/sing v0.5.2
|
||||
github.com/sagernet/sing-mux v0.2.1
|
||||
github.com/sagernet/sing-shadowtls v0.1.5
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7
|
||||
github.com/samber/lo v1.49.1
|
||||
github.com/shirou/gopsutil/v4 v4.25.1
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
@@ -98,7 +99,6 @@ require (
|
||||
github.com/quic-go/qpack v0.4.0 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
|
||||
github.com/sagernet/nftables v0.3.0-beta.4 // indirect
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect
|
||||
github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b // indirect
|
||||
github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c // indirect
|
||||
github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e // indirect
|
||||
@@ -117,4 +117,4 @@ require (
|
||||
golang.org/x/tools v0.24.0 // indirect
|
||||
)
|
||||
|
||||
replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20241121030428-33b6ebc52000
|
||||
replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20250228041610-d94509dc612a
|
||||
|
@@ -111,8 +111,8 @@ github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiL
|
||||
github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY=
|
||||
github.com/metacubex/reality v0.0.0-20250219003814-74e8d7850629 h1:aHsYiTvubfgMa3JMTDY//hDXVvFWrHg6ARckR52ttZs=
|
||||
github.com/metacubex/reality v0.0.0-20250219003814-74e8d7850629/go.mod h1:TTeIOZLdGmzc07Oedn++vWUUfkZoXLF4sEMxWuhBFr8=
|
||||
github.com/metacubex/sing v0.0.0-20241121030428-33b6ebc52000 h1:gUbMXcQXhXGj0vCpCVFTUyIH7TMpD1dpTcNv/MCS+ok=
|
||||
github.com/metacubex/sing v0.0.0-20241121030428-33b6ebc52000/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/metacubex/sing v0.0.0-20250228041610-d94509dc612a h1:xjPXdDTlIKq4U/KnKpoCtkxD03T8GimtQrvHy/3dN00=
|
||||
github.com/metacubex/sing v0.0.0-20250228041610-d94509dc612a/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/metacubex/sing-quic v0.0.0-20250119013740-2a19cce83925 h1:UkPoRAnoBQMn7IK5qpoIV3OejU15q+rqel3NrbSCFKA=
|
||||
github.com/metacubex/sing-quic v0.0.0-20250119013740-2a19cce83925/go.mod h1:g7Mxj7b7zm7YVqD975mk/hSmrb0A0G4bVvIMr2MMzn8=
|
||||
github.com/metacubex/sing-shadowsocks v0.2.8 h1:wIhlaigswzjPw4hej75sEvWte3QR0+AJRafgwBHO5B4=
|
||||
@@ -121,8 +121,8 @@ github.com/metacubex/sing-shadowsocks2 v0.2.2 h1:eaf42uVx4Lr21S6MDYs0ZdTvGA0GEhD
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.2/go.mod h1:BhOug03a/RbI7y6hp6q+6ITM1dXjnLTmeWBHSTwvv2Q=
|
||||
github.com/metacubex/sing-tun v0.4.5 h1:kWSyQzuzHI40r50OFBczfWIDvMBMy1RIk+JsXeBPRB0=
|
||||
github.com/metacubex/sing-tun v0.4.5/go.mod h1:V0N4rr0dWPBEE20ESkTXdbtx2riQYcb6YtwC5w/9wl0=
|
||||
github.com/metacubex/sing-vmess v0.1.14-0.20250203033000-f61322b3dbe3 h1:2kq6azIvsTjTnyw66xXDl5zMzIJqF7GTbvLpkroHssg=
|
||||
github.com/metacubex/sing-vmess v0.1.14-0.20250203033000-f61322b3dbe3/go.mod h1:nE7Mdzj/QUDwgRi/8BASPtsxtIFZTHA4Yst5GgwbGCQ=
|
||||
github.com/metacubex/sing-vmess v0.1.14-0.20250228002636-abc39e113b82 h1:zZp5uct9+/0Hb1jKGyqDjCU4/72t43rs7qOq3Rc9oU8=
|
||||
github.com/metacubex/sing-vmess v0.1.14-0.20250228002636-abc39e113b82/go.mod h1:nE7Mdzj/QUDwgRi/8BASPtsxtIFZTHA4Yst5GgwbGCQ=
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20241126021510-0827d417b589 h1:Z6bNy0HLTjx6BKIkV48sV/yia/GP8Bnyb5JQuGgSGzg=
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20241126021510-0827d417b589/go.mod h1:4NclTLIZuk+QkHVCGrP87rHi/y8YjgPytxTgApJNMhc=
|
||||
github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422 h1:zGeQt3UyNydIVrMRB97AA5WsYEau/TyCnRtTf1yUmJY=
|
||||
|
@@ -0,0 +1,16 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/metacubex/mihomo/component/auth"
|
||||
"github.com/metacubex/mihomo/listener/reality"
|
||||
)
|
||||
|
||||
// AuthServer for http/socks/mixed server
|
||||
type AuthServer struct {
|
||||
Enable bool
|
||||
Listen string
|
||||
AuthStore auth.AuthStore
|
||||
Certificate string
|
||||
PrivateKey string
|
||||
RealityConfig reality.Config
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/metacubex/mihomo/listener/reality"
|
||||
"github.com/metacubex/mihomo/listener/sing"
|
||||
)
|
||||
|
||||
type TrojanUser struct {
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
type TrojanServer struct {
|
||||
Enable bool
|
||||
Listen string
|
||||
Users []TrojanUser
|
||||
WsPath string
|
||||
GrpcServiceName string
|
||||
Certificate string
|
||||
PrivateKey string
|
||||
RealityConfig reality.Config
|
||||
MuxOption sing.MuxOption
|
||||
TrojanSSOption TrojanSSOption
|
||||
}
|
||||
|
||||
// TrojanSSOption from https://github.com/p4gefau1t/trojan-go/blob/v0.10.6/tunnel/shadowsocks/config.go#L5
|
||||
type TrojanSSOption struct {
|
||||
Enabled bool
|
||||
Method string
|
||||
Password string
|
||||
}
|
||||
|
||||
func (t TrojanServer) String() string {
|
||||
b, _ := json.Marshal(t)
|
||||
return string(b)
|
||||
}
|
@@ -14,14 +14,15 @@ type VlessUser struct {
|
||||
}
|
||||
|
||||
type VlessServer struct {
|
||||
Enable bool
|
||||
Listen string
|
||||
Users []VlessUser
|
||||
WsPath string
|
||||
Certificate string
|
||||
PrivateKey string
|
||||
RealityConfig reality.Config
|
||||
MuxOption sing.MuxOption `yaml:"mux-option" json:"mux-option,omitempty"`
|
||||
Enable bool
|
||||
Listen string
|
||||
Users []VlessUser
|
||||
WsPath string
|
||||
GrpcServiceName string
|
||||
Certificate string
|
||||
PrivateKey string
|
||||
RealityConfig reality.Config
|
||||
MuxOption sing.MuxOption `yaml:"mux-option" json:"mux-option,omitempty"`
|
||||
}
|
||||
|
||||
func (t VlessServer) String() string {
|
||||
|
@@ -14,14 +14,15 @@ type VmessUser struct {
|
||||
}
|
||||
|
||||
type VmessServer struct {
|
||||
Enable bool
|
||||
Listen string
|
||||
Users []VmessUser
|
||||
WsPath string
|
||||
Certificate string
|
||||
PrivateKey string
|
||||
RealityConfig reality.Config
|
||||
MuxOption sing.MuxOption `yaml:"mux-option" json:"mux-option,omitempty"`
|
||||
Enable bool
|
||||
Listen string
|
||||
Users []VmessUser
|
||||
WsPath string
|
||||
GrpcServiceName string
|
||||
Certificate string
|
||||
PrivateKey string
|
||||
RealityConfig reality.Config
|
||||
MuxOption sing.MuxOption `yaml:"mux-option" json:"mux-option,omitempty"`
|
||||
}
|
||||
|
||||
func (t VmessServer) String() string {
|
||||
|
@@ -1,12 +1,16 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
|
||||
"github.com/metacubex/mihomo/adapter/inbound"
|
||||
"github.com/metacubex/mihomo/component/auth"
|
||||
N "github.com/metacubex/mihomo/common/net"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
authStore "github.com/metacubex/mihomo/listener/auth"
|
||||
LC "github.com/metacubex/mihomo/listener/config"
|
||||
"github.com/metacubex/mihomo/listener/reality"
|
||||
)
|
||||
|
||||
type Listener struct {
|
||||
@@ -32,7 +36,7 @@ func (l *Listener) Close() error {
|
||||
}
|
||||
|
||||
func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) {
|
||||
return NewWithAuthenticator(addr, tunnel, authStore.Default, additions...)
|
||||
return NewWithConfig(LC.AuthServer{Enable: true, Listen: addr, AuthStore: authStore.Default}, tunnel, additions...)
|
||||
}
|
||||
|
||||
// NewWithAuthenticate
|
||||
@@ -40,12 +44,12 @@ func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener
|
||||
func NewWithAuthenticate(addr string, tunnel C.Tunnel, authenticate bool, additions ...inbound.Addition) (*Listener, error) {
|
||||
store := authStore.Default
|
||||
if !authenticate {
|
||||
store = authStore.Default
|
||||
store = authStore.Nil
|
||||
}
|
||||
return NewWithAuthenticator(addr, tunnel, store, additions...)
|
||||
return NewWithConfig(LC.AuthServer{Enable: true, Listen: addr, AuthStore: store}, tunnel, additions...)
|
||||
}
|
||||
|
||||
func NewWithAuthenticator(addr string, tunnel C.Tunnel, store auth.AuthStore, additions ...inbound.Addition) (*Listener, error) {
|
||||
func NewWithConfig(config LC.AuthServer, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) {
|
||||
isDefault := false
|
||||
if len(additions) == 0 {
|
||||
isDefault = true
|
||||
@@ -55,15 +59,42 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, store auth.AuthStore, ad
|
||||
}
|
||||
}
|
||||
|
||||
l, err := inbound.Listen("tcp", addr)
|
||||
l, err := inbound.Listen("tcp", config.Listen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tlsConfig := &tls.Config{}
|
||||
var realityBuilder *reality.Builder
|
||||
|
||||
if config.Certificate != "" && config.PrivateKey != "" {
|
||||
cert, err := N.ParseCert(config.Certificate, config.PrivateKey, C.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsConfig.Certificates = []tls.Certificate{cert}
|
||||
}
|
||||
if config.RealityConfig.PrivateKey != "" {
|
||||
if tlsConfig.Certificates != nil {
|
||||
return nil, errors.New("certificate is unavailable in reality")
|
||||
}
|
||||
realityBuilder, err = config.RealityConfig.Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if realityBuilder != nil {
|
||||
l = realityBuilder.NewListener(l)
|
||||
} else if len(tlsConfig.Certificates) > 0 {
|
||||
l = tls.NewListener(l, tlsConfig)
|
||||
}
|
||||
|
||||
hl := &Listener{
|
||||
listener: l,
|
||||
addr: addr,
|
||||
addr: config.Listen,
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
conn, err := hl.listener.Accept()
|
||||
@@ -74,7 +105,7 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, store auth.AuthStore, ad
|
||||
continue
|
||||
}
|
||||
|
||||
store := store
|
||||
store := config.AuthStore
|
||||
if isDefault || store == authStore.Default { // only apply on default listener
|
||||
if !inbound.IsRemoteAddrDisAllowed(conn.RemoteAddr()) {
|
||||
_ = conn.Close()
|
||||
|
@@ -1,6 +1,8 @@
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/listener/anytls"
|
||||
LC "github.com/metacubex/mihomo/listener/config"
|
||||
@@ -52,12 +54,13 @@ func (v *AnyTLS) Config() C.InboundConfig {
|
||||
|
||||
// Address implements constant.InboundListener
|
||||
func (v *AnyTLS) Address() string {
|
||||
var addrList []string
|
||||
if v.l != nil {
|
||||
for _, addr := range v.l.AddrList() {
|
||||
return addr.String()
|
||||
addrList = append(addrList, addr.String())
|
||||
}
|
||||
}
|
||||
return ""
|
||||
return strings.Join(addrList, ",")
|
||||
}
|
||||
|
||||
// Listen implements constant.InboundListener
|
||||
|
@@ -5,8 +5,10 @@ import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/metacubex/mihomo/adapter/inbound"
|
||||
"github.com/metacubex/mihomo/common/utils"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
)
|
||||
|
||||
@@ -15,7 +17,7 @@ type Base struct {
|
||||
name string
|
||||
specialRules string
|
||||
listenAddr netip.Addr
|
||||
port int
|
||||
ports utils.IntRanges[uint16]
|
||||
}
|
||||
|
||||
func NewBase(options *BaseOption) (*Base, error) {
|
||||
@@ -26,11 +28,15 @@ func NewBase(options *BaseOption) (*Base, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ports, err := utils.NewUnsignedRanges[uint16](options.Port)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Base{
|
||||
name: options.Name(),
|
||||
listenAddr: addr,
|
||||
specialRules: options.SpecialRules,
|
||||
port: options.Port,
|
||||
ports: ports,
|
||||
config: options,
|
||||
}, nil
|
||||
}
|
||||
@@ -57,7 +63,15 @@ func (b *Base) Name() string {
|
||||
|
||||
// RawAddress implements constant.InboundListener
|
||||
func (b *Base) RawAddress() string {
|
||||
return net.JoinHostPort(b.listenAddr.String(), strconv.Itoa(int(b.port)))
|
||||
if len(b.ports) == 0 {
|
||||
return net.JoinHostPort(b.listenAddr.String(), "0")
|
||||
}
|
||||
address := make([]string, 0, len(b.ports))
|
||||
b.ports.Range(func(port uint16) bool {
|
||||
address = append(address, net.JoinHostPort(b.listenAddr.String(), strconv.Itoa(int(port))))
|
||||
return true
|
||||
})
|
||||
return strings.Join(address, ",")
|
||||
}
|
||||
|
||||
// Listen implements constant.InboundListener
|
||||
@@ -74,7 +88,7 @@ var _ C.InboundListener = (*Base)(nil)
|
||||
type BaseOption struct {
|
||||
NameStr string `inbound:"name"`
|
||||
Listen string `inbound:"listen,omitempty"`
|
||||
Port int `inbound:"port,omitempty"`
|
||||
Port string `inbound:"port,omitempty"`
|
||||
SpecialRules string `inbound:"rule,omitempty"`
|
||||
SpecialProxy string `inbound:"proxy,omitempty"`
|
||||
}
|
||||
|
@@ -1,14 +1,22 @@
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
LC "github.com/metacubex/mihomo/listener/config"
|
||||
"github.com/metacubex/mihomo/listener/http"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
)
|
||||
|
||||
type HTTPOption struct {
|
||||
BaseOption
|
||||
Users AuthUsers `inbound:"users,omitempty"`
|
||||
Users AuthUsers `inbound:"users,omitempty"`
|
||||
Certificate string `inbound:"certificate,omitempty"`
|
||||
PrivateKey string `inbound:"private-key,omitempty"`
|
||||
RealityConfig RealityConfig `inbound:"reality-config,omitempty"`
|
||||
}
|
||||
|
||||
func (o HTTPOption) Equal(config C.InboundConfig) bool {
|
||||
@@ -18,7 +26,7 @@ func (o HTTPOption) Equal(config C.InboundConfig) bool {
|
||||
type HTTP struct {
|
||||
*Base
|
||||
config *HTTPOption
|
||||
l *http.Listener
|
||||
l []*http.Listener
|
||||
}
|
||||
|
||||
func NewHTTP(options *HTTPOption) (*HTTP, error) {
|
||||
@@ -39,15 +47,32 @@ func (h *HTTP) Config() C.InboundConfig {
|
||||
|
||||
// Address implements constant.InboundListener
|
||||
func (h *HTTP) Address() string {
|
||||
return h.l.Address()
|
||||
var addrList []string
|
||||
for _, l := range h.l {
|
||||
addrList = append(addrList, l.Address())
|
||||
}
|
||||
return strings.Join(addrList, ",")
|
||||
}
|
||||
|
||||
// Listen implements constant.InboundListener
|
||||
func (h *HTTP) Listen(tunnel C.Tunnel) error {
|
||||
var err error
|
||||
h.l, err = http.NewWithAuthenticator(h.RawAddress(), tunnel, h.config.Users.GetAuthStore(), h.Additions()...)
|
||||
if err != nil {
|
||||
return err
|
||||
for _, addr := range strings.Split(h.RawAddress(), ",") {
|
||||
l, err := http.NewWithConfig(
|
||||
LC.AuthServer{
|
||||
Enable: true,
|
||||
Listen: addr,
|
||||
AuthStore: h.config.Users.GetAuthStore(),
|
||||
Certificate: h.config.Certificate,
|
||||
PrivateKey: h.config.PrivateKey,
|
||||
RealityConfig: h.config.RealityConfig.Build(),
|
||||
},
|
||||
tunnel,
|
||||
h.Additions()...,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h.l = append(h.l, l)
|
||||
}
|
||||
log.Infoln("HTTP[%s] proxy listening at: %s", h.Name(), h.Address())
|
||||
return nil
|
||||
@@ -55,8 +80,15 @@ func (h *HTTP) Listen(tunnel C.Tunnel) error {
|
||||
|
||||
// Close implements constant.InboundListener
|
||||
func (h *HTTP) Close() error {
|
||||
if h.l != nil {
|
||||
return h.l.Close()
|
||||
var errs []error
|
||||
for _, l := range h.l {
|
||||
err := l.Close()
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("close tcp listener %s err: %w", l.Address(), err))
|
||||
}
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@@ -1,6 +1,8 @@
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
LC "github.com/metacubex/mihomo/listener/config"
|
||||
"github.com/metacubex/mihomo/listener/sing_hysteria2"
|
||||
@@ -83,12 +85,13 @@ func (t *Hysteria2) Config() C.InboundConfig {
|
||||
|
||||
// Address implements constant.InboundListener
|
||||
func (t *Hysteria2) Address() string {
|
||||
var addrList []string
|
||||
if t.l != nil {
|
||||
for _, addr := range t.l.AddrList() {
|
||||
return addr.String()
|
||||
addrList = append(addrList, addr.String())
|
||||
}
|
||||
}
|
||||
return ""
|
||||
return strings.Join(addrList, ",")
|
||||
}
|
||||
|
||||
// Listen implements constant.InboundListener
|
||||
|
@@ -1,19 +1,24 @@
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
|
||||
LC "github.com/metacubex/mihomo/listener/config"
|
||||
"github.com/metacubex/mihomo/listener/mixed"
|
||||
"github.com/metacubex/mihomo/listener/socks"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
)
|
||||
|
||||
type MixedOption struct {
|
||||
BaseOption
|
||||
Users AuthUsers `inbound:"users,omitempty"`
|
||||
UDP bool `inbound:"udp,omitempty"`
|
||||
Users AuthUsers `inbound:"users,omitempty"`
|
||||
UDP bool `inbound:"udp,omitempty"`
|
||||
Certificate string `inbound:"certificate,omitempty"`
|
||||
PrivateKey string `inbound:"private-key,omitempty"`
|
||||
RealityConfig RealityConfig `inbound:"reality-config,omitempty"`
|
||||
}
|
||||
|
||||
func (o MixedOption) Equal(config C.InboundConfig) bool {
|
||||
@@ -23,8 +28,8 @@ func (o MixedOption) Equal(config C.InboundConfig) bool {
|
||||
type Mixed struct {
|
||||
*Base
|
||||
config *MixedOption
|
||||
l *mixed.Listener
|
||||
lUDP *socks.UDPListener
|
||||
l []*mixed.Listener
|
||||
lUDP []*socks.UDPListener
|
||||
udp bool
|
||||
}
|
||||
|
||||
@@ -47,21 +52,39 @@ func (m *Mixed) Config() C.InboundConfig {
|
||||
|
||||
// Address implements constant.InboundListener
|
||||
func (m *Mixed) Address() string {
|
||||
return m.l.Address()
|
||||
var addrList []string
|
||||
for _, l := range m.l {
|
||||
addrList = append(addrList, l.Address())
|
||||
}
|
||||
return strings.Join(addrList, ",")
|
||||
}
|
||||
|
||||
// Listen implements constant.InboundListener
|
||||
func (m *Mixed) Listen(tunnel C.Tunnel) error {
|
||||
var err error
|
||||
m.l, err = mixed.NewWithAuthenticator(m.RawAddress(), tunnel, m.config.Users.GetAuthStore(), m.Additions()...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if m.udp {
|
||||
m.lUDP, err = socks.NewUDP(m.RawAddress(), tunnel, m.Additions()...)
|
||||
for _, addr := range strings.Split(m.RawAddress(), ",") {
|
||||
l, err := mixed.NewWithConfig(
|
||||
LC.AuthServer{
|
||||
Enable: true,
|
||||
Listen: addr,
|
||||
AuthStore: m.config.Users.GetAuthStore(),
|
||||
Certificate: m.config.Certificate,
|
||||
PrivateKey: m.config.PrivateKey,
|
||||
RealityConfig: m.config.RealityConfig.Build(),
|
||||
},
|
||||
tunnel,
|
||||
m.Additions()...,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.l = append(m.l, l)
|
||||
if m.udp {
|
||||
lUDP, err := socks.NewUDP(addr, tunnel, m.Additions()...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.lUDP = append(m.lUDP, lUDP)
|
||||
}
|
||||
}
|
||||
log.Infoln("Mixed(http+socks)[%s] proxy listening at: %s", m.Name(), m.Address())
|
||||
return nil
|
||||
@@ -69,22 +92,23 @@ func (m *Mixed) Listen(tunnel C.Tunnel) error {
|
||||
|
||||
// Close implements constant.InboundListener
|
||||
func (m *Mixed) Close() error {
|
||||
var err error
|
||||
if m.l != nil {
|
||||
if tcpErr := m.l.Close(); tcpErr != nil {
|
||||
err = tcpErr
|
||||
var errs []error
|
||||
for _, l := range m.l {
|
||||
err := l.Close()
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("close tcp listener %s err: %w", l.Address(), err))
|
||||
}
|
||||
}
|
||||
if m.udp && m.lUDP != nil {
|
||||
if udpErr := m.lUDP.Close(); udpErr != nil {
|
||||
if err == nil {
|
||||
err = udpErr
|
||||
} else {
|
||||
return fmt.Errorf("close tcp err: %s, close udp err: %s", err.Error(), udpErr.Error())
|
||||
}
|
||||
for _, l := range m.lUDP {
|
||||
err := l.Close()
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("close udp listener %s err: %w", l.Address(), err))
|
||||
}
|
||||
}
|
||||
return err
|
||||
if len(errs) > 0 {
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ C.InboundListener = (*Mixed)(nil)
|
||||
|
@@ -1,6 +1,10 @@
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/listener/redir"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
@@ -17,7 +21,7 @@ func (o RedirOption) Equal(config C.InboundConfig) bool {
|
||||
type Redir struct {
|
||||
*Base
|
||||
config *RedirOption
|
||||
l *redir.Listener
|
||||
l []*redir.Listener
|
||||
}
|
||||
|
||||
func NewRedir(options *RedirOption) (*Redir, error) {
|
||||
@@ -38,15 +42,21 @@ func (r *Redir) Config() C.InboundConfig {
|
||||
|
||||
// Address implements constant.InboundListener
|
||||
func (r *Redir) Address() string {
|
||||
return r.l.Address()
|
||||
var addrList []string
|
||||
for _, l := range r.l {
|
||||
addrList = append(addrList, l.Address())
|
||||
}
|
||||
return strings.Join(addrList, ",")
|
||||
}
|
||||
|
||||
// Listen implements constant.InboundListener
|
||||
func (r *Redir) Listen(tunnel C.Tunnel) error {
|
||||
var err error
|
||||
r.l, err = redir.New(r.RawAddress(), tunnel, r.Additions()...)
|
||||
if err != nil {
|
||||
return err
|
||||
for _, addr := range strings.Split(r.RawAddress(), ",") {
|
||||
l, err := redir.New(addr, tunnel, r.Additions()...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.l = append(r.l, l)
|
||||
}
|
||||
log.Infoln("Redir[%s] proxy listening at: %s", r.Name(), r.Address())
|
||||
return nil
|
||||
@@ -54,8 +64,15 @@ func (r *Redir) Listen(tunnel C.Tunnel) error {
|
||||
|
||||
// Close implements constant.InboundListener
|
||||
func (r *Redir) Close() error {
|
||||
if r.l != nil {
|
||||
r.l.Close()
|
||||
var errs []error
|
||||
for _, l := range r.l {
|
||||
err := l.Close()
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("close redir listener %s err: %w", l.Address(), err))
|
||||
}
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@@ -1,6 +1,8 @@
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
LC "github.com/metacubex/mihomo/listener/config"
|
||||
"github.com/metacubex/mihomo/listener/sing_shadowsocks"
|
||||
@@ -52,12 +54,13 @@ func (s *ShadowSocks) Config() C.InboundConfig {
|
||||
|
||||
// Address implements constant.InboundListener
|
||||
func (s *ShadowSocks) Address() string {
|
||||
var addrList []string
|
||||
if s.l != nil {
|
||||
for _, addr := range s.l.AddrList() {
|
||||
return addr.String()
|
||||
addrList = append(addrList, addr.String())
|
||||
}
|
||||
}
|
||||
return ""
|
||||
return strings.Join(addrList, ",")
|
||||
}
|
||||
|
||||
// Listen implements constant.InboundListener
|
||||
|
@@ -1,16 +1,23 @@
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
LC "github.com/metacubex/mihomo/listener/config"
|
||||
"github.com/metacubex/mihomo/listener/socks"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
)
|
||||
|
||||
type SocksOption struct {
|
||||
BaseOption
|
||||
Users AuthUsers `inbound:"users,omitempty"`
|
||||
UDP bool `inbound:"udp,omitempty"`
|
||||
Users AuthUsers `inbound:"users,omitempty"`
|
||||
UDP bool `inbound:"udp,omitempty"`
|
||||
Certificate string `inbound:"certificate,omitempty"`
|
||||
PrivateKey string `inbound:"private-key,omitempty"`
|
||||
RealityConfig RealityConfig `inbound:"reality-config,omitempty"`
|
||||
}
|
||||
|
||||
func (o SocksOption) Equal(config C.InboundConfig) bool {
|
||||
@@ -21,8 +28,8 @@ type Socks struct {
|
||||
*Base
|
||||
config *SocksOption
|
||||
udp bool
|
||||
stl *socks.Listener
|
||||
sul *socks.UDPListener
|
||||
stl []*socks.Listener
|
||||
sul []*socks.UDPListener
|
||||
}
|
||||
|
||||
func NewSocks(options *SocksOption) (*Socks, error) {
|
||||
@@ -44,40 +51,60 @@ func (s *Socks) Config() C.InboundConfig {
|
||||
|
||||
// Close implements constant.InboundListener
|
||||
func (s *Socks) Close() error {
|
||||
var err error
|
||||
if s.stl != nil {
|
||||
if tcpErr := s.stl.Close(); tcpErr != nil {
|
||||
err = tcpErr
|
||||
var errs []error
|
||||
for _, l := range s.stl {
|
||||
err := l.Close()
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("close tcp listener %s err: %w", l.Address(), err))
|
||||
}
|
||||
}
|
||||
if s.udp && s.sul != nil {
|
||||
if udpErr := s.sul.Close(); udpErr != nil {
|
||||
if err == nil {
|
||||
err = udpErr
|
||||
} else {
|
||||
return fmt.Errorf("close tcp err: %s, close udp err: %s", err.Error(), udpErr.Error())
|
||||
}
|
||||
for _, l := range s.sul {
|
||||
err := l.Close()
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("close udp listener %s err: %w", l.Address(), err))
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
if len(errs) > 0 {
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Address implements constant.InboundListener
|
||||
func (s *Socks) Address() string {
|
||||
return s.stl.Address()
|
||||
var addrList []string
|
||||
for _, l := range s.stl {
|
||||
addrList = append(addrList, l.Address())
|
||||
}
|
||||
return strings.Join(addrList, ",")
|
||||
}
|
||||
|
||||
// Listen implements constant.InboundListener
|
||||
func (s *Socks) Listen(tunnel C.Tunnel) error {
|
||||
var err error
|
||||
if s.stl, err = socks.NewWithAuthenticator(s.RawAddress(), tunnel, s.config.Users.GetAuthStore(), s.Additions()...); err != nil {
|
||||
return err
|
||||
}
|
||||
if s.udp {
|
||||
if s.sul, err = socks.NewUDP(s.RawAddress(), tunnel, s.Additions()...); err != nil {
|
||||
for _, addr := range strings.Split(s.RawAddress(), ",") {
|
||||
stl, err := socks.NewWithConfig(
|
||||
LC.AuthServer{
|
||||
Enable: true,
|
||||
Listen: addr,
|
||||
AuthStore: s.config.Users.GetAuthStore(),
|
||||
Certificate: s.config.Certificate,
|
||||
PrivateKey: s.config.PrivateKey,
|
||||
RealityConfig: s.config.RealityConfig.Build(),
|
||||
},
|
||||
tunnel,
|
||||
s.Additions()...,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.stl = append(s.stl, stl)
|
||||
if s.udp {
|
||||
sul, err := socks.NewUDP(addr, tunnel, s.Additions()...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.sul = append(s.sul, sul)
|
||||
}
|
||||
}
|
||||
|
||||
log.Infoln("SOCKS[%s] proxy listening at: %s", s.Name(), s.Address())
|
||||
|
@@ -1,7 +1,9 @@
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
"github.com/metacubex/mihomo/listener/tproxy"
|
||||
@@ -20,8 +22,8 @@ func (o TProxyOption) Equal(config C.InboundConfig) bool {
|
||||
type TProxy struct {
|
||||
*Base
|
||||
config *TProxyOption
|
||||
lUDP *tproxy.UDPListener
|
||||
lTCP *tproxy.Listener
|
||||
lUDP []*tproxy.UDPListener
|
||||
lTCP []*tproxy.Listener
|
||||
udp bool
|
||||
}
|
||||
|
||||
@@ -45,21 +47,28 @@ func (t *TProxy) Config() C.InboundConfig {
|
||||
|
||||
// Address implements constant.InboundListener
|
||||
func (t *TProxy) Address() string {
|
||||
return t.lTCP.Address()
|
||||
var addrList []string
|
||||
for _, l := range t.lTCP {
|
||||
addrList = append(addrList, l.Address())
|
||||
}
|
||||
return strings.Join(addrList, ",")
|
||||
}
|
||||
|
||||
// Listen implements constant.InboundListener
|
||||
func (t *TProxy) Listen(tunnel C.Tunnel) error {
|
||||
var err error
|
||||
t.lTCP, err = tproxy.New(t.RawAddress(), tunnel, t.Additions()...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if t.udp {
|
||||
t.lUDP, err = tproxy.NewUDP(t.RawAddress(), tunnel, t.Additions()...)
|
||||
for _, addr := range strings.Split(t.RawAddress(), ",") {
|
||||
lTCP, err := tproxy.New(addr, tunnel, t.Additions()...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.lTCP = append(t.lTCP, lTCP)
|
||||
if t.udp {
|
||||
lUDP, err := tproxy.NewUDP(addr, tunnel, t.Additions()...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.lUDP = append(t.lUDP, lUDP)
|
||||
}
|
||||
}
|
||||
log.Infoln("TProxy[%s] proxy listening at: %s", t.Name(), t.Address())
|
||||
return nil
|
||||
@@ -67,23 +76,21 @@ func (t *TProxy) Listen(tunnel C.Tunnel) error {
|
||||
|
||||
// Close implements constant.InboundListener
|
||||
func (t *TProxy) Close() error {
|
||||
var tcpErr error
|
||||
var udpErr error
|
||||
if t.lTCP != nil {
|
||||
tcpErr = t.lTCP.Close()
|
||||
var errs []error
|
||||
for _, l := range t.lTCP {
|
||||
err := l.Close()
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("close tcp listener %s err: %w", l.Address(), err))
|
||||
}
|
||||
}
|
||||
if t.lUDP != nil {
|
||||
udpErr = t.lUDP.Close()
|
||||
for _, l := range t.lUDP {
|
||||
err := l.Close()
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("close udp listener %s err: %w", l.Address(), err))
|
||||
}
|
||||
}
|
||||
|
||||
if tcpErr != nil && udpErr != nil {
|
||||
return fmt.Errorf("tcp close err: %s and udp close err: %s", tcpErr, udpErr)
|
||||
}
|
||||
if tcpErr != nil {
|
||||
return tcpErr
|
||||
}
|
||||
if udpErr != nil {
|
||||
return udpErr
|
||||
if len(errs) > 0 {
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@@ -0,0 +1,113 @@
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
LC "github.com/metacubex/mihomo/listener/config"
|
||||
"github.com/metacubex/mihomo/listener/trojan"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
)
|
||||
|
||||
type TrojanOption struct {
|
||||
BaseOption
|
||||
Users []TrojanUser `inbound:"users"`
|
||||
WsPath string `inbound:"ws-path,omitempty"`
|
||||
GrpcServiceName string `inbound:"grpc-service-name,omitempty"`
|
||||
Certificate string `inbound:"certificate,omitempty"`
|
||||
PrivateKey string `inbound:"private-key,omitempty"`
|
||||
RealityConfig RealityConfig `inbound:"reality-config,omitempty"`
|
||||
MuxOption MuxOption `inbound:"mux-option,omitempty"`
|
||||
SSOption TrojanSSOption `inbound:"ss-option,omitempty"`
|
||||
}
|
||||
|
||||
type TrojanUser struct {
|
||||
Username string `inbound:"username,omitempty"`
|
||||
Password string `inbound:"password"`
|
||||
}
|
||||
|
||||
// TrojanSSOption from https://github.com/p4gefau1t/trojan-go/blob/v0.10.6/tunnel/shadowsocks/config.go#L5
|
||||
type TrojanSSOption struct {
|
||||
Enabled bool `inbound:"enabled,omitempty"`
|
||||
Method string `inbound:"method,omitempty"`
|
||||
Password string `inbound:"password,omitempty"`
|
||||
}
|
||||
|
||||
func (o TrojanOption) Equal(config C.InboundConfig) bool {
|
||||
return optionToString(o) == optionToString(config)
|
||||
}
|
||||
|
||||
type Trojan struct {
|
||||
*Base
|
||||
config *TrojanOption
|
||||
l C.MultiAddrListener
|
||||
vs LC.TrojanServer
|
||||
}
|
||||
|
||||
func NewTrojan(options *TrojanOption) (*Trojan, error) {
|
||||
base, err := NewBase(&options.BaseOption)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
users := make([]LC.TrojanUser, len(options.Users))
|
||||
for i, v := range options.Users {
|
||||
users[i] = LC.TrojanUser{
|
||||
Username: v.Username,
|
||||
Password: v.Password,
|
||||
}
|
||||
}
|
||||
return &Trojan{
|
||||
Base: base,
|
||||
config: options,
|
||||
vs: LC.TrojanServer{
|
||||
Enable: true,
|
||||
Listen: base.RawAddress(),
|
||||
Users: users,
|
||||
WsPath: options.WsPath,
|
||||
GrpcServiceName: options.GrpcServiceName,
|
||||
Certificate: options.Certificate,
|
||||
PrivateKey: options.PrivateKey,
|
||||
RealityConfig: options.RealityConfig.Build(),
|
||||
MuxOption: options.MuxOption.Build(),
|
||||
TrojanSSOption: LC.TrojanSSOption{
|
||||
Enabled: options.SSOption.Enabled,
|
||||
Method: options.SSOption.Method,
|
||||
Password: options.SSOption.Password,
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Config implements constant.InboundListener
|
||||
func (v *Trojan) Config() C.InboundConfig {
|
||||
return v.config
|
||||
}
|
||||
|
||||
// Address implements constant.InboundListener
|
||||
func (v *Trojan) Address() string {
|
||||
var addrList []string
|
||||
if v.l != nil {
|
||||
for _, addr := range v.l.AddrList() {
|
||||
addrList = append(addrList, addr.String())
|
||||
}
|
||||
}
|
||||
return strings.Join(addrList, ",")
|
||||
}
|
||||
|
||||
// Listen implements constant.InboundListener
|
||||
func (v *Trojan) Listen(tunnel C.Tunnel) error {
|
||||
var err error
|
||||
v.l, err = trojan.New(v.vs, tunnel, v.Additions()...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Infoln("Trojan[%s] proxy listening at: %s", v.Name(), v.Address())
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close implements constant.InboundListener
|
||||
func (v *Trojan) Close() error {
|
||||
return v.l.Close()
|
||||
}
|
||||
|
||||
var _ C.InboundListener = (*Trojan)(nil)
|
@@ -1,6 +1,8 @@
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
LC "github.com/metacubex/mihomo/listener/config"
|
||||
"github.com/metacubex/mihomo/listener/tuic"
|
||||
@@ -66,12 +68,13 @@ func (t *Tuic) Config() C.InboundConfig {
|
||||
|
||||
// Address implements constant.InboundListener
|
||||
func (t *Tuic) Address() string {
|
||||
var addrList []string
|
||||
if t.l != nil {
|
||||
for _, addr := range t.l.AddrList() {
|
||||
return addr.String()
|
||||
addrList = append(addrList, addr.String())
|
||||
}
|
||||
}
|
||||
return ""
|
||||
return strings.Join(addrList, ",")
|
||||
}
|
||||
|
||||
// Listen implements constant.InboundListener
|
||||
|
@@ -1,7 +1,9 @@
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
LT "github.com/metacubex/mihomo/listener/tunnel"
|
||||
@@ -21,8 +23,8 @@ func (o TunnelOption) Equal(config C.InboundConfig) bool {
|
||||
type Tunnel struct {
|
||||
*Base
|
||||
config *TunnelOption
|
||||
ttl *LT.Listener
|
||||
tul *LT.PacketConn
|
||||
ttl []*LT.Listener
|
||||
tul []*LT.PacketConn
|
||||
}
|
||||
|
||||
func NewTunnel(options *TunnelOption) (*Tunnel, error) {
|
||||
@@ -43,56 +45,62 @@ func (t *Tunnel) Config() C.InboundConfig {
|
||||
|
||||
// Close implements constant.InboundListener
|
||||
func (t *Tunnel) Close() error {
|
||||
var err error
|
||||
if t.ttl != nil {
|
||||
if tcpErr := t.ttl.Close(); tcpErr != nil {
|
||||
err = tcpErr
|
||||
var errs []error
|
||||
for _, l := range t.ttl {
|
||||
err := l.Close()
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("close tcp listener %s err: %w", l.Address(), err))
|
||||
}
|
||||
}
|
||||
if t.tul != nil {
|
||||
if udpErr := t.tul.Close(); udpErr != nil {
|
||||
if err == nil {
|
||||
err = udpErr
|
||||
} else {
|
||||
return fmt.Errorf("close tcp err: %s, close udp err: %s", err.Error(), udpErr.Error())
|
||||
}
|
||||
for _, l := range t.tul {
|
||||
err := l.Close()
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("close udp listener %s err: %w", l.Address(), err))
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Address implements constant.InboundListener
|
||||
func (t *Tunnel) Address() string {
|
||||
if t.ttl != nil {
|
||||
return t.ttl.Address()
|
||||
}
|
||||
if t.tul != nil {
|
||||
return t.tul.Address()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Listen implements constant.InboundListener
|
||||
func (t *Tunnel) Listen(tunnel C.Tunnel) error {
|
||||
var err error
|
||||
for _, network := range t.config.Network {
|
||||
switch network {
|
||||
case "tcp":
|
||||
if t.ttl, err = LT.New(t.RawAddress(), t.config.Target, t.config.SpecialProxy, tunnel, t.Additions()...); err != nil {
|
||||
return err
|
||||
}
|
||||
case "udp":
|
||||
if t.tul, err = LT.NewUDP(t.RawAddress(), t.config.Target, t.config.SpecialProxy, tunnel, t.Additions()...); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
log.Warnln("unknown network type: %s, passed", network)
|
||||
continue
|
||||
}
|
||||
log.Infoln("Tunnel[%s](%s/%s)proxy listening at: %s", t.Name(), network, t.config.Target, t.Address())
|
||||
if len(errs) > 0 {
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Address implements constant.InboundListener
|
||||
func (t *Tunnel) Address() string {
|
||||
var addrList []string
|
||||
for _, l := range t.ttl {
|
||||
addrList = append(addrList, "tcp://"+l.Address())
|
||||
}
|
||||
for _, l := range t.tul {
|
||||
addrList = append(addrList, "udp://"+l.Address())
|
||||
}
|
||||
return strings.Join(addrList, ",")
|
||||
}
|
||||
|
||||
// Listen implements constant.InboundListener
|
||||
func (t *Tunnel) Listen(tunnel C.Tunnel) error {
|
||||
for _, addr := range strings.Split(t.RawAddress(), ",") {
|
||||
for _, network := range t.config.Network {
|
||||
switch network {
|
||||
case "tcp":
|
||||
ttl, err := LT.New(addr, t.config.Target, t.config.SpecialProxy, tunnel, t.Additions()...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.ttl = append(t.ttl, ttl)
|
||||
case "udp":
|
||||
tul, err := LT.NewUDP(addr, t.config.Target, t.config.SpecialProxy, tunnel, t.Additions()...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.tul = append(t.tul, tul)
|
||||
default:
|
||||
log.Warnln("unknown network type: %s, passed", network)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
log.Infoln("Tunnel[%s](%s)proxy listening at: %s", t.Name(), t.config.Target, t.Address())
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ C.InboundListener = (*Tunnel)(nil)
|
||||
|
@@ -1,6 +1,8 @@
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
LC "github.com/metacubex/mihomo/listener/config"
|
||||
"github.com/metacubex/mihomo/listener/sing_vless"
|
||||
@@ -9,12 +11,13 @@ import (
|
||||
|
||||
type VlessOption struct {
|
||||
BaseOption
|
||||
Users []VlessUser `inbound:"users"`
|
||||
WsPath string `inbound:"ws-path,omitempty"`
|
||||
Certificate string `inbound:"certificate,omitempty"`
|
||||
PrivateKey string `inbound:"private-key,omitempty"`
|
||||
RealityConfig RealityConfig `inbound:"reality-config,omitempty"`
|
||||
MuxOption MuxOption `inbound:"mux-option,omitempty"`
|
||||
Users []VlessUser `inbound:"users"`
|
||||
WsPath string `inbound:"ws-path,omitempty"`
|
||||
GrpcServiceName string `inbound:"grpc-service-name,omitempty"`
|
||||
Certificate string `inbound:"certificate,omitempty"`
|
||||
PrivateKey string `inbound:"private-key,omitempty"`
|
||||
RealityConfig RealityConfig `inbound:"reality-config,omitempty"`
|
||||
MuxOption MuxOption `inbound:"mux-option,omitempty"`
|
||||
}
|
||||
|
||||
type VlessUser struct {
|
||||
@@ -51,14 +54,15 @@ func NewVless(options *VlessOption) (*Vless, error) {
|
||||
Base: base,
|
||||
config: options,
|
||||
vs: LC.VlessServer{
|
||||
Enable: true,
|
||||
Listen: base.RawAddress(),
|
||||
Users: users,
|
||||
WsPath: options.WsPath,
|
||||
Certificate: options.Certificate,
|
||||
PrivateKey: options.PrivateKey,
|
||||
RealityConfig: options.RealityConfig.Build(),
|
||||
MuxOption: options.MuxOption.Build(),
|
||||
Enable: true,
|
||||
Listen: base.RawAddress(),
|
||||
Users: users,
|
||||
WsPath: options.WsPath,
|
||||
GrpcServiceName: options.GrpcServiceName,
|
||||
Certificate: options.Certificate,
|
||||
PrivateKey: options.PrivateKey,
|
||||
RealityConfig: options.RealityConfig.Build(),
|
||||
MuxOption: options.MuxOption.Build(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@@ -70,25 +74,18 @@ func (v *Vless) Config() C.InboundConfig {
|
||||
|
||||
// Address implements constant.InboundListener
|
||||
func (v *Vless) Address() string {
|
||||
var addrList []string
|
||||
if v.l != nil {
|
||||
for _, addr := range v.l.AddrList() {
|
||||
return addr.String()
|
||||
addrList = append(addrList, addr.String())
|
||||
}
|
||||
}
|
||||
return ""
|
||||
return strings.Join(addrList, ",")
|
||||
}
|
||||
|
||||
// Listen implements constant.InboundListener
|
||||
func (v *Vless) Listen(tunnel C.Tunnel) error {
|
||||
var err error
|
||||
users := make([]LC.VlessUser, len(v.config.Users))
|
||||
for i, v := range v.config.Users {
|
||||
users[i] = LC.VlessUser{
|
||||
Username: v.Username,
|
||||
UUID: v.UUID,
|
||||
Flow: v.Flow,
|
||||
}
|
||||
}
|
||||
v.l, err = sing_vless.New(v.vs, tunnel, v.Additions()...)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@@ -1,6 +1,8 @@
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
LC "github.com/metacubex/mihomo/listener/config"
|
||||
"github.com/metacubex/mihomo/listener/sing_vmess"
|
||||
@@ -9,12 +11,13 @@ import (
|
||||
|
||||
type VmessOption struct {
|
||||
BaseOption
|
||||
Users []VmessUser `inbound:"users"`
|
||||
WsPath string `inbound:"ws-path,omitempty"`
|
||||
Certificate string `inbound:"certificate,omitempty"`
|
||||
PrivateKey string `inbound:"private-key,omitempty"`
|
||||
RealityConfig RealityConfig `inbound:"reality-config,omitempty"`
|
||||
MuxOption MuxOption `inbound:"mux-option,omitempty"`
|
||||
Users []VmessUser `inbound:"users"`
|
||||
WsPath string `inbound:"ws-path,omitempty"`
|
||||
GrpcServiceName string `inbound:"grpc-service-name,omitempty"`
|
||||
Certificate string `inbound:"certificate,omitempty"`
|
||||
PrivateKey string `inbound:"private-key,omitempty"`
|
||||
RealityConfig RealityConfig `inbound:"reality-config,omitempty"`
|
||||
MuxOption MuxOption `inbound:"mux-option,omitempty"`
|
||||
}
|
||||
|
||||
type VmessUser struct {
|
||||
@@ -51,14 +54,15 @@ func NewVmess(options *VmessOption) (*Vmess, error) {
|
||||
Base: base,
|
||||
config: options,
|
||||
vs: LC.VmessServer{
|
||||
Enable: true,
|
||||
Listen: base.RawAddress(),
|
||||
Users: users,
|
||||
WsPath: options.WsPath,
|
||||
Certificate: options.Certificate,
|
||||
PrivateKey: options.PrivateKey,
|
||||
RealityConfig: options.RealityConfig.Build(),
|
||||
MuxOption: options.MuxOption.Build(),
|
||||
Enable: true,
|
||||
Listen: base.RawAddress(),
|
||||
Users: users,
|
||||
WsPath: options.WsPath,
|
||||
GrpcServiceName: options.GrpcServiceName,
|
||||
Certificate: options.Certificate,
|
||||
PrivateKey: options.PrivateKey,
|
||||
RealityConfig: options.RealityConfig.Build(),
|
||||
MuxOption: options.MuxOption.Build(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@@ -70,25 +74,18 @@ func (v *Vmess) Config() C.InboundConfig {
|
||||
|
||||
// Address implements constant.InboundListener
|
||||
func (v *Vmess) Address() string {
|
||||
var addrList []string
|
||||
if v.l != nil {
|
||||
for _, addr := range v.l.AddrList() {
|
||||
return addr.String()
|
||||
addrList = append(addrList, addr.String())
|
||||
}
|
||||
}
|
||||
return ""
|
||||
return strings.Join(addrList, ",")
|
||||
}
|
||||
|
||||
// Listen implements constant.InboundListener
|
||||
func (v *Vmess) Listen(tunnel C.Tunnel) error {
|
||||
var err error
|
||||
users := make([]LC.VmessUser, len(v.config.Users))
|
||||
for i, v := range v.config.Users {
|
||||
users[i] = LC.VmessUser{
|
||||
Username: v.Username,
|
||||
UUID: v.UUID,
|
||||
AlterID: v.AlterID,
|
||||
}
|
||||
}
|
||||
v.l, err = sing_vmess.New(v.vs, tunnel, v.Additions()...)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@@ -1,6 +1,8 @@
|
||||
package mixed
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
|
||||
"github.com/metacubex/mihomo/adapter/inbound"
|
||||
@@ -8,7 +10,9 @@ import (
|
||||
"github.com/metacubex/mihomo/component/auth"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
authStore "github.com/metacubex/mihomo/listener/auth"
|
||||
LC "github.com/metacubex/mihomo/listener/config"
|
||||
"github.com/metacubex/mihomo/listener/http"
|
||||
"github.com/metacubex/mihomo/listener/reality"
|
||||
"github.com/metacubex/mihomo/listener/socks"
|
||||
"github.com/metacubex/mihomo/transport/socks4"
|
||||
"github.com/metacubex/mihomo/transport/socks5"
|
||||
@@ -37,10 +41,10 @@ func (l *Listener) Close() error {
|
||||
}
|
||||
|
||||
func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) {
|
||||
return NewWithAuthenticator(addr, tunnel, authStore.Default, additions...)
|
||||
return NewWithConfig(LC.AuthServer{Enable: true, Listen: addr, AuthStore: authStore.Default}, tunnel, additions...)
|
||||
}
|
||||
|
||||
func NewWithAuthenticator(addr string, tunnel C.Tunnel, store auth.AuthStore, additions ...inbound.Addition) (*Listener, error) {
|
||||
func NewWithConfig(config LC.AuthServer, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) {
|
||||
isDefault := false
|
||||
if len(additions) == 0 {
|
||||
isDefault = true
|
||||
@@ -50,14 +54,40 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, store auth.AuthStore, ad
|
||||
}
|
||||
}
|
||||
|
||||
l, err := inbound.Listen("tcp", addr)
|
||||
l, err := inbound.Listen("tcp", config.Listen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tlsConfig := &tls.Config{}
|
||||
var realityBuilder *reality.Builder
|
||||
|
||||
if config.Certificate != "" && config.PrivateKey != "" {
|
||||
cert, err := N.ParseCert(config.Certificate, config.PrivateKey, C.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsConfig.Certificates = []tls.Certificate{cert}
|
||||
}
|
||||
if config.RealityConfig.PrivateKey != "" {
|
||||
if tlsConfig.Certificates != nil {
|
||||
return nil, errors.New("certificate is unavailable in reality")
|
||||
}
|
||||
realityBuilder, err = config.RealityConfig.Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if realityBuilder != nil {
|
||||
l = realityBuilder.NewListener(l)
|
||||
} else if len(tlsConfig.Certificates) > 0 {
|
||||
l = tls.NewListener(l, tlsConfig)
|
||||
}
|
||||
|
||||
ml := &Listener{
|
||||
listener: l,
|
||||
addr: addr,
|
||||
addr: config.Listen,
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
@@ -68,7 +98,7 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, store auth.AuthStore, ad
|
||||
}
|
||||
continue
|
||||
}
|
||||
store := store
|
||||
store := config.AuthStore
|
||||
if isDefault || store == authStore.Default { // only apply on default listener
|
||||
if !inbound.IsRemoteAddrDisAllowed(c.RemoteAddr()) {
|
||||
_ = c.Close()
|
||||
|
@@ -93,6 +93,13 @@ func ParseListener(mapping map[string]any) (C.InboundListener, error) {
|
||||
return nil, err
|
||||
}
|
||||
listener, err = IN.NewVless(vlessOption)
|
||||
case "trojan":
|
||||
trojanOption := &IN.TrojanOption{}
|
||||
err = decoder.Decode(mapping, trojanOption)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
listener, err = IN.NewTrojan(trojanOption)
|
||||
case "hysteria2":
|
||||
hysteria2Option := &IN.Hysteria2Option{}
|
||||
err = decoder.Decode(mapping, hysteria2Option)
|
||||
|
@@ -8,6 +8,7 @@ import (
|
||||
N "github.com/metacubex/mihomo/common/net"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
LC "github.com/metacubex/mihomo/listener/config"
|
||||
"github.com/metacubex/mihomo/listener/sing"
|
||||
"github.com/metacubex/mihomo/transport/shadowsocks/core"
|
||||
"github.com/metacubex/mihomo/transport/socks5"
|
||||
)
|
||||
@@ -18,6 +19,7 @@ type Listener struct {
|
||||
listeners []net.Listener
|
||||
udpListeners []*UDPListener
|
||||
pickCipher core.Cipher
|
||||
handler *sing.ListenerHandler
|
||||
}
|
||||
|
||||
var _listener *Listener
|
||||
@@ -28,7 +30,17 @@ func New(config LC.ShadowsocksServer, tunnel C.Tunnel, additions ...inbound.Addi
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sl := &Listener{false, config, nil, nil, pickCipher}
|
||||
h, err := sing.NewListenerHandler(sing.ListenerConfig{
|
||||
Tunnel: tunnel,
|
||||
Type: C.SHADOWSOCKS,
|
||||
Additions: additions,
|
||||
MuxOption: config.MuxOption,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sl := &Listener{false, config, nil, nil, pickCipher, h}
|
||||
_listener = sl
|
||||
|
||||
for _, addr := range strings.Split(config.Listen, ",") {
|
||||
@@ -107,7 +119,8 @@ func (l *Listener) HandleConn(conn net.Conn, tunnel C.Tunnel, additions ...inbou
|
||||
_ = conn.Close()
|
||||
return
|
||||
}
|
||||
tunnel.HandleTCPConn(inbound.NewSocket(target, conn, C.SHADOWSOCKS, additions...))
|
||||
l.handler.HandleSocket(target, conn, additions...)
|
||||
//tunnel.HandleTCPConn(inbound.NewSocket(target, conn, C.SHADOWSOCKS, additions...))
|
||||
}
|
||||
|
||||
func HandleShadowSocks(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) bool {
|
||||
|
@@ -136,8 +136,8 @@ func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, meta
|
||||
cMetadata.RawDstAddr = metadata.Destination.Unwrap().TCPAddr()
|
||||
}
|
||||
inbound.ApplyAdditions(cMetadata, inbound.WithDstAddr(metadata.Destination), inbound.WithSrcAddr(metadata.Source), inbound.WithInAddr(conn.LocalAddr()))
|
||||
inbound.ApplyAdditions(cMetadata, getAdditions(ctx)...)
|
||||
inbound.ApplyAdditions(cMetadata, h.Additions...)
|
||||
inbound.ApplyAdditions(cMetadata, getAdditions(ctx)...)
|
||||
|
||||
h.Tunnel.HandleTCPConn(conn, cMetadata) // this goroutine must exit after conn unused
|
||||
return nil
|
||||
@@ -198,8 +198,8 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.
|
||||
cMetadata.RawDstAddr = dest.Unwrap().UDPAddr()
|
||||
}
|
||||
inbound.ApplyAdditions(cMetadata, inbound.WithDstAddr(dest), inbound.WithSrcAddr(metadata.Source), inbound.WithInAddr(conn.LocalAddr()))
|
||||
inbound.ApplyAdditions(cMetadata, getAdditions(ctx)...)
|
||||
inbound.ApplyAdditions(cMetadata, h.Additions...)
|
||||
inbound.ApplyAdditions(cMetadata, getAdditions(ctx)...)
|
||||
|
||||
h.Tunnel.HandleUDPPacket(cPacket, cMetadata)
|
||||
}
|
||||
|
@@ -0,0 +1,24 @@
|
||||
package sing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"github.com/metacubex/mihomo/adapter/inbound"
|
||||
"github.com/metacubex/mihomo/transport/socks5"
|
||||
)
|
||||
|
||||
// HandleSocket like inbound.NewSocket combine with Tunnel.HandleTCPConn but also handel specialFqdn
|
||||
func (h *ListenerHandler) HandleSocket(target socks5.Addr, conn net.Conn, _additions ...inbound.Addition) {
|
||||
conn, metadata := inbound.NewSocket(target, conn, h.Type, h.Additions...)
|
||||
if h.IsSpecialFqdn(metadata.Host) {
|
||||
_ = h.ParseSpecialFqdn(
|
||||
WithAdditions(context.Background(), _additions...),
|
||||
conn,
|
||||
ConvertMetadata(metadata),
|
||||
)
|
||||
} else {
|
||||
inbound.ApplyAdditions(metadata, _additions...)
|
||||
h.Tunnel.HandleTCPConn(conn, metadata)
|
||||
}
|
||||
}
|
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/metacubex/mihomo/listener/reality"
|
||||
"github.com/metacubex/mihomo/listener/sing"
|
||||
"github.com/metacubex/mihomo/log"
|
||||
"github.com/metacubex/mihomo/transport/gun"
|
||||
mihomoVMess "github.com/metacubex/mihomo/transport/vmess"
|
||||
|
||||
"github.com/metacubex/sing-vmess/vless"
|
||||
@@ -92,7 +93,7 @@ func New(config LC.VlessServer, tunnel C.Tunnel, additions ...inbound.Addition)
|
||||
|
||||
tlsConfig := &tls.Config{}
|
||||
var realityBuilder *reality.Builder
|
||||
var httpMux *http.ServeMux
|
||||
var httpHandler http.Handler
|
||||
|
||||
if config.Certificate != "" && config.PrivateKey != "" {
|
||||
cert, err := N.ParseCert(config.Certificate, config.PrivateKey, C.Path)
|
||||
@@ -111,17 +112,28 @@ func New(config LC.VlessServer, tunnel C.Tunnel, additions ...inbound.Addition)
|
||||
}
|
||||
}
|
||||
if config.WsPath != "" {
|
||||
httpMux = http.NewServeMux()
|
||||
httpMux := http.NewServeMux()
|
||||
httpMux.HandleFunc(config.WsPath, func(w http.ResponseWriter, r *http.Request) {
|
||||
conn, err := mihomoVMess.StreamUpgradedWebsocketConn(w, r)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
sl.HandleConn(conn, tunnel)
|
||||
sl.HandleConn(conn, tunnel, additions...)
|
||||
})
|
||||
httpHandler = httpMux
|
||||
tlsConfig.NextProtos = append(tlsConfig.NextProtos, "http/1.1")
|
||||
}
|
||||
if config.GrpcServiceName != "" {
|
||||
httpHandler = gun.NewServerHandler(gun.ServerOption{
|
||||
ServiceName: config.GrpcServiceName,
|
||||
ConnHandler: func(conn net.Conn) {
|
||||
sl.HandleConn(conn, tunnel, additions...)
|
||||
},
|
||||
HttpHandler: httpHandler,
|
||||
})
|
||||
tlsConfig.NextProtos = append([]string{"h2"}, tlsConfig.NextProtos...) // h2 must before http/1.1
|
||||
}
|
||||
|
||||
for _, addr := range strings.Split(config.Listen, ",") {
|
||||
addr := addr
|
||||
@@ -141,8 +153,8 @@ func New(config LC.VlessServer, tunnel C.Tunnel, additions ...inbound.Addition)
|
||||
sl.listeners = append(sl.listeners, l)
|
||||
|
||||
go func() {
|
||||
if httpMux != nil {
|
||||
_ = http.Serve(l, httpMux)
|
||||
if httpHandler != nil {
|
||||
_ = http.Serve(l, httpHandler)
|
||||
return
|
||||
}
|
||||
for {
|
||||
|
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/metacubex/mihomo/listener/reality"
|
||||
"github.com/metacubex/mihomo/listener/sing"
|
||||
"github.com/metacubex/mihomo/ntp"
|
||||
"github.com/metacubex/mihomo/transport/gun"
|
||||
mihomoVMess "github.com/metacubex/mihomo/transport/vmess"
|
||||
|
||||
vmess "github.com/metacubex/sing-vmess"
|
||||
@@ -76,7 +77,7 @@ func New(config LC.VmessServer, tunnel C.Tunnel, additions ...inbound.Addition)
|
||||
|
||||
tlsConfig := &tls.Config{}
|
||||
var realityBuilder *reality.Builder
|
||||
var httpMux *http.ServeMux
|
||||
var httpHandler http.Handler
|
||||
|
||||
if config.Certificate != "" && config.PrivateKey != "" {
|
||||
cert, err := N.ParseCert(config.Certificate, config.PrivateKey, C.Path)
|
||||
@@ -95,17 +96,28 @@ func New(config LC.VmessServer, tunnel C.Tunnel, additions ...inbound.Addition)
|
||||
}
|
||||
}
|
||||
if config.WsPath != "" {
|
||||
httpMux = http.NewServeMux()
|
||||
httpMux := http.NewServeMux()
|
||||
httpMux.HandleFunc(config.WsPath, func(w http.ResponseWriter, r *http.Request) {
|
||||
conn, err := mihomoVMess.StreamUpgradedWebsocketConn(w, r)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
sl.HandleConn(conn, tunnel)
|
||||
sl.HandleConn(conn, tunnel, additions...)
|
||||
})
|
||||
httpHandler = httpMux
|
||||
tlsConfig.NextProtos = append(tlsConfig.NextProtos, "http/1.1")
|
||||
}
|
||||
if config.GrpcServiceName != "" {
|
||||
httpHandler = gun.NewServerHandler(gun.ServerOption{
|
||||
ServiceName: config.GrpcServiceName,
|
||||
ConnHandler: func(conn net.Conn) {
|
||||
sl.HandleConn(conn, tunnel, additions...)
|
||||
},
|
||||
HttpHandler: httpHandler,
|
||||
})
|
||||
tlsConfig.NextProtos = append([]string{"h2"}, tlsConfig.NextProtos...) // h2 must before http/1.1
|
||||
}
|
||||
|
||||
for _, addr := range strings.Split(config.Listen, ",") {
|
||||
addr := addr
|
||||
@@ -123,8 +135,8 @@ func New(config LC.VmessServer, tunnel C.Tunnel, additions ...inbound.Addition)
|
||||
sl.listeners = append(sl.listeners, l)
|
||||
|
||||
go func() {
|
||||
if httpMux != nil {
|
||||
_ = http.Serve(l, httpMux)
|
||||
if httpHandler != nil {
|
||||
_ = http.Serve(l, httpHandler)
|
||||
return
|
||||
}
|
||||
for {
|
||||
|
@@ -1,6 +1,8 @@
|
||||
package socks
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
|
||||
@@ -9,6 +11,8 @@ import (
|
||||
"github.com/metacubex/mihomo/component/auth"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
authStore "github.com/metacubex/mihomo/listener/auth"
|
||||
LC "github.com/metacubex/mihomo/listener/config"
|
||||
"github.com/metacubex/mihomo/listener/reality"
|
||||
"github.com/metacubex/mihomo/transport/socks4"
|
||||
"github.com/metacubex/mihomo/transport/socks5"
|
||||
)
|
||||
@@ -36,10 +40,10 @@ func (l *Listener) Close() error {
|
||||
}
|
||||
|
||||
func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) {
|
||||
return NewWithAuthenticator(addr, tunnel, authStore.Default, additions...)
|
||||
return NewWithConfig(LC.AuthServer{Enable: true, Listen: addr, AuthStore: authStore.Default}, tunnel, additions...)
|
||||
}
|
||||
|
||||
func NewWithAuthenticator(addr string, tunnel C.Tunnel, store auth.AuthStore, additions ...inbound.Addition) (*Listener, error) {
|
||||
func NewWithConfig(config LC.AuthServer, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) {
|
||||
isDefault := false
|
||||
if len(additions) == 0 {
|
||||
isDefault = true
|
||||
@@ -49,14 +53,40 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, store auth.AuthStore, ad
|
||||
}
|
||||
}
|
||||
|
||||
l, err := inbound.Listen("tcp", addr)
|
||||
l, err := inbound.Listen("tcp", config.Listen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tlsConfig := &tls.Config{}
|
||||
var realityBuilder *reality.Builder
|
||||
|
||||
if config.Certificate != "" && config.PrivateKey != "" {
|
||||
cert, err := N.ParseCert(config.Certificate, config.PrivateKey, C.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsConfig.Certificates = []tls.Certificate{cert}
|
||||
}
|
||||
if config.RealityConfig.PrivateKey != "" {
|
||||
if tlsConfig.Certificates != nil {
|
||||
return nil, errors.New("certificate is unavailable in reality")
|
||||
}
|
||||
realityBuilder, err = config.RealityConfig.Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if realityBuilder != nil {
|
||||
l = realityBuilder.NewListener(l)
|
||||
} else if len(tlsConfig.Certificates) > 0 {
|
||||
l = tls.NewListener(l, tlsConfig)
|
||||
}
|
||||
|
||||
sl := &Listener{
|
||||
listener: l,
|
||||
addr: addr,
|
||||
addr: config.Listen,
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
@@ -67,7 +97,7 @@ func NewWithAuthenticator(addr string, tunnel C.Tunnel, store auth.AuthStore, ad
|
||||
}
|
||||
continue
|
||||
}
|
||||
store := store
|
||||
store := config.AuthStore
|
||||
if isDefault || store == authStore.Default { // only apply on default listener
|
||||
if !inbound.IsRemoteAddrDisAllowed(c.RemoteAddr()) {
|
||||
_ = c.Close()
|
||||
|
@@ -0,0 +1,43 @@
|
||||
package trojan
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
)
|
||||
|
||||
type packet struct {
|
||||
pc net.PacketConn
|
||||
rAddr net.Addr
|
||||
payload []byte
|
||||
put func()
|
||||
}
|
||||
|
||||
func (c *packet) Data() []byte {
|
||||
return c.payload
|
||||
}
|
||||
|
||||
// WriteBack wirtes UDP packet with source(ip, port) = `addr`
|
||||
func (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {
|
||||
if addr == nil {
|
||||
err = errors.New("address is invalid")
|
||||
return
|
||||
}
|
||||
return c.pc.WriteTo(b, addr)
|
||||
}
|
||||
|
||||
// LocalAddr returns the source IP/Port of UDP Packet
|
||||
func (c *packet) LocalAddr() net.Addr {
|
||||
return c.rAddr
|
||||
}
|
||||
|
||||
func (c *packet) Drop() {
|
||||
if c.put != nil {
|
||||
c.put()
|
||||
c.put = nil
|
||||
}
|
||||
c.payload = nil
|
||||
}
|
||||
|
||||
func (c *packet) InAddr() net.Addr {
|
||||
return c.pc.LocalAddr()
|
||||
}
|
@@ -0,0 +1,283 @@
|
||||
package trojan
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/metacubex/mihomo/adapter/inbound"
|
||||
N "github.com/metacubex/mihomo/common/net"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
LC "github.com/metacubex/mihomo/listener/config"
|
||||
"github.com/metacubex/mihomo/listener/reality"
|
||||
"github.com/metacubex/mihomo/listener/sing"
|
||||
"github.com/metacubex/mihomo/transport/gun"
|
||||
"github.com/metacubex/mihomo/transport/shadowsocks/core"
|
||||
"github.com/metacubex/mihomo/transport/socks5"
|
||||
"github.com/metacubex/mihomo/transport/trojan"
|
||||
mihomoVMess "github.com/metacubex/mihomo/transport/vmess"
|
||||
|
||||
"github.com/sagernet/smux"
|
||||
)
|
||||
|
||||
type Listener struct {
|
||||
closed bool
|
||||
config LC.TrojanServer
|
||||
listeners []net.Listener
|
||||
keys map[[trojan.KeyLength]byte]string
|
||||
pickCipher core.Cipher
|
||||
handler *sing.ListenerHandler
|
||||
}
|
||||
|
||||
func New(config LC.TrojanServer, tunnel C.Tunnel, additions ...inbound.Addition) (sl *Listener, err error) {
|
||||
if len(additions) == 0 {
|
||||
additions = []inbound.Addition{
|
||||
inbound.WithInName("DEFAULT-TROJAN"),
|
||||
inbound.WithSpecialRules(""),
|
||||
}
|
||||
}
|
||||
h, err := sing.NewListenerHandler(sing.ListenerConfig{
|
||||
Tunnel: tunnel,
|
||||
Type: C.TROJAN,
|
||||
Additions: additions,
|
||||
MuxOption: config.MuxOption,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
keys := make(map[[trojan.KeyLength]byte]string)
|
||||
for _, user := range config.Users {
|
||||
keys[trojan.Key(user.Password)] = user.Username
|
||||
}
|
||||
|
||||
var pickCipher core.Cipher
|
||||
if config.TrojanSSOption.Enabled {
|
||||
if config.TrojanSSOption.Password == "" {
|
||||
return nil, errors.New("empty password")
|
||||
}
|
||||
if config.TrojanSSOption.Method == "" {
|
||||
config.TrojanSSOption.Method = "AES-128-GCM"
|
||||
}
|
||||
pickCipher, err = core.PickCipher(config.TrojanSSOption.Method, nil, config.TrojanSSOption.Password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
sl = &Listener{false, config, nil, keys, pickCipher, h}
|
||||
|
||||
tlsConfig := &tls.Config{}
|
||||
var realityBuilder *reality.Builder
|
||||
var httpHandler http.Handler
|
||||
|
||||
if config.Certificate != "" && config.PrivateKey != "" {
|
||||
cert, err := N.ParseCert(config.Certificate, config.PrivateKey, C.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsConfig.Certificates = []tls.Certificate{cert}
|
||||
}
|
||||
if config.RealityConfig.PrivateKey != "" {
|
||||
if tlsConfig.Certificates != nil {
|
||||
return nil, errors.New("certificate is unavailable in reality")
|
||||
}
|
||||
realityBuilder, err = config.RealityConfig.Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if config.WsPath != "" {
|
||||
httpMux := http.NewServeMux()
|
||||
httpMux.HandleFunc(config.WsPath, func(w http.ResponseWriter, r *http.Request) {
|
||||
conn, err := mihomoVMess.StreamUpgradedWebsocketConn(w, r)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
sl.HandleConn(conn, tunnel, additions...)
|
||||
})
|
||||
httpHandler = httpMux
|
||||
tlsConfig.NextProtos = append(tlsConfig.NextProtos, "http/1.1")
|
||||
}
|
||||
if config.GrpcServiceName != "" {
|
||||
httpHandler = gun.NewServerHandler(gun.ServerOption{
|
||||
ServiceName: config.GrpcServiceName,
|
||||
ConnHandler: func(conn net.Conn) {
|
||||
sl.HandleConn(conn, tunnel, additions...)
|
||||
},
|
||||
HttpHandler: httpHandler,
|
||||
})
|
||||
tlsConfig.NextProtos = append([]string{"h2"}, tlsConfig.NextProtos...) // h2 must before http/1.1
|
||||
}
|
||||
|
||||
for _, addr := range strings.Split(config.Listen, ",") {
|
||||
addr := addr
|
||||
|
||||
//TCP
|
||||
l, err := inbound.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if realityBuilder != nil {
|
||||
l = realityBuilder.NewListener(l)
|
||||
} else if len(tlsConfig.Certificates) > 0 {
|
||||
l = tls.NewListener(l, tlsConfig)
|
||||
} else if !config.TrojanSSOption.Enabled {
|
||||
return nil, errors.New("disallow using Trojan without both certificates/reality/ss config")
|
||||
}
|
||||
sl.listeners = append(sl.listeners, l)
|
||||
|
||||
go func() {
|
||||
if httpHandler != nil {
|
||||
_ = http.Serve(l, httpHandler)
|
||||
return
|
||||
}
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
if sl.closed {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
go sl.HandleConn(c, tunnel, additions...)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
return sl, nil
|
||||
}
|
||||
|
||||
func (l *Listener) Close() error {
|
||||
l.closed = true
|
||||
var retErr error
|
||||
for _, lis := range l.listeners {
|
||||
err := lis.Close()
|
||||
if err != nil {
|
||||
retErr = err
|
||||
}
|
||||
}
|
||||
return retErr
|
||||
}
|
||||
|
||||
func (l *Listener) Config() string {
|
||||
return l.config.String()
|
||||
}
|
||||
|
||||
func (l *Listener) AddrList() (addrList []net.Addr) {
|
||||
for _, lis := range l.listeners {
|
||||
addrList = append(addrList, lis.Addr())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (l *Listener) HandleConn(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) {
|
||||
defer conn.Close()
|
||||
|
||||
if l.pickCipher != nil {
|
||||
conn = l.pickCipher.StreamConn(conn)
|
||||
}
|
||||
|
||||
var key [trojan.KeyLength]byte
|
||||
if _, err := io.ReadFull(conn, key[:]); err != nil {
|
||||
//log.Warnln("read key error: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if user, ok := l.keys[key]; ok {
|
||||
additions = append(additions, inbound.WithInUser(user))
|
||||
} else {
|
||||
//log.Warnln("no such key")
|
||||
return
|
||||
}
|
||||
|
||||
var crlf [2]byte
|
||||
if _, err := io.ReadFull(conn, crlf[:]); err != nil {
|
||||
//log.Warnln("read crlf error: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
l.handleConn(false, conn, tunnel, additions...)
|
||||
}
|
||||
|
||||
func (l *Listener) handleConn(inMux bool, conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) {
|
||||
if inMux {
|
||||
defer conn.Close()
|
||||
}
|
||||
|
||||
command, err := socks5.ReadByte(conn)
|
||||
if err != nil {
|
||||
//log.Warnln("read command error: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
switch command {
|
||||
case trojan.CommandTCP, trojan.CommandUDP, trojan.CommandMux:
|
||||
default:
|
||||
//log.Warnln("unknown command: %d", command)
|
||||
return
|
||||
}
|
||||
|
||||
target, err := socks5.ReadAddr0(conn)
|
||||
if err != nil {
|
||||
//log.Warnln("read target error: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if !inMux {
|
||||
var crlf [2]byte
|
||||
if _, err := io.ReadFull(conn, crlf[:]); err != nil {
|
||||
//log.Warnln("read crlf error: %s", err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
switch command {
|
||||
case trojan.CommandTCP:
|
||||
//tunnel.HandleTCPConn(inbound.NewSocket(target, conn, C.TROJAN, additions...))
|
||||
l.handler.HandleSocket(target, conn, additions...)
|
||||
case trojan.CommandUDP:
|
||||
pc := trojan.NewPacketConn(conn)
|
||||
for {
|
||||
data, put, remoteAddr, err := pc.WaitReadFrom()
|
||||
if err != nil {
|
||||
if put != nil {
|
||||
put()
|
||||
}
|
||||
break
|
||||
}
|
||||
cPacket := &packet{
|
||||
pc: pc,
|
||||
rAddr: remoteAddr,
|
||||
payload: data,
|
||||
put: put,
|
||||
}
|
||||
|
||||
tunnel.HandleUDPPacket(inbound.NewPacket(target, cPacket, C.TROJAN, additions...))
|
||||
}
|
||||
case trojan.CommandMux:
|
||||
if inMux {
|
||||
//log.Warnln("invalid command: %d", command)
|
||||
return
|
||||
}
|
||||
smuxConfig := smux.DefaultConfig()
|
||||
smuxConfig.KeepAliveDisabled = true
|
||||
session, err := smux.Server(conn, smuxConfig)
|
||||
if err != nil {
|
||||
//log.Warnln("smux server error: %s", err.Error())
|
||||
return
|
||||
}
|
||||
defer session.Close()
|
||||
for {
|
||||
stream, err := session.AcceptStream()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
go l.handleConn(true, stream, tunnel, additions...)
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,7 +1,6 @@
|
||||
package tuic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"strings"
|
||||
@@ -93,23 +92,14 @@ func New(config LC.TuicServer, tunnel C.Tunnel, additions ...inbound.Addition) (
|
||||
quicConfig.MaxDatagramFrameSize = int64(maxDatagramFrameSize)
|
||||
|
||||
handleTcpFn := func(conn net.Conn, addr socks5.Addr, _additions ...inbound.Addition) error {
|
||||
newAdditions := additions
|
||||
if len(_additions) > 0 {
|
||||
newAdditions = slices.Clone(additions)
|
||||
newAdditions = append(newAdditions, _additions...)
|
||||
}
|
||||
conn, metadata := inbound.NewSocket(addr, conn, C.TUIC, newAdditions...)
|
||||
if h.IsSpecialFqdn(metadata.Host) {
|
||||
go func() { // ParseSpecialFqdn will block, so open a new goroutine
|
||||
_ = h.ParseSpecialFqdn(
|
||||
sing.WithAdditions(context.Background(), newAdditions...),
|
||||
conn,
|
||||
sing.ConvertMetadata(metadata),
|
||||
)
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
go tunnel.HandleTCPConn(conn, metadata)
|
||||
//newAdditions := additions
|
||||
//if len(_additions) > 0 {
|
||||
// newAdditions = slices.Clone(additions)
|
||||
// newAdditions = append(newAdditions, _additions...)
|
||||
//}
|
||||
//conn, metadata := inbound.NewSocket(addr, conn, C.TUIC, newAdditions...)
|
||||
//go tunnel.HandleTCPConn(conn, metadata)
|
||||
go h.HandleSocket(addr, conn, _additions...) // h.HandleSocket will block, so open a new goroutine
|
||||
return nil
|
||||
}
|
||||
handleUdpFn := func(addr socks5.Addr, packet C.UDPPacket, _additions ...inbound.Addition) error {
|
||||
|
@@ -22,19 +22,19 @@ type ClientConfig struct {
|
||||
Password string
|
||||
IdleSessionCheckInterval time.Duration
|
||||
IdleSessionTimeout time.Duration
|
||||
MinIdleSession int
|
||||
Server M.Socksaddr
|
||||
Dialer N.Dialer
|
||||
TLSConfig *vmess.TLSConfig
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
passwordSha256 []byte
|
||||
tlsConfig *vmess.TLSConfig
|
||||
clientFingerprint string
|
||||
dialer N.Dialer
|
||||
server M.Socksaddr
|
||||
sessionClient *session.Client
|
||||
padding atomic.TypedValue[*padding.PaddingFactory]
|
||||
passwordSha256 []byte
|
||||
tlsConfig *vmess.TLSConfig
|
||||
dialer N.Dialer
|
||||
server M.Socksaddr
|
||||
sessionClient *session.Client
|
||||
padding atomic.TypedValue[*padding.PaddingFactory]
|
||||
}
|
||||
|
||||
func NewClient(ctx context.Context, config ClientConfig) *Client {
|
||||
@@ -47,7 +47,7 @@ func NewClient(ctx context.Context, config ClientConfig) *Client {
|
||||
}
|
||||
// Initialize the padding state of this client
|
||||
padding.UpdatePaddingScheme(padding.DefaultPaddingScheme, &c.padding)
|
||||
c.sessionClient = session.NewClient(ctx, c.CreateOutboundTLSConnection, &c.padding, config.IdleSessionCheckInterval, config.IdleSessionTimeout)
|
||||
c.sessionClient = session.NewClient(ctx, c.CreateOutboundTLSConnection, &c.padding, config.IdleSessionCheckInterval, config.IdleSessionTimeout, config.MinIdleSession)
|
||||
return c
|
||||
}
|
||||
|
||||
|
@@ -19,22 +19,29 @@ type Client struct {
|
||||
die context.Context
|
||||
dieCancel context.CancelFunc
|
||||
|
||||
dialOut func(ctx context.Context) (net.Conn, error)
|
||||
dialOut util.DialOutFunc
|
||||
|
||||
sessionCounter atomic.Uint64
|
||||
|
||||
sessionCounter atomic.Uint64
|
||||
idleSession *skiplist.SkipList[uint64, *Session]
|
||||
idleSessionLock sync.Mutex
|
||||
|
||||
sessions map[uint64]*Session
|
||||
sessionsLock sync.Mutex
|
||||
|
||||
padding *atomic.TypedValue[*padding.PaddingFactory]
|
||||
|
||||
idleSessionTimeout time.Duration
|
||||
minIdleSession int
|
||||
}
|
||||
|
||||
func NewClient(ctx context.Context, dialOut func(ctx context.Context) (net.Conn, error), _padding *atomic.TypedValue[*padding.PaddingFactory], idleSessionCheckInterval, idleSessionTimeout time.Duration) *Client {
|
||||
func NewClient(ctx context.Context, dialOut util.DialOutFunc, _padding *atomic.TypedValue[*padding.PaddingFactory], idleSessionCheckInterval, idleSessionTimeout time.Duration, minIdleSession int) *Client {
|
||||
c := &Client{
|
||||
sessions: make(map[uint64]*Session),
|
||||
dialOut: dialOut,
|
||||
padding: _padding,
|
||||
idleSessionTimeout: idleSessionTimeout,
|
||||
minIdleSession: minIdleSession,
|
||||
}
|
||||
if idleSessionCheckInterval <= time.Second*5 {
|
||||
idleSessionCheckInterval = time.Second * 30
|
||||
@@ -81,10 +88,16 @@ func (c *Client) CreateStream(ctx context.Context) (net.Conn, error) {
|
||||
session.dieHook()
|
||||
}
|
||||
} else {
|
||||
c.idleSessionLock.Lock()
|
||||
session.idleSince = time.Now()
|
||||
c.idleSession.Insert(math.MaxUint64-session.seq, session)
|
||||
c.idleSessionLock.Unlock()
|
||||
select {
|
||||
case <-c.die.Done():
|
||||
// Now client has been closed
|
||||
go session.Close()
|
||||
default:
|
||||
c.idleSessionLock.Lock()
|
||||
session.idleSince = time.Now()
|
||||
c.idleSession.Insert(math.MaxUint64-session.seq, session)
|
||||
c.idleSessionLock.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,14 +135,35 @@ func (c *Client) createSession(ctx context.Context) (*Session, error) {
|
||||
c.idleSessionLock.Lock()
|
||||
c.idleSession.Remove(math.MaxUint64 - session.seq)
|
||||
c.idleSessionLock.Unlock()
|
||||
|
||||
c.sessionsLock.Lock()
|
||||
delete(c.sessions, session.seq)
|
||||
c.sessionsLock.Unlock()
|
||||
}
|
||||
|
||||
c.sessionsLock.Lock()
|
||||
c.sessions[session.seq] = session
|
||||
c.sessionsLock.Unlock()
|
||||
|
||||
session.Run()
|
||||
return session, nil
|
||||
}
|
||||
|
||||
func (c *Client) Close() error {
|
||||
c.dieCancel()
|
||||
go c.idleCleanupExpTime(time.Now())
|
||||
|
||||
c.sessionsLock.Lock()
|
||||
sessionToClose := make([]*Session, 0, len(c.sessions))
|
||||
for seq, session := range c.sessions {
|
||||
sessionToClose = append(sessionToClose, session)
|
||||
delete(c.sessions, seq)
|
||||
}
|
||||
c.sessionsLock.Unlock()
|
||||
|
||||
for _, session := range sessionToClose {
|
||||
session.Close()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -138,17 +172,30 @@ func (c *Client) idleCleanup() {
|
||||
}
|
||||
|
||||
func (c *Client) idleCleanupExpTime(expTime time.Time) {
|
||||
var sessionToRemove = make([]*Session, 0)
|
||||
sessionToRemove := make([]*Session, 0, c.idleSession.Len())
|
||||
|
||||
c.idleSessionLock.Lock()
|
||||
it := c.idleSession.Iterate()
|
||||
|
||||
activeCount := 0
|
||||
for it.IsNotEnd() {
|
||||
session := it.Value()
|
||||
if session.idleSince.Before(expTime) {
|
||||
sessionToRemove = append(sessionToRemove, session)
|
||||
c.idleSession.Remove(it.Key())
|
||||
}
|
||||
key := it.Key()
|
||||
it.MoveToNext()
|
||||
|
||||
if !session.idleSince.Before(expTime) {
|
||||
activeCount++
|
||||
continue
|
||||
}
|
||||
|
||||
if activeCount < c.minIdleSession {
|
||||
session.idleSince = time.Now()
|
||||
activeCount++
|
||||
continue
|
||||
}
|
||||
|
||||
sessionToRemove = append(sessionToRemove, session)
|
||||
c.idleSession.Remove(key)
|
||||
}
|
||||
c.idleSessionLock.Unlock()
|
||||
|
||||
|
@@ -0,0 +1,8 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
)
|
||||
|
||||
type DialOutFunc func(ctx context.Context) (net.Conn, error)
|
@@ -38,15 +38,17 @@ var defaultHeader = http.Header{
|
||||
type DialFn = func(network, addr string) (net.Conn, error)
|
||||
|
||||
type Conn struct {
|
||||
response *http.Response
|
||||
request *http.Request
|
||||
transport *TransportWrap
|
||||
writer *io.PipeWriter
|
||||
once sync.Once
|
||||
close atomic.Bool
|
||||
err error
|
||||
remain int
|
||||
br *bufio.Reader
|
||||
initFn func() (io.ReadCloser, error)
|
||||
writer io.Writer
|
||||
flusher http.Flusher
|
||||
netAddr
|
||||
|
||||
reader io.ReadCloser
|
||||
once sync.Once
|
||||
close atomic.Bool
|
||||
err error
|
||||
remain int
|
||||
br *bufio.Reader
|
||||
// deadlines
|
||||
deadline *time.Timer
|
||||
}
|
||||
@@ -57,26 +59,32 @@ type Config struct {
|
||||
ClientFingerprint string
|
||||
}
|
||||
|
||||
func (g *Conn) initRequest() {
|
||||
response, err := g.transport.RoundTrip(g.request)
|
||||
func (g *Conn) initReader() {
|
||||
reader, err := g.initFn()
|
||||
if err != nil {
|
||||
g.err = err
|
||||
g.writer.Close()
|
||||
if closer, ok := g.writer.(io.Closer); ok {
|
||||
closer.Close()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if !g.close.Load() {
|
||||
g.response = response
|
||||
g.br = bufio.NewReader(response.Body)
|
||||
g.reader = reader
|
||||
g.br = bufio.NewReader(reader)
|
||||
} else {
|
||||
response.Body.Close()
|
||||
reader.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Conn) Init() error {
|
||||
g.once.Do(g.initReader)
|
||||
return g.err
|
||||
}
|
||||
|
||||
func (g *Conn) Read(b []byte) (n int, err error) {
|
||||
g.once.Do(g.initRequest)
|
||||
if g.err != nil {
|
||||
return 0, g.err
|
||||
if err = g.Init(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if g.remain > 0 {
|
||||
@@ -88,7 +96,7 @@ func (g *Conn) Read(b []byte) (n int, err error) {
|
||||
n, err = io.ReadFull(g.br, b[:size])
|
||||
g.remain -= n
|
||||
return
|
||||
} else if g.response == nil {
|
||||
} else if g.reader == nil {
|
||||
return 0, net.ErrClosed
|
||||
}
|
||||
|
||||
@@ -139,6 +147,10 @@ func (g *Conn) Write(b []byte) (n int, err error) {
|
||||
err = g.err
|
||||
}
|
||||
|
||||
if g.flusher != nil {
|
||||
g.flusher.Flush()
|
||||
}
|
||||
|
||||
return len(b), err
|
||||
}
|
||||
|
||||
@@ -158,6 +170,10 @@ func (g *Conn) WriteBuffer(buffer *buf.Buffer) error {
|
||||
err = g.err
|
||||
}
|
||||
|
||||
if g.flusher != nil {
|
||||
g.flusher.Flush()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -167,15 +183,16 @@ func (g *Conn) FrontHeadroom() int {
|
||||
|
||||
func (g *Conn) Close() error {
|
||||
g.close.Store(true)
|
||||
if r := g.response; r != nil {
|
||||
r.Body.Close()
|
||||
if reader := g.reader; reader != nil {
|
||||
reader.Close()
|
||||
}
|
||||
|
||||
return g.writer.Close()
|
||||
if closer, ok := g.writer.(io.Closer); ok {
|
||||
return closer.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Conn) LocalAddr() net.Addr { return g.transport.LocalAddr() }
|
||||
func (g *Conn) RemoteAddr() net.Addr { return g.transport.RemoteAddr() }
|
||||
func (g *Conn) SetReadDeadline(t time.Time) error { return g.SetDeadline(t) }
|
||||
func (g *Conn) SetWriteDeadline(t time.Time) error { return g.SetDeadline(t) }
|
||||
|
||||
@@ -200,6 +217,7 @@ func NewHTTP2Client(dialFn DialFn, tlsConfig *tls.Config, Fingerprint string, re
|
||||
return nil, err
|
||||
}
|
||||
wrap.remoteAddr = pconn.RemoteAddr()
|
||||
wrap.localAddr = pconn.LocalAddr()
|
||||
|
||||
if tlsConfig == nil {
|
||||
return pconn, nil
|
||||
@@ -286,13 +304,18 @@ func StreamGunWithTransport(transport *TransportWrap, cfg *Config) (net.Conn, er
|
||||
}
|
||||
|
||||
conn := &Conn{
|
||||
request: request,
|
||||
transport: transport,
|
||||
writer: writer,
|
||||
close: atomic.NewBool(false),
|
||||
initFn: func() (io.ReadCloser, error) {
|
||||
response, err := transport.RoundTrip(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response.Body, nil
|
||||
},
|
||||
writer: writer,
|
||||
netAddr: transport.netAddr,
|
||||
}
|
||||
|
||||
go conn.once.Do(conn.initRequest)
|
||||
go conn.Init()
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,117 @@
|
||||
package gun
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/metacubex/mihomo/common/buf"
|
||||
N "github.com/metacubex/mihomo/common/net"
|
||||
C "github.com/metacubex/mihomo/constant"
|
||||
|
||||
"golang.org/x/net/http2"
|
||||
"golang.org/x/net/http2/h2c"
|
||||
)
|
||||
|
||||
const idleTimeout = 30 * time.Second
|
||||
|
||||
type ServerOption struct {
|
||||
ServiceName string
|
||||
ConnHandler func(conn net.Conn)
|
||||
HttpHandler http.Handler
|
||||
}
|
||||
|
||||
func NewServerHandler(options ServerOption) http.Handler {
|
||||
path := "/" + options.ServiceName + "/Tun"
|
||||
connHandler := options.ConnHandler
|
||||
httpHandler := options.HttpHandler
|
||||
if httpHandler == nil {
|
||||
httpHandler = http.NewServeMux()
|
||||
}
|
||||
// using h2c.NewHandler to ensure we can work in plain http2
|
||||
// and some tls conn is not *tls.Conn (like *reality.Conn)
|
||||
return h2c.NewHandler(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
||||
if request.URL.Path == path &&
|
||||
request.Method == http.MethodPost &&
|
||||
strings.HasPrefix(request.Header.Get("Content-Type"), "application/grpc") {
|
||||
|
||||
writer.Header().Set("Content-Type", "application/grpc")
|
||||
writer.Header().Set("TE", "trailers")
|
||||
writer.WriteHeader(http.StatusOK)
|
||||
|
||||
conn := &Conn{
|
||||
initFn: func() (io.ReadCloser, error) {
|
||||
return request.Body, nil
|
||||
},
|
||||
writer: writer,
|
||||
flusher: writer.(http.Flusher),
|
||||
}
|
||||
if request.RemoteAddr != "" {
|
||||
metadata := C.Metadata{}
|
||||
if err := metadata.SetRemoteAddress(request.RemoteAddr); err == nil {
|
||||
conn.remoteAddr = net.TCPAddrFromAddrPort(metadata.AddrPort())
|
||||
}
|
||||
}
|
||||
if addr, ok := request.Context().Value(http.LocalAddrContextKey).(net.Addr); ok {
|
||||
conn.localAddr = addr
|
||||
}
|
||||
|
||||
wrapper := &h2ConnWrapper{
|
||||
// gun.Conn can't correct handle ReadDeadline
|
||||
// so call N.NewDeadlineConn to add a safe wrapper
|
||||
ExtendedConn: N.NewDeadlineConn(conn),
|
||||
}
|
||||
connHandler(wrapper)
|
||||
wrapper.CloseWrapper()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
httpHandler.ServeHTTP(writer, request)
|
||||
}), &http2.Server{
|
||||
IdleTimeout: idleTimeout,
|
||||
})
|
||||
}
|
||||
|
||||
// h2ConnWrapper used to avoid "panic: Write called after Handler finished" for gun.Conn
|
||||
type h2ConnWrapper struct {
|
||||
N.ExtendedConn
|
||||
access sync.Mutex
|
||||
closed bool
|
||||
}
|
||||
|
||||
func (w *h2ConnWrapper) Write(p []byte) (n int, err error) {
|
||||
w.access.Lock()
|
||||
defer w.access.Unlock()
|
||||
if w.closed {
|
||||
return 0, net.ErrClosed
|
||||
}
|
||||
return w.ExtendedConn.Write(p)
|
||||
}
|
||||
|
||||
func (w *h2ConnWrapper) WriteBuffer(buffer *buf.Buffer) error {
|
||||
w.access.Lock()
|
||||
defer w.access.Unlock()
|
||||
if w.closed {
|
||||
return net.ErrClosed
|
||||
}
|
||||
return w.ExtendedConn.WriteBuffer(buffer)
|
||||
}
|
||||
|
||||
func (w *h2ConnWrapper) CloseWrapper() {
|
||||
w.access.Lock()
|
||||
defer w.access.Unlock()
|
||||
w.closed = true
|
||||
}
|
||||
|
||||
func (w *h2ConnWrapper) Close() error {
|
||||
w.CloseWrapper()
|
||||
return w.ExtendedConn.Close()
|
||||
}
|
||||
|
||||
func (w *h2ConnWrapper) Upstream() any {
|
||||
return w.ExtendedConn
|
||||
}
|
@@ -7,8 +7,7 @@ import (
|
||||
|
||||
type TransportWrap struct {
|
||||
*http2.Transport
|
||||
remoteAddr net.Addr
|
||||
localAddr net.Addr
|
||||
netAddr
|
||||
}
|
||||
|
||||
func (tw *TransportWrap) RemoteAddr() net.Addr {
|
||||
@@ -18,3 +17,16 @@ func (tw *TransportWrap) RemoteAddr() net.Addr {
|
||||
func (tw *TransportWrap) LocalAddr() net.Addr {
|
||||
return tw.localAddr
|
||||
}
|
||||
|
||||
type netAddr struct {
|
||||
remoteAddr net.Addr
|
||||
localAddr net.Addr
|
||||
}
|
||||
|
||||
func (addr *netAddr) RemoteAddr() net.Addr {
|
||||
return addr.remoteAddr
|
||||
}
|
||||
|
||||
func (addr *netAddr) LocalAddr() net.Addr {
|
||||
return addr.localAddr
|
||||
}
|
||||
|
@@ -38,10 +38,9 @@ type Command = byte
|
||||
const (
|
||||
CommandTCP byte = 1
|
||||
CommandUDP byte = 3
|
||||
CommandMux byte = 0x7f
|
||||
|
||||
// deprecated XTLS commands, as souvenirs
|
||||
commandXRD byte = 0xf0 // XTLS direct mode
|
||||
commandXRO byte = 0xf1 // XTLS origin mode
|
||||
KeyLength = 56
|
||||
)
|
||||
|
||||
type Option struct {
|
||||
@@ -65,7 +64,7 @@ type WebsocketOption struct {
|
||||
|
||||
type Trojan struct {
|
||||
option *Option
|
||||
hexPassword []byte
|
||||
hexPassword [KeyLength]byte
|
||||
}
|
||||
|
||||
func (t *Trojan) StreamConn(ctx context.Context, conn net.Conn) (net.Conn, error) {
|
||||
@@ -152,7 +151,7 @@ func (t *Trojan) WriteHeader(w io.Writer, command Command, socks5Addr []byte) er
|
||||
buf := pool.GetBuffer()
|
||||
defer pool.PutBuffer(buf)
|
||||
|
||||
buf.Write(t.hexPassword)
|
||||
buf.Write(t.hexPassword[:])
|
||||
buf.Write(crlf)
|
||||
|
||||
buf.WriteByte(command)
|
||||
@@ -245,7 +244,7 @@ func ReadPacket(r io.Reader, payload []byte) (net.Addr, int, int, error) {
|
||||
}
|
||||
|
||||
func New(option *Option) *Trojan {
|
||||
return &Trojan{option, hexSha224([]byte(option.Password))}
|
||||
return &Trojan{option, Key(option.Password)}
|
||||
}
|
||||
|
||||
var _ N.EnhancePacketConn = (*PacketConn)(nil)
|
||||
@@ -340,9 +339,12 @@ func (pc *PacketConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, er
|
||||
return
|
||||
}
|
||||
|
||||
func hexSha224(data []byte) []byte {
|
||||
buf := make([]byte, 56)
|
||||
hash := sha256.Sum224(data)
|
||||
hex.Encode(buf, hash[:])
|
||||
return buf
|
||||
func NewPacketConn(conn net.Conn) *PacketConn {
|
||||
return &PacketConn{Conn: conn}
|
||||
}
|
||||
|
||||
func Key(password string) (key [56]byte) {
|
||||
hash := sha256.Sum224([]byte(password))
|
||||
hex.Encode(key[:], hash[:])
|
||||
return
|
||||
}
|
||||
|
@@ -57,7 +57,7 @@ require (
|
||||
github.com/metacubex/sing-shadowsocks v0.2.8 // indirect
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.2 // indirect
|
||||
github.com/metacubex/sing-tun v0.4.5 // indirect
|
||||
github.com/metacubex/sing-vmess v0.1.14-0.20250203033000-f61322b3dbe3 // indirect
|
||||
github.com/metacubex/sing-vmess v0.1.14-0.20250228002636-abc39e113b82 // indirect
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20241126021510-0827d417b589 // indirect
|
||||
github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422 // indirect
|
||||
github.com/metacubex/utls v1.6.6 // indirect
|
||||
@@ -77,7 +77,7 @@ require (
|
||||
github.com/sagernet/fswatch v0.1.1 // indirect
|
||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect
|
||||
github.com/sagernet/nftables v0.3.0-beta.4 // indirect
|
||||
github.com/sagernet/sing v0.5.1 // indirect
|
||||
github.com/sagernet/sing v0.5.2 // indirect
|
||||
github.com/sagernet/sing-mux v0.2.1 // indirect
|
||||
github.com/sagernet/sing-shadowtls v0.1.5 // indirect
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect
|
||||
@@ -113,7 +113,7 @@ require (
|
||||
lukechampine.com/blake3 v1.3.0 // indirect
|
||||
)
|
||||
|
||||
replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20241121030428-33b6ebc52000
|
||||
replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20250228041610-d94509dc612a
|
||||
|
||||
replace cfa => ../../main/golang
|
||||
|
||||
|
@@ -111,8 +111,8 @@ github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiL
|
||||
github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY=
|
||||
github.com/metacubex/reality v0.0.0-20250219003814-74e8d7850629 h1:aHsYiTvubfgMa3JMTDY//hDXVvFWrHg6ARckR52ttZs=
|
||||
github.com/metacubex/reality v0.0.0-20250219003814-74e8d7850629/go.mod h1:TTeIOZLdGmzc07Oedn++vWUUfkZoXLF4sEMxWuhBFr8=
|
||||
github.com/metacubex/sing v0.0.0-20241121030428-33b6ebc52000 h1:gUbMXcQXhXGj0vCpCVFTUyIH7TMpD1dpTcNv/MCS+ok=
|
||||
github.com/metacubex/sing v0.0.0-20241121030428-33b6ebc52000/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/metacubex/sing v0.0.0-20250228041610-d94509dc612a h1:xjPXdDTlIKq4U/KnKpoCtkxD03T8GimtQrvHy/3dN00=
|
||||
github.com/metacubex/sing v0.0.0-20250228041610-d94509dc612a/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/metacubex/sing-quic v0.0.0-20250119013740-2a19cce83925 h1:UkPoRAnoBQMn7IK5qpoIV3OejU15q+rqel3NrbSCFKA=
|
||||
github.com/metacubex/sing-quic v0.0.0-20250119013740-2a19cce83925/go.mod h1:g7Mxj7b7zm7YVqD975mk/hSmrb0A0G4bVvIMr2MMzn8=
|
||||
github.com/metacubex/sing-shadowsocks v0.2.8 h1:wIhlaigswzjPw4hej75sEvWte3QR0+AJRafgwBHO5B4=
|
||||
@@ -121,8 +121,8 @@ github.com/metacubex/sing-shadowsocks2 v0.2.2 h1:eaf42uVx4Lr21S6MDYs0ZdTvGA0GEhD
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.2/go.mod h1:BhOug03a/RbI7y6hp6q+6ITM1dXjnLTmeWBHSTwvv2Q=
|
||||
github.com/metacubex/sing-tun v0.4.5 h1:kWSyQzuzHI40r50OFBczfWIDvMBMy1RIk+JsXeBPRB0=
|
||||
github.com/metacubex/sing-tun v0.4.5/go.mod h1:V0N4rr0dWPBEE20ESkTXdbtx2riQYcb6YtwC5w/9wl0=
|
||||
github.com/metacubex/sing-vmess v0.1.14-0.20250203033000-f61322b3dbe3 h1:2kq6azIvsTjTnyw66xXDl5zMzIJqF7GTbvLpkroHssg=
|
||||
github.com/metacubex/sing-vmess v0.1.14-0.20250203033000-f61322b3dbe3/go.mod h1:nE7Mdzj/QUDwgRi/8BASPtsxtIFZTHA4Yst5GgwbGCQ=
|
||||
github.com/metacubex/sing-vmess v0.1.14-0.20250228002636-abc39e113b82 h1:zZp5uct9+/0Hb1jKGyqDjCU4/72t43rs7qOq3Rc9oU8=
|
||||
github.com/metacubex/sing-vmess v0.1.14-0.20250228002636-abc39e113b82/go.mod h1:nE7Mdzj/QUDwgRi/8BASPtsxtIFZTHA4Yst5GgwbGCQ=
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20241126021510-0827d417b589 h1:Z6bNy0HLTjx6BKIkV48sV/yia/GP8Bnyb5JQuGgSGzg=
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20241126021510-0827d417b589/go.mod h1:4NclTLIZuk+QkHVCGrP87rHi/y8YjgPytxTgApJNMhc=
|
||||
github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422 h1:zGeQt3UyNydIVrMRB97AA5WsYEau/TyCnRtTf1yUmJY=
|
||||
|
@@ -11,7 +11,7 @@ require (
|
||||
|
||||
replace github.com/metacubex/mihomo => ../../foss/golang/clash
|
||||
|
||||
replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20241121030428-33b6ebc52000
|
||||
replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20250228041610-d94509dc612a
|
||||
|
||||
require (
|
||||
github.com/3andne/restls-client-go v0.1.6 // indirect
|
||||
@@ -65,7 +65,7 @@ require (
|
||||
github.com/metacubex/sing-shadowsocks v0.2.8 // indirect
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.2 // indirect
|
||||
github.com/metacubex/sing-tun v0.4.5 // indirect
|
||||
github.com/metacubex/sing-vmess v0.1.14-0.20250203033000-f61322b3dbe3 // indirect
|
||||
github.com/metacubex/sing-vmess v0.1.14-0.20250228002636-abc39e113b82 // indirect
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20241126021510-0827d417b589 // indirect
|
||||
github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422 // indirect
|
||||
github.com/metacubex/utls v1.6.6 // indirect
|
||||
@@ -85,7 +85,7 @@ require (
|
||||
github.com/sagernet/fswatch v0.1.1 // indirect
|
||||
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect
|
||||
github.com/sagernet/nftables v0.3.0-beta.4 // indirect
|
||||
github.com/sagernet/sing v0.5.1 // indirect
|
||||
github.com/sagernet/sing v0.5.2 // indirect
|
||||
github.com/sagernet/sing-mux v0.2.1 // indirect
|
||||
github.com/sagernet/sing-shadowtls v0.1.5 // indirect
|
||||
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect
|
||||
|
@@ -112,8 +112,8 @@ github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiL
|
||||
github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY=
|
||||
github.com/metacubex/reality v0.0.0-20250219003814-74e8d7850629 h1:aHsYiTvubfgMa3JMTDY//hDXVvFWrHg6ARckR52ttZs=
|
||||
github.com/metacubex/reality v0.0.0-20250219003814-74e8d7850629/go.mod h1:TTeIOZLdGmzc07Oedn++vWUUfkZoXLF4sEMxWuhBFr8=
|
||||
github.com/metacubex/sing v0.0.0-20241121030428-33b6ebc52000 h1:gUbMXcQXhXGj0vCpCVFTUyIH7TMpD1dpTcNv/MCS+ok=
|
||||
github.com/metacubex/sing v0.0.0-20241121030428-33b6ebc52000/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/metacubex/sing v0.0.0-20250228041610-d94509dc612a h1:xjPXdDTlIKq4U/KnKpoCtkxD03T8GimtQrvHy/3dN00=
|
||||
github.com/metacubex/sing v0.0.0-20250228041610-d94509dc612a/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/metacubex/sing-quic v0.0.0-20250119013740-2a19cce83925 h1:UkPoRAnoBQMn7IK5qpoIV3OejU15q+rqel3NrbSCFKA=
|
||||
github.com/metacubex/sing-quic v0.0.0-20250119013740-2a19cce83925/go.mod h1:g7Mxj7b7zm7YVqD975mk/hSmrb0A0G4bVvIMr2MMzn8=
|
||||
github.com/metacubex/sing-shadowsocks v0.2.8 h1:wIhlaigswzjPw4hej75sEvWte3QR0+AJRafgwBHO5B4=
|
||||
@@ -122,8 +122,8 @@ github.com/metacubex/sing-shadowsocks2 v0.2.2 h1:eaf42uVx4Lr21S6MDYs0ZdTvGA0GEhD
|
||||
github.com/metacubex/sing-shadowsocks2 v0.2.2/go.mod h1:BhOug03a/RbI7y6hp6q+6ITM1dXjnLTmeWBHSTwvv2Q=
|
||||
github.com/metacubex/sing-tun v0.4.5 h1:kWSyQzuzHI40r50OFBczfWIDvMBMy1RIk+JsXeBPRB0=
|
||||
github.com/metacubex/sing-tun v0.4.5/go.mod h1:V0N4rr0dWPBEE20ESkTXdbtx2riQYcb6YtwC5w/9wl0=
|
||||
github.com/metacubex/sing-vmess v0.1.14-0.20250203033000-f61322b3dbe3 h1:2kq6azIvsTjTnyw66xXDl5zMzIJqF7GTbvLpkroHssg=
|
||||
github.com/metacubex/sing-vmess v0.1.14-0.20250203033000-f61322b3dbe3/go.mod h1:nE7Mdzj/QUDwgRi/8BASPtsxtIFZTHA4Yst5GgwbGCQ=
|
||||
github.com/metacubex/sing-vmess v0.1.14-0.20250228002636-abc39e113b82 h1:zZp5uct9+/0Hb1jKGyqDjCU4/72t43rs7qOq3Rc9oU8=
|
||||
github.com/metacubex/sing-vmess v0.1.14-0.20250228002636-abc39e113b82/go.mod h1:nE7Mdzj/QUDwgRi/8BASPtsxtIFZTHA4Yst5GgwbGCQ=
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20241126021510-0827d417b589 h1:Z6bNy0HLTjx6BKIkV48sV/yia/GP8Bnyb5JQuGgSGzg=
|
||||
github.com/metacubex/sing-wireguard v0.0.0-20241126021510-0827d417b589/go.mod h1:4NclTLIZuk+QkHVCGrP87rHi/y8YjgPytxTgApJNMhc=
|
||||
github.com/metacubex/tfo-go v0.0.0-20241231083714-66613d49c422 h1:zGeQt3UyNydIVrMRB97AA5WsYEau/TyCnRtTf1yUmJY=
|
||||
|
14
clash-meta/.github/mihomo.service
vendored
14
clash-meta/.github/mihomo.service
vendored
@@ -1,17 +1,17 @@
|
||||
[Unit]
|
||||
Description=mihomo Daemon, Another Clash Kernel.
|
||||
After=network.target NetworkManager.service systemd-networkd.service iwd.service
|
||||
Documentation=https://wiki.metacubex.one
|
||||
After=network.target nss-lookup.target network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
LimitNPROC=500
|
||||
LimitNOFILE=1000000
|
||||
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_TIME CAP_SYS_PTRACE CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE
|
||||
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_TIME CAP_SYS_PTRACE CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE
|
||||
Restart=always
|
||||
ExecStartPre=/usr/bin/sleep 2s
|
||||
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
|
||||
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
|
||||
ExecStart=/usr/bin/mihomo -d /etc/mihomo
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
LimitNOFILE=infinity
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
17
clash-meta/.github/mihomo@.service
vendored
Normal file
17
clash-meta/.github/mihomo@.service
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
[Unit]
|
||||
Description=mihomo Daemon, Another Clash Kernel.
|
||||
Documentation=https://wiki.metacubex.one
|
||||
After=network.target nss-lookup.target network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
|
||||
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
|
||||
ExecStart=/usr/bin/mihomo -d /etc/mihomo
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
LimitNOFILE=infinity
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
13
clash-meta/.github/workflows/build.yml
vendored
13
clash-meta/.github/workflows/build.yml
vendored
@@ -276,17 +276,20 @@ jobs:
|
||||
|
||||
mkdir -p mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/DEBIAN
|
||||
mkdir -p mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/usr/bin
|
||||
mkdir -p mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/etc/mihomo
|
||||
mkdir -p mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/etc/systemd/system/
|
||||
mkdir -p mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/usr/share/licenses/mihomo
|
||||
mkdir -p mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/etc/mihomo
|
||||
mkdir -p mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/lib/systemd/system
|
||||
|
||||
cp mihomo mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/usr/bin/mihomo
|
||||
cp mihomo mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/usr/bin/
|
||||
cp LICENSE mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/usr/share/licenses/mihomo/
|
||||
cp .github/mihomo.service mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/etc/systemd/system/
|
||||
cp .github/{mihomo.service,mihomo@.service} mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/lib/systemd/system/
|
||||
|
||||
cat > mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/etc/mihomo/config.yaml <<EOF
|
||||
mixed-port: 7890
|
||||
external-controller: 127.0.0.1:9090
|
||||
EOF
|
||||
|
||||
cat > mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/DEBIAN/conffiles <<EOF
|
||||
/etc/mihomo/config.yaml
|
||||
EOF
|
||||
|
||||
cat > mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/DEBIAN/control <<EOF
|
||||
|
151
clash-nyanpasu/.github/workflows/ci.yml
vendored
151
clash-nyanpasu/.github/workflows/ci.yml
vendored
@@ -114,6 +114,9 @@ jobs:
|
||||
- os: macos-latest
|
||||
- os: windows-latest
|
||||
fail-fast: false
|
||||
if: >
|
||||
github.event_name != 'pull_request' ||
|
||||
contains(github.event.pull_request.title, 'crate')
|
||||
runs-on: ${{ matrix.targets.os }}
|
||||
needs: lint
|
||||
steps:
|
||||
@@ -175,77 +178,103 @@ jobs:
|
||||
- name: Prepare sidecar and resources
|
||||
run: pnpm check
|
||||
|
||||
- name: Build Tauri
|
||||
run: pnpm tauri build
|
||||
- name: Prepare frontend
|
||||
run: pnpm -r build
|
||||
env:
|
||||
NODE_OPTIONS: '--max_old_space_size=4096'
|
||||
# 以后完善了新的测试套件后再添加
|
||||
# test_unit:
|
||||
# name: Unit Test
|
||||
# needs: lint
|
||||
# # we want to run on the latest linux environment
|
||||
# runs-on: ubuntu-latest
|
||||
|
||||
# # the steps our job runs **in order**
|
||||
# steps:
|
||||
# # checkout the code on the workflow runner
|
||||
# - uses: actions/checkout@v4
|
||||
- name: Build Backend
|
||||
run: cargo build --release --manifest-path backend/Cargo.toml
|
||||
|
||||
# # install system dependencies that Tauri needs to compile on Linux.
|
||||
# # note the extra dependencies for `tauri-driver` to run which are: `webkit2gtk-driver` and `xvfb`
|
||||
# - name: Tauri dependencies
|
||||
# run: >-
|
||||
# sudo apt-get update &&
|
||||
# sudo apt-get install -y
|
||||
# libgtk-3-dev
|
||||
# libayatana-appindicator3-dev
|
||||
# libwebkit2gtk-4.0-dev
|
||||
# webkit2gtk-driver
|
||||
# xvfb
|
||||
test_unit:
|
||||
name: Unit Test
|
||||
needs: lint
|
||||
if: >
|
||||
github.event_name != 'pull_request' ||
|
||||
contains(github.event.pull_request.title, 'crate')
|
||||
|
||||
# # install the latest Rust stable
|
||||
# - name: Rust stable
|
||||
# run: rustup toolchain install stable --profile minimal && rustup default stable
|
||||
# - uses: Swatinem/rust-cache@v2
|
||||
# with:
|
||||
# workspaces: "./backend/"
|
||||
# prefix-key: "rust-stable"
|
||||
# shared-key: "ci"
|
||||
# save-if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' }}
|
||||
# we want to run on the latest linux environment
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- ubuntu-latest
|
||||
- macos-latest
|
||||
- windows-latest
|
||||
fail-fast: false
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
# - name: Install Node.js
|
||||
# uses: actions/setup-node@v4
|
||||
# with:
|
||||
# node-version: 20
|
||||
# the steps our job runs **in order**
|
||||
steps:
|
||||
# checkout the code on the workflow runner
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
# - uses: pnpm/action-setup@v2
|
||||
# name: Install pnpm
|
||||
# with:
|
||||
# version: 8
|
||||
# run_install: false
|
||||
# install system dependencies that Tauri needs to compile on Linux.
|
||||
# note the extra dependencies for `tauri-driver` to run which are: `webkit2gtk-driver` and `xvfb`
|
||||
- name: Tauri dependencies
|
||||
if: startsWith(matrix.os, 'ubuntu-')
|
||||
run: >-
|
||||
sudo apt-get update &&
|
||||
sudo apt-get install -y
|
||||
libgtk-3-dev
|
||||
libayatana-appindicator3-dev
|
||||
libwebkit2gtk-4.1-dev
|
||||
librsvg2-dev
|
||||
libxdo-dev
|
||||
webkit2gtk-driver
|
||||
xvfb
|
||||
|
||||
# - name: Get pnpm store directory
|
||||
# shell: bash
|
||||
# run: |
|
||||
# echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
- uses: maxim-lobanov/setup-xcode@v1
|
||||
if: startsWith(matrix.os, 'macos-')
|
||||
with:
|
||||
xcode-version: 'latest-stable'
|
||||
|
||||
# - uses: actions/cache@v4
|
||||
# name: Setup pnpm cache
|
||||
# with:
|
||||
# path: ${{ env.STORE_PATH }}
|
||||
# key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
# restore-keys: |
|
||||
# ${{ runner.os }}-pnpm-store-
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
# - name: Install dependencies
|
||||
# run: pnpm install
|
||||
# - name: Prepare fronend
|
||||
# run: pnpm web:build # Build frontend
|
||||
# - name: Prepare sidecar and resources
|
||||
# run: pnpm check
|
||||
# - name: Test
|
||||
# # run: pnpm test:unit && pnpm test:backend
|
||||
# run: pnpm test:backend
|
||||
- uses: actions/cache@v4
|
||||
name: Cache Rust dependencies
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/bin/
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
target/
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
with:
|
||||
run_install: false
|
||||
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
|
||||
- uses: actions/cache@v4
|
||||
name: Setup pnpm cache
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --no-frozen-lockfile
|
||||
|
||||
- name: Prepare sidecar and resources
|
||||
run: pnpm check
|
||||
|
||||
- name: Prepare frontend
|
||||
run: pnpm -r build
|
||||
env:
|
||||
NODE_OPTIONS: '--max_old_space_size=4096'
|
||||
|
||||
- name: Test
|
||||
run: pnpm test
|
||||
|
||||
# test_e2e:
|
||||
# # the display name of the test job
|
||||
|
@@ -41,7 +41,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: 'macos-13'
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
|
||||
- uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: 14
|
||||
xcode-version: 15
|
||||
|
||||
- name: install Rust stable
|
||||
run: |
|
||||
@@ -69,6 +69,9 @@ jobs:
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: latest
|
||||
- uses: denoland/setup-deno@v2
|
||||
with:
|
||||
deno-version: v2.x
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
@@ -114,15 +117,12 @@ jobs:
|
||||
NODE_OPTIONS: '--max_old_space_size=4096'
|
||||
|
||||
- name: Upload to release
|
||||
shell: bash
|
||||
run: |
|
||||
find ./backend/target \( -name "*.dmg" -o -name "*.sig" -o -name "*.tar.gz" \) | while read file; do
|
||||
echo "Uploading $file to release"
|
||||
gh release upload ${{ inputs.tag }} "$file" --clobber
|
||||
done
|
||||
deno run -A scripts/deno/upload-macos-updater.ts
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_TAG: ${{ inputs.tag }}
|
||||
TARGET_ARCH: ${{ inputs.aarch64 == true && 'aarch64' || 'x86_64' }}
|
||||
|
||||
- name: Upload to Github Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
|
199
clash-nyanpasu/backend/Cargo.lock
generated
199
clash-nyanpasu/backend/Cargo.lock
generated
@@ -767,9 +767,9 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.86"
|
||||
version = "0.1.87"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d"
|
||||
checksum = "d556ec1359574147ec0c4fc5eb525f3f23263a592b1a9c07e0a75b427de55c97"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -810,6 +810,15 @@ dependencies = [
|
||||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atomic-polyfill"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4"
|
||||
dependencies = [
|
||||
"critical-section",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atomic-waker"
|
||||
version = "1.1.2"
|
||||
@@ -884,7 +893,7 @@ version = "0.5.0"
|
||||
source = "git+https://github.com/libnyanpasu/auto-launch.git#729d5429dd689067047489af4a0a32f7013854c8"
|
||||
dependencies = [
|
||||
"dirs 5.0.1",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
"windows-registry 0.3.0",
|
||||
"windows-result 0.2.0",
|
||||
]
|
||||
@@ -1297,7 +1306,7 @@ dependencies = [
|
||||
"static_assertions",
|
||||
"tap",
|
||||
"thin-vec",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
"time",
|
||||
]
|
||||
|
||||
@@ -1384,6 +1393,8 @@ dependencies = [
|
||||
name = "boa_utils"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-fs",
|
||||
"boa_engine",
|
||||
"boa_gc",
|
||||
"boa_parser",
|
||||
@@ -1392,8 +1403,13 @@ dependencies = [
|
||||
"indoc",
|
||||
"isahc",
|
||||
"log",
|
||||
"mime",
|
||||
"postcard",
|
||||
"rustc-hash 2.1.1",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"smol",
|
||||
"tempfile",
|
||||
"test-log",
|
||||
"textwrap",
|
||||
"tracing",
|
||||
@@ -1631,7 +1647,7 @@ dependencies = [
|
||||
"semver 1.0.25",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1910,7 +1926,7 @@ dependencies = [
|
||||
"chrono",
|
||||
"clap",
|
||||
"colored",
|
||||
"convert_case 0.7.1",
|
||||
"convert_case 0.8.0",
|
||||
"ctrlc",
|
||||
"dashmap 6.1.0",
|
||||
"deelevate",
|
||||
@@ -1999,7 +2015,7 @@ dependencies = [
|
||||
"tauri-specta",
|
||||
"tempfile",
|
||||
"test-log",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
"time",
|
||||
"timeago",
|
||||
"tokio",
|
||||
@@ -2018,7 +2034,7 @@ dependencies = [
|
||||
"webview2-com",
|
||||
"which 7.0.2",
|
||||
"whoami",
|
||||
"window-vibrancy 0.5.3",
|
||||
"window-vibrancy",
|
||||
"windows-core 0.60.1",
|
||||
"windows-registry 0.5.0",
|
||||
"windows-sys 0.59.0",
|
||||
@@ -2036,6 +2052,12 @@ dependencies = [
|
||||
"error-code",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cobs"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15"
|
||||
|
||||
[[package]]
|
||||
name = "codespan-reporting"
|
||||
version = "0.11.1"
|
||||
@@ -2130,9 +2152,9 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.7.1"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7"
|
||||
checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
@@ -2279,6 +2301,12 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "critical-section"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
|
||||
|
||||
[[package]]
|
||||
name = "cron_clock"
|
||||
version = "0.8.0"
|
||||
@@ -2743,7 +2771,7 @@ version = "0.1.0"
|
||||
source = "git+https://github.com/libnyanpasu/nyanpasu-utils.git#757e46139a3710fb26a89724e24cdb7f2fe83885"
|
||||
dependencies = [
|
||||
"dirs 6.0.0",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
"tracing",
|
||||
"windows 0.59.0",
|
||||
]
|
||||
@@ -2769,7 +2797,7 @@ dependencies = [
|
||||
"objc2-foundation 0.3.0",
|
||||
"scopeguard",
|
||||
"smithay-client-toolkit",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
"widestring 1.1.0",
|
||||
"windows 0.59.0",
|
||||
"xcb",
|
||||
@@ -3086,6 +3114,18 @@ version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7"
|
||||
|
||||
[[package]]
|
||||
name = "embedded-io"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced"
|
||||
|
||||
[[package]]
|
||||
name = "embedded-io"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d"
|
||||
|
||||
[[package]]
|
||||
name = "ena"
|
||||
version = "0.14.3"
|
||||
@@ -4057,7 +4097,7 @@ dependencies = [
|
||||
"objc2-app-kit 0.3.0",
|
||||
"once_cell",
|
||||
"serde",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
"windows-sys 0.59.0",
|
||||
"x11-dl",
|
||||
]
|
||||
@@ -4331,6 +4371,15 @@ dependencies = [
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hash32"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hash32"
|
||||
version = "0.3.1"
|
||||
@@ -4370,13 +4419,27 @@ dependencies = [
|
||||
"foldhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heapless"
|
||||
version = "0.7.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f"
|
||||
dependencies = [
|
||||
"atomic-polyfill",
|
||||
"hash32 0.2.1",
|
||||
"rustc_version",
|
||||
"serde",
|
||||
"spin",
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heapless"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
|
||||
dependencies = [
|
||||
"hash32",
|
||||
"hash32 0.3.1",
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
@@ -5938,7 +6001,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"png",
|
||||
"serde",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
@@ -5977,7 +6040,7 @@ dependencies = [
|
||||
"spirv",
|
||||
"strum 0.26.3",
|
||||
"termcolor",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
@@ -6443,7 +6506,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"simd-json",
|
||||
"specta",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
@@ -6478,7 +6541,7 @@ dependencies = [
|
||||
"shared_child",
|
||||
"specta",
|
||||
"sysinfo",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"tracing-attributes",
|
||||
@@ -7096,7 +7159,7 @@ dependencies = [
|
||||
"objc2-osa-kit",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7426,7 +7489,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
"ucd-trie",
|
||||
]
|
||||
|
||||
@@ -7760,6 +7823,19 @@ version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e"
|
||||
|
||||
[[package]]
|
||||
name = "postcard"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "170a2601f67cc9dba8edd8c4870b15f71a6a2dc196daec8c83f72b59dff628a8"
|
||||
dependencies = [
|
||||
"cobs",
|
||||
"embedded-io 0.4.0",
|
||||
"embedded-io 0.6.1",
|
||||
"heapless 0.7.17",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "powerfmt"
|
||||
version = "0.2.0"
|
||||
@@ -7861,9 +7937,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.93"
|
||||
version = "1.0.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
||||
checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -8011,7 +8087,7 @@ dependencies = [
|
||||
"rustc-hash 2.1.1",
|
||||
"rustls",
|
||||
"socket2",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
@@ -8030,7 +8106,7 @@ dependencies = [
|
||||
"rustls",
|
||||
"rustls-pki-types",
|
||||
"slab",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
"tinyvec",
|
||||
"tracing",
|
||||
"web-time",
|
||||
@@ -8333,7 +8409,7 @@ checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
|
||||
dependencies = [
|
||||
"getrandom 0.2.15",
|
||||
"libredox",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -8704,7 +8780,7 @@ version = "0.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "421400d13ccfd26dfa5858199c30a5d76f9c54e0dba7575273025b43c5175dbb"
|
||||
dependencies = [
|
||||
"heapless",
|
||||
"heapless 0.8.0",
|
||||
"num-traits",
|
||||
"smallvec",
|
||||
]
|
||||
@@ -9460,7 +9536,7 @@ checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
"time",
|
||||
]
|
||||
|
||||
@@ -9754,6 +9830,9 @@ name = "spin"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spirv"
|
||||
@@ -10315,14 +10394,14 @@ dependencies = [
|
||||
"tauri-runtime",
|
||||
"tauri-runtime-wry",
|
||||
"tauri-utils",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
"tray-icon",
|
||||
"url",
|
||||
"urlpattern",
|
||||
"webkit2gtk",
|
||||
"webview2-com",
|
||||
"window-vibrancy 0.6.0",
|
||||
"window-vibrancy",
|
||||
"windows 0.60.0",
|
||||
]
|
||||
|
||||
@@ -10368,7 +10447,7 @@ dependencies = [
|
||||
"sha2 0.10.8",
|
||||
"syn 2.0.98",
|
||||
"tauri-utils",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
"time",
|
||||
"url",
|
||||
"uuid",
|
||||
@@ -10418,7 +10497,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10450,7 +10529,7 @@ dependencies = [
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"tauri-plugin-fs",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
"url",
|
||||
]
|
||||
|
||||
@@ -10471,7 +10550,7 @@ dependencies = [
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"tauri-utils",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
"toml",
|
||||
"url",
|
||||
"uuid",
|
||||
@@ -10489,7 +10568,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10506,7 +10585,7 @@ dependencies = [
|
||||
"serde_repr",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
"time",
|
||||
"url",
|
||||
]
|
||||
@@ -10526,7 +10605,7 @@ dependencies = [
|
||||
"sys-locale",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10556,7 +10635,7 @@ dependencies = [
|
||||
"shared_child",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
@@ -10583,7 +10662,7 @@ dependencies = [
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"tempfile",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
"time",
|
||||
"tokio",
|
||||
"url",
|
||||
@@ -10605,7 +10684,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri-utils",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
"url",
|
||||
"windows 0.60.0",
|
||||
]
|
||||
@@ -10650,7 +10729,7 @@ dependencies = [
|
||||
"specta-typescript",
|
||||
"tauri",
|
||||
"tauri-specta-macros",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10694,7 +10773,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"serde_with",
|
||||
"swift-rs",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
"toml",
|
||||
"url",
|
||||
"urlpattern",
|
||||
@@ -10878,11 +10957,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.11"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
|
||||
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||
dependencies = [
|
||||
"thiserror-impl 2.0.11",
|
||||
"thiserror-impl 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10898,9 +10977,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.11"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
|
||||
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -11348,7 +11427,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"png",
|
||||
"serde",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
@@ -11429,7 +11508,7 @@ dependencies = [
|
||||
"log",
|
||||
"rand 0.9.0",
|
||||
"sha1",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
"utf-8",
|
||||
]
|
||||
|
||||
@@ -12269,7 +12348,7 @@ version = "0.36.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfb27fccd3c27f68e9a6af1bcf48c2d82534b8675b83608a4d81446d095a17ac"
|
||||
dependencies = [
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
"windows 0.60.0",
|
||||
"windows-core 0.60.1",
|
||||
]
|
||||
@@ -12325,7 +12404,7 @@ dependencies = [
|
||||
"raw-window-handle",
|
||||
"rustc-hash 1.1.0",
|
||||
"smallvec",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
"wgpu-hal",
|
||||
"wgpu-types",
|
||||
]
|
||||
@@ -12364,7 +12443,7 @@ dependencies = [
|
||||
"renderdoc-sys",
|
||||
"rustc-hash 1.1.0",
|
||||
"smallvec",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
"wgpu-types",
|
||||
@@ -12461,20 +12540,6 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "window-vibrancy"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "831ad7678290beae36be6f9fad9234139c7f00f3b536347de7745621716be82d"
|
||||
dependencies = [
|
||||
"objc2 0.5.2",
|
||||
"objc2-app-kit 0.2.2",
|
||||
"objc2-foundation 0.2.2",
|
||||
"raw-window-handle",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "window-vibrancy"
|
||||
version = "0.6.0"
|
||||
@@ -13282,7 +13347,7 @@ dependencies = [
|
||||
"sha2 0.10.8",
|
||||
"soup3",
|
||||
"tao-macros",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
"url",
|
||||
"webkit2gtk",
|
||||
"webkit2gtk-sys",
|
||||
@@ -13757,7 +13822,7 @@ dependencies = [
|
||||
"pbkdf2",
|
||||
"rand 0.8.5",
|
||||
"sha1",
|
||||
"thiserror 2.0.11",
|
||||
"thiserror 2.0.12",
|
||||
"time",
|
||||
"zeroize",
|
||||
"zopfli",
|
||||
|
@@ -11,7 +11,7 @@ authors = ["zzzgydi", "keiko233"]
|
||||
[workspace.dependencies]
|
||||
thiserror = "2"
|
||||
tracing = "0.1"
|
||||
boa_engine = { version = "0.20" }
|
||||
boa_engine = { version = "0.20", features = ["annex-b"] }
|
||||
|
||||
[profile.release]
|
||||
panic = "unwind"
|
||||
|
@@ -6,11 +6,14 @@ edition.workspace = true
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
rustc-hash = { version = "2", features = ["std"] }
|
||||
boa_engine.workspace = true
|
||||
boa_engine = { workspace = true, features = ["annex-b"] }
|
||||
boa_gc = { version = "0.20" }
|
||||
boa_parser = { version = "0.20" }
|
||||
boa_parser = { version = "0.20", features = ["annex-b"] }
|
||||
isahc = "1.7"
|
||||
futures-util = "0.3"
|
||||
futures-concurrency = "7"
|
||||
@@ -18,8 +21,19 @@ smol = "2"
|
||||
tracing = "0.1"
|
||||
url = "2"
|
||||
log = "0.4"
|
||||
anyhow = "1.0"
|
||||
|
||||
# for cacheing
|
||||
mime = "0.3.17"
|
||||
async-fs = "2.1.2"
|
||||
|
||||
# for encoding/decoding
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
postcard = { version = "1.1.1", features = ["use-std"] }
|
||||
serde_json = { version = "1.0", features = ["preserve_order"] }
|
||||
|
||||
[dev-dependencies]
|
||||
indoc = "2"
|
||||
textwrap = "0.16"
|
||||
test-log = "0.2"
|
||||
tempfile = "3.17"
|
||||
|
@@ -15,20 +15,14 @@
|
||||
mod tests;
|
||||
|
||||
use boa_engine::{
|
||||
Context, JsArgs, JsData, JsResult, JsStr, JsString, js_str, js_string,
|
||||
Context, JsArgs, JsData, JsError, JsResult, JsStr, JsString, js_str, js_string,
|
||||
native_function::NativeFunction,
|
||||
object::{JsObject, ObjectInitializer},
|
||||
value::{JsValue, Numeric},
|
||||
};
|
||||
use boa_gc::{Finalize, Trace};
|
||||
use rustc_hash::FxHashMap;
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
collections::hash_map::Entry,
|
||||
rc::Rc,
|
||||
sync::{Arc, Mutex, OnceLock},
|
||||
time::SystemTime,
|
||||
};
|
||||
use std::{cell::RefCell, collections::hash_map::Entry, rc::Rc, sync::Arc, time::SystemTime};
|
||||
|
||||
/// This represents the different types of log messages.
|
||||
#[derive(Debug)]
|
||||
@@ -54,38 +48,37 @@ fn logger(msg: LogMessage, console_state: &Console) {
|
||||
}
|
||||
|
||||
pub trait Logger {
|
||||
fn log(&self, msg: LogMessage, console_state: &Console);
|
||||
type Item;
|
||||
fn log(&mut self, msg: LogMessage, console_state: &Console);
|
||||
fn take(&mut self) -> Vec<Self::Item>;
|
||||
}
|
||||
|
||||
trait LoggerBox = Logger + Sync + Send + 'static;
|
||||
pub trait LoggerBox = Logger<Item = LogMessage> + Sync + Send + 'static;
|
||||
|
||||
struct ConsoleLogger;
|
||||
|
||||
impl Logger for ConsoleLogger {
|
||||
fn log(&self, msg: LogMessage, console_state: &Console) {
|
||||
type Item = LogMessage;
|
||||
fn log(&mut self, msg: LogMessage, console_state: &Console) {
|
||||
logger(msg, console_state);
|
||||
}
|
||||
}
|
||||
|
||||
pub static LOGGER: OnceLock<Mutex<Arc<dyn LoggerBox>>> = OnceLock::new();
|
||||
|
||||
fn default_logger() -> Mutex<Arc<dyn LoggerBox>> {
|
||||
Mutex::new(Arc::new(ConsoleLogger))
|
||||
}
|
||||
|
||||
impl Logger for OnceLock<Mutex<Arc<dyn LoggerBox>>> {
|
||||
fn log(&self, msg: LogMessage, console_state: &Console) {
|
||||
let guard = self.get_or_init(default_logger);
|
||||
guard
|
||||
.lock()
|
||||
.expect("failed to lock")
|
||||
.log(msg, console_state);
|
||||
fn take(&mut self) -> Vec<Self::Item> {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_logger(logger: Arc<dyn LoggerBox>) {
|
||||
let guard = LOGGER.get_or_init(default_logger);
|
||||
*guard.lock().expect("failed to lock") = logger;
|
||||
thread_local! {
|
||||
static LOGGER: RefCell<Box<dyn LoggerBox>> = RefCell::new(Box::new(ConsoleLogger));
|
||||
}
|
||||
|
||||
pub fn inspect_logger<R>(f: impl FnOnce(&mut dyn LoggerBox) -> R) -> R {
|
||||
LOGGER.with(|cell| f(cell.borrow_mut().as_mut()))
|
||||
}
|
||||
|
||||
pub fn set_logger(logger: Box<dyn LoggerBox>) {
|
||||
LOGGER.with(|cell| {
|
||||
*cell.borrow_mut() = logger;
|
||||
});
|
||||
}
|
||||
|
||||
/// This represents the `console` formatter.
|
||||
@@ -323,7 +316,12 @@ impl Console {
|
||||
args[0] = JsValue::new(concat);
|
||||
}
|
||||
|
||||
LOGGER.log(LogMessage::Error(formatter(&args, context)?), console);
|
||||
LOGGER.with(|logger| {
|
||||
logger
|
||||
.borrow_mut()
|
||||
.log(LogMessage::Error(formatter(&args, context)?), console);
|
||||
Ok::<_, JsError>(())
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(JsValue::undefined())
|
||||
@@ -361,7 +359,12 @@ impl Console {
|
||||
console: &Self,
|
||||
context: &mut Context,
|
||||
) -> JsResult<JsValue> {
|
||||
LOGGER.log(LogMessage::Log(formatter(args, context)?), console);
|
||||
LOGGER.with(|logger| {
|
||||
logger
|
||||
.borrow_mut()
|
||||
.log(LogMessage::Log(formatter(args, context)?), console);
|
||||
Ok::<_, JsError>(())
|
||||
})?;
|
||||
Ok(JsValue::undefined())
|
||||
}
|
||||
|
||||
@@ -381,7 +384,12 @@ impl Console {
|
||||
console: &Self,
|
||||
context: &mut Context,
|
||||
) -> JsResult<JsValue> {
|
||||
LOGGER.log(LogMessage::Error(formatter(args, context)?), console);
|
||||
LOGGER.with(|logger| {
|
||||
logger
|
||||
.borrow_mut()
|
||||
.log(LogMessage::Error(formatter(args, context)?), console);
|
||||
Ok::<_, JsError>(())
|
||||
})?;
|
||||
Ok(JsValue::undefined())
|
||||
}
|
||||
|
||||
@@ -401,7 +409,12 @@ impl Console {
|
||||
console: &Self,
|
||||
context: &mut Context,
|
||||
) -> JsResult<JsValue> {
|
||||
LOGGER.log(LogMessage::Info(formatter(args, context)?), console);
|
||||
LOGGER.with(|logger| {
|
||||
logger
|
||||
.borrow_mut()
|
||||
.log(LogMessage::Info(formatter(args, context)?), console);
|
||||
Ok::<_, JsError>(())
|
||||
})?;
|
||||
Ok(JsValue::undefined())
|
||||
}
|
||||
|
||||
@@ -421,7 +434,12 @@ impl Console {
|
||||
console: &Self,
|
||||
context: &mut Context,
|
||||
) -> JsResult<JsValue> {
|
||||
LOGGER.log(LogMessage::Log(formatter(args, context)?), console);
|
||||
LOGGER.with(|logger| {
|
||||
logger
|
||||
.borrow_mut()
|
||||
.log(LogMessage::Log(formatter(args, context)?), console);
|
||||
Ok::<_, JsError>(())
|
||||
})?;
|
||||
Ok(JsValue::undefined())
|
||||
}
|
||||
|
||||
@@ -442,7 +460,12 @@ impl Console {
|
||||
context: &mut Context,
|
||||
) -> JsResult<JsValue> {
|
||||
if !args.is_empty() {
|
||||
LOGGER.log(LogMessage::Log(formatter(args, context)?), console);
|
||||
LOGGER.with(|logger| {
|
||||
logger
|
||||
.borrow_mut()
|
||||
.log(LogMessage::Log(formatter(args, context)?), console);
|
||||
Ok::<_, JsError>(())
|
||||
})?;
|
||||
}
|
||||
|
||||
let stack_trace_dump = context
|
||||
@@ -453,7 +476,12 @@ impl Console {
|
||||
.map(JsString::to_std_string_escaped)
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
LOGGER.log(LogMessage::Log(stack_trace_dump), console);
|
||||
LOGGER.with(|logger| {
|
||||
logger
|
||||
.borrow_mut()
|
||||
.log(LogMessage::Log(stack_trace_dump), console);
|
||||
Ok::<_, JsError>(())
|
||||
})?;
|
||||
|
||||
Ok(JsValue::undefined())
|
||||
}
|
||||
@@ -474,7 +502,12 @@ impl Console {
|
||||
console: &Self,
|
||||
context: &mut Context,
|
||||
) -> JsResult<JsValue> {
|
||||
LOGGER.log(LogMessage::Warn(formatter(args, context)?), console);
|
||||
LOGGER.with(|logger| {
|
||||
logger
|
||||
.borrow_mut()
|
||||
.log(LogMessage::Warn(formatter(args, context)?), console);
|
||||
Ok::<_, JsError>(())
|
||||
})?;
|
||||
Ok(JsValue::undefined())
|
||||
}
|
||||
|
||||
@@ -503,7 +536,12 @@ impl Console {
|
||||
let c = console.count_map.entry(label).or_insert(0);
|
||||
*c += 1;
|
||||
|
||||
LOGGER.log(LogMessage::Info(format!("{msg} {c}")), console);
|
||||
let msg = format!("{msg} {c}");
|
||||
|
||||
LOGGER.with(|logger| {
|
||||
logger.borrow_mut().log(LogMessage::Info(msg), console);
|
||||
Ok::<_, JsError>(())
|
||||
})?;
|
||||
Ok(JsValue::undefined())
|
||||
}
|
||||
|
||||
@@ -620,7 +658,9 @@ impl Console {
|
||||
for msg in args.iter().skip(1) {
|
||||
concat = concat + " " + &msg.display().to_string();
|
||||
}
|
||||
LOGGER.log(LogMessage::Log(concat), console);
|
||||
LOGGER.with(|logger| {
|
||||
logger.borrow_mut().log(LogMessage::Log(concat), console);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
@@ -692,7 +732,12 @@ impl Console {
|
||||
) -> JsResult<JsValue> {
|
||||
let group_label = formatter(args, context)?;
|
||||
|
||||
LOGGER.log(LogMessage::Info(format!("group: {group_label}")), console);
|
||||
LOGGER.with(|logger| {
|
||||
logger
|
||||
.borrow_mut()
|
||||
.log(LogMessage::Info(format!("group: {group_label}")), console);
|
||||
Ok::<_, JsError>(())
|
||||
})?;
|
||||
console.groups.push(group_label);
|
||||
|
||||
Ok(JsValue::undefined())
|
||||
|
@@ -1,6 +1,7 @@
|
||||
#![feature(trait_alias)]
|
||||
#![feature(auto_traits)]
|
||||
#![feature(negative_impls)]
|
||||
#![feature(let_chains)]
|
||||
|
||||
//! Boa's **boa_runtime** crate contains an example runtime and basic runtime features and
|
||||
//! functionality for the `boa_engine` crate for runtime implementors.
|
||||
@@ -9,7 +10,7 @@
|
||||
//!
|
||||
//! 1. Add **boa_runtime** as a dependency to your project along with **boa_engine**.
|
||||
//!
|
||||
//! ```
|
||||
//! ```no_run
|
||||
//! use boa_engine::{js_string, property::Attribute, Context, Source};
|
||||
//! use boa_runtime::Console;
|
||||
//!
|
||||
@@ -58,7 +59,7 @@ mod console;
|
||||
pub mod module;
|
||||
#[doc(inline)]
|
||||
pub use console::Console;
|
||||
pub use console::{LogMessage, Logger, set_logger};
|
||||
pub use console::{LogMessage, Logger, LoggerBox, inspect_logger, set_logger};
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod test {
|
||||
|
@@ -1,14 +1,15 @@
|
||||
use std::{
|
||||
cell::{Cell, RefCell},
|
||||
collections::VecDeque,
|
||||
rc::Rc,
|
||||
path::PathBuf,
|
||||
str::FromStr,
|
||||
time::{Duration, SystemTime},
|
||||
};
|
||||
|
||||
use async_fs::create_dir_all;
|
||||
use boa_engine::{
|
||||
Context, JsNativeError, JsResult, JsString, JsValue, Module,
|
||||
builtins::promise::PromiseState,
|
||||
job::{FutureJob, JobQueue, NativeJob},
|
||||
js_string,
|
||||
module::ModuleLoader,
|
||||
};
|
||||
use boa_parser::Source;
|
||||
@@ -17,13 +18,94 @@ use isahc::{
|
||||
AsyncReadResponseExt, Request, RequestExt,
|
||||
config::{Configurable, RedirectPolicy},
|
||||
};
|
||||
use mime::Mime;
|
||||
use smol::{LocalExecutor, future};
|
||||
use url::Url;
|
||||
|
||||
// Most of the boilerplate is taken from the `futures.rs` example.
|
||||
// This file only explains what is exclusive of async module loading.
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct HttpModuleLoader;
|
||||
pub struct HttpModuleLoader {
|
||||
cache_dir: PathBuf,
|
||||
max_age: Duration,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct CachedItem {
|
||||
pub mime: String,
|
||||
/// raw string content
|
||||
/// We have no plan for now to support binary content,
|
||||
/// so we just use `String` to store the content.
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
impl HttpModuleLoader {
|
||||
pub fn new(cache_dir: PathBuf, max_age: Duration) -> Self {
|
||||
Self { cache_dir, max_age }
|
||||
}
|
||||
|
||||
fn mapping_cache_dir(&self, url: &url::Url) -> PathBuf {
|
||||
let mut buf = self.cache_dir.clone();
|
||||
let host = match url.host() {
|
||||
Some(host) => host.to_string().replace('.', "--"),
|
||||
None => "unknown".to_string(),
|
||||
};
|
||||
let port = match url.port() {
|
||||
Some(port) => format!("__{port}"),
|
||||
None => "".to_string(),
|
||||
};
|
||||
buf.push(format!("{}_{}{}", url.scheme(), host, port));
|
||||
buf.push(url.path().replace('/', "_").replace(".", "--"));
|
||||
buf
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(finish_load, context))]
|
||||
fn handle_cached_item(
|
||||
item: CachedItem,
|
||||
finish_load: Box<dyn FnOnce(JsResult<Module>, &mut Context)>,
|
||||
context: &mut Context,
|
||||
) {
|
||||
let Ok(mime) = Mime::from_str(item.mime.as_str()) else {
|
||||
log::error!("failed to parse mime type `{}`", item.mime);
|
||||
finish_load(
|
||||
Err(JsNativeError::typ()
|
||||
.with_message("failed to parse mime type")
|
||||
.into()),
|
||||
context,
|
||||
);
|
||||
return;
|
||||
};
|
||||
let source_str = match (mime.type_(), mime.subtype()) {
|
||||
(mime::APPLICATION, mime::JAVASCRIPT) => item.content.clone(),
|
||||
(mime::APPLICATION, mime::JSON) => {
|
||||
format!("export default {};", item.content)
|
||||
}
|
||||
_ => {
|
||||
let Ok(escaped_str) = serde_json::to_string(&item.content) else {
|
||||
log::error!("failed to serialize content.");
|
||||
finish_load(
|
||||
Err(JsNativeError::typ()
|
||||
.with_message("failed to serialize content")
|
||||
.into()),
|
||||
context,
|
||||
);
|
||||
return;
|
||||
};
|
||||
format!("export const text = {escaped_str};")
|
||||
}
|
||||
};
|
||||
|
||||
// Could also add a path if needed.
|
||||
let source = Source::from_bytes(source_str.as_bytes());
|
||||
|
||||
let module = Module::parse(source, None, context);
|
||||
// TODO: rm cache or create cache after judge module is ok
|
||||
|
||||
// We don't do any error handling, `finish_load` takes care of that for us.
|
||||
finish_load(module, context);
|
||||
}
|
||||
}
|
||||
|
||||
impl ModuleLoader for HttpModuleLoader {
|
||||
fn load_imported_module(
|
||||
@@ -34,30 +116,119 @@ impl ModuleLoader for HttpModuleLoader {
|
||||
context: &mut Context,
|
||||
) {
|
||||
let url = specifier.to_std_string_escaped();
|
||||
let url = Url::from_str(&url).expect("invalid url"); // SAFETY: `url` is a valid URL, if it's not, its caller side issue
|
||||
let cache_path = self.mapping_cache_dir(&url);
|
||||
let parent_dir = match cache_path.parent() {
|
||||
Some(parent) => parent.to_path_buf(),
|
||||
None => {
|
||||
log::error!("failed to get parent directory for `{url}`");
|
||||
finish_load(
|
||||
Err(JsNativeError::typ()
|
||||
.with_message(format!(
|
||||
"failed to get cache parent directory for `{url}`; path: `{}`",
|
||||
cache_path.display()
|
||||
))
|
||||
.into()),
|
||||
context,
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let max_age = self.max_age;
|
||||
|
||||
let fetch = async move {
|
||||
log::debug!("checking cache for `{url}`...");
|
||||
|
||||
let now = SystemTime::now();
|
||||
let should_use_cached_content = match async_fs::metadata(&cache_path).await {
|
||||
Ok(metadata)
|
||||
if metadata
|
||||
.modified()
|
||||
.is_ok_and(|modified| modified > now - max_age) =>
|
||||
{
|
||||
true
|
||||
}
|
||||
Err(err) => {
|
||||
// create dir if not exists
|
||||
if err.kind() == std::io::ErrorKind::NotFound
|
||||
&& let Err(e) = async_fs::metadata(&parent_dir).await
|
||||
&& e.kind() == std::io::ErrorKind::NotFound
|
||||
{
|
||||
if let Err(err) = create_dir_all(parent_dir).await {
|
||||
log::error!(
|
||||
"failed to create cache directory for `{url}`; path: `{}`. error: `{}`",
|
||||
cache_path.display(),
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
|
||||
// Adding some prints to show the non-deterministic nature of the async fetches.
|
||||
// Try to run the example several times to see how sometimes the fetches start in order
|
||||
// but finish in disorder.
|
||||
log::debug!("fetching `{url}`...");
|
||||
// This could also retry fetching in case there's an error while requesting the module.
|
||||
let body: Result<_, isahc::Error> = async {
|
||||
let mut response = Request::get(&url)
|
||||
.redirect_policy(RedirectPolicy::Limit(5))
|
||||
.body(())?
|
||||
.send_async()
|
||||
.await?;
|
||||
|
||||
Ok(response.text().await?)
|
||||
// This could also retry fetching in case there's an error while requesting the module.
|
||||
let item: anyhow::Result<CachedItem> = if should_use_cached_content {
|
||||
async {
|
||||
log::debug!("fetching `{url}` from cache...");
|
||||
let item = async_fs::read(&cache_path).await?;
|
||||
let item = postcard::from_bytes(&item)?;
|
||||
log::debug!("finished fetching `{url}` from cache");
|
||||
Ok(item)
|
||||
}
|
||||
.await
|
||||
} else {
|
||||
async {
|
||||
log::debug!("fetching `{url}`...");
|
||||
let mut response = Request::get(url.as_str())
|
||||
.redirect_policy(RedirectPolicy::Limit(5))
|
||||
.body(())?
|
||||
.send_async()
|
||||
.await?;
|
||||
|
||||
let mime = response
|
||||
.headers()
|
||||
.get("content-type")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or(mime::TEXT_PLAIN.to_string());
|
||||
let body = response.text().await?;
|
||||
|
||||
log::debug!("finished fetching `{url}`");
|
||||
Ok(CachedItem {
|
||||
mime,
|
||||
content: body,
|
||||
})
|
||||
}
|
||||
.await
|
||||
};
|
||||
|
||||
if let Ok(item) = &item {
|
||||
match postcard::to_stdvec(&item) {
|
||||
Ok(item) => {
|
||||
if let Err(err) = async_fs::write(&cache_path, &item).await {
|
||||
log::error!(
|
||||
"failed to write cache for `{url}`; path: `{}`. error: `{}`",
|
||||
cache_path.display(),
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
log::error!("failed to serialize content: {err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
.await;
|
||||
log::debug!("finished fetching `{url}`");
|
||||
|
||||
// Since the async context cannot take the `context` by ref, we have to continue
|
||||
// parsing inside a new `NativeJob` that will be enqueued into the promise job queue.
|
||||
NativeJob::new(move |context| -> JsResult<JsValue> {
|
||||
let body = match body {
|
||||
Ok(body) => body,
|
||||
let item = match item {
|
||||
Ok(item) => item,
|
||||
Err(err) => {
|
||||
// On error we always call `finish_load` to notify the load promise about the
|
||||
// error.
|
||||
@@ -70,15 +241,7 @@ impl ModuleLoader for HttpModuleLoader {
|
||||
return Ok(JsValue::undefined());
|
||||
}
|
||||
};
|
||||
|
||||
// Could also add a path if needed.
|
||||
let source = Source::from_bytes(body.as_bytes());
|
||||
|
||||
let module = Module::parse(source, None, context);
|
||||
|
||||
// We don't do any error handling, `finish_load` takes care of that for us.
|
||||
finish_load(module, context);
|
||||
|
||||
Self::handle_cached_item(item, finish_load, context);
|
||||
// Also needed to match `NativeJob::new`.
|
||||
Ok(JsValue::undefined())
|
||||
})
|
||||
@@ -94,6 +257,9 @@ impl ModuleLoader for HttpModuleLoader {
|
||||
|
||||
#[test]
|
||||
fn test_http_module_loader() -> JsResult<()> {
|
||||
use boa_engine::{builtins::promise::PromiseState, js_string};
|
||||
use std::rc::Rc;
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
// A simple snippet that imports modules from the web instead of the file system.
|
||||
const SRC: &str = r#"
|
||||
import YAML from 'https://esm.run/yaml@2.3.4';
|
||||
@@ -120,7 +286,10 @@ fn test_http_module_loader() -> JsResult<()> {
|
||||
let context = &mut Context::builder()
|
||||
.job_queue(queue)
|
||||
// NEW: sets the context module loader to our custom loader
|
||||
.module_loader(Rc::new(HttpModuleLoader))
|
||||
.module_loader(Rc::new(HttpModuleLoader::new(
|
||||
temp_dir.path().to_path_buf(),
|
||||
Duration::from_secs(10),
|
||||
)))
|
||||
.build()?;
|
||||
|
||||
let module = Module::parse(Source::from_bytes(SRC.as_bytes()), None, context)?;
|
||||
|
@@ -10,6 +10,9 @@ license = "MIT OR Apache-2.0"
|
||||
readme = "README.md"
|
||||
include = ["src/**", "Cargo.toml", "LICENSE_*"]
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
dirs = "6"
|
||||
log = "0.4"
|
||||
|
@@ -12,6 +12,7 @@ build = "build.rs"
|
||||
[lib]
|
||||
name = "clash_nyanpasu_lib"
|
||||
crate-type = ["staticlib", "cdylib", "rlib"]
|
||||
doctest = false
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "2.0.1", features = [] }
|
||||
@@ -42,7 +43,7 @@ futures-util = "0.3"
|
||||
glob = "0.3.1"
|
||||
timeago = "0.4"
|
||||
humansize = "2.1.3"
|
||||
convert_case = "0.7.0"
|
||||
convert_case = "0.8.0"
|
||||
anyhow = "1.0"
|
||||
pretty_assertions = "1.4.0"
|
||||
chrono = "0.4.31"
|
||||
@@ -182,8 +183,8 @@ mlua = { version = "0.10", features = [
|
||||
] }
|
||||
|
||||
# JavaScript Integration
|
||||
boa_utils = { path = "../boa_utils" } # should be removed when boa support console customize
|
||||
boa_engine.workspace = true
|
||||
boa_utils = { path = "../boa_utils" } # should be removed when boa support console customize
|
||||
boa_engine = { workspace = true, features = ["annex-b"] }
|
||||
|
||||
# Tauri Dependencies
|
||||
tauri = { version = "2.2", features = ["tray-icon", "image-png", "image-ico"] }
|
||||
@@ -196,7 +197,7 @@ tauri-plugin-process = "2.2"
|
||||
tauri-plugin-updater = "2.2"
|
||||
tauri-plugin-shell = "2.2"
|
||||
tauri-plugin-notification = "2.2"
|
||||
window-vibrancy = { version = "0.5.2" }
|
||||
window-vibrancy = { version = "0.6.0" }
|
||||
|
||||
# Strong typed api binding between typescript and rust
|
||||
specta-typescript = "0.0.9"
|
||||
|
@@ -1,12 +1,12 @@
|
||||
use super::runner::{ProcessOutput, Runner, wrap_result};
|
||||
use crate::enhance::utils::{Logs, LogsExt, take_logs};
|
||||
use crate::enhance::utils::{LogSpan, Logs, LogsExt, take_logs};
|
||||
use anyhow::Context as _;
|
||||
use async_trait::async_trait;
|
||||
use boa_engine::{
|
||||
Context, JsError, JsNativeError, JsValue, Source,
|
||||
builtins::promise::PromiseState,
|
||||
js_string,
|
||||
module::{Module, ModuleLoader as BoaModuleLoader, SimpleModuleLoader},
|
||||
module::{Module, SimpleModuleLoader},
|
||||
property::Attribute,
|
||||
};
|
||||
use boa_utils::{
|
||||
@@ -17,13 +17,12 @@ use boa_utils::{
|
||||
},
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
use parking_lot::Mutex;
|
||||
use serde_yaml::Mapping;
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
path::{Path, PathBuf},
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
use tracing_attributes::instrument;
|
||||
use utils::wrap_script_if_not_esm;
|
||||
@@ -55,16 +54,49 @@ pub enum JsRunnerError {
|
||||
Other(String),
|
||||
}
|
||||
|
||||
pub struct BoaConsoleLogger(Arc<Mutex<Option<Logs>>>);
|
||||
pub struct BoaConsoleLogger(Logs);
|
||||
impl boa_utils::Logger for BoaConsoleLogger {
|
||||
fn log(&self, msg: boa_utils::LogMessage, _: &Console) {
|
||||
type Item = boa_utils::LogMessage;
|
||||
fn log(&mut self, msg: boa_utils::LogMessage, _: &Console) {
|
||||
match msg {
|
||||
boa_utils::LogMessage::Log(msg) => self.0.lock().as_mut().unwrap().log(msg),
|
||||
boa_utils::LogMessage::Info(msg) => self.0.lock().as_mut().unwrap().info(msg),
|
||||
boa_utils::LogMessage::Warn(msg) => self.0.lock().as_mut().unwrap().warn(msg),
|
||||
boa_utils::LogMessage::Error(msg) => self.0.lock().as_mut().unwrap().error(msg),
|
||||
boa_utils::LogMessage::Log(msg) => self.0.log(msg),
|
||||
boa_utils::LogMessage::Info(msg) => self.0.info(msg),
|
||||
boa_utils::LogMessage::Warn(msg) => self.0.warn(msg),
|
||||
boa_utils::LogMessage::Error(msg) => self.0.error(msg),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn take(&mut self) -> Vec<Self::Item> {
|
||||
std::mem::take(&mut self.0)
|
||||
.into_iter()
|
||||
.map(|(span, msg)| match span {
|
||||
LogSpan::Log => boa_utils::LogMessage::Log(msg),
|
||||
LogSpan::Info => boa_utils::LogMessage::Info(msg),
|
||||
LogSpan::Warn => boa_utils::LogMessage::Warn(msg),
|
||||
LogSpan::Error => boa_utils::LogMessage::Error(msg),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl BoaConsoleLogger {
|
||||
pub fn take(&mut self) -> Logs {
|
||||
std::mem::take(&mut self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn take_console_logs() -> Logs {
|
||||
let logs = boa_utils::inspect_logger(|logger| logger.take());
|
||||
logs.into_iter()
|
||||
.map(|msg| match msg {
|
||||
boa_utils::LogMessage::Log(msg) => (LogSpan::Log, msg),
|
||||
boa_utils::LogMessage::Info(msg) => (LogSpan::Info, msg),
|
||||
boa_utils::LogMessage::Warn(msg) => (LogSpan::Warn, msg),
|
||||
boa_utils::LogMessage::Error(msg) => (LogSpan::Error, msg),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub struct JSRunner;
|
||||
@@ -77,9 +109,10 @@ pub struct BoaRunner {
|
||||
|
||||
impl BoaRunner {
|
||||
pub fn try_new() -> Result<Self> {
|
||||
let cache_dir = crate::utils::dirs::cache_dir().unwrap();
|
||||
let loader = Rc::new(CombineModuleLoader::new(
|
||||
SimpleModuleLoader::new(CUSTOM_SCRIPTS_DIR.as_path())?,
|
||||
HttpModuleLoader::default(),
|
||||
HttpModuleLoader::new(cache_dir, Duration::from_secs(60 * 60 * 24 * 30)),
|
||||
));
|
||||
let simple_loader = loader.clone_simple();
|
||||
let queue = Rc::new(Queue::default());
|
||||
@@ -96,7 +129,7 @@ impl BoaRunner {
|
||||
pub fn setup_console(&self, logger: BoaConsoleLogger) -> Result<()> {
|
||||
let ctx = &mut self.ctx.borrow_mut();
|
||||
// it not concurrency safe. we should move to new boa_runtime console when it is ready for custom logger
|
||||
boa_utils::set_logger(Arc::new(logger));
|
||||
boa_utils::set_logger(Box::new(logger) as Box<dyn boa_utils::LoggerBox>);
|
||||
let console = Console::init(ctx);
|
||||
ctx.register_global_property(js_string!(Console::NAME), console, Attribute::all())?;
|
||||
Ok(())
|
||||
@@ -180,14 +213,13 @@ impl Runner for JSRunner {
|
||||
// boa engine is single-thread runner so that we can use it in tokio::task::spawn_blocking
|
||||
let res = tokio::task::spawn_blocking(move || {
|
||||
let wrapped_fn = move || {
|
||||
let logs = Arc::new(Mutex::new(Some(Logs::new())));
|
||||
let logger = BoaConsoleLogger(logs.clone());
|
||||
let boa_runner = wrap_result!(BoaRunner::try_new(), take_logs(logs));
|
||||
wrap_result!(boa_runner.setup_console(logger), take_logs(logs));
|
||||
let mut logger = BoaConsoleLogger(Logs::new());
|
||||
let boa_runner = wrap_result!(BoaRunner::try_new(), logger.take());
|
||||
wrap_result!(boa_runner.setup_console(logger), take_console_logs());
|
||||
let config = wrap_result!(
|
||||
serde_json::to_string(&mapping)
|
||||
.map_err(|e| { std::io::Error::new(std::io::ErrorKind::InvalidData, e) }),
|
||||
take_logs(logs)
|
||||
take_console_logs()
|
||||
);
|
||||
let config = serde_json::to_string(&config).unwrap(); // escape the string
|
||||
let execute_module = format!(
|
||||
@@ -206,28 +238,28 @@ impl Runner for JSRunner {
|
||||
// wrap_result!(boa_runner.execute_module(&process_module));
|
||||
let main_module = wrap_result!(
|
||||
boa_runner.parse_module(&execute_module, "main"),
|
||||
take_logs(logs)
|
||||
take_console_logs()
|
||||
);
|
||||
wrap_result!(boa_runner.execute_module(&main_module));
|
||||
let ctx = boa_runner.get_ctx();
|
||||
let namespace = main_module.namespace(&mut ctx.borrow_mut());
|
||||
let result = wrap_result!(
|
||||
namespace.get(js_string!("result"), &mut ctx.borrow_mut()),
|
||||
take_logs(logs)
|
||||
take_console_logs()
|
||||
);
|
||||
let mut result = wrap_result!(
|
||||
let result = wrap_result!(
|
||||
result
|
||||
.as_string()
|
||||
.ok_or_else(|| JsNativeError::typ().with_message("Expected string"))
|
||||
.map(|str| str.to_std_string_escaped()),
|
||||
take_logs(logs)
|
||||
take_console_logs()
|
||||
);
|
||||
let mapping = wrap_result!(
|
||||
serde_json::from_str(&result)
|
||||
.map_err(|e| { std::io::Error::new(std::io::ErrorKind::InvalidData, e) }),
|
||||
take_logs(logs)
|
||||
take_console_logs()
|
||||
);
|
||||
(Ok::<Mapping, JsRunnerError>(mapping), take_logs(logs))
|
||||
(Ok::<Mapping, JsRunnerError>(mapping), take_console_logs())
|
||||
};
|
||||
let (res, logs) = wrapped_fn();
|
||||
match res {
|
||||
@@ -517,7 +549,9 @@ const foreignNameservers = [
|
||||
serde_yaml::Value::Sequence(vec![
|
||||
serde_yaml::Value::String("RULE-SET,custom-reject,REJECT".to_string()),
|
||||
serde_yaml::Value::String("RULE-SET,custom-direct,DIRECT".to_string()),
|
||||
serde_yaml::Value::String("RULE-SET,custom-proxy,🚀".to_string())
|
||||
serde_yaml::Value::String("RULE-SET,custom-proxy,🚀".to_string()),
|
||||
serde_yaml::Value::String("aGVsbG8=".to_string()),
|
||||
serde_yaml::Value::String("d29ybGQ=".to_string()),
|
||||
])
|
||||
);
|
||||
let outs = serde_json::to_string(&logs).unwrap();
|
||||
|
@@ -324,6 +324,7 @@ pub fn check_core_permission(core: &nyanpasu_utils::core::CoreType) -> anyhow::R
|
||||
|
||||
mod test {
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_dir_placeholder() {
|
||||
let placeholder = super::APP_DIR_PLACEHOLDER.clone();
|
||||
if cfg!(windows) {
|
||||
|
@@ -53,12 +53,12 @@
|
||||
"@csstools/normalize.css": "12.1.1",
|
||||
"@emotion/babel-plugin": "11.13.5",
|
||||
"@emotion/react": "11.14.0",
|
||||
"@iconify/json": "2.2.311",
|
||||
"@iconify/json": "2.2.312",
|
||||
"@monaco-editor/react": "4.7.0",
|
||||
"@tanstack/react-query": "5.66.11",
|
||||
"@tanstack/react-router": "1.112.0",
|
||||
"@tanstack/router-devtools": "1.112.0",
|
||||
"@tanstack/router-plugin": "1.112.0",
|
||||
"@tanstack/router-devtools": "1.112.6",
|
||||
"@tanstack/router-plugin": "1.112.3",
|
||||
"@tauri-apps/plugin-clipboard-manager": "2.2.1",
|
||||
"@tauri-apps/plugin-dialog": "2.2.0",
|
||||
"@tauri-apps/plugin-fs": "2.2.0",
|
||||
@@ -75,7 +75,7 @@
|
||||
"@vitejs/plugin-react-swc": "3.8.0",
|
||||
"change-case": "5.4.4",
|
||||
"clsx": "2.1.1",
|
||||
"core-js": "3.40.0",
|
||||
"core-js": "3.41.0",
|
||||
"filesize": "10.1.6",
|
||||
"meta-json-schema": "1.19.1",
|
||||
"monaco-yaml": "5.3.1",
|
||||
|
@@ -46,6 +46,6 @@
|
||||
"sass-embedded": "1.85.1",
|
||||
"tailwind-merge": "3.0.2",
|
||||
"typescript-plugin-css-modules": "5.1.0",
|
||||
"vite-plugin-dts": "4.5.1"
|
||||
"vite-plugin-dts": "4.5.3"
|
||||
}
|
||||
}
|
||||
|
@@ -2,7 +2,7 @@
|
||||
"manifest_version": 1,
|
||||
"latest": {
|
||||
"mihomo": "v1.19.2",
|
||||
"mihomo_alpha": "alpha-05e8f13",
|
||||
"mihomo_alpha": "alpha-a7e56f1",
|
||||
"clash_rs": "v0.7.6",
|
||||
"clash_premium": "2023-09-05-gdcc8d87",
|
||||
"clash_rs_alpha": "0.7.6-alpha+sha.14e7d32"
|
||||
@@ -69,5 +69,5 @@
|
||||
"linux-armv7hf": "clash-armv7-unknown-linux-gnueabihf"
|
||||
}
|
||||
},
|
||||
"updated_at": "2025-02-28T22:20:51.050Z"
|
||||
"updated_at": "2025-03-02T22:21:03.218Z"
|
||||
}
|
||||
|
86
clash-nyanpasu/pnpm-lock.yaml
generated
86
clash-nyanpasu/pnpm-lock.yaml
generated
@@ -333,8 +333,8 @@ importers:
|
||||
specifier: 11.14.0
|
||||
version: 11.14.0(@types/react@19.0.10)(react@19.0.0)
|
||||
'@iconify/json':
|
||||
specifier: 2.2.311
|
||||
version: 2.2.311
|
||||
specifier: 2.2.312
|
||||
version: 2.2.312
|
||||
'@monaco-editor/react':
|
||||
specifier: 4.7.0
|
||||
version: 4.7.0(monaco-editor@0.52.2)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
@@ -345,11 +345,11 @@ importers:
|
||||
specifier: 1.112.0
|
||||
version: 1.112.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@tanstack/router-devtools':
|
||||
specifier: 1.112.0
|
||||
version: 1.112.0(@tanstack/react-router@1.112.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(csstype@3.1.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
specifier: 1.112.6
|
||||
version: 1.112.6(@tanstack/react-router@1.112.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(csstype@3.1.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@tanstack/router-plugin':
|
||||
specifier: 1.112.0
|
||||
version: 1.112.0(@tanstack/react-router@1.112.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(vite@6.2.0(@types/node@22.13.8)(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.112.3
|
||||
version: 1.112.3(@tanstack/react-router@1.112.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(vite@6.2.0(@types/node@22.13.8)(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
|
||||
@@ -399,8 +399,8 @@ importers:
|
||||
specifier: 2.1.1
|
||||
version: 2.1.1
|
||||
core-js:
|
||||
specifier: 3.40.0
|
||||
version: 3.40.0
|
||||
specifier: 3.41.0
|
||||
version: 3.41.0
|
||||
filesize:
|
||||
specifier: 10.1.6
|
||||
version: 10.1.6
|
||||
@@ -535,8 +535,8 @@ importers:
|
||||
specifier: 5.1.0
|
||||
version: 5.1.0(typescript@5.8.2)
|
||||
vite-plugin-dts:
|
||||
specifier: 4.5.1
|
||||
version: 4.5.1(@types/node@22.13.8)(rollup@4.34.3)(typescript@5.8.2)(vite@6.2.0(@types/node@22.13.8)(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: 4.5.3
|
||||
version: 4.5.3(@types/node@22.13.8)(rollup@4.34.3)(typescript@5.8.2)(vite@6.2.0(@types/node@22.13.8)(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))
|
||||
|
||||
scripts:
|
||||
dependencies:
|
||||
@@ -605,8 +605,8 @@ importers:
|
||||
specifier: 2.26.22
|
||||
version: 2.26.22
|
||||
undici:
|
||||
specifier: 7.3.0
|
||||
version: 7.3.0
|
||||
specifier: 7.4.0
|
||||
version: 7.4.0
|
||||
yargs:
|
||||
specifier: 17.7.2
|
||||
version: 17.7.2
|
||||
@@ -1666,8 +1666,8 @@ packages:
|
||||
'@vue/compiler-sfc':
|
||||
optional: true
|
||||
|
||||
'@iconify/json@2.2.311':
|
||||
resolution: {integrity: sha512-Qt9Q9MuyEfKpd+3027fzbCMhEkhNpUTc9mnUQImbVYr5BiyFxgAxwGcTl+oD6WEblzdnGU3KIXqH/Laekud35w==}
|
||||
'@iconify/json@2.2.312':
|
||||
resolution: {integrity: sha512-sujKsprCACPMVZHeNaxvc7HyONW916tUVw4zfEK+GmT7PkH73PdplmW1w2UWC07IC6oUYPZ2EE0pfdWHhWHjpQ==}
|
||||
|
||||
'@iconify/types@2.0.0':
|
||||
resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
|
||||
@@ -2768,8 +2768,8 @@ packages:
|
||||
resolution: {integrity: sha512-kmpMiBuz17Hxyl+ZO+B6/F98p07NSEmgr2JlZkKXcdupLIBAWqcXw+bjowFXNcTEwe9RWsS/WjAC/bBTftr0rA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
'@tanstack/router-devtools@1.112.0':
|
||||
resolution: {integrity: sha512-kbCvkY8y6vDnF09At9Q9q0yitXBFpO/BEuvMtLTkAKWwoZqPoEp7981AiWZeN62sphtdREn/1sTxvMNY8bBW2Q==}
|
||||
'@tanstack/router-devtools@1.112.6':
|
||||
resolution: {integrity: sha512-OhLZsDnrItA+8BiVdmyyWB2VgQyoCZAjSRshQJpbZdeAV69OvT2rqN2TtiLJsbSAJWmqL4/UcY/13DI9Iv+k3Q==}
|
||||
engines: {node: '>=12'}
|
||||
peerDependencies:
|
||||
'@tanstack/react-router': ^1.112.0
|
||||
@@ -2780,8 +2780,8 @@ packages:
|
||||
csstype:
|
||||
optional: true
|
||||
|
||||
'@tanstack/router-generator@1.112.0':
|
||||
resolution: {integrity: sha512-c1wA2TMfmL1igw6OFKdOZVrFqAJ/PB3ZJE0+upofmwVydUMH7tipvmztWGiRmcxGd66sl6o1l1X39308ObwAGQ==}
|
||||
'@tanstack/router-generator@1.112.3':
|
||||
resolution: {integrity: sha512-RUT+O/j7YIjbemVJjkP4qM8MYaaOltKYhyp9VGtcWxWGS8U2QDwC9UsskjBVOj7QV7aq3UcnExicABwK/AMCCQ==}
|
||||
engines: {node: '>=12'}
|
||||
peerDependencies:
|
||||
'@tanstack/react-router': ^1.112.0
|
||||
@@ -2789,8 +2789,8 @@ packages:
|
||||
'@tanstack/react-router':
|
||||
optional: true
|
||||
|
||||
'@tanstack/router-plugin@1.112.0':
|
||||
resolution: {integrity: sha512-0ZFbHqAHtvbJzdDlIuxuJiOcbR5oue9IVA5OR93gS7sPCueZI1uZQB/hdtC+kcpdnYQKDcxjeNranhqbcaZZnQ==}
|
||||
'@tanstack/router-plugin@1.112.3':
|
||||
resolution: {integrity: sha512-XhKXFoJ7eajqghAPwHXfggyB8khopr5yVXiYQRiL+9Gek2q5M8N4z9+Uh2MM31KjTuiaJ72lZpUgT5FDj1m6Tg==}
|
||||
engines: {node: '>=12'}
|
||||
peerDependencies:
|
||||
'@rsbuild/core': '>=1.0.2'
|
||||
@@ -3288,8 +3288,8 @@ packages:
|
||||
'@vue/compiler-vue2@2.7.16':
|
||||
resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==}
|
||||
|
||||
'@vue/language-core@2.2.4':
|
||||
resolution: {integrity: sha512-eGGdw7eWUwdIn9Fy/irJ7uavCGfgemuHQABgJ/hU1UgZFnbTg9VWeXvHQdhY+2SPQZWJqWXvRWIg67t4iWEa+Q==}
|
||||
'@vue/language-core@2.2.0':
|
||||
resolution: {integrity: sha512-O1ZZFaaBGkKbsRfnVH1ifOK1/1BUkyK+3SQsfnh6PmMmD4qJcTU8godCeA96jjDRTL6zgnK7YzCHfaUlH2r0Mw==}
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
peerDependenciesMeta:
|
||||
@@ -3362,8 +3362,8 @@ packages:
|
||||
ajv@8.17.1:
|
||||
resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==}
|
||||
|
||||
alien-signals@1.0.4:
|
||||
resolution: {integrity: sha512-DJqqQD3XcsaQcQ1s+iE2jDUZmmQpXwHiR6fCAim/w87luaW+vmLY8fMlrdkmRwzaFXhkxf3rqPCR59tKVv1MDw==}
|
||||
alien-signals@0.4.14:
|
||||
resolution: {integrity: sha512-itUAVzhczTmP2U5yX67xVpsbbOiquusbWVyA9N+sy6+r6YVbFkahXvNCeEPWEOMhwDYwbVbGHFkVL03N9I5g+Q==}
|
||||
|
||||
allotment@1.20.3:
|
||||
resolution: {integrity: sha512-JCnklt7j0OsyDjD7A9AdT6wqJ3FSoo1ASV6w02Am02lo6NwO25yhG1DcWW8ueBV38ppXQmvrXBXuzX7iVkq6Tw==}
|
||||
@@ -3842,8 +3842,8 @@ packages:
|
||||
core-js-compat@3.40.0:
|
||||
resolution: {integrity: sha512-0XEDpr5y5mijvw8Lbc6E5AkjrHfp7eEoPlu36SWeAbcL8fn1G1ANe8DBlo2XoNN89oVpxWwOjYIPVzR4ZvsKCQ==}
|
||||
|
||||
core-js@3.40.0:
|
||||
resolution: {integrity: sha512-7vsMc/Lty6AGnn7uFpYT56QesI5D2Y/UkgKounk87OP9Z2H9Z8kj6jzcSGAxFmUtDOS0ntK6lbQz+Nsa0Jj6mQ==}
|
||||
core-js@3.41.0:
|
||||
resolution: {integrity: sha512-SJ4/EHwS36QMJd6h/Rg+GyR4A5xE0FSI3eZ+iBVpfqf1x0eTSg1smWLHrA+2jQThZSh97fmSgFSU8B61nxosxA==}
|
||||
|
||||
cosmiconfig-typescript-loader@6.1.0:
|
||||
resolution: {integrity: sha512-tJ1w35ZRUiM5FeTzT7DtYWAFFv37ZLqSRkGi2oeCK1gPhvaWjkAtfXvLmvE1pRfxxp9aQo6ba/Pvg1dKj05D4g==}
|
||||
@@ -7672,8 +7672,8 @@ packages:
|
||||
resolution: {integrity: sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==}
|
||||
engines: {node: '>=14.0'}
|
||||
|
||||
undici@7.3.0:
|
||||
resolution: {integrity: sha512-Qy96NND4Dou5jKoSJ2gm8ax8AJM/Ey9o9mz7KN1bb9GP+G0l20Zw8afxTnY2f4b7hmhn/z8aC2kfArVQlAhFBw==}
|
||||
undici@7.4.0:
|
||||
resolution: {integrity: sha512-PUQM3/es3noM24oUn10u3kNNap0AbxESOmnssmW+dOi9yGwlUSi5nTNYl3bNbTkWOF8YZDkx2tCmj9OtQ3iGGw==}
|
||||
engines: {node: '>=20.18.1'}
|
||||
|
||||
unicode-canonical-property-names-ecmascript@2.0.1:
|
||||
@@ -7868,8 +7868,8 @@ packages:
|
||||
engines: {node: ^18.19.0 || >=20.6.0}
|
||||
hasBin: true
|
||||
|
||||
vite-plugin-dts@4.5.1:
|
||||
resolution: {integrity: sha512-Yo1dHT05B2nD47AVB7b0+wK1FPFpJJnUf/muRF7+tP+sbPFRhLs70TTRGwJw7NDBwAUAmSwhrD+ZPTe4P6Wv9w==}
|
||||
vite-plugin-dts@4.5.3:
|
||||
resolution: {integrity: sha512-P64VnD00dR+e8S26ESoFELqc17+w7pKkwlBpgXteOljFyT0zDwD8hH4zXp49M/kciy//7ZbVXIwQCekBJjfWzA==}
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
vite: '*'
|
||||
@@ -9426,7 +9426,7 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@iconify/json@2.2.311':
|
||||
'@iconify/json@2.2.312':
|
||||
dependencies:
|
||||
'@iconify/types': 2.0.0
|
||||
pathe: 1.1.2
|
||||
@@ -10500,7 +10500,7 @@ snapshots:
|
||||
'@tanstack/history': 1.99.13
|
||||
'@tanstack/store': 0.7.0
|
||||
|
||||
'@tanstack/router-devtools@1.112.0(@tanstack/react-router@1.112.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(csstype@3.1.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||
'@tanstack/router-devtools@1.112.6(@tanstack/react-router@1.112.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(csstype@3.1.3)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||
dependencies:
|
||||
'@tanstack/react-router': 1.112.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
clsx: 2.1.1
|
||||
@@ -10510,7 +10510,7 @@ snapshots:
|
||||
optionalDependencies:
|
||||
csstype: 3.1.3
|
||||
|
||||
'@tanstack/router-generator@1.112.0(@tanstack/react-router@1.112.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))':
|
||||
'@tanstack/router-generator@1.112.3(@tanstack/react-router@1.112.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))':
|
||||
dependencies:
|
||||
'@tanstack/virtual-file-routes': 1.99.0
|
||||
prettier: 3.5.2
|
||||
@@ -10519,7 +10519,7 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@tanstack/react-router': 1.112.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
|
||||
'@tanstack/router-plugin@1.112.0(@tanstack/react-router@1.112.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(vite@6.2.0(@types/node@22.13.8)(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.112.3(@tanstack/react-router@1.112.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(vite@6.2.0(@types/node@22.13.8)(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)
|
||||
@@ -10528,7 +10528,7 @@ snapshots:
|
||||
'@babel/traverse': 7.26.9
|
||||
'@babel/types': 7.26.9
|
||||
'@tanstack/router-core': 1.112.0
|
||||
'@tanstack/router-generator': 1.112.0(@tanstack/react-router@1.112.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))
|
||||
'@tanstack/router-generator': 1.112.3(@tanstack/react-router@1.112.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))
|
||||
'@tanstack/router-utils': 1.102.2
|
||||
'@tanstack/virtual-file-routes': 1.99.0
|
||||
'@types/babel__core': 7.20.5
|
||||
@@ -11051,7 +11051,7 @@ snapshots:
|
||||
'@babel/preset-env': 7.26.9(@babel/core@7.26.9)
|
||||
browserslist: 4.24.4
|
||||
browserslist-to-esbuild: 2.1.1(browserslist@4.24.4)
|
||||
core-js: 3.40.0
|
||||
core-js: 3.41.0
|
||||
magic-string: 0.30.17
|
||||
regenerator-runtime: 0.14.1
|
||||
systemjs: 6.15.1
|
||||
@@ -11108,13 +11108,13 @@ snapshots:
|
||||
de-indent: 1.0.2
|
||||
he: 1.2.0
|
||||
|
||||
'@vue/language-core@2.2.4(typescript@5.8.2)':
|
||||
'@vue/language-core@2.2.0(typescript@5.8.2)':
|
||||
dependencies:
|
||||
'@volar/language-core': 2.4.11
|
||||
'@vue/compiler-dom': 3.5.13
|
||||
'@vue/compiler-vue2': 2.7.16
|
||||
'@vue/shared': 3.5.13
|
||||
alien-signals: 1.0.4
|
||||
alien-signals: 0.4.14
|
||||
minimatch: 9.0.5
|
||||
muggle-string: 0.4.1
|
||||
path-browserify: 1.0.1
|
||||
@@ -11198,7 +11198,7 @@ snapshots:
|
||||
json-schema-traverse: 1.0.0
|
||||
require-from-string: 2.0.2
|
||||
|
||||
alien-signals@1.0.4: {}
|
||||
alien-signals@0.4.14: {}
|
||||
|
||||
allotment@1.20.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
|
||||
dependencies:
|
||||
@@ -11715,7 +11715,7 @@ snapshots:
|
||||
dependencies:
|
||||
browserslist: 4.24.4
|
||||
|
||||
core-js@3.40.0: {}
|
||||
core-js@3.41.0: {}
|
||||
|
||||
cosmiconfig-typescript-loader@6.1.0(@types/node@22.13.8)(cosmiconfig@9.0.0(typescript@5.8.2))(typescript@5.8.2):
|
||||
dependencies:
|
||||
@@ -16010,7 +16010,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@fastify/busboy': 2.1.1
|
||||
|
||||
undici@7.3.0: {}
|
||||
undici@7.4.0: {}
|
||||
|
||||
unicode-canonical-property-names-ecmascript@2.0.1: {}
|
||||
|
||||
@@ -16206,12 +16206,12 @@ snapshots:
|
||||
- rollup
|
||||
- supports-color
|
||||
|
||||
vite-plugin-dts@4.5.1(@types/node@22.13.8)(rollup@4.34.3)(typescript@5.8.2)(vite@6.2.0(@types/node@22.13.8)(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)):
|
||||
vite-plugin-dts@4.5.3(@types/node@22.13.8)(rollup@4.34.3)(typescript@5.8.2)(vite@6.2.0(@types/node@22.13.8)(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:
|
||||
'@microsoft/api-extractor': 7.51.0(@types/node@22.13.8)
|
||||
'@rollup/pluginutils': 5.1.4(rollup@4.34.3)
|
||||
'@volar/typescript': 2.4.11
|
||||
'@vue/language-core': 2.2.4(typescript@5.8.2)
|
||||
'@vue/language-core': 2.2.0(typescript@5.8.2)
|
||||
compare-versions: 6.1.1
|
||||
debug: 4.4.0
|
||||
kolorist: 1.8.0
|
||||
|
2
clash-nyanpasu/scripts/.gitignore
vendored
Normal file
2
clash-nyanpasu/scripts/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
!.vscode/settings.json
|
||||
!.vscode/
|
4
clash-nyanpasu/scripts/.vscode/settings.json
vendored
Normal file
4
clash-nyanpasu/scripts/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"deno.enable": true,
|
||||
"deno.enablePaths": ["./deno"]
|
||||
}
|
3
clash-nyanpasu/scripts/deno/README.md
Normal file
3
clash-nyanpasu/scripts/deno/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Deno scripts
|
||||
|
||||
When we migrated all the scripts to Deno, let's move them to outer directory.
|
8
clash-nyanpasu/scripts/deno/deno.jsonc
Normal file
8
clash-nyanpasu/scripts/deno/deno.jsonc
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"tasks": {
|
||||
"upload-macos-updater": {
|
||||
"description": "Upload macOS updater to GitHub Releases",
|
||||
"command": "deno run -A upload-macos-updater.ts",
|
||||
},
|
||||
},
|
||||
}
|
146
clash-nyanpasu/scripts/deno/deno.lock
generated
Normal file
146
clash-nyanpasu/scripts/deno/deno.lock
generated
Normal file
@@ -0,0 +1,146 @@
|
||||
{
|
||||
"version": "4",
|
||||
"specifiers": {
|
||||
"jsr:@std/path@*": "1.0.8",
|
||||
"npm:colorize-template@*": "1.0.0",
|
||||
"npm:consola@*": "3.4.0",
|
||||
"npm:globby@*": "14.1.0",
|
||||
"npm:picocolors@*": "1.1.1"
|
||||
},
|
||||
"jsr": {
|
||||
"@std/path@1.0.8": {
|
||||
"integrity": "548fa456bb6a04d3c1a1e7477986b6cffbce95102d0bb447c67c4ee70e0364be"
|
||||
}
|
||||
},
|
||||
"npm": {
|
||||
"@nodelib/fs.scandir@2.1.5": {
|
||||
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
|
||||
"dependencies": [
|
||||
"@nodelib/fs.stat",
|
||||
"run-parallel"
|
||||
]
|
||||
},
|
||||
"@nodelib/fs.stat@2.0.5": {
|
||||
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="
|
||||
},
|
||||
"@nodelib/fs.walk@1.2.8": {
|
||||
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
|
||||
"dependencies": [
|
||||
"@nodelib/fs.scandir",
|
||||
"fastq"
|
||||
]
|
||||
},
|
||||
"@sindresorhus/merge-streams@2.3.0": {
|
||||
"integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg=="
|
||||
},
|
||||
"braces@3.0.3": {
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"dependencies": [
|
||||
"fill-range"
|
||||
]
|
||||
},
|
||||
"colorize-template@1.0.0": {
|
||||
"integrity": "sha512-beJ9v9RjpbYZ8OdwJgIRZD3YUkZPXmi1MK+yX0J24UupKVHa9yk0jiARgt2i6MBX6AKjYA0SNsBn65bUPuVQiw=="
|
||||
},
|
||||
"consola@3.4.0": {
|
||||
"integrity": "sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA=="
|
||||
},
|
||||
"fast-glob@3.3.3": {
|
||||
"integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
|
||||
"dependencies": [
|
||||
"@nodelib/fs.stat",
|
||||
"@nodelib/fs.walk",
|
||||
"glob-parent",
|
||||
"merge2",
|
||||
"micromatch"
|
||||
]
|
||||
},
|
||||
"fastq@1.19.1": {
|
||||
"integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
|
||||
"dependencies": [
|
||||
"reusify"
|
||||
]
|
||||
},
|
||||
"fill-range@7.1.1": {
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"dependencies": [
|
||||
"to-regex-range"
|
||||
]
|
||||
},
|
||||
"glob-parent@5.1.2": {
|
||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||
"dependencies": [
|
||||
"is-glob"
|
||||
]
|
||||
},
|
||||
"globby@14.1.0": {
|
||||
"integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==",
|
||||
"dependencies": [
|
||||
"@sindresorhus/merge-streams",
|
||||
"fast-glob",
|
||||
"ignore",
|
||||
"path-type",
|
||||
"slash",
|
||||
"unicorn-magic"
|
||||
]
|
||||
},
|
||||
"ignore@7.0.3": {
|
||||
"integrity": "sha512-bAH5jbK/F3T3Jls4I0SO1hmPR0dKU0a7+SY6n1yzRtG54FLO8d6w/nxLFX2Nb7dBu6cCWXPaAME6cYqFUMmuCA=="
|
||||
},
|
||||
"is-extglob@2.1.1": {
|
||||
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="
|
||||
},
|
||||
"is-glob@4.0.3": {
|
||||
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
||||
"dependencies": [
|
||||
"is-extglob"
|
||||
]
|
||||
},
|
||||
"is-number@7.0.0": {
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
|
||||
},
|
||||
"merge2@1.4.1": {
|
||||
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="
|
||||
},
|
||||
"micromatch@4.0.8": {
|
||||
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
|
||||
"dependencies": [
|
||||
"braces",
|
||||
"picomatch"
|
||||
]
|
||||
},
|
||||
"path-type@6.0.0": {
|
||||
"integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ=="
|
||||
},
|
||||
"picocolors@1.1.1": {
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
|
||||
},
|
||||
"picomatch@2.3.1": {
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
|
||||
},
|
||||
"queue-microtask@1.2.3": {
|
||||
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="
|
||||
},
|
||||
"reusify@1.1.0": {
|
||||
"integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="
|
||||
},
|
||||
"run-parallel@1.2.0": {
|
||||
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
|
||||
"dependencies": [
|
||||
"queue-microtask"
|
||||
]
|
||||
},
|
||||
"slash@5.1.0": {
|
||||
"integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg=="
|
||||
},
|
||||
"to-regex-range@5.0.1": {
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"dependencies": [
|
||||
"is-number"
|
||||
]
|
||||
},
|
||||
"unicorn-magic@0.3.0": {
|
||||
"integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA=="
|
||||
}
|
||||
}
|
||||
}
|
58
clash-nyanpasu/scripts/deno/upload-macos-updater.ts
Normal file
58
clash-nyanpasu/scripts/deno/upload-macos-updater.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import * as path from 'jsr:@std/path'
|
||||
import { globby } from 'npm:globby'
|
||||
import { consola } from './utils/logger.ts'
|
||||
|
||||
const WORKSPACE_ROOT = path.join(Deno.cwd(), '../..')
|
||||
consola.info(`WORKSPACE_ROOT: ${WORKSPACE_ROOT}`)
|
||||
|
||||
const GITHUB_TOKEN = Deno.env.get('GITHUB_TOKEN') || Deno.env.get('GH_TOKEN')
|
||||
const GITHUB_TAG = Deno.env.get('GITHUB_TAG')
|
||||
const TARGET_ARCH = Deno.env.get('TARGET_ARCH') || Deno.build.arch
|
||||
|
||||
if (!GITHUB_TOKEN) {
|
||||
consola.fatal('GITHUB_TOKEN is not set')
|
||||
Deno.exit(1)
|
||||
}
|
||||
|
||||
if (!GITHUB_TAG) {
|
||||
consola.fatal('GITHUB_TAG is not set')
|
||||
Deno.exit(1)
|
||||
}
|
||||
|
||||
const BACKEND_BUILD_DIR = path.join(WORKSPACE_ROOT, 'backend/target')
|
||||
|
||||
const files = await globby(['**/*.tar.gz', '**/*.sig', '**/*.dmg'], {
|
||||
cwd: BACKEND_BUILD_DIR,
|
||||
})
|
||||
|
||||
for (let file of files) {
|
||||
file = path.join(BACKEND_BUILD_DIR, file)
|
||||
const p = path.parse(file)
|
||||
consola.info(`Found file: ${p.base}`)
|
||||
if (p.base.endsWith('.app.tar.gz')) {
|
||||
const newName = p.name.split('.')[0] + `.${TARGET_ARCH}.app.tar.gz`
|
||||
const newPath = path.join(p.dir, newName)
|
||||
consola.info(`Renaming ${file} to ${newPath}`)
|
||||
await Deno.rename(file, newPath)
|
||||
file = newPath
|
||||
}
|
||||
consola.info(`Uploading ${file}...`)
|
||||
const cmd = new Deno.Command('gh', {
|
||||
args: ['release', 'upload', GITHUB_TAG, file, '--clobber'],
|
||||
stdout: 'piped',
|
||||
stderr: 'piped',
|
||||
env: {
|
||||
GH_TOKEN: GITHUB_TOKEN,
|
||||
GITHUB_TOKEN,
|
||||
},
|
||||
})
|
||||
|
||||
const output = await cmd.output()
|
||||
if (output.code !== 0) {
|
||||
consola.error(output.stderr)
|
||||
consola.error(`Failed to upload ${file}`)
|
||||
Deno.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
consola.success('Uploaded all files')
|
21
clash-nyanpasu/scripts/deno/utils/logger.ts
Normal file
21
clash-nyanpasu/scripts/deno/utils/logger.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { createColorize } from 'npm:colorize-template'
|
||||
import { createConsola } from 'npm:consola'
|
||||
import pc from 'npm:picocolors'
|
||||
|
||||
const logLevelStr = Deno.env.get('LOG_LEVEL')
|
||||
|
||||
export const consola = createConsola({
|
||||
level: logLevelStr ? Number.parseInt(logLevelStr) : 5,
|
||||
fancy: true,
|
||||
formatOptions: {
|
||||
colors: true,
|
||||
compact: false,
|
||||
date: true,
|
||||
},
|
||||
})
|
||||
|
||||
export const colorize = createColorize({
|
||||
...pc,
|
||||
success: pc.green,
|
||||
error: pc.red,
|
||||
})
|
@@ -26,7 +26,7 @@
|
||||
"picocolors": "1.1.1",
|
||||
"tar": "7.4.3",
|
||||
"telegram": "2.26.22",
|
||||
"undici": "7.3.0",
|
||||
"undici": "7.4.0",
|
||||
"yargs": "17.7.2"
|
||||
}
|
||||
}
|
||||
|
@@ -17,4 +17,5 @@
|
||||
"composite": true,
|
||||
},
|
||||
"include": ["./"],
|
||||
"exclude": ["deno"],
|
||||
}
|
||||
|
134
clash-verge-rev/src-tauri/Cargo.lock
generated
134
clash-verge-rev/src-tauri/Cargo.lock
generated
@@ -116,6 +116,56 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.95"
|
||||
@@ -191,6 +241,16 @@ dependencies = [
|
||||
"zbus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "assert-json-diff"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-broadcast"
|
||||
version = "0.7.2"
|
||||
@@ -1003,6 +1063,7 @@ version = "2.1.2"
|
||||
dependencies = [
|
||||
"aes-gcm",
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"base64 0.22.1",
|
||||
"boa_engine",
|
||||
"chrono",
|
||||
@@ -1010,12 +1071,14 @@ dependencies = [
|
||||
"delay_timer",
|
||||
"dirs 6.0.0",
|
||||
"dunce",
|
||||
"env_logger",
|
||||
"futures",
|
||||
"getrandom 0.2.15",
|
||||
"image 0.24.9",
|
||||
"imageproc",
|
||||
"log",
|
||||
"log4rs",
|
||||
"mockito",
|
||||
"nanoid",
|
||||
"network-interface",
|
||||
"once_cell",
|
||||
@@ -1047,6 +1110,7 @@ dependencies = [
|
||||
"tauri-plugin-shell",
|
||||
"tauri-plugin-updater",
|
||||
"tauri-plugin-window-state",
|
||||
"tempfile",
|
||||
"tokio",
|
||||
"tokio-tungstenite 0.26.1",
|
||||
"url",
|
||||
@@ -1132,6 +1196,12 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
version = "2.2.0"
|
||||
@@ -1927,6 +1997,29 @@ dependencies = [
|
||||
"syn 2.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_filter"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.11.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"env_filter",
|
||||
"humantime",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
@@ -2924,6 +3017,7 @@ dependencies = [
|
||||
"http 1.2.0",
|
||||
"http-body 1.0.1",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa 1.0.14",
|
||||
"pin-project-lite",
|
||||
"smallvec",
|
||||
@@ -3344,6 +3438,12 @@ dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.5"
|
||||
@@ -3845,6 +3945,30 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mockito"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "652cd6d169a36eaf9d1e6bce1a221130439a966d7f27858af66a33a66e9c4ee2"
|
||||
dependencies = [
|
||||
"assert-json-diff",
|
||||
"bytes",
|
||||
"colored",
|
||||
"futures-util",
|
||||
"http 1.2.0",
|
||||
"http-body 1.0.1",
|
||||
"http-body-util",
|
||||
"hyper 1.6.0",
|
||||
"hyper-util",
|
||||
"log",
|
||||
"rand 0.8.5",
|
||||
"regex",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"similar",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "muda"
|
||||
version = "0.15.3"
|
||||
@@ -6161,6 +6285,12 @@ version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||
|
||||
[[package]]
|
||||
name = "similar"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.3.11"
|
||||
@@ -7002,9 +7132,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.16.0"
|
||||
version = "3.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91"
|
||||
checksum = "22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand 2.3.0",
|
||||
|
@@ -67,6 +67,7 @@ getrandom = "0.2"
|
||||
tokio-tungstenite = "0.26.1"
|
||||
futures = "0.3"
|
||||
sys-locale = "0.3.1"
|
||||
async-trait = "0.1.86"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
runas = "=1.2.0"
|
||||
@@ -120,3 +121,8 @@ strip = false # 不剥离符号,保留调试信息
|
||||
[lib]
|
||||
name = "app_lib"
|
||||
crate-type = ["staticlib", "cdylib", "rlib"]
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.11.0"
|
||||
mockito = "1.2.0"
|
||||
tempfile = "3.17.1"
|
||||
|
@@ -14,6 +14,8 @@ pub mod clash;
|
||||
pub mod verge;
|
||||
pub mod runtime;
|
||||
pub mod save_profile;
|
||||
pub mod system;
|
||||
pub mod proxy;
|
||||
|
||||
// Re-export all command functions for backwards compatibility
|
||||
pub use profile::*;
|
||||
@@ -26,3 +28,5 @@ pub use clash::*;
|
||||
pub use verge::*;
|
||||
pub use runtime::*;
|
||||
pub use save_profile::*;
|
||||
pub use system::*;
|
||||
pub use proxy::*;
|
35
clash-verge-rev/src-tauri/src/cmd/proxy.rs
Normal file
35
clash-verge-rev/src-tauri/src/cmd/proxy.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
use super::CmdResult;
|
||||
use crate::module::mihomo::MihomoManager;
|
||||
use tauri::async_runtime;
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_proxies() -> CmdResult<serde_json::Value> {
|
||||
let proxies = async_runtime::spawn_blocking(|| {
|
||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||
let manager = MihomoManager::new();
|
||||
{
|
||||
let mut write_guard = manager.write();
|
||||
rt.block_on(write_guard.refresh_proxies());
|
||||
}
|
||||
let read_guard = manager.read();
|
||||
read_guard.fetch_proxies().clone()
|
||||
})
|
||||
.await.map_err(|e| e.to_string())?;
|
||||
Ok(proxies)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_providers_proxies() -> CmdResult<serde_json::Value> {
|
||||
let providers_proxies = async_runtime::spawn_blocking(|| {
|
||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||
let manager = MihomoManager::new();
|
||||
{
|
||||
let mut write_guard = manager.write();
|
||||
rt.block_on(write_guard.refresh_providers_proxies());
|
||||
}
|
||||
let read_guard = manager.read();
|
||||
read_guard.fetch_providers_proxies().clone()
|
||||
})
|
||||
.await.map_err(|e| e.to_string())?;
|
||||
Ok(providers_proxies)
|
||||
}
|
34
clash-verge-rev/src-tauri/src/cmd/system.rs
Normal file
34
clash-verge-rev/src-tauri/src/cmd/system.rs
Normal file
@@ -0,0 +1,34 @@
|
||||
use super::CmdResult;
|
||||
use crate::{core::handle, model::sysinfo::PlatformSpecification};
|
||||
use tauri_plugin_clipboard_manager::ClipboardExt;
|
||||
use crate::{core::{self, CoreManager, service}, wrap_err};
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn export_diagnostic_info() -> CmdResult<()> {
|
||||
let sysinfo = PlatformSpecification::new();
|
||||
let info = format!("{:?}", sysinfo);
|
||||
|
||||
let app_handle = handle::Handle::global().app_handle().unwrap();
|
||||
let cliboard = app_handle.clipboard();
|
||||
|
||||
if let Err(_) = cliboard.write_text(info) {
|
||||
log::error!(target: "app", "Failed to write to clipboard");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 获取当前内核运行模式
|
||||
#[tauri::command]
|
||||
pub async fn get_running_mode() -> Result<String, String> {
|
||||
match CoreManager::global().get_running_mode().await {
|
||||
core::RunningMode::Service => Ok("service".to_string()),
|
||||
core::RunningMode::Sidecar => Ok("sidecar".to_string()),
|
||||
core::RunningMode::NotRunning => Ok("not_running".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
/// 安装/重装系统服务
|
||||
#[tauri::command]
|
||||
pub async fn install_service() -> CmdResult {
|
||||
wrap_err!(service::reinstall_service().await)
|
||||
}
|
1
clash-verge-rev/src-tauri/src/config/api/mihomo.rs
Normal file
1
clash-verge-rev/src-tauri/src/config/api/mihomo.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub const MIHOMO_URL: &str = concat!("http://", "127.0.0.1", ":", "9097");
|
1
clash-verge-rev/src-tauri/src/config/api/mod.rs
Normal file
1
clash-verge-rev/src-tauri/src/config/api/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod mihomo;
|
@@ -21,3 +21,6 @@ pub const DEFAULT_PAC: &str = r#"function FindProxyForURL(url, host) {
|
||||
return "PROXY 127.0.0.1:%mixed-port%; SOCKS5 127.0.0.1:%mixed-port%; DIRECT;";
|
||||
}
|
||||
"#;
|
||||
|
||||
|
||||
pub mod api;
|
@@ -7,7 +7,7 @@ use crate::utils::{dirs, help};
|
||||
use anyhow::{bail, Result};
|
||||
use once_cell::sync::OnceCell;
|
||||
use serde_yaml::Mapping;
|
||||
use std::{sync::Arc, time::Duration};
|
||||
use std::{sync::Arc, time::Duration, path::PathBuf};
|
||||
use tauri_plugin_shell::ShellExt;
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::time::sleep;
|
||||
@@ -17,6 +17,17 @@ pub struct CoreManager {
|
||||
running: Arc<Mutex<bool>>,
|
||||
}
|
||||
|
||||
/// 内核运行模式
|
||||
#[derive(Debug, Clone, serde::Serialize)]
|
||||
pub enum RunningMode {
|
||||
/// 服务模式运行
|
||||
Service,
|
||||
/// Sidecar模式运行
|
||||
Sidecar,
|
||||
/// 未运行
|
||||
NotRunning,
|
||||
}
|
||||
|
||||
impl CoreManager {
|
||||
pub fn global() -> &'static CoreManager {
|
||||
static CORE_MANAGER: OnceCell<CoreManager> = OnceCell::new();
|
||||
@@ -53,12 +64,37 @@ impl CoreManager {
|
||||
// 服务模式
|
||||
if service::check_service().await.is_ok() {
|
||||
log::info!(target: "app", "stop the core by service");
|
||||
service::stop_core_by_service().await?;
|
||||
match service::stop_core_by_service().await {
|
||||
Ok(_) => {
|
||||
log::info!(target: "app", "core stopped successfully by service");
|
||||
}
|
||||
Err(err) => {
|
||||
log::warn!(target: "app", "failed to stop core by service: {}", err);
|
||||
// 服务停止失败,尝试停止可能的sidecar进程
|
||||
self.stop_sidecar_process();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 如果没有使用服务,尝试停止sidecar进程
|
||||
self.stop_sidecar_process();
|
||||
}
|
||||
|
||||
*running = false;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 停止通过sidecar启动的进程
|
||||
fn stop_sidecar_process(&self) {
|
||||
if let Some(process) = handle::Handle::global().take_core_process() {
|
||||
log::info!(target: "app", "stopping core process in sidecar mode");
|
||||
if let Err(e) = process.kill() {
|
||||
log::warn!(target: "app", "failed to kill core process: {}", e);
|
||||
} else {
|
||||
log::info!(target: "app", "core process stopped successfully");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 启动核心
|
||||
pub async fn start_core(&self) -> Result<()> {
|
||||
let mut running = self.running.lock().await;
|
||||
@@ -69,11 +105,26 @@ impl CoreManager {
|
||||
|
||||
let config_path = Config::generate_file(ConfigType::Run)?;
|
||||
|
||||
// 服务模式
|
||||
// 先尝试服务模式
|
||||
if service::check_service().await.is_ok() {
|
||||
log::info!(target: "app", "try to run core in service mode");
|
||||
service::run_core_by_service(&config_path).await?;
|
||||
match service::run_core_by_service(&config_path).await {
|
||||
Ok(_) => {
|
||||
log::info!(target: "app", "core started successfully in service mode");
|
||||
},
|
||||
Err(err) => {
|
||||
// 服务启动失败,尝试sidecar模式
|
||||
log::warn!(target: "app", "failed to start core in service mode: {}", err);
|
||||
log::info!(target: "app", "trying to run core in sidecar mode");
|
||||
self.run_core_by_sidecar(&config_path).await?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 服务不可用,直接使用sidecar模式
|
||||
log::info!(target: "app", "service not available, running core in sidecar mode");
|
||||
self.run_core_by_sidecar(&config_path).await?;
|
||||
}
|
||||
|
||||
// 流量订阅
|
||||
#[cfg(target_os = "macos")]
|
||||
log_err!(Tray::global().subscribe_traffic().await);
|
||||
@@ -83,11 +134,43 @@ impl CoreManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 通过sidecar启动内核
|
||||
async fn run_core_by_sidecar(&self, config_path: &PathBuf) -> Result<()> {
|
||||
let clash_core = { Config::verge().latest().clash_core.clone() };
|
||||
let clash_core = clash_core.unwrap_or("verge-mihomo".into());
|
||||
|
||||
log::info!(target: "app", "starting core {} in sidecar mode", clash_core);
|
||||
|
||||
let app_handle = handle::Handle::global().app_handle().ok_or(anyhow::anyhow!("failed to get app handle"))?;
|
||||
|
||||
// 获取配置目录
|
||||
let config_dir = dirs::app_home_dir()?;
|
||||
let config_path_str = dirs::path_to_str(config_path)?;
|
||||
|
||||
// 启动核心进程并转入后台运行
|
||||
let (_, child) = app_handle
|
||||
.shell()
|
||||
.sidecar(clash_core)?
|
||||
.args(["-d", dirs::path_to_str(&config_dir)?, "-f", config_path_str])
|
||||
.spawn()?;
|
||||
|
||||
// 保存进程ID以便后续管理
|
||||
handle::Handle::global().set_core_process(child);
|
||||
|
||||
// 等待短暂时间确保启动成功
|
||||
sleep(Duration::from_millis(300)).await;
|
||||
|
||||
log::info!(target: "app", "core started in sidecar mode");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 重启内核
|
||||
pub async fn restart_core(&self) -> Result<()> {
|
||||
// 重新启动app
|
||||
log::info!(target: "app", "restarting core");
|
||||
self.stop_core().await?;
|
||||
self.start_core().await?;
|
||||
log::info!(target: "app", "core restarted successfully");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -139,9 +222,11 @@ impl CoreManager {
|
||||
}
|
||||
Err(err) => {
|
||||
println!("[切换内核] 内核切换失败: {}", err);
|
||||
Config::verge().discard();
|
||||
Config::runtime().discard();
|
||||
Err(err)
|
||||
// 即使使用服务失败,我们也尝试使用sidecar模式启动
|
||||
log::info!(target: "app", "trying sidecar mode after service failure");
|
||||
self.start_core().await?;
|
||||
Config::runtime().apply();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -159,8 +244,10 @@ impl CoreManager {
|
||||
}
|
||||
Err(err) => {
|
||||
println!("[切换内核] 内核切换失败: {}", err);
|
||||
Config::verge().discard();
|
||||
Err(err)
|
||||
// 即使使用服务失败,我们也尝试使用sidecar模式启动
|
||||
log::info!(target: "app", "trying sidecar mode after service failure with default config");
|
||||
self.start_core().await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -495,4 +582,38 @@ impl CoreManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取当前内核运行模式
|
||||
pub async fn get_running_mode(&self) -> RunningMode {
|
||||
let running = self.running.lock().await;
|
||||
if !*running {
|
||||
return RunningMode::NotRunning;
|
||||
}
|
||||
|
||||
// 检查服务状态
|
||||
match service::check_service().await {
|
||||
Ok(_) => {
|
||||
// 检查服务是否实际运行核心
|
||||
match service::is_service_running().await {
|
||||
Ok(true) => RunningMode::Service,
|
||||
_ => {
|
||||
// 服务存在但可能没有运行,检查是否有sidecar进程
|
||||
if handle::Handle::global().has_core_process() {
|
||||
RunningMode::Sidecar
|
||||
} else {
|
||||
RunningMode::NotRunning
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
// 服务不可用,检查是否有sidecar进程
|
||||
if handle::Handle::global().has_core_process() {
|
||||
RunningMode::Sidecar
|
||||
} else {
|
||||
RunningMode::NotRunning
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -3,11 +3,13 @@ use once_cell::sync::OnceCell;
|
||||
use parking_lot::RwLock;
|
||||
use std::sync::Arc;
|
||||
use tauri::{AppHandle, Emitter, Manager, WebviewWindow};
|
||||
use tauri_plugin_shell::process::CommandChild;
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct Handle {
|
||||
pub app_handle: Arc<RwLock<Option<AppHandle>>>,
|
||||
pub is_exiting: Arc<RwLock<bool>>,
|
||||
pub core_process: Arc<RwLock<Option<CommandChild>>>,
|
||||
}
|
||||
|
||||
impl Handle {
|
||||
@@ -17,6 +19,7 @@ impl Handle {
|
||||
HANDLE.get_or_init(|| Handle {
|
||||
app_handle: Arc::new(RwLock::new(None)),
|
||||
is_exiting: Arc::new(RwLock::new(false)),
|
||||
core_process: Arc::new(RwLock::new(None)),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -68,6 +71,21 @@ impl Handle {
|
||||
*is_exiting = true;
|
||||
}
|
||||
|
||||
pub fn set_core_process(&self, process: CommandChild) {
|
||||
let mut core_process = self.core_process.write();
|
||||
*core_process = Some(process);
|
||||
}
|
||||
|
||||
pub fn take_core_process(&self) -> Option<CommandChild> {
|
||||
let mut core_process = self.core_process.write();
|
||||
core_process.take()
|
||||
}
|
||||
|
||||
/// 检查是否有运行中的核心进程
|
||||
pub fn has_core_process(&self) -> bool {
|
||||
self.core_process.read().is_some()
|
||||
}
|
||||
|
||||
pub fn is_exiting(&self) -> bool {
|
||||
*self.is_exiting.read()
|
||||
}
|
||||
|
@@ -279,3 +279,15 @@ pub(super) async fn stop_core_by_service() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 检查服务是否正在运行
|
||||
pub async fn is_service_running() -> Result<bool> {
|
||||
let resp = check_service().await?;
|
||||
|
||||
// 检查服务状态码和消息
|
||||
if resp.code == 200 && resp.msg == "success" && resp.data.is_some() {
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
@@ -85,6 +85,7 @@ pub fn use_seq(seq: SeqMap, mut config: Mapping, field: &str) -> Mapping {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[allow(unused_imports)]
|
||||
use serde_yaml::Value;
|
||||
|
||||
#[test]
|
||||
|
@@ -4,6 +4,8 @@ mod core;
|
||||
mod enhance;
|
||||
mod feat;
|
||||
mod utils;
|
||||
mod model;
|
||||
mod module;
|
||||
use crate::core::hotkey;
|
||||
use crate::utils::{resolve, resolve::resolve_scheme, server};
|
||||
use config::Config;
|
||||
@@ -146,6 +148,9 @@ pub fn run() {
|
||||
cmd::get_network_interfaces,
|
||||
cmd::restart_core,
|
||||
cmd::restart_app,
|
||||
// 添加新的命令
|
||||
cmd::get_running_mode,
|
||||
cmd::install_service,
|
||||
// clash
|
||||
cmd::get_clash_info,
|
||||
cmd::patch_clash_config,
|
||||
@@ -157,6 +162,8 @@ pub fn run() {
|
||||
cmd::get_runtime_logs,
|
||||
cmd::invoke_uwp_tool,
|
||||
cmd::copy_clash_env,
|
||||
cmd::get_proxies,
|
||||
cmd::get_providers_proxies,
|
||||
// verge
|
||||
cmd::get_verge_config,
|
||||
cmd::patch_verge_config,
|
||||
@@ -191,6 +198,8 @@ pub fn run() {
|
||||
cmd::list_webdav_backup,
|
||||
cmd::delete_webdav_backup,
|
||||
cmd::restore_webdav_backup,
|
||||
// export diagnostic info for issue reporting
|
||||
cmd::export_diagnostic_info,
|
||||
]);
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
|
20
clash-verge-rev/src-tauri/src/model/api/common.rs
Normal file
20
clash-verge-rev/src-tauri/src/model/api/common.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
use reqwest::Client;
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) struct ApiCaller<'a> {
|
||||
pub(crate) url: &'a str,
|
||||
pub(crate) client: Client,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_api_caller() {
|
||||
let _api_caller = ApiCaller {
|
||||
url: "https://example.com",
|
||||
client: Client::new(),
|
||||
};
|
||||
}
|
||||
}
|
5
clash-verge-rev/src-tauri/src/model/api/mihomo.rs
Normal file
5
clash-verge-rev/src-tauri/src/model/api/mihomo.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
use super::common::ApiCaller;
|
||||
|
||||
pub struct MihomoAPICaller {
|
||||
pub(crate) caller: ApiCaller<'static>,
|
||||
}
|
2
clash-verge-rev/src-tauri/src/model/api/mod.rs
Normal file
2
clash-verge-rev/src-tauri/src/model/api/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod common;
|
||||
pub mod mihomo;
|
2
clash-verge-rev/src-tauri/src/model/mod.rs
Normal file
2
clash-verge-rev/src-tauri/src/model/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod api;
|
||||
pub mod sysinfo;
|
18
clash-verge-rev/src-tauri/src/model/sysinfo.rs
Normal file
18
clash-verge-rev/src-tauri/src/model/sysinfo.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
|
||||
pub struct PlatformSpecification {
|
||||
pub system_name: String,
|
||||
pub system_version: String,
|
||||
pub system_kernel_version: String,
|
||||
pub system_arch: String,
|
||||
}
|
||||
|
||||
impl Debug for PlatformSpecification {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"System Name: {}\nSystem Version: {}\nSystem kernel Version: {}\nSystem Arch: {}",
|
||||
self.system_name, self.system_version, self.system_kernel_version, self.system_arch
|
||||
)
|
||||
}
|
||||
}
|
70
clash-verge-rev/src-tauri/src/module/api/common.rs
Normal file
70
clash-verge-rev/src-tauri/src/module/api/common.rs
Normal file
@@ -0,0 +1,70 @@
|
||||
use crate::model::api::common::ApiCaller;
|
||||
use async_trait::async_trait;
|
||||
use reqwest::{
|
||||
header::{HeaderMap, HeaderName, HeaderValue},
|
||||
RequestBuilder,
|
||||
};
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
impl<'a> ApiCaller<'a> {
|
||||
pub async fn send_request(
|
||||
&self,
|
||||
method: &str,
|
||||
path: &str,
|
||||
body: Option<&str>,
|
||||
headers: Option<Vec<(&str, &str)>>,
|
||||
) -> Result<String, String> {
|
||||
let full_url = format!("{}{}", self.url, path); // 拼接完整 URL
|
||||
let mut request: RequestBuilder = match method {
|
||||
"GET" => self.client.get(&full_url),
|
||||
"POST" => self
|
||||
.client
|
||||
.post(&full_url)
|
||||
.body(body.unwrap_or("").to_string()),
|
||||
"PUT" => self
|
||||
.client
|
||||
.put(&full_url)
|
||||
.body(body.unwrap_or("").to_string()),
|
||||
"DELETE" => self.client.delete(&full_url),
|
||||
_ => return Err("Unsupported HTTP method".to_string()),
|
||||
};
|
||||
|
||||
// 处理 headers
|
||||
if let Some(hdrs) = headers {
|
||||
let mut header_map = HeaderMap::new();
|
||||
for (key, value) in hdrs {
|
||||
if let (Ok(header_name), Ok(header_value)) = (
|
||||
HeaderName::from_bytes(key.as_bytes()),
|
||||
HeaderValue::from_str(value),
|
||||
) {
|
||||
header_map.insert(header_name, header_value);
|
||||
}
|
||||
}
|
||||
request = request.headers(header_map);
|
||||
}
|
||||
|
||||
let response = request.send().await.map_err(|e| e.to_string())?;
|
||||
response.text().await.map_err(|e| e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[async_trait]
|
||||
pub trait ApiCallerTrait: Send + Sync {
|
||||
async fn call_api<T>(
|
||||
&self,
|
||||
method: &str,
|
||||
path: &str,
|
||||
body: Option<&str>,
|
||||
headers: Option<Vec<(&str, &str)>>
|
||||
) -> Result<T, String>
|
||||
where
|
||||
T: DeserializeOwned + Send + Sync;
|
||||
|
||||
fn parse_json_response<T>(json_str: &str) -> Result<T, String>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
serde_json::from_str(json_str).map_err(|e| e.to_string())
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user