mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-09-14 04:15:06 +08:00
✨ Sync 2026-08-13 21:00:03
This commit is contained in:
+3
-3
@@ -5,10 +5,10 @@
|
|||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_NAME:=dae
|
PKG_NAME:=dae
|
||||||
PKG_VERSION:=2026.08.05
|
PKG_VERSION:=2026.08.13
|
||||||
PKG_RELEASE:=34
|
PKG_RELEASE:=35
|
||||||
|
|
||||||
PKG_SOURCE:=dae-src-2026.08.05-fc1a5bf558fd.tar.gz
|
PKG_SOURCE:=dae-src-2026.08.13-44554e556f44.tar.gz
|
||||||
PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-daede/releases/download/dae-src
|
PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-daede/releases/download/dae-src
|
||||||
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
|
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
|
||||||
PKG_HASH:=skip
|
PKG_HASH:=skip
|
||||||
|
|||||||
+3
-3
@@ -5,10 +5,10 @@
|
|||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_NAME:=daed
|
PKG_NAME:=daed
|
||||||
PKG_VERSION:=2026.08.05
|
PKG_VERSION:=2026.08.13
|
||||||
PKG_RELEASE:=45
|
PKG_RELEASE:=46
|
||||||
|
|
||||||
PKG_SOURCE:=daed-src-2026.08.05-1d87c2949acc.tar.gz
|
PKG_SOURCE:=daed-src-2026.08.13-b5028aedb364.tar.gz
|
||||||
PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-daede/releases/download/daed-src
|
PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-daede/releases/download/daed-src
|
||||||
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
|
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
|
||||||
PKG_HASH:=skip
|
PKG_HASH:=skip
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ function parseShareLink(uri, features) {
|
|||||||
case 'socks':
|
case 'socks':
|
||||||
case 'socks4':
|
case 'socks4':
|
||||||
case 'socks4a':
|
case 'socks4a':
|
||||||
case 'socsk5':
|
case 'socks5':
|
||||||
case 'socks5h':
|
case 'socks5h':
|
||||||
url = new URL('http://' + uri[1]);
|
url = new URL('http://' + uri[1]);
|
||||||
|
|
||||||
|
|||||||
@@ -52,8 +52,7 @@
|
|||||||
8.150.64.0/23
|
8.150.64.0/23
|
||||||
8.152.0.0/13
|
8.152.0.0/13
|
||||||
8.160.0.0/15
|
8.160.0.0/15
|
||||||
8.162.0.0/18
|
8.162.0.0/17
|
||||||
8.162.64.0/19
|
|
||||||
8.163.0.0/16
|
8.163.0.0/16
|
||||||
8.164.0.0/14
|
8.164.0.0/14
|
||||||
8.168.0.0/15
|
8.168.0.0/15
|
||||||
@@ -67,7 +66,6 @@
|
|||||||
14.144.0.0/12
|
14.144.0.0/12
|
||||||
14.204.0.0/15
|
14.204.0.0/15
|
||||||
14.208.0.0/12
|
14.208.0.0/12
|
||||||
14.241.232.0/21
|
|
||||||
14.255.16.0/24
|
14.255.16.0/24
|
||||||
14.255.238.0/24
|
14.255.238.0/24
|
||||||
14.255.254.0/24
|
14.255.254.0/24
|
||||||
@@ -312,6 +310,7 @@
|
|||||||
43.248.108.0/22
|
43.248.108.0/22
|
||||||
43.248.112.0/21
|
43.248.112.0/21
|
||||||
43.248.128.0/20
|
43.248.128.0/20
|
||||||
|
43.248.184.0/21
|
||||||
43.248.192.0/20
|
43.248.192.0/20
|
||||||
43.248.232.0/22
|
43.248.232.0/22
|
||||||
43.248.244.0/22
|
43.248.244.0/22
|
||||||
@@ -485,7 +484,7 @@
|
|||||||
52.82.134.0/23
|
52.82.134.0/23
|
||||||
52.82.136.0/24
|
52.82.136.0/24
|
||||||
52.82.144.0/23
|
52.82.144.0/23
|
||||||
52.82.148.0/22
|
52.82.148.0/23
|
||||||
52.82.160.0/21
|
52.82.160.0/21
|
||||||
52.82.170.0/23
|
52.82.170.0/23
|
||||||
52.82.172.0/22
|
52.82.172.0/22
|
||||||
@@ -562,7 +561,7 @@
|
|||||||
59.111.18.0/23
|
59.111.18.0/23
|
||||||
59.111.21.0/24
|
59.111.21.0/24
|
||||||
59.111.22.0/24
|
59.111.22.0/24
|
||||||
59.111.24.0/21
|
59.111.24.0/22
|
||||||
59.111.32.0/24
|
59.111.32.0/24
|
||||||
59.111.35.0/24
|
59.111.35.0/24
|
||||||
59.111.36.0/24
|
59.111.36.0/24
|
||||||
@@ -577,7 +576,9 @@
|
|||||||
59.111.128.0/20
|
59.111.128.0/20
|
||||||
59.111.144.0/24
|
59.111.144.0/24
|
||||||
59.111.152.0/21
|
59.111.152.0/21
|
||||||
59.111.160.0/21
|
59.111.160.0/23
|
||||||
|
59.111.162.0/24
|
||||||
|
59.111.164.0/22
|
||||||
59.111.168.0/22
|
59.111.168.0/22
|
||||||
59.111.173.0/24
|
59.111.173.0/24
|
||||||
59.111.175.0/24
|
59.111.175.0/24
|
||||||
@@ -623,18 +624,12 @@
|
|||||||
60.253.128.0/17
|
60.253.128.0/17
|
||||||
60.255.0.0/16
|
60.255.0.0/16
|
||||||
61.4.80.0/23
|
61.4.80.0/23
|
||||||
61.10.96.0/20
|
|
||||||
61.10.128.0/20
|
|
||||||
61.10.160.0/19
|
|
||||||
61.15.0.0/18
|
|
||||||
61.18.0.0/18
|
|
||||||
61.18.64.0/19
|
|
||||||
61.28.20.0/24
|
61.28.20.0/24
|
||||||
61.28.22.0/24
|
61.28.22.0/24
|
||||||
61.28.49.0/24
|
61.28.49.0/24
|
||||||
61.28.108.0/24
|
61.28.108.0/24
|
||||||
61.28.112.0/20
|
61.28.112.0/20
|
||||||
61.29.128.0/18
|
61.29.161.0/24
|
||||||
61.47.128.0/18
|
61.47.128.0/18
|
||||||
61.48.0.0/13
|
61.48.0.0/13
|
||||||
61.87.192.0/18
|
61.87.192.0/18
|
||||||
@@ -731,6 +726,7 @@
|
|||||||
101.124.0.0/20
|
101.124.0.0/20
|
||||||
101.124.19.0/24
|
101.124.19.0/24
|
||||||
101.124.22.0/24
|
101.124.22.0/24
|
||||||
|
101.124.24.0/21
|
||||||
101.124.62.0/24
|
101.124.62.0/24
|
||||||
101.125.0.0/22
|
101.125.0.0/22
|
||||||
101.125.4.0/23
|
101.125.4.0/23
|
||||||
@@ -775,10 +771,9 @@
|
|||||||
101.237.128.0/20
|
101.237.128.0/20
|
||||||
101.240.0.0/14
|
101.240.0.0/14
|
||||||
101.245.0.0/17
|
101.245.0.0/17
|
||||||
101.245.128.0/20
|
101.245.128.0/19
|
||||||
101.245.144.0/22
|
|
||||||
101.245.152.0/21
|
|
||||||
101.245.160.0/20
|
101.245.160.0/20
|
||||||
|
101.245.176.0/22
|
||||||
101.246.172.0/22
|
101.246.172.0/22
|
||||||
101.246.176.0/20
|
101.246.176.0/20
|
||||||
101.248.0.0/15
|
101.248.0.0/15
|
||||||
@@ -793,6 +788,8 @@
|
|||||||
101.254.32.0/19
|
101.254.32.0/19
|
||||||
101.254.64.0/18
|
101.254.64.0/18
|
||||||
101.254.128.0/17
|
101.254.128.0/17
|
||||||
|
102.141.200.0/24
|
||||||
|
102.220.90.0/24
|
||||||
103.1.8.0/22
|
103.1.8.0/22
|
||||||
103.1.20.0/22
|
103.1.20.0/22
|
||||||
103.1.168.0/22
|
103.1.168.0/22
|
||||||
@@ -1151,6 +1148,7 @@
|
|||||||
103.144.66.0/23
|
103.144.66.0/23
|
||||||
103.144.70.0/24
|
103.144.70.0/24
|
||||||
103.144.158.0/23
|
103.144.158.0/23
|
||||||
|
103.144.244.0/24
|
||||||
103.145.42.0/23
|
103.145.42.0/23
|
||||||
103.146.126.0/23
|
103.146.126.0/23
|
||||||
103.147.124.0/24
|
103.147.124.0/24
|
||||||
@@ -1188,7 +1186,6 @@
|
|||||||
103.170.4.0/23
|
103.170.4.0/23
|
||||||
103.170.212.0/23
|
103.170.212.0/23
|
||||||
103.174.94.0/23
|
103.174.94.0/23
|
||||||
103.177.28.0/23
|
|
||||||
103.179.78.0/23
|
103.179.78.0/23
|
||||||
103.181.234.0/24
|
103.181.234.0/24
|
||||||
103.183.66.0/23
|
103.183.66.0/23
|
||||||
@@ -1222,6 +1219,8 @@
|
|||||||
103.200.144.0/21
|
103.200.144.0/21
|
||||||
103.200.220.0/22
|
103.200.220.0/22
|
||||||
103.200.224.0/24
|
103.200.224.0/24
|
||||||
|
103.200.228.0/24
|
||||||
|
103.200.232.0/24
|
||||||
103.201.192.0/24
|
103.201.192.0/24
|
||||||
103.201.196.0/24
|
103.201.196.0/24
|
||||||
103.201.200.0/24
|
103.201.200.0/24
|
||||||
@@ -1255,6 +1254,7 @@
|
|||||||
103.212.4.0/22
|
103.212.4.0/22
|
||||||
103.212.12.0/22
|
103.212.12.0/22
|
||||||
103.212.48.0/23
|
103.212.48.0/23
|
||||||
|
103.212.50.0/24
|
||||||
103.212.109.0/24
|
103.212.109.0/24
|
||||||
103.213.49.0/24
|
103.213.49.0/24
|
||||||
103.213.50.0/23
|
103.213.50.0/23
|
||||||
@@ -1343,6 +1343,7 @@
|
|||||||
103.236.56.0/21
|
103.236.56.0/21
|
||||||
103.236.64.0/19
|
103.236.64.0/19
|
||||||
103.236.96.0/22
|
103.236.96.0/22
|
||||||
|
103.236.116.0/23
|
||||||
103.236.120.0/22
|
103.236.120.0/22
|
||||||
103.236.244.0/22
|
103.236.244.0/22
|
||||||
103.236.248.0/21
|
103.236.248.0/21
|
||||||
@@ -1379,6 +1380,7 @@
|
|||||||
103.240.124.0/22
|
103.240.124.0/22
|
||||||
103.240.244.0/22
|
103.240.244.0/22
|
||||||
103.241.95.0/24
|
103.241.95.0/24
|
||||||
|
103.241.220.0/22
|
||||||
103.242.168.0/23
|
103.242.168.0/23
|
||||||
103.242.172.0/22
|
103.242.172.0/22
|
||||||
103.242.212.0/22
|
103.242.212.0/22
|
||||||
@@ -1409,12 +1411,11 @@
|
|||||||
103.251.96.0/22
|
103.251.96.0/22
|
||||||
103.251.124.0/22
|
103.251.124.0/22
|
||||||
103.251.160.0/22
|
103.251.160.0/22
|
||||||
103.251.204.0/24
|
103.251.204.0/23
|
||||||
103.251.207.0/24
|
103.251.207.0/24
|
||||||
103.251.240.0/22
|
103.251.240.0/22
|
||||||
103.252.36.0/24
|
103.252.36.0/24
|
||||||
103.252.172.0/22
|
103.252.172.0/22
|
||||||
103.252.248.0/22
|
|
||||||
103.253.60.0/22
|
103.253.60.0/22
|
||||||
103.253.204.0/22
|
103.253.204.0/22
|
||||||
103.254.68.0/22
|
103.254.68.0/22
|
||||||
@@ -1425,6 +1426,7 @@
|
|||||||
103.255.92.0/22
|
103.255.92.0/22
|
||||||
103.255.140.0/22
|
103.255.140.0/22
|
||||||
103.255.200.0/22
|
103.255.200.0/22
|
||||||
|
104.251.176.0/22
|
||||||
106.0.4.0/22
|
106.0.4.0/22
|
||||||
106.2.37.0/24
|
106.2.37.0/24
|
||||||
106.2.40.0/23
|
106.2.40.0/23
|
||||||
@@ -1445,7 +1447,6 @@
|
|||||||
106.2.100.0/22
|
106.2.100.0/22
|
||||||
106.2.104.0/21
|
106.2.104.0/21
|
||||||
106.2.112.0/21
|
106.2.112.0/21
|
||||||
106.2.124.0/22
|
|
||||||
106.2.128.0/19
|
106.2.128.0/19
|
||||||
106.3.0.0/19
|
106.3.0.0/19
|
||||||
106.3.32.0/20
|
106.3.32.0/20
|
||||||
@@ -1655,7 +1656,6 @@
|
|||||||
113.130.112.0/21
|
113.130.112.0/21
|
||||||
113.132.0.0/14
|
113.132.0.0/14
|
||||||
113.136.0.0/13
|
113.136.0.0/13
|
||||||
113.164.25.0/24
|
|
||||||
113.194.0.0/15
|
113.194.0.0/15
|
||||||
113.197.101.0/24
|
113.197.101.0/24
|
||||||
113.197.103.0/24
|
113.197.103.0/24
|
||||||
@@ -1895,7 +1895,9 @@
|
|||||||
116.198.160.0/20
|
116.198.160.0/20
|
||||||
116.198.176.0/21
|
116.198.176.0/21
|
||||||
116.198.192.0/18
|
116.198.192.0/18
|
||||||
116.199.0.0/17
|
116.199.0.0/18
|
||||||
|
116.199.66.0/23
|
||||||
|
116.199.110.0/23
|
||||||
116.204.0.0/17
|
116.204.0.0/17
|
||||||
116.205.0.0/16
|
116.205.0.0/16
|
||||||
116.207.0.0/16
|
116.207.0.0/16
|
||||||
@@ -2063,7 +2065,10 @@
|
|||||||
118.193.96.0/19
|
118.193.96.0/19
|
||||||
118.194.32.0/19
|
118.194.32.0/19
|
||||||
118.194.134.0/23
|
118.194.134.0/23
|
||||||
118.194.240.0/21
|
118.194.240.0/24
|
||||||
|
118.194.242.0/23
|
||||||
|
118.194.244.0/24
|
||||||
|
118.194.246.0/23
|
||||||
118.195.128.0/17
|
118.195.128.0/17
|
||||||
118.196.0.0/16
|
118.196.0.0/16
|
||||||
118.197.0.0/18
|
118.197.0.0/18
|
||||||
@@ -2095,6 +2100,7 @@
|
|||||||
119.18.208.0/21
|
119.18.208.0/21
|
||||||
119.18.224.0/24
|
119.18.224.0/24
|
||||||
119.18.226.0/24
|
119.18.226.0/24
|
||||||
|
119.18.234.0/24
|
||||||
119.18.237.0/24
|
119.18.237.0/24
|
||||||
119.18.239.0/24
|
119.18.239.0/24
|
||||||
119.23.0.0/16
|
119.23.0.0/16
|
||||||
@@ -2102,8 +2108,8 @@
|
|||||||
119.28.28.0/24
|
119.28.28.0/24
|
||||||
119.29.0.0/16
|
119.29.0.0/16
|
||||||
119.31.192.0/19
|
119.31.192.0/19
|
||||||
119.32.0.0/15
|
119.32.0.0/18
|
||||||
119.34.0.0/16
|
119.33.194.0/23
|
||||||
119.36.0.0/16
|
119.36.0.0/16
|
||||||
119.37.0.0/23
|
119.37.0.0/23
|
||||||
119.37.2.0/24
|
119.37.2.0/24
|
||||||
@@ -2127,8 +2133,7 @@
|
|||||||
119.44.16.0/22
|
119.44.16.0/22
|
||||||
119.44.20.0/23
|
119.44.20.0/23
|
||||||
119.44.23.0/24
|
119.44.23.0/24
|
||||||
119.44.24.0/22
|
119.44.24.0/23
|
||||||
119.44.28.0/23
|
|
||||||
119.44.45.0/24
|
119.44.45.0/24
|
||||||
119.44.46.0/24
|
119.44.46.0/24
|
||||||
119.44.50.0/23
|
119.44.50.0/23
|
||||||
@@ -2137,8 +2142,6 @@
|
|||||||
119.44.192.0/24
|
119.44.192.0/24
|
||||||
119.44.200.0/24
|
119.44.200.0/24
|
||||||
119.44.205.0/24
|
119.44.205.0/24
|
||||||
119.44.208.0/22
|
|
||||||
119.44.212.0/23
|
|
||||||
119.44.214.0/24
|
119.44.214.0/24
|
||||||
119.44.217.0/24
|
119.44.217.0/24
|
||||||
119.44.218.0/23
|
119.44.218.0/23
|
||||||
@@ -2488,7 +2491,7 @@
|
|||||||
123.254.96.0/21
|
123.254.96.0/21
|
||||||
124.14.5.0/24
|
124.14.5.0/24
|
||||||
124.14.8.0/24
|
124.14.8.0/24
|
||||||
124.14.12.0/24
|
124.14.12.0/23
|
||||||
124.14.15.0/24
|
124.14.15.0/24
|
||||||
124.14.22.0/24
|
124.14.22.0/24
|
||||||
124.14.64.0/18
|
124.14.64.0/18
|
||||||
@@ -2558,7 +2561,6 @@
|
|||||||
125.98.128.0/17
|
125.98.128.0/17
|
||||||
125.104.0.0/13
|
125.104.0.0/13
|
||||||
125.112.0.0/12
|
125.112.0.0/12
|
||||||
125.171.0.0/16
|
|
||||||
125.208.0.0/19
|
125.208.0.0/19
|
||||||
125.208.32.0/20
|
125.208.32.0/20
|
||||||
125.208.49.0/24
|
125.208.49.0/24
|
||||||
@@ -2736,7 +2738,6 @@
|
|||||||
156.227.239.0/24
|
156.227.239.0/24
|
||||||
156.232.9.0/24
|
156.232.9.0/24
|
||||||
156.232.10.0/23
|
156.232.10.0/23
|
||||||
156.242.5.0/24
|
|
||||||
157.0.0.0/16
|
157.0.0.0/16
|
||||||
157.10.35.0/24
|
157.10.35.0/24
|
||||||
157.10.112.0/23
|
157.10.112.0/23
|
||||||
@@ -2793,6 +2794,7 @@
|
|||||||
163.61.202.0/23
|
163.61.202.0/23
|
||||||
163.61.214.0/23
|
163.61.214.0/23
|
||||||
163.125.0.0/16
|
163.125.0.0/16
|
||||||
|
163.128.254.0/23
|
||||||
163.142.0.0/16
|
163.142.0.0/16
|
||||||
163.177.0.0/16
|
163.177.0.0/16
|
||||||
163.179.0.0/16
|
163.179.0.0/16
|
||||||
@@ -2802,6 +2804,11 @@
|
|||||||
163.244.246.0/24
|
163.244.246.0/24
|
||||||
165.101.70.0/24
|
165.101.70.0/24
|
||||||
165.101.208.0/23
|
165.101.208.0/23
|
||||||
|
166.0.97.0/24
|
||||||
|
166.0.100.0/23
|
||||||
|
166.1.139.0/24
|
||||||
|
166.1.218.0/24
|
||||||
|
166.1.232.0/24
|
||||||
166.111.0.0/16
|
166.111.0.0/16
|
||||||
167.139.0.0/16
|
167.139.0.0/16
|
||||||
167.220.244.0/22
|
167.220.244.0/22
|
||||||
@@ -2944,6 +2951,7 @@
|
|||||||
183.78.180.0/22
|
183.78.180.0/22
|
||||||
183.81.180.0/22
|
183.81.180.0/22
|
||||||
183.84.0.0/15
|
183.84.0.0/15
|
||||||
|
183.91.37.0/24
|
||||||
183.91.39.0/24
|
183.91.39.0/24
|
||||||
183.91.40.0/21
|
183.91.40.0/21
|
||||||
183.91.48.0/21
|
183.91.48.0/21
|
||||||
@@ -3017,9 +3025,10 @@
|
|||||||
202.47.104.0/21
|
202.47.104.0/21
|
||||||
202.51.32.0/20
|
202.51.32.0/20
|
||||||
202.55.0.0/19
|
202.55.0.0/19
|
||||||
202.57.192.0/24
|
202.57.192.0/23
|
||||||
202.57.196.0/22
|
202.57.196.0/22
|
||||||
202.57.200.0/23
|
202.57.200.0/23
|
||||||
|
202.57.202.0/24
|
||||||
202.57.204.0/23
|
202.57.204.0/23
|
||||||
202.59.213.0/24
|
202.59.213.0/24
|
||||||
202.59.214.0/23
|
202.59.214.0/23
|
||||||
@@ -3135,8 +3144,10 @@
|
|||||||
203.8.30.0/24
|
203.8.30.0/24
|
||||||
203.12.91.0/24
|
203.12.91.0/24
|
||||||
203.12.93.0/24
|
203.12.93.0/24
|
||||||
|
203.12.95.0/24
|
||||||
203.13.81.0/24
|
203.13.81.0/24
|
||||||
203.15.0.0/20
|
203.15.0.0/20
|
||||||
|
203.15.114.0/24
|
||||||
203.25.52.0/24
|
203.25.52.0/24
|
||||||
203.25.208.0/20
|
203.25.208.0/20
|
||||||
203.32.48.0/23
|
203.32.48.0/23
|
||||||
@@ -3303,6 +3314,7 @@
|
|||||||
203.95.2.0/24
|
203.95.2.0/24
|
||||||
203.95.4.0/22
|
203.95.4.0/22
|
||||||
203.95.128.0/23
|
203.95.128.0/23
|
||||||
|
203.99.18.0/24
|
||||||
203.99.21.0/24
|
203.99.21.0/24
|
||||||
203.99.22.0/23
|
203.99.22.0/23
|
||||||
203.99.26.0/23
|
203.99.26.0/23
|
||||||
@@ -3321,7 +3333,7 @@
|
|||||||
203.107.72.0/21
|
203.107.72.0/21
|
||||||
203.107.80.0/21
|
203.107.80.0/21
|
||||||
203.107.96.0/23
|
203.107.96.0/23
|
||||||
203.107.116.0/22
|
203.107.112.0/21
|
||||||
203.107.120.0/22
|
203.107.120.0/22
|
||||||
203.107.124.0/23
|
203.107.124.0/23
|
||||||
203.110.160.0/19
|
203.110.160.0/19
|
||||||
@@ -3393,8 +3405,8 @@
|
|||||||
203.195.128.0/17
|
203.195.128.0/17
|
||||||
203.196.0.0/21
|
203.196.0.0/21
|
||||||
203.202.236.0/23
|
203.202.236.0/23
|
||||||
203.205.88.0/23
|
203.205.88.0/22
|
||||||
203.205.90.0/24
|
203.205.95.0/24
|
||||||
203.207.64.0/19
|
203.207.64.0/19
|
||||||
203.207.96.0/21
|
203.207.96.0/21
|
||||||
203.207.104.0/22
|
203.207.104.0/22
|
||||||
@@ -3506,8 +3518,7 @@
|
|||||||
210.79.224.0/19
|
210.79.224.0/19
|
||||||
210.82.0.0/15
|
210.82.0.0/15
|
||||||
210.87.128.0/18
|
210.87.128.0/18
|
||||||
210.185.192.0/18
|
210.192.96.0/21
|
||||||
210.192.96.0/20
|
|
||||||
210.192.116.0/22
|
210.192.116.0/22
|
||||||
210.192.120.0/21
|
210.192.120.0/21
|
||||||
211.64.0.0/13
|
211.64.0.0/13
|
||||||
@@ -3611,7 +3622,8 @@
|
|||||||
211.147.208.0/20
|
211.147.208.0/20
|
||||||
211.147.224.0/19
|
211.147.224.0/19
|
||||||
211.148.0.0/18
|
211.148.0.0/18
|
||||||
211.148.128.0/17
|
211.148.128.0/18
|
||||||
|
211.148.192.0/19
|
||||||
211.149.128.0/17
|
211.149.128.0/17
|
||||||
211.150.0.0/18
|
211.150.0.0/18
|
||||||
211.150.64.0/19
|
211.150.64.0/19
|
||||||
@@ -3644,7 +3656,6 @@
|
|||||||
211.155.240.0/20
|
211.155.240.0/20
|
||||||
211.156.0.0/19
|
211.156.0.0/19
|
||||||
211.156.128.0/19
|
211.156.128.0/19
|
||||||
211.156.176.0/20
|
|
||||||
211.156.192.0/20
|
211.156.192.0/20
|
||||||
211.156.208.0/21
|
211.156.208.0/21
|
||||||
211.156.216.0/22
|
211.156.216.0/22
|
||||||
@@ -3785,8 +3796,6 @@
|
|||||||
218.247.64.0/19
|
218.247.64.0/19
|
||||||
218.247.128.0/17
|
218.247.128.0/17
|
||||||
218.249.0.0/16
|
218.249.0.0/16
|
||||||
218.252.0.0/17
|
|
||||||
218.253.0.0/18
|
|
||||||
219.72.0.0/16
|
219.72.0.0/16
|
||||||
219.82.0.0/16
|
219.82.0.0/16
|
||||||
219.83.160.0/19
|
219.83.160.0/19
|
||||||
@@ -3823,6 +3832,7 @@
|
|||||||
219.235.32.0/19
|
219.235.32.0/19
|
||||||
219.235.64.0/18
|
219.235.64.0/18
|
||||||
219.235.128.0/19
|
219.235.128.0/19
|
||||||
|
219.235.164.0/23
|
||||||
219.235.192.0/23
|
219.235.192.0/23
|
||||||
219.235.207.0/24
|
219.235.207.0/24
|
||||||
219.235.224.0/22
|
219.235.224.0/22
|
||||||
@@ -3982,9 +3992,6 @@
|
|||||||
222.126.240.0/20
|
222.126.240.0/20
|
||||||
222.128.0.0/12
|
222.128.0.0/12
|
||||||
222.160.0.0/14
|
222.160.0.0/14
|
||||||
222.166.96.0/20
|
|
||||||
222.166.160.0/20
|
|
||||||
222.166.240.0/20
|
|
||||||
222.168.0.0/13
|
222.168.0.0/13
|
||||||
222.176.0.0/12
|
222.176.0.0/12
|
||||||
222.192.0.0/11
|
222.192.0.0/11
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
20260704060757
|
20260811040949
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
2001:daa:2::/47
|
2001:daa:2::/47
|
||||||
2001:daa:4::/47
|
2001:daa:4::/47
|
||||||
2001:daa:6::/48
|
2001:daa:6::/48
|
||||||
|
2001:daa:9::/48
|
||||||
2001:dc7::/32
|
2001:dc7::/32
|
||||||
2001:dd8:1::/48
|
2001:dd8:1::/48
|
||||||
2001:dd9::/48
|
2001:dd9::/48
|
||||||
@@ -57,12 +58,14 @@
|
|||||||
2400:9380:90b1::/48
|
2400:9380:90b1::/48
|
||||||
2400:9380:90b2::/47
|
2400:9380:90b2::/47
|
||||||
2400:9380:90b4::/46
|
2400:9380:90b4::/46
|
||||||
|
2400:9380:9109::/48
|
||||||
2400:9380:9121::/48
|
2400:9380:9121::/48
|
||||||
2400:9380:9220::/47
|
2400:9380:9220::/47
|
||||||
2400:9380:9250::/47
|
2400:9380:9250::/47
|
||||||
2400:9380:9260::/48
|
2400:9380:9260::/48
|
||||||
2400:9380:9271::/48
|
2400:9380:9271::/48
|
||||||
2400:9380:9280::/47
|
2400:9380:9280::/47
|
||||||
|
2400:9380:9282::/48
|
||||||
2400:9380:92b1::/48
|
2400:9380:92b1::/48
|
||||||
2400:9380:92b2::/47
|
2400:9380:92b2::/47
|
||||||
2400:9380:92b4::/46
|
2400:9380:92b4::/46
|
||||||
@@ -131,6 +134,7 @@
|
|||||||
2401:fa00:40::/48
|
2401:fa00:40::/48
|
||||||
2402:840:d000::/46
|
2402:840:d000::/46
|
||||||
2402:840:e000::/46
|
2402:840:e000::/46
|
||||||
|
2402:840:f000::/38
|
||||||
2402:c60::/48
|
2402:c60::/48
|
||||||
2402:1440::/32
|
2402:1440::/32
|
||||||
2402:2000::/32
|
2402:2000::/32
|
||||||
@@ -237,6 +241,8 @@
|
|||||||
2404:7600::/32
|
2404:7600::/32
|
||||||
2404:7940::/32
|
2404:7940::/32
|
||||||
2404:c2c0::/40
|
2404:c2c0::/40
|
||||||
|
2404:c2c0:111::/48
|
||||||
|
2404:c2c0:112::/48
|
||||||
2404:c2c0:240::/44
|
2404:c2c0:240::/44
|
||||||
2404:c2c0:280::/44
|
2404:c2c0:280::/44
|
||||||
2404:c2c0:2c0::/44
|
2404:c2c0:2c0::/44
|
||||||
@@ -278,7 +284,6 @@
|
|||||||
2405:e000::/32
|
2405:e000::/32
|
||||||
2405:f940::/32
|
2405:f940::/32
|
||||||
2406:280::/32
|
2406:280::/32
|
||||||
2406:840:9680::/44
|
|
||||||
2406:840:f889::/48
|
2406:840:f889::/48
|
||||||
2406:1080::/48
|
2406:1080::/48
|
||||||
2406:2700::/32
|
2406:2700::/32
|
||||||
@@ -463,7 +468,7 @@
|
|||||||
2408:8406:a980::/42
|
2408:8406:a980::/42
|
||||||
2408:8406:b500::/41
|
2408:8406:b500::/41
|
||||||
2408:8406:b580::/42
|
2408:8406:b580::/42
|
||||||
2408:8407:500::/42
|
2408:8407:520::/43
|
||||||
2408:8409::/40
|
2408:8409::/40
|
||||||
2408:8409:100::/41
|
2408:8409:100::/41
|
||||||
2408:8409:1a0::/43
|
2408:8409:1a0::/43
|
||||||
@@ -807,6 +812,7 @@
|
|||||||
2408:882c::/32
|
2408:882c::/32
|
||||||
2408:883a::/32
|
2408:883a::/32
|
||||||
2408:8844::/43
|
2408:8844::/43
|
||||||
|
2408:8844:40::/43
|
||||||
2408:8856::/31
|
2408:8856::/31
|
||||||
2408:8858::/30
|
2408:8858::/30
|
||||||
2408:8862::/31
|
2408:8862::/31
|
||||||
@@ -983,26 +989,25 @@
|
|||||||
2a04:f580:9270::/48
|
2a04:f580:9270::/48
|
||||||
2a04:f580:9280::/48
|
2a04:f580:9280::/48
|
||||||
2a04:f580:9290::/48
|
2a04:f580:9290::/48
|
||||||
|
2a05:1086::/32
|
||||||
2a0a:d685:1ff::/48
|
2a0a:d685:1ff::/48
|
||||||
2a0a:d685:300::/40
|
2a0a:d685:300::/40
|
||||||
2a0b:4340:90::/48
|
|
||||||
2a0b:4340:97::/48
|
2a0b:4340:97::/48
|
||||||
2a0d:c7c7:400::/38
|
2a0d:c7c7:400::/38
|
||||||
2a0e:aa06:49f::/48
|
2a0e:aa06:49f::/48
|
||||||
2a0e:b107:740::/44
|
2a0e:b107:740::/44
|
||||||
2a0e:b107:2440::/44
|
|
||||||
2a0f:1cc5:1600::/44
|
2a0f:1cc5:1600::/44
|
||||||
2a0f:1cc5:4700::/40
|
2a0f:1cc5:4700::/40
|
||||||
|
2a0f:9400:6110::/48
|
||||||
|
2a12:cb41:600::/44
|
||||||
2a12:cb41:1200::/44
|
2a12:cb41:1200::/44
|
||||||
2a12:cb41:1300::/43
|
2a12:cb41:1300::/44
|
||||||
2a12:cb41:13f0::/44
|
2a12:cb41:13f0::/44
|
||||||
2a13:1801:180::/43
|
2a13:1801:180::/43
|
||||||
2a13:a5c3:f000::/40
|
2a13:a5c3:f000::/40
|
||||||
2a14:7580:d00::/43
|
2a14:7580:d10::/44
|
||||||
2a14:7580:d20::/46
|
2a14:7580:d20::/46
|
||||||
2a14:7580:d41::/48
|
2a14:7583:efe7::/48
|
||||||
2a14:7580:d42::/47
|
|
||||||
2a14:7580:dff::/48
|
|
||||||
2c0f:f7a8:8011::/48
|
2c0f:f7a8:8011::/48
|
||||||
2c0f:f7a8:8050::/48
|
2c0f:f7a8:8050::/48
|
||||||
2c0f:f7a8:805f::/48
|
2c0f:f7a8:805f::/48
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
20260704060757
|
20260811040949
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
|||||||
202607052256
|
202608102230
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
173ng.com
|
173ng.com
|
||||||
18board.com
|
18board.com
|
||||||
18comic.org
|
18comic.org
|
||||||
|
18comic.vip
|
||||||
18mh.net
|
18mh.net
|
||||||
1984bbs.com
|
1984bbs.com
|
||||||
1991way.com
|
1991way.com
|
||||||
@@ -127,6 +128,7 @@ agoogleaday.com
|
|||||||
agro.hk
|
agro.hk
|
||||||
ai.dev
|
ai.dev
|
||||||
ai.studio
|
ai.studio
|
||||||
|
aihub.top
|
||||||
aiosearch.com
|
aiosearch.com
|
||||||
aiph.net
|
aiph.net
|
||||||
airconsole.com
|
airconsole.com
|
||||||
@@ -294,7 +296,6 @@ atlanta168.com
|
|||||||
atnext.com
|
atnext.com
|
||||||
auctions.yahoo.co.jp
|
auctions.yahoo.co.jp
|
||||||
audacy.com
|
audacy.com
|
||||||
auntology.fandom.com
|
|
||||||
autodraw.com
|
autodraw.com
|
||||||
av.movie
|
av.movie
|
||||||
av01.tv
|
av01.tv
|
||||||
@@ -342,6 +343,7 @@ baramangaonline.com
|
|||||||
barnabu.co.uk
|
barnabu.co.uk
|
||||||
bartender.dowjones.com
|
bartender.dowjones.com
|
||||||
barton.de
|
barton.de
|
||||||
|
basemaps.arcgis.com
|
||||||
bastillepost.com
|
bastillepost.com
|
||||||
bayvoice.net
|
bayvoice.net
|
||||||
bb-chat.tv
|
bb-chat.tv
|
||||||
@@ -686,6 +688,7 @@ centauro.com.br
|
|||||||
centerforhumanreprod.com
|
centerforhumanreprod.com
|
||||||
centralnation.com
|
centralnation.com
|
||||||
centurys.net
|
centurys.net
|
||||||
|
cerebras.ai
|
||||||
certificate-transparency.org
|
certificate-transparency.org
|
||||||
certificate.revocationcheck.com
|
certificate.revocationcheck.com
|
||||||
cfr.org
|
cfr.org
|
||||||
@@ -766,10 +769,12 @@ chrome.com
|
|||||||
chromecast.com
|
chromecast.com
|
||||||
chromeexperiments.com
|
chromeexperiments.com
|
||||||
chromestatus.com
|
chromestatus.com
|
||||||
|
cht.com.tw
|
||||||
ci-en.jp
|
ci-en.jp
|
||||||
cia.gov
|
cia.gov
|
||||||
cici.com
|
cici.com
|
||||||
ciciai.com
|
ciciai.com
|
||||||
|
circle19.org
|
||||||
cirosantilli.com
|
cirosantilli.com
|
||||||
citizencn.com
|
citizencn.com
|
||||||
citizenlab.ca
|
citizenlab.ca
|
||||||
@@ -782,6 +787,7 @@ civilmedia.tw
|
|||||||
civitai.com
|
civitai.com
|
||||||
cixiaoya.club
|
cixiaoya.club
|
||||||
ck101.com
|
ck101.com
|
||||||
|
clashyun.com
|
||||||
classicalguitarblog.net
|
classicalguitarblog.net
|
||||||
claude.ai
|
claude.ai
|
||||||
claude.com
|
claude.com
|
||||||
@@ -991,6 +997,7 @@ disneyplus.com
|
|||||||
disp.cc
|
disp.cc
|
||||||
disqus.com
|
disqus.com
|
||||||
dit-inc.us
|
dit-inc.us
|
||||||
|
divdyn.com
|
||||||
diyin.org
|
diyin.org
|
||||||
dizhuzhishang.com
|
dizhuzhishang.com
|
||||||
dl-laby.jp
|
dl-laby.jp
|
||||||
@@ -1140,6 +1147,7 @@ eromanga-kingdom.com
|
|||||||
eromangadouzin.com
|
eromangadouzin.com
|
||||||
eromon.net
|
eromon.net
|
||||||
esg.t91y.com
|
esg.t91y.com
|
||||||
|
esim.redteago.com
|
||||||
eslite.com
|
eslite.com
|
||||||
esurance.com
|
esurance.com
|
||||||
etherdelta.com
|
etherdelta.com
|
||||||
@@ -1162,6 +1170,7 @@ exmormon.org
|
|||||||
expatshield.com
|
expatshield.com
|
||||||
expecthim.com
|
expecthim.com
|
||||||
expekt.com
|
expekt.com
|
||||||
|
experience.arcgis.com
|
||||||
exploader.net
|
exploader.net
|
||||||
expressnews.com
|
expressnews.com
|
||||||
expressvpn.com
|
expressvpn.com
|
||||||
@@ -1201,6 +1210,7 @@ falundafaindia.org
|
|||||||
falunhr.org
|
falunhr.org
|
||||||
famunion.com
|
famunion.com
|
||||||
fanbox.cc
|
fanbox.cc
|
||||||
|
fandom.com
|
||||||
fangeqiang.com
|
fangeqiang.com
|
||||||
fanglizhi.info
|
fanglizhi.info
|
||||||
fangong.org
|
fangong.org
|
||||||
@@ -1892,7 +1902,6 @@ holyspiritspeaks.org
|
|||||||
home.saxo
|
home.saxo
|
||||||
homedepot.com
|
homedepot.com
|
||||||
homeservershow.com
|
homeservershow.com
|
||||||
hongkong.fandom.com
|
|
||||||
hongkongfp.com
|
hongkongfp.com
|
||||||
hongzhi.li
|
hongzhi.li
|
||||||
honven.xyz
|
honven.xyz
|
||||||
@@ -2171,6 +2180,7 @@ kanshifang.com
|
|||||||
kantie.org
|
kantie.org
|
||||||
kaotic.com
|
kaotic.com
|
||||||
karayou.com
|
karayou.com
|
||||||
|
kards.com
|
||||||
kathmandupost.com
|
kathmandupost.com
|
||||||
kawaiikawaii.jp
|
kawaiikawaii.jp
|
||||||
kawase.com
|
kawase.com
|
||||||
@@ -2255,6 +2265,7 @@ lenwhite.com
|
|||||||
leonardo.ai
|
leonardo.ai
|
||||||
lesoir.be
|
lesoir.be
|
||||||
letscorp.net
|
letscorp.net
|
||||||
|
level-plus.net
|
||||||
lexica.art
|
lexica.art
|
||||||
lhakar.org
|
lhakar.org
|
||||||
li.taipei
|
li.taipei
|
||||||
@@ -2518,7 +2529,6 @@ mosavi.io
|
|||||||
motherless.com
|
motherless.com
|
||||||
movements.org
|
movements.org
|
||||||
moviefap.com
|
moviefap.com
|
||||||
mozilla.ai
|
|
||||||
mpettis.com
|
mpettis.com
|
||||||
mpfinance.com
|
mpfinance.com
|
||||||
mpinews.com
|
mpinews.com
|
||||||
@@ -2673,6 +2683,7 @@ ntd.tv
|
|||||||
ntdtv.com
|
ntdtv.com
|
||||||
ntdtv.com.tw
|
ntdtv.com.tw
|
||||||
ntdtv.cz
|
ntdtv.cz
|
||||||
|
nthlink.com
|
||||||
nuexpo.com
|
nuexpo.com
|
||||||
nurgo-software.com
|
nurgo-software.com
|
||||||
nusatrip.com
|
nusatrip.com
|
||||||
@@ -2987,7 +2998,6 @@ puffinbrowser.com
|
|||||||
pullfolio.co
|
pullfolio.co
|
||||||
pure18.com
|
pure18.com
|
||||||
pureconcepts.net
|
pureconcepts.net
|
||||||
puredns.org
|
|
||||||
pureinsight.org
|
pureinsight.org
|
||||||
purepdf.com
|
purepdf.com
|
||||||
purevpn.com
|
purevpn.com
|
||||||
@@ -3032,7 +3042,6 @@ radio.co
|
|||||||
radio.garden
|
radio.garden
|
||||||
radioaustralia.net.au
|
radioaustralia.net.au
|
||||||
radiohilight.net
|
radiohilight.net
|
||||||
radiojar.com
|
|
||||||
radioline.co
|
radioline.co
|
||||||
radiovaticana.org
|
radiovaticana.org
|
||||||
radiovncr.com
|
radiovncr.com
|
||||||
@@ -3145,7 +3154,6 @@ sadpanda.us
|
|||||||
safechat.com
|
safechat.com
|
||||||
safeguarddefenders.com
|
safeguarddefenders.com
|
||||||
safervpn.com
|
safervpn.com
|
||||||
sagernet.org
|
|
||||||
saintyculture.com
|
saintyculture.com
|
||||||
sakuralive.com
|
sakuralive.com
|
||||||
salvation.org.hk
|
salvation.org.hk
|
||||||
@@ -3435,6 +3443,7 @@ synapse.org
|
|||||||
syncback.com
|
syncback.com
|
||||||
synergyse.com
|
synergyse.com
|
||||||
syosetu.com
|
syosetu.com
|
||||||
|
sysub.com
|
||||||
t-g.com
|
t-g.com
|
||||||
t.co
|
t.co
|
||||||
t.me
|
t.me
|
||||||
@@ -3648,6 +3657,7 @@ to-porno.com
|
|||||||
togetter.com
|
togetter.com
|
||||||
token.im
|
token.im
|
||||||
tokenlon.im
|
tokenlon.im
|
||||||
|
tokenplus.app
|
||||||
tokyo-porn-tube.com
|
tokyo-porn-tube.com
|
||||||
tokyocn.com
|
tokyocn.com
|
||||||
tomp3.cc
|
tomp3.cc
|
||||||
@@ -3689,6 +3699,7 @@ trtworld.com
|
|||||||
truebuddha-md.org
|
truebuddha-md.org
|
||||||
trustwallet.com
|
trustwallet.com
|
||||||
truthsocial.com
|
truthsocial.com
|
||||||
|
tryai.dev
|
||||||
tryheart.jp
|
tryheart.jp
|
||||||
tsdr.uspto.gov
|
tsdr.uspto.gov
|
||||||
tt1069.com
|
tt1069.com
|
||||||
@@ -3870,6 +3881,7 @@ uyghurstudies.org
|
|||||||
uyghurtribunal.com
|
uyghurtribunal.com
|
||||||
uygur.fc2web.com
|
uygur.fc2web.com
|
||||||
uymaarip.com
|
uymaarip.com
|
||||||
|
v.recipes
|
||||||
v2.help
|
v2.help
|
||||||
v2.hysteria.network
|
v2.hysteria.network
|
||||||
v2ex.com
|
v2ex.com
|
||||||
@@ -4135,7 +4147,6 @@ www.ajsands.com
|
|||||||
www.antd.org
|
www.antd.org
|
||||||
www.aolnews.com
|
www.aolnews.com
|
||||||
www.bing.com
|
www.bing.com
|
||||||
www.clashverge.dev
|
|
||||||
www.cmoinc.org
|
www.cmoinc.org
|
||||||
www.dmm.com
|
www.dmm.com
|
||||||
www.eastturkistan.net
|
www.eastturkistan.net
|
||||||
@@ -4313,6 +4324,7 @@ zdnet.com.tw
|
|||||||
zello.com
|
zello.com
|
||||||
zenmate.com
|
zenmate.com
|
||||||
zenmate.com.ru
|
zenmate.com.ru
|
||||||
|
zenmux.ai
|
||||||
zerohedge.com
|
zerohedge.com
|
||||||
zeronet.io
|
zeronet.io
|
||||||
zh-hans.cfsh99.com
|
zh-hans.cfsh99.com
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
202607052256
|
202608102230
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import { connect } from 'ubus';
|
|||||||
import { cursor } from 'uci';
|
import { cursor } from 'uci';
|
||||||
|
|
||||||
import { urldecode, urlencode } from 'luci.http';
|
import { urldecode, urlencode } from 'luci.http';
|
||||||
import { init_action } from 'luci.sys';
|
|
||||||
import { remove_subscription_nodes } from '/etc/homeproxy/scripts/node_references.uc';
|
import { remove_subscription_nodes } from '/etc/homeproxy/scripts/node_references.uc';
|
||||||
import { parse_surge_subscription } from '/etc/homeproxy/scripts/subscription_parsers.uc';
|
import { parse_surge_subscription } from '/etc/homeproxy/scripts/subscription_parsers.uc';
|
||||||
|
|
||||||
@@ -216,6 +215,10 @@ function apply_tls_cert_pin_policy(config, label, pin) {
|
|||||||
version));
|
version));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function service_action(action) {
|
||||||
|
return system([ '/etc/init.d/homeproxy', action ]);
|
||||||
|
}
|
||||||
|
|
||||||
function parse_uri(uri) {
|
function parse_uri(uri) {
|
||||||
let config, url, params;
|
let config, url, params;
|
||||||
|
|
||||||
@@ -857,8 +860,8 @@ function main() {
|
|||||||
|
|
||||||
if (need_restart) {
|
if (need_restart) {
|
||||||
log('Restarting service...');
|
log('Restarting service...');
|
||||||
init_action('homeproxy', 'stop');
|
service_action('stop');
|
||||||
init_action('homeproxy', 'start');
|
service_action('start');
|
||||||
}
|
}
|
||||||
|
|
||||||
log(sprintf('%s nodes added, %s updated, %s removed.', added, updated, removed));
|
log(sprintf('%s nodes added, %s updated, %s removed.', added, updated, removed));
|
||||||
@@ -885,8 +888,8 @@ if (!isEmpty(subscription_urls)) {
|
|||||||
'请查看 HomeProxy 日志中的 [SUBSCRIBE] 记录并修复订阅配置');
|
'请查看 HomeProxy 日志中的 [SUBSCRIBE] 记录并修复订阅配置');
|
||||||
|
|
||||||
log('Restarting service...');
|
log('Restarting service...');
|
||||||
init_action('homeproxy', 'stop');
|
service_action('stop');
|
||||||
init_action('homeproxy', 'start');
|
service_action('start');
|
||||||
writeSubscriptionUpdateStatus(false, true, false, status_error);
|
writeSubscriptionUpdateStatus(false, true, false, status_error);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
|
|||||||
|
|
||||||
PKG_NAME:=luci-app-passwall
|
PKG_NAME:=luci-app-passwall
|
||||||
PKG_VERSION:=26.8.12
|
PKG_VERSION:=26.8.12
|
||||||
PKG_RELEASE:=215
|
PKG_RELEASE:=216
|
||||||
PKG_PO_VERSION:=$(PKG_VERSION)
|
PKG_PO_VERSION:=$(PKG_VERSION)
|
||||||
|
|
||||||
PKG_CONFIG_DEPENDS:= \
|
PKG_CONFIG_DEPENDS:= \
|
||||||
|
|||||||
@@ -20,19 +20,31 @@ function is_timehhmm(str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (typeof cbi_validators !== 'undefined' && cbi_validators !== null) {
|
if (typeof cbi_validators !== 'undefined' && cbi_validators !== null) {
|
||||||
|
cbi_validators['base64'] = function() {
|
||||||
|
return isBase64(this);
|
||||||
|
}
|
||||||
cbi_validators['json'] = function() {
|
cbi_validators['json'] = function() {
|
||||||
return is_json(this);
|
return is_json(this);
|
||||||
}
|
}
|
||||||
cbi_validators['timehhmm'] = function() {
|
cbi_validators['timehhmm'] = function() {
|
||||||
return is_timehhmm(this);
|
return is_timehhmm(this);
|
||||||
}
|
}
|
||||||
|
cbi_validators['uuid'] = function() {
|
||||||
|
return isUUID(this);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
L.require('validation').then(function(validation) {
|
L.require('validation').then(function(validation) {
|
||||||
|
validation.types['base64'] = function() {
|
||||||
|
return this.assert(isBase64(this.value), _('Must be Base64 text!'));
|
||||||
|
}
|
||||||
validation.types['json'] = function() {
|
validation.types['json'] = function() {
|
||||||
return this.assert(is_json(this.value), _('Must be JSON text!'));
|
return this.assert(is_json(this.value), _('Must be JSON text!'));
|
||||||
}
|
}
|
||||||
validation.types['timehhmm'] = function() {
|
validation.types['timehhmm'] = function() {
|
||||||
return this.assert(is_timehhmm(this.value), _('valid time (hh:mm)'));
|
return this.assert(is_timehhmm(this.value), _('valid time (hh:mm)'));
|
||||||
}
|
}
|
||||||
|
validation.types['uuid'] = function() {
|
||||||
|
return this.assert(isUUID(this.value), _('Must be UUID format!'));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,11 @@ function arraysEqual(a, b) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isBase64(str) {
|
||||||
|
const base64Regex = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$/;
|
||||||
|
return base64Regex.test(str);
|
||||||
|
}
|
||||||
|
|
||||||
function decodeIfBase64(str) {
|
function decodeIfBase64(str) {
|
||||||
try {
|
try {
|
||||||
let s = str.replace(/-/g, '+').replace(/_/g, '/');
|
let s = str.replace(/-/g, '+').replace(/_/g, '/');
|
||||||
@@ -24,6 +29,11 @@ function decodeIfBase64(str) {
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isUUID(str) {
|
||||||
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
||||||
|
return uuidRegex.test(str);
|
||||||
|
}
|
||||||
|
|
||||||
function waitForElement(selector, callback) {
|
function waitForElement(selector, callback) {
|
||||||
const el = document.querySelector(selector);
|
const el = document.querySelector(selector);
|
||||||
if (el) return callback(el);
|
if (el) return callback(el);
|
||||||
|
|||||||
@@ -57,12 +57,13 @@ function index()
|
|||||||
|
|
||||||
--[[ Server ]]
|
--[[ Server ]]
|
||||||
entry({"admin", "services", appname, "server"}, cbi(appname .. "/server/index"), _("Server-Side"), 99).leaf = true
|
entry({"admin", "services", appname, "server"}, cbi(appname .. "/server/index"), _("Server-Side"), 99).leaf = true
|
||||||
entry({"admin", "services", appname, "server_user"}, cbi(appname .. "/server/user")).leaf = true
|
entry({"admin", "services", appname, "server_config"}, cbi(appname .. "/server/server_config")).leaf = true
|
||||||
|
entry({"admin", "services", appname, "server_user_config"}, cbi(appname .. "/server/user_config")).leaf = true
|
||||||
|
|
||||||
--[[ API ]]
|
--[[ API ]]
|
||||||
entry({"admin", "services", appname, "server_update_config"}, call("server_update_config")).leaf = true
|
entry({"admin", "services", appname, "server_update_config"}, call("server_update_config")).leaf = true
|
||||||
entry({"admin", "services", appname, "server_user_status"}, call("server_user_status")).leaf = true
|
entry({"admin", "services", appname, "server_status"}, call("server_status")).leaf = true
|
||||||
entry({"admin", "services", appname, "server_user_log"}, call("server_user_log")).leaf = true
|
entry({"admin", "services", appname, "server_log"}, call("server_log")).leaf = true
|
||||||
entry({"admin", "services", appname, "server_get_log"}, call("server_get_log")).leaf = true
|
entry({"admin", "services", appname, "server_get_log"}, call("server_get_log")).leaf = true
|
||||||
entry({"admin", "services", appname, "server_clear_log"}, call("server_clear_log")).leaf = true
|
entry({"admin", "services", appname, "server_clear_log"}, call("server_clear_log")).leaf = true
|
||||||
entry({"admin", "services", appname, "link_add_node"}, call("link_add_node")).leaf = true
|
entry({"admin", "services", appname, "link_add_node"}, call("link_add_node")).leaf = true
|
||||||
@@ -89,7 +90,6 @@ function index()
|
|||||||
entry({"admin", "services", appname, "delete_select_nodes"}, call("delete_select_nodes")).leaf = true
|
entry({"admin", "services", appname, "delete_select_nodes"}, call("delete_select_nodes")).leaf = true
|
||||||
entry({"admin", "services", appname, "reassign_group"}, call("reassign_group")).leaf = true
|
entry({"admin", "services", appname, "reassign_group"}, call("reassign_group")).leaf = true
|
||||||
entry({"admin", "services", appname, "get_node"}, call("get_node")).leaf = true
|
entry({"admin", "services", appname, "get_node"}, call("get_node")).leaf = true
|
||||||
entry({"admin", "services", appname, "save_node_order"}, call("save_node_order")).leaf = true
|
|
||||||
entry({"admin", "services", appname, "save_node_list_opt"}, call("save_node_list_opt")).leaf = true
|
entry({"admin", "services", appname, "save_node_list_opt"}, call("save_node_list_opt")).leaf = true
|
||||||
entry({"admin", "services", appname, "update_rules"}, call("update_rules")).leaf = true
|
entry({"admin", "services", appname, "update_rules"}, call("update_rules")).leaf = true
|
||||||
entry({"admin", "services", appname, "rollback_rules"}, call("rollback_rules")).leaf = true
|
entry({"admin", "services", appname, "rollback_rules"}, call("rollback_rules")).leaf = true
|
||||||
@@ -101,7 +101,6 @@ function index()
|
|||||||
entry({"admin", "services", appname, "get_shunt_rules"}, call("get_shunt_rules")).leaf = true
|
entry({"admin", "services", appname, "get_shunt_rules"}, call("get_shunt_rules")).leaf = true
|
||||||
entry({"admin", "services", appname, "add_shunt_rule"}, call("add_shunt_rule")).leaf = true
|
entry({"admin", "services", appname, "add_shunt_rule"}, call("add_shunt_rule")).leaf = true
|
||||||
entry({"admin", "services", appname, "delete_select_shunt_rules"}, call("delete_select_shunt_rules")).leaf = true
|
entry({"admin", "services", appname, "delete_select_shunt_rules"}, call("delete_select_shunt_rules")).leaf = true
|
||||||
entry({"admin", "services", appname, "save_shunt_rule_order"}, call("save_shunt_rule_order")).leaf = true
|
|
||||||
|
|
||||||
--[[rule_list]]
|
--[[rule_list]]
|
||||||
entry({"admin", "services", appname, "read_rulelist"}, call("read_rulelist")).leaf = true
|
entry({"admin", "services", appname, "read_rulelist"}, call("read_rulelist")).leaf = true
|
||||||
@@ -797,19 +796,6 @@ function get_node()
|
|||||||
http_write_json(result)
|
http_write_json(result)
|
||||||
end
|
end
|
||||||
|
|
||||||
function save_node_order()
|
|
||||||
local ids = http.formvalue("ids") or ""
|
|
||||||
local new_order = {}
|
|
||||||
for id in ids:gmatch("([^,]+)") do
|
|
||||||
new_order[#new_order + 1] = id
|
|
||||||
end
|
|
||||||
for idx, name in ipairs(new_order) do
|
|
||||||
luci.sys.call(string.format("uci -q reorder %s.%s=%d", appname, name, idx - 1))
|
|
||||||
end
|
|
||||||
api.sh_uci_commit(appname)
|
|
||||||
http_write_json({ status = "ok" })
|
|
||||||
end
|
|
||||||
|
|
||||||
function reassign_group()
|
function reassign_group()
|
||||||
local ids = http.formvalue("ids") or ""
|
local ids = http.formvalue("ids") or ""
|
||||||
local group = http.formvalue("group") or "default"
|
local group = http.formvalue("group") or "default"
|
||||||
@@ -878,14 +864,14 @@ function server_update_config()
|
|||||||
http_write_json_error()
|
http_write_json_error()
|
||||||
end
|
end
|
||||||
|
|
||||||
function server_user_status()
|
function server_status()
|
||||||
local e = {}
|
local e = {}
|
||||||
e.index = http.formvalue("index")
|
e.index = http.formvalue("index")
|
||||||
e.status = luci.sys.call(string.format("/bin/busybox top -bn1 | grep -v 'grep' | grep '%s/bin/' | grep -i '%s' >/dev/null", appname .. "_server", http.formvalue("id"))) == 0
|
e.status = luci.sys.call(string.format("/bin/busybox top -bn1 | grep -v 'grep' | grep '%s/bin/' | grep -i '%s' >/dev/null", appname .. "_server", http.formvalue("id"))) == 0
|
||||||
http_write_json(e)
|
http_write_json(e)
|
||||||
end
|
end
|
||||||
|
|
||||||
function server_user_log()
|
function server_log()
|
||||||
local id = http.formvalue("id")
|
local id = http.formvalue("id")
|
||||||
if fs.access("/tmp/etc/passwall_server/" .. id .. ".log") then
|
if fs.access("/tmp/etc/passwall_server/" .. id .. ".log") then
|
||||||
local content = luci.sys.exec("cat /tmp/etc/passwall_server/" .. id .. ".log")
|
local content = luci.sys.exec("cat /tmp/etc/passwall_server/" .. id .. ".log")
|
||||||
@@ -1286,16 +1272,3 @@ function delete_select_shunt_rules()
|
|||||||
api.uci_save(uci, appname, true, true)
|
api.uci_save(uci, appname, true, true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function save_shunt_rule_order()
|
|
||||||
local ids = http.formvalue("ids") or ""
|
|
||||||
local new_order = {}
|
|
||||||
for id in ids:gmatch("([^,]+)") do
|
|
||||||
new_order[#new_order + 1] = id
|
|
||||||
end
|
|
||||||
for idx, name in ipairs(new_order) do
|
|
||||||
luci.sys.call(string.format("uci -q reorder %s.%s=%d", appname, name, idx - 1))
|
|
||||||
end
|
|
||||||
api.sh_uci_commit(appname)
|
|
||||||
http_write_json({ status = "ok" })
|
|
||||||
end
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
local api = require "luci.passwall.api"
|
local api = require "luci.passwall.api"
|
||||||
local appname = "passwall"
|
local appname = "passwall"
|
||||||
local sys = api.sys
|
|
||||||
local datatypes = api.datatypes
|
|
||||||
|
|
||||||
api.set_default_cbi()
|
api.set_default_cbi()
|
||||||
|
|
||||||
@@ -30,6 +28,29 @@ o:value("https://connectivitycheck.platform.hicloud.com/generate_204", "HiCloud
|
|||||||
o:value("https://wifi.vivo.com.cn/generate_204", "VIVO (CN)")
|
o:value("https://wifi.vivo.com.cn/generate_204", "VIVO (CN)")
|
||||||
o.default = o.keylist[3]
|
o.default = o.keylist[3]
|
||||||
|
|
||||||
m:append(Template(appname .. "/node_list/node_list"))
|
if true then
|
||||||
|
local o = Template(appname .. "/node_list/node_list")
|
||||||
|
o.map = m
|
||||||
|
o.api = api
|
||||||
|
m:append(o)
|
||||||
|
|
||||||
|
if luci.http.formvalue("cbi.submit") == "1" then
|
||||||
|
local group_order = {}
|
||||||
|
group_order = luci.http.formvaluetable("group.order")
|
||||||
|
if group_order then
|
||||||
|
for k, v in pairs(group_order) do
|
||||||
|
if v and v~= "" then
|
||||||
|
local new_order = {}
|
||||||
|
string.gsub(v, "[^" .. " " .. "]+", function(w)
|
||||||
|
new_order[#new_order + 1] = w
|
||||||
|
end)
|
||||||
|
for idx, name in ipairs(new_order) do
|
||||||
|
m.uci:reorder(appname, name, idx - 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return api.return_map(m)
|
return api.return_map(m)
|
||||||
|
|||||||
@@ -146,7 +146,26 @@ end
|
|||||||
s:append(Template(appname .. "/rule/rule_version"))
|
s:append(Template(appname .. "/rule/rule_version"))
|
||||||
|
|
||||||
if has_xray or has_singbox then
|
if has_xray or has_singbox then
|
||||||
m:append(Template(appname .. "/rule/shunt_rule_list"))
|
local o = Template(appname .. "/rule/shunt_rule_list")
|
||||||
|
o.api = api
|
||||||
|
m:append(o)
|
||||||
|
|
||||||
|
if luci.http.formvalue("cbi.submit") == "1" then
|
||||||
|
local group_order = luci.http.formvaluetable("group.order")
|
||||||
|
if group_order then
|
||||||
|
for k, v in pairs(group_order) do
|
||||||
|
if v and v~= "" then
|
||||||
|
local new_order = {}
|
||||||
|
string.gsub(v, "[^" .. " " .. "]+", function(w)
|
||||||
|
new_order[#new_order + 1] = w
|
||||||
|
end)
|
||||||
|
for idx, name in ipairs(new_order) do
|
||||||
|
m.uci:reorder(appname, name, idx - 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return api.return_map(m)
|
return api.return_map(m)
|
||||||
|
|||||||
@@ -18,38 +18,39 @@ e.default = "90"
|
|||||||
e.datatype = "range(0,600)"
|
e.datatype = "range(0,600)"
|
||||||
e.rmempty = false
|
e.rmempty = false
|
||||||
|
|
||||||
local cfgname = "user"
|
|
||||||
t = m:section(TypedSection, cfgname, translate("Users Manager"))
|
s_server = m:section(TypedSection, "server", translate("Servers Manager"))
|
||||||
t.anonymous = true
|
s_server.anonymous = true
|
||||||
t.addremove = true
|
s_server.addremove = true
|
||||||
t.sortable = true
|
s_server.sortable = true
|
||||||
t.template = "cbi/tblsection"
|
s_server.template = "cbi/tblsection"
|
||||||
t.extedit = api.url("server_user", "%s")
|
s_server.extedit = api.url("server_config", "%s")
|
||||||
function t.create(e, t)
|
function s_server.create(self, section)
|
||||||
local uid = api.gen_random_char()
|
local uid = api.gen_random_char()
|
||||||
TypedSection.create(e, uid)
|
TypedSection.create(self, uid)
|
||||||
luci.http.redirect(e.extedit:format(uid))
|
luci.http.redirect(self.extedit:format(uid))
|
||||||
end
|
end
|
||||||
function t.remove(e, t)
|
function s_server.remove(self, section)
|
||||||
e.map.proceed = true
|
local o = m:get(section) or {}
|
||||||
e.map:del(t)
|
if o[".type"] == self.sectiontype then
|
||||||
luci.http.redirect(api.url("server"))
|
m:del(section)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
e = t:option(Flag, "enable", translate("Enable"))
|
e = s_server:option(Flag, "enable", translate("Enable"))
|
||||||
e.width = "5%"
|
e.width = "5%"
|
||||||
e.rmempty = false
|
e.rmempty = false
|
||||||
|
|
||||||
e = t:option(DummyValue, "status", translate("Status"))
|
e = s_server:option(DummyValue, "status", translate("Status"))
|
||||||
e.rawhtml = true
|
e.rawhtml = true
|
||||||
e.cfgvalue = function(t, n)
|
e.cfgvalue = function(t, n)
|
||||||
return string.format('<font class="_users_status">%s</font>', translate("Collecting data..."))
|
return string.format('<font class="_servers_status">%s</font>', translate("Collecting data..."))
|
||||||
end
|
end
|
||||||
|
|
||||||
e = t:option(DummyValue, "remarks", translate("Remarks"))
|
e = s_server:option(DummyValue, "remarks", translate("Remarks"))
|
||||||
e.width = "15%"
|
e.width = "15%"
|
||||||
|
|
||||||
e = t:option(DummyValue, "type", translate("Type"))
|
e = s_server:option(DummyValue, "type", translate("Type"))
|
||||||
e.width = "20%"
|
e.width = "20%"
|
||||||
e.rawhtml = true
|
e.rawhtml = true
|
||||||
e.cfgvalue = function(t, n)
|
e.cfgvalue = function(t, n)
|
||||||
@@ -87,20 +88,74 @@ e.cfgvalue = function(t, n)
|
|||||||
return str
|
return str
|
||||||
end
|
end
|
||||||
|
|
||||||
e = t:option(DummyValue, "port", translate("Port"))
|
e = s_server:option(DummyValue, "port", translate("Port"))
|
||||||
|
|
||||||
e = t:option(Flag, "log", translate("Log"))
|
e = s_server:option(Flag, "log", translate("Log"))
|
||||||
e.default = "1"
|
e.default = "1"
|
||||||
e.rmempty = false
|
e.rmempty = false
|
||||||
|
|
||||||
local sortable = Template(appname .. "/cbi/sortable")
|
local sortable = Template(appname .. "/cbi/sortable")
|
||||||
sortable.api = api
|
sortable.api = api
|
||||||
sortable.appname = m.config
|
sortable.appname = m.config
|
||||||
sortable.target_cfgname = cfgname
|
sortable.target_cfgname = s_server.sectiontype
|
||||||
|
m:append(sortable)
|
||||||
|
|
||||||
|
local server_list_status = Template(appname .. "/server/server_list_status")
|
||||||
|
server_list_status.api = api
|
||||||
|
server_list_status.appname = m.config
|
||||||
|
server_list_status.sectiontype = s_server.sectiontype
|
||||||
|
m:append(server_list_status)
|
||||||
|
|
||||||
|
s_user = m:section(TypedSection, "user", translate("Users Manager"))
|
||||||
|
s_user.anonymous = true
|
||||||
|
s_user.addremove = true
|
||||||
|
s_user.sortable = true
|
||||||
|
s_user.template = "cbi/tblsection"
|
||||||
|
s_user.extedit = api.url("server_user_config", "%s")
|
||||||
|
s_user.create = function(e, section)
|
||||||
|
local uid = api.gen_random_char()
|
||||||
|
TypedSection.create(e, uid)
|
||||||
|
luci.http.redirect(e.extedit:format(uid))
|
||||||
|
end
|
||||||
|
s_user.remove = function(self, section)
|
||||||
|
local o = m:get(section) or {}
|
||||||
|
if o[".type"] == self.sectiontype then
|
||||||
|
m.uci:foreach(m.config, "server", function(o)
|
||||||
|
if o.user and section == o.user then
|
||||||
|
m:set(o[".name"], "user", "")
|
||||||
|
end
|
||||||
|
local changed = false
|
||||||
|
local users = o.users or {}
|
||||||
|
for i = #users, 1, -1 do
|
||||||
|
if section == users[i] then
|
||||||
|
table.remove(users, i)
|
||||||
|
changed = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if changed then
|
||||||
|
m:set(o[".name"], "users", users)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
m:del(section)
|
||||||
|
luci.http.redirect(api.url("server"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
e = s_user:option(DummyValue, "username", translate("Username"))
|
||||||
|
e.width = "25%"
|
||||||
|
|
||||||
|
e = s_user:option(DummyValue, "password", translate("Password"))
|
||||||
|
e.width = "25%"
|
||||||
|
|
||||||
|
e = s_user:option(DummyValue, "uuid", "UUID")
|
||||||
|
e.width = "25%"
|
||||||
|
|
||||||
|
local sortable = Template(appname .. "/cbi/sortable")
|
||||||
|
sortable.api = api
|
||||||
|
sortable.appname = m.config
|
||||||
|
sortable.target_cfgname = s_user.sectiontype
|
||||||
m:append(sortable)
|
m:append(sortable)
|
||||||
|
|
||||||
m:append(Template(appname .. "/server/log"))
|
m:append(Template(appname .. "/server/log"))
|
||||||
|
|
||||||
m:append(Template(appname .. "/server/users_list_status"))
|
|
||||||
|
|
||||||
return api.return_map(m)
|
return api.return_map(m)
|
||||||
|
|||||||
+6
-1
@@ -20,7 +20,12 @@ m:append(header)
|
|||||||
|
|
||||||
m:append(Template(appname .. "/cbi/nodes_listvalue_com"))
|
m:append(Template(appname .. "/cbi/nodes_listvalue_com"))
|
||||||
|
|
||||||
s = m:section(NamedSection, arg[1], "user", "")
|
user_list = {}
|
||||||
|
m.uci:foreach(m.config, "user", function(s)
|
||||||
|
user_list[#user_list + 1] = s
|
||||||
|
end)
|
||||||
|
|
||||||
|
s = m:section(NamedSection, arg[1], "server", "")
|
||||||
s.addremove = false
|
s.addremove = false
|
||||||
s.dynamic = false
|
s.dynamic = false
|
||||||
|
|
||||||
@@ -28,6 +28,12 @@ o = s:option(Value, _n("port"), translate("Listen Port"))
|
|||||||
o.datatype = "port"
|
o.datatype = "port"
|
||||||
o:depends({ [_n("custom")] = false })
|
o:depends({ [_n("custom")] = false })
|
||||||
|
|
||||||
|
o = s:option(DynamicList, _n("users"), translate("User"))
|
||||||
|
for i, v in ipairs(user_list) do
|
||||||
|
o:value(v[".name"], v.username)
|
||||||
|
end
|
||||||
|
o:depends({ [_n("custom")] = false })
|
||||||
|
|
||||||
o = s:option(Flag, _n("realms"), translate("Realms"))
|
o = s:option(Flag, _n("realms"), translate("Realms"))
|
||||||
o.default = "0"
|
o.default = "0"
|
||||||
o.rewrite_option = o.option
|
o.rewrite_option = o.option
|
||||||
@@ -48,11 +54,6 @@ o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio
|
|||||||
o.rewrite_option = o.option
|
o.rewrite_option = o.option
|
||||||
o:depends({ [_n("realms")] = "1" })
|
o:depends({ [_n("realms")] = "1" })
|
||||||
|
|
||||||
o = s:option(Value, _n("auth_password"), translate("Auth Password"))
|
|
||||||
o.password = true
|
|
||||||
o.rewrite_option = o.option
|
|
||||||
o:depends({ [_n("custom")] = false })
|
|
||||||
|
|
||||||
o = s:option(ListValue, _n("obfs_type"), translate("Obfs Type"))
|
o = s:option(ListValue, _n("obfs_type"), translate("Obfs Type"))
|
||||||
o:value("", translate("Disable"))
|
o:value("", translate("Disable"))
|
||||||
o:value("salamander")
|
o:value("salamander")
|
||||||
|
|||||||
@@ -48,27 +48,18 @@ o = s:option(Value, _n("port"), translate("Listen Port"))
|
|||||||
o.datatype = "port"
|
o.datatype = "port"
|
||||||
o:depends({ [_n("custom")] = false })
|
o:depends({ [_n("custom")] = false })
|
||||||
|
|
||||||
o = s:option(Flag, _n("auth"), translate("Auth"))
|
|
||||||
o.validate = function(self, value, t)
|
o = s:option(DynamicList, _n("users"), translate("User"))
|
||||||
if value and value == "1" then
|
for i, v in ipairs(user_list) do
|
||||||
local user_v = s.fields[_n("username")] and s.fields[_n("username")]:formvalue(t) or ""
|
o:value(v[".name"], v.username)
|
||||||
local pass_v = s.fields[_n("password")] and s.fields[_n("password")]:formvalue(t) or ""
|
|
||||||
if user_v == "" or pass_v == "" then
|
|
||||||
return nil, translate("Username and Password must be used together!")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return value
|
|
||||||
end
|
end
|
||||||
o:depends({ [_n("protocol")] = "socks" })
|
|
||||||
o:depends({ [_n("protocol")] = "http" })
|
o:depends({ [_n("protocol")] = "http" })
|
||||||
|
o:depends({ [_n("protocol")] = "socks" })
|
||||||
o = s:option(Value, _n("username"), translate("Username"))
|
|
||||||
o:depends({ [_n("auth")] = true })
|
|
||||||
|
|
||||||
o = s:option(Value, _n("password"), translate("Password"))
|
|
||||||
o.password = true
|
|
||||||
o:depends({ [_n("auth")] = true })
|
|
||||||
o:depends({ [_n("protocol")] = "shadowsocks" })
|
o:depends({ [_n("protocol")] = "shadowsocks" })
|
||||||
|
o:depends({ [_n("protocol")] = "vmess" })
|
||||||
|
o:depends({ [_n("protocol")] = "vless" })
|
||||||
|
o:depends({ [_n("protocol")] = "trojan" })
|
||||||
|
o:depends({ [_n("protocol")] = "hysteria2" })
|
||||||
|
|
||||||
o = s:option(ListValue, _n("d_protocol"), translate("Destination protocol"))
|
o = s:option(ListValue, _n("d_protocol"), translate("Destination protocol"))
|
||||||
o:value("tcp", "TCP")
|
o:value("tcp", "TCP")
|
||||||
@@ -97,6 +88,9 @@ o.rewrite_option = "method"
|
|||||||
for a, t in ipairs(x_ss_method_list) do o:value(t) end
|
for a, t in ipairs(x_ss_method_list) do o:value(t) end
|
||||||
o:depends({ [_n("protocol")] = "shadowsocks" })
|
o:depends({ [_n("protocol")] = "shadowsocks" })
|
||||||
|
|
||||||
|
o = s:option(Value, _n("ss_password"), translate("Password"))
|
||||||
|
o:depends({ [_n("protocol")] = "shadowsocks" })
|
||||||
|
|
||||||
o = s:option(ListValue, _n("ss_network"), translate("Transport"))
|
o = s:option(ListValue, _n("ss_network"), translate("Transport"))
|
||||||
o.default = "tcp,udp"
|
o.default = "tcp,udp"
|
||||||
o:value("tcp", "TCP")
|
o:value("tcp", "TCP")
|
||||||
@@ -109,14 +103,6 @@ o.default = "1"
|
|||||||
o.rmempty = false
|
o.rmempty = false
|
||||||
o:depends({ [_n("protocol")] = "socks" })
|
o:depends({ [_n("protocol")] = "socks" })
|
||||||
|
|
||||||
o = s:option(DynamicList, _n("uuid"), translate("ID") .. "/" .. translate("Password"))
|
|
||||||
for i = 1, 3 do
|
|
||||||
o:value(api.gen_uuid())
|
|
||||||
end
|
|
||||||
o:depends({ [_n("protocol")] = "vmess" })
|
|
||||||
o:depends({ [_n("protocol")] = "vless" })
|
|
||||||
o:depends({ [_n("protocol")] = "trojan" })
|
|
||||||
|
|
||||||
o = s:option(ListValue, _n("flow"), translate("flow"))
|
o = s:option(ListValue, _n("flow"), translate("flow"))
|
||||||
o.default = ""
|
o.default = ""
|
||||||
o:value("", translate("Disable"))
|
o:value("", translate("Disable"))
|
||||||
@@ -141,10 +127,6 @@ o = s:option(DynamicList, _n("hysteria2_realm_stun"), translate("Realm STUN"))
|
|||||||
o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" }
|
o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" }
|
||||||
o:depends({ [_n("hysteria2_realms")] = "1" })
|
o:depends({ [_n("hysteria2_realms")] = "1" })
|
||||||
|
|
||||||
o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password"))
|
|
||||||
o.password = true
|
|
||||||
o:depends({ [_n("protocol")] = "hysteria2"})
|
|
||||||
|
|
||||||
o = s:option(ListValue, _n("hysteria2_obfs_type"), translate("Obfs Type"))
|
o = s:option(ListValue, _n("hysteria2_obfs_type"), translate("Obfs Type"))
|
||||||
o:value("", translate("Disable"))
|
o:value("", translate("Disable"))
|
||||||
o:value("salamander")
|
o:value("salamander")
|
||||||
|
|||||||
@@ -60,35 +60,27 @@ o = s:option(Value, _n("port"), translate("Listen Port"))
|
|||||||
o.datatype = "port"
|
o.datatype = "port"
|
||||||
o:depends({ [_n("custom")] = false })
|
o:depends({ [_n("custom")] = false })
|
||||||
|
|
||||||
o = s:option(Flag, _n("auth"), translate("Auth"))
|
o = s:option(DynamicList, _n("users"), translate("User"))
|
||||||
o.validate = function(self, value, t)
|
for i, v in ipairs(user_list) do
|
||||||
if value and value == "1" then
|
o:value(v[".name"], v.username)
|
||||||
local user_v = s.fields[_n("username")] and s.fields[_n("username")]:formvalue(t) or ""
|
|
||||||
local pass_v = s.fields[_n("password")] and s.fields[_n("password")]:formvalue(t) or ""
|
|
||||||
if user_v == "" or pass_v == "" then
|
|
||||||
return nil, translate("Username and Password must be used together!")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return value
|
|
||||||
end
|
end
|
||||||
o:depends({ [_n("protocol")] = "mixed" })
|
o:depends({ [_n("protocol")] = "mixed" })
|
||||||
o:depends({ [_n("protocol")] = "socks" })
|
o:depends({ [_n("protocol")] = "socks" })
|
||||||
o:depends({ [_n("protocol")] = "http" })
|
o:depends({ [_n("protocol")] = "http" })
|
||||||
|
|
||||||
o = s:option(Value, _n("username"), translate("Username"))
|
|
||||||
o:depends({ [_n("auth")] = true })
|
|
||||||
o:depends({ [_n("protocol")] = "naive" })
|
|
||||||
o:depends({ [_n("protocol")] = "anytls" })
|
|
||||||
|
|
||||||
o = s:option(Value, _n("password"), translate("Password"))
|
|
||||||
o.password = true
|
|
||||||
o:depends({ [_n("auth")] = true })
|
|
||||||
o:depends({ [_n("protocol")] = "shadowsocks" })
|
o:depends({ [_n("protocol")] = "shadowsocks" })
|
||||||
|
o:depends({ [_n("protocol")] = "vmess" })
|
||||||
|
o:depends({ [_n("protocol")] = "vless" })
|
||||||
|
o:depends({ [_n("protocol")] = "trojan" })
|
||||||
o:depends({ [_n("protocol")] = "naive" })
|
o:depends({ [_n("protocol")] = "naive" })
|
||||||
|
o:depends({ [_n("protocol")] = "hysteria" })
|
||||||
o:depends({ [_n("protocol")] = "tuic" })
|
o:depends({ [_n("protocol")] = "tuic" })
|
||||||
|
o:depends({ [_n("protocol")] = "hysteria2" })
|
||||||
o:depends({ [_n("protocol")] = "anytls" })
|
o:depends({ [_n("protocol")] = "anytls" })
|
||||||
|
|
||||||
if singbox_tags:find("with_quic") then
|
if singbox_tags:find("with_quic") then
|
||||||
|
o = s:option(Value, _n("hysteria_obfs"), translate("Obfs Password"))
|
||||||
|
o:depends({ [_n("protocol")] = "hysteria" })
|
||||||
|
|
||||||
o = s:option(Value, _n("hysteria_up_mbps"), translate("Max upload Mbps"))
|
o = s:option(Value, _n("hysteria_up_mbps"), translate("Max upload Mbps"))
|
||||||
o.default = "100"
|
o.default = "100"
|
||||||
o:depends({ [_n("protocol")] = "hysteria" })
|
o:depends({ [_n("protocol")] = "hysteria" })
|
||||||
@@ -97,20 +89,6 @@ if singbox_tags:find("with_quic") then
|
|||||||
o.default = "100"
|
o.default = "100"
|
||||||
o:depends({ [_n("protocol")] = "hysteria" })
|
o:depends({ [_n("protocol")] = "hysteria" })
|
||||||
|
|
||||||
o = s:option(Value, _n("hysteria_obfs"), translate("Obfs Password"))
|
|
||||||
o:depends({ [_n("protocol")] = "hysteria" })
|
|
||||||
|
|
||||||
o = s:option(ListValue, _n("hysteria_auth_type"), translate("Auth Type"))
|
|
||||||
o:value("disable", translate("Disable"))
|
|
||||||
o:value("string", translate("STRING"))
|
|
||||||
o:value("base64", translate("BASE64"))
|
|
||||||
o:depends({ [_n("protocol")] = "hysteria" })
|
|
||||||
|
|
||||||
o = s:option(Value, _n("hysteria_auth_password"), translate("Auth Password"))
|
|
||||||
o.password = true
|
|
||||||
o:depends({ [_n("protocol")] = "hysteria", [_n("hysteria_auth_type")] = "string"})
|
|
||||||
o:depends({ [_n("protocol")] = "hysteria", [_n("hysteria_auth_type")] = "base64"})
|
|
||||||
|
|
||||||
o = s:option(Value, _n("hysteria_recv_window_conn"), translate("QUIC stream receive window"))
|
o = s:option(Value, _n("hysteria_recv_window_conn"), translate("QUIC stream receive window"))
|
||||||
o:depends({ [_n("protocol")] = "hysteria" })
|
o:depends({ [_n("protocol")] = "hysteria" })
|
||||||
|
|
||||||
@@ -174,10 +152,6 @@ if singbox_tags:find("with_quic") then
|
|||||||
o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" }
|
o.default = { "stun.sip.us:3478", "stun.nextcloud.com:3478", "global.stun.twilio.com:3478" }
|
||||||
o:depends({ [_n("hysteria2_realms")] = "1" })
|
o:depends({ [_n("hysteria2_realms")] = "1" })
|
||||||
|
|
||||||
o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password"))
|
|
||||||
o.password = true
|
|
||||||
o:depends({ [_n("protocol")] = "hysteria2"})
|
|
||||||
|
|
||||||
o = s:option(ListValue, _n("hysteria2_obfs_type"), translate("Obfs Type"))
|
o = s:option(ListValue, _n("hysteria2_obfs_type"), translate("Obfs Type"))
|
||||||
o:value("", translate("Disable"))
|
o:value("", translate("Disable"))
|
||||||
o:value("salamander")
|
o:value("salamander")
|
||||||
@@ -233,14 +207,8 @@ o.rewrite_option = "method"
|
|||||||
for a, t in ipairs(ss_method_list) do o:value(t) end
|
for a, t in ipairs(ss_method_list) do o:value(t) end
|
||||||
o:depends({ [_n("protocol")] = "shadowsocks" })
|
o:depends({ [_n("protocol")] = "shadowsocks" })
|
||||||
|
|
||||||
o = s:option(DynamicList, _n("uuid"), translate("ID") .. "/" .. translate("Password"))
|
o = s:option(Value, _n("ss_password"), translate("Password"))
|
||||||
for i = 1, 3 do
|
o:depends({ [_n("protocol")] = "shadowsocks" })
|
||||||
o:value(api.gen_uuid())
|
|
||||||
end
|
|
||||||
o:depends({ [_n("protocol")] = "vmess" })
|
|
||||||
o:depends({ [_n("protocol")] = "vless" })
|
|
||||||
o:depends({ [_n("protocol")] = "trojan" })
|
|
||||||
o:depends({ [_n("protocol")] = "tuic" })
|
|
||||||
|
|
||||||
o = s:option(ListValue, _n("flow"), translate("flow"))
|
o = s:option(ListValue, _n("flow"), translate("flow"))
|
||||||
o.default = ""
|
o.default = ""
|
||||||
|
|||||||
@@ -27,22 +27,11 @@ o = s:option(Value, _n("port"), translate("Listen Port"))
|
|||||||
o.datatype = "port"
|
o.datatype = "port"
|
||||||
|
|
||||||
o = s:option(Flag, _n("auth"), translate("Auth"))
|
o = s:option(Flag, _n("auth"), translate("Auth"))
|
||||||
o.validate = function(self, value, t)
|
|
||||||
if value and value == "1" then
|
o = s:option(ListValue, _n("user"), translate("User"))
|
||||||
local user_v = s.fields[_n("username")] and s.fields[_n("username")]:formvalue(t) or ""
|
for i, v in ipairs(user_list) do
|
||||||
local pass_v = s.fields[_n("password")] and s.fields[_n("password")]:formvalue(t) or ""
|
o:value(v[".name"], v.username)
|
||||||
if user_v == "" or pass_v == "" then
|
|
||||||
return nil, translate("Username and Password must be used together!")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return value
|
|
||||||
end
|
end
|
||||||
|
|
||||||
o = s:option(Value, _n("username"), translate("Username"))
|
|
||||||
o:depends({ [_n("auth")] = true })
|
|
||||||
|
|
||||||
o = s:option(Value, _n("password"), translate("Password"))
|
|
||||||
o.password = true
|
|
||||||
o:depends({ [_n("auth")] = true })
|
o:depends({ [_n("auth")] = true })
|
||||||
|
|
||||||
o = s:option(Flag, _n("log"), translate("Log"))
|
o = s:option(Flag, _n("log"), translate("Log"))
|
||||||
|
|||||||
@@ -32,8 +32,10 @@ o = s:option(Value, _n("port"), translate("Listen Port"))
|
|||||||
o.datatype = "port"
|
o.datatype = "port"
|
||||||
o:depends({ [_n("custom")] = false })
|
o:depends({ [_n("custom")] = false })
|
||||||
|
|
||||||
o = s:option(Value, _n("password"), translate("Password"))
|
o = s:option(ListValue, _n("user"), translate("User"))
|
||||||
o.password = true
|
for i, v in ipairs(user_list) do
|
||||||
|
o:value(v[".name"], v.username)
|
||||||
|
end
|
||||||
o:depends({ [_n("custom")] = false })
|
o:depends({ [_n("custom")] = false })
|
||||||
|
|
||||||
o = s:option(ListValue, _n("method"), translate("Encrypt Method"))
|
o = s:option(ListValue, _n("method"), translate("Encrypt Method"))
|
||||||
|
|||||||
@@ -45,8 +45,10 @@ o = s:option(Value, _n("port"), translate("Listen Port"))
|
|||||||
o.datatype = "port"
|
o.datatype = "port"
|
||||||
o:depends({ [_n("custom")] = false })
|
o:depends({ [_n("custom")] = false })
|
||||||
|
|
||||||
o = s:option(Value, _n("password"), translate("Password"))
|
o = s:option(ListValue, _n("user"), translate("User"))
|
||||||
o.password = true
|
for i, v in ipairs(user_list) do
|
||||||
|
o:value(v[".name"], v.username)
|
||||||
|
end
|
||||||
o:depends({ [_n("custom")] = false })
|
o:depends({ [_n("custom")] = false })
|
||||||
|
|
||||||
o = s:option(ListValue, _n("method"), translate("Encrypt Method"))
|
o = s:option(ListValue, _n("method"), translate("Encrypt Method"))
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
api = require "luci.passwall.api"
|
||||||
|
appname = api.appname
|
||||||
|
fs = api.fs
|
||||||
|
|
||||||
|
api.set_default_cbi()
|
||||||
|
|
||||||
|
m = Map("passwall_server", translate("User Config"))
|
||||||
|
m.redirect = api.url("server")
|
||||||
|
api.set_apply_on_parse(m)
|
||||||
|
|
||||||
|
if not arg[1] or not m:get(arg[1]) then
|
||||||
|
luci.http.redirect(m.redirect)
|
||||||
|
end
|
||||||
|
|
||||||
|
s = m:section(NamedSection, arg[1], "user", "")
|
||||||
|
s.addremove = false
|
||||||
|
s.dynamic = false
|
||||||
|
|
||||||
|
o = s:option(Value, "username", translate("Username"))
|
||||||
|
o.datatype = "and(uciname,maxlength(24))"
|
||||||
|
o.rmempty = false
|
||||||
|
|
||||||
|
o = s:option(Value, "password", translate("Password"))
|
||||||
|
o.rmempty = false
|
||||||
|
|
||||||
|
o = s:option(Value, "uuid", "UUID")
|
||||||
|
o.datatype = "uuid"
|
||||||
|
o.default = api.gen_uuid()
|
||||||
|
o.rmempty = false
|
||||||
|
|
||||||
|
return api.return_map(m)
|
||||||
@@ -369,6 +369,12 @@ function is_timehhmm(str)
|
|||||||
end
|
end
|
||||||
datatypes.timehhmm = is_timehhmm
|
datatypes.timehhmm = is_timehhmm
|
||||||
|
|
||||||
|
function is_uuid(str)
|
||||||
|
local pattern = "^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$"
|
||||||
|
return string.match(str, pattern) ~= nil
|
||||||
|
end
|
||||||
|
datatypes.uuid = is_uuid
|
||||||
|
|
||||||
function is_normal_node(e)
|
function is_normal_node(e)
|
||||||
if e and e.type and e.protocol and (e.protocol == "_balancing" or e.protocol == "_shunt" or e.protocol == "_iface" or e.protocol == "_urltest") then
|
if e and e.type and e.protocol and (e.protocol == "_balancing" or e.protocol == "_shunt" or e.protocol == "_iface" or e.protocol == "_urltest") then
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -102,29 +102,33 @@ local function start()
|
|||||||
nft_file:write('flush chain inet fw4 PSW-SERVER\n')
|
nft_file:write('flush chain inet fw4 PSW-SERVER\n')
|
||||||
nft_file:write('insert rule inet fw4 input position 0 jump PSW-SERVER comment "PSW-SERVER"\n')
|
nft_file:write('insert rule inet fw4 input position 0 jump PSW-SERVER comment "PSW-SERVER"\n')
|
||||||
end
|
end
|
||||||
uci:foreach(CONFIG, "user", function(user)
|
uci:foreach(CONFIG, "server", function(server)
|
||||||
local id = user[".name"]
|
local id = server[".name"]
|
||||||
local enable = user.enable
|
local enable = server.enable
|
||||||
if enable and tonumber(enable) == 1 then
|
if enable and tonumber(enable) == 1 then
|
||||||
local enable_log = user.log
|
local enable_log = server.log
|
||||||
local log_path = nil
|
local log_path = nil
|
||||||
if enable_log and enable_log == "1" then
|
if enable_log and enable_log == "1" then
|
||||||
log_path = CONFIG_PATH .. "/" .. id .. ".log"
|
log_path = CONFIG_PATH .. "/" .. id .. ".log"
|
||||||
else
|
else
|
||||||
log_path = nil
|
log_path = nil
|
||||||
end
|
end
|
||||||
local remarks = user.remarks
|
local remarks = server.remarks
|
||||||
local port = tonumber(user.port)
|
local port = tonumber(server.port)
|
||||||
local bin
|
local bin
|
||||||
local config = {}
|
local config = {}
|
||||||
local config_file = CONFIG_PATH .. "/" .. id .. ".json"
|
local config_file = CONFIG_PATH .. "/" .. id .. ".json"
|
||||||
local udp_forward = 1
|
local udp_forward = 1
|
||||||
local type = user.type or ""
|
local type = server.type or ""
|
||||||
if type == "Socks" then
|
if type == "Socks" then
|
||||||
local auth = ""
|
local auth = ""
|
||||||
if user.auth and user.auth == "1" then
|
if server.auth and server.auth == "1" then
|
||||||
local username = user.username or ""
|
local user = nil
|
||||||
local password = user.password or ""
|
if server.user then
|
||||||
|
user = uci:get_all("passwall_server", server.user)
|
||||||
|
end
|
||||||
|
local username = user and user.username or ""
|
||||||
|
local password = user and user.password or ""
|
||||||
if username ~= "" and password ~= "" then
|
if username ~= "" and password ~= "" then
|
||||||
username = "-u " .. username
|
username = "-u " .. username
|
||||||
password = "-P " .. password
|
password = "-P " .. password
|
||||||
@@ -133,59 +137,59 @@ local function start()
|
|||||||
end
|
end
|
||||||
bin = ln_run("/usr/bin/microsocks", "microsocks_" .. id, string.format("-i :: -p %s %s", port, auth), log_path)
|
bin = ln_run("/usr/bin/microsocks", "microsocks_" .. id, string.format("-i :: -p %s %s", port, auth), log_path)
|
||||||
elseif type == "SSR" then
|
elseif type == "SSR" then
|
||||||
if user.custom == "1" and user.config_str then
|
if server.custom == "1" and server.config_str then
|
||||||
config = jsonc.parse(api.base64Decode(user.config_str))
|
config = jsonc.parse(api.base64Decode(server.config_str))
|
||||||
else
|
else
|
||||||
config = require(require_dir .. "util_shadowsocks").gen_config_server(user)
|
config = require(require_dir .. "util_shadowsocks").gen_config_server(server)
|
||||||
end
|
end
|
||||||
local udp_param = ""
|
local udp_param = ""
|
||||||
udp_forward = tonumber(user.udp_forward) or 1
|
udp_forward = tonumber(server.udp_forward) or 1
|
||||||
if udp_forward == 1 then
|
if udp_forward == 1 then
|
||||||
udp_param = "-u"
|
udp_param = "-u"
|
||||||
end
|
end
|
||||||
type = type:lower()
|
type = type:lower()
|
||||||
bin = ln_run("/usr/bin/" .. type .. "-server", type .. "-server", "-c " .. config_file .. " " .. udp_param, log_path)
|
bin = ln_run("/usr/bin/" .. type .. "-server", type .. "-server", "-c " .. config_file .. " " .. udp_param, log_path)
|
||||||
elseif type == "SS-Rust" then
|
elseif type == "SS-Rust" then
|
||||||
if user.custom == "1" and user.config_str then
|
if server.custom == "1" and server.config_str then
|
||||||
config = jsonc.parse(api.base64Decode(user.config_str))
|
config = jsonc.parse(api.base64Decode(server.config_str))
|
||||||
else
|
else
|
||||||
config = require(require_dir .. "util_shadowsocks").gen_config_server(user)
|
config = require(require_dir .. "util_shadowsocks").gen_config_server(server)
|
||||||
end
|
end
|
||||||
bin = ln_run("/usr/bin/ssserver", "ssserver", "-c " .. config_file, log_path)
|
bin = ln_run("/usr/bin/ssserver", "ssserver", "-c " .. config_file, log_path)
|
||||||
elseif type == "Xray" then
|
elseif type == "Xray" then
|
||||||
if user.custom == "1" and user.config_str then
|
if server.custom == "1" and server.config_str then
|
||||||
config = jsonc.parse(api.base64Decode(user.config_str))
|
config = jsonc.parse(api.base64Decode(server.config_str))
|
||||||
if log_path then
|
if log_path then
|
||||||
if not config.log then
|
if not config.log then
|
||||||
config.log = {}
|
config.log = {}
|
||||||
end
|
end
|
||||||
config.log.loglevel = user.loglevel
|
config.log.loglevel = server.loglevel
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
config = require(require_dir .. "util_xray").gen_config_server(user)
|
config = require(require_dir .. "util_xray").gen_config_server(server)
|
||||||
end
|
end
|
||||||
bin = ln_run(api.get_app_path("xray"), "xray", "run -c " .. config_file, log_path)
|
bin = ln_run(api.get_app_path("xray"), "xray", "run -c " .. config_file, log_path)
|
||||||
elseif type == "sing-box" then
|
elseif type == "sing-box" then
|
||||||
if user.custom == "1" and user.config_str then
|
if server.custom == "1" and server.config_str then
|
||||||
config = jsonc.parse(api.base64Decode(user.config_str))
|
config = jsonc.parse(api.base64Decode(server.config_str))
|
||||||
if log_path then
|
if log_path then
|
||||||
if not config.log then
|
if not config.log then
|
||||||
config.log = {}
|
config.log = {}
|
||||||
end
|
end
|
||||||
config.log.timestamp = true
|
config.log.timestamp = true
|
||||||
config.log.disabled = false
|
config.log.disabled = false
|
||||||
config.log.level = user.loglevel
|
config.log.level = server.loglevel
|
||||||
config.log.output = log_path
|
config.log.output = log_path
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
config = require(require_dir .. "util_sing-box").gen_config_server(user)
|
config = require(require_dir .. "util_sing-box").gen_config_server(server)
|
||||||
end
|
end
|
||||||
bin = ln_run(api.get_app_path("sing-box"), "sing-box", "run -c " .. config_file, log_path)
|
bin = ln_run(api.get_app_path("sing-box"), "sing-box", "run -c " .. config_file, log_path)
|
||||||
elseif type == "Hysteria2" then
|
elseif type == "Hysteria2" then
|
||||||
if user.custom == "1" and user.config_str then
|
if server.custom == "1" and server.config_str then
|
||||||
config = jsonc.parse(api.base64Decode(user.config_str))
|
config = jsonc.parse(api.base64Decode(server.config_str))
|
||||||
else
|
else
|
||||||
config = require(require_dir .. "util_hysteria2").gen_config_server(user)
|
config = require(require_dir .. "util_hysteria2").gen_config_server(server)
|
||||||
end
|
end
|
||||||
bin = ln_run(api.get_app_path("hysteria"), "hysteria", "-c " .. config_file .. " server", log_path)
|
bin = ln_run(api.get_app_path("hysteria"), "hysteria", "-c " .. config_file .. " server", log_path)
|
||||||
end
|
end
|
||||||
@@ -203,7 +207,7 @@ local function start()
|
|||||||
cmd(bin)
|
cmd(bin)
|
||||||
end
|
end
|
||||||
|
|
||||||
local bind_local = user.bind_local or 0
|
local bind_local = server.bind_local or 0
|
||||||
if bind_local and tonumber(bind_local) ~= 1 and port then
|
if bind_local and tonumber(bind_local) ~= 1 and port then
|
||||||
if nft_flag == "0" then
|
if nft_flag == "0" then
|
||||||
ipt(string.format('-A PSW-SERVER -p tcp --dport %s -m comment --comment "%s" -j ACCEPT', port, remarks))
|
ipt(string.format('-A PSW-SERVER -p tcp --dport %s -m comment --comment "%s" -j ACCEPT', port, remarks))
|
||||||
|
|||||||
@@ -4,6 +4,20 @@ local uci = api.uci
|
|||||||
local jsonc = api.jsonc
|
local jsonc = api.jsonc
|
||||||
|
|
||||||
function gen_config_server(node)
|
function gen_config_server(node)
|
||||||
|
local users = node.users or {}
|
||||||
|
local users = nil
|
||||||
|
if node.users and #node.users > 0 then
|
||||||
|
users = {}
|
||||||
|
for i, v in ipairs(node.users) do
|
||||||
|
local user = uci:get_all("passwall2_server", v) or {}
|
||||||
|
if user[".type"] == "user" then
|
||||||
|
users[user.username] = user.password
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not next(users) then
|
||||||
|
users = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
local config = {
|
local config = {
|
||||||
listen = (function()
|
listen = (function()
|
||||||
if node.hysteria2_realms and node.hysteria2_realm_url then
|
if node.hysteria2_realms and node.hysteria2_realm_url then
|
||||||
@@ -25,10 +39,10 @@ function gen_config_server(node)
|
|||||||
password = node.hysteria2_obfs_password
|
password = node.hysteria2_obfs_password
|
||||||
}
|
}
|
||||||
} or nil,
|
} or nil,
|
||||||
auth = {
|
auth = users and {
|
||||||
type = "password",
|
type = "userpass",
|
||||||
password = node.hysteria2_auth_password
|
userpass = users
|
||||||
},
|
} or nil,
|
||||||
bandwidth = (node.hysteria2_up_mbps or node.hysteria2_down_mbps) and {
|
bandwidth = (node.hysteria2_up_mbps or node.hysteria2_down_mbps) and {
|
||||||
up = node.hysteria2_up_mbps and node.hysteria2_up_mbps .. " mbps" or nil,
|
up = node.hysteria2_up_mbps and node.hysteria2_up_mbps .. " mbps" or nil,
|
||||||
down = node.hysteria2_down_mbps and node.hysteria2_down_mbps .. " mbps" or nil
|
down = node.hysteria2_down_mbps and node.hysteria2_down_mbps .. " mbps" or nil
|
||||||
|
|||||||
@@ -4,9 +4,13 @@ local uci = api.uci
|
|||||||
local jsonc = api.jsonc
|
local jsonc = api.jsonc
|
||||||
|
|
||||||
function gen_config_server(node)
|
function gen_config_server(node)
|
||||||
|
local user = nil
|
||||||
|
if node.user then
|
||||||
|
user = uci:get_all("passwall2_server", node.user)
|
||||||
|
end
|
||||||
local config = {}
|
local config = {}
|
||||||
config.server_port = tonumber(node.port)
|
config.server_port = tonumber(node.port)
|
||||||
config.password = node.password
|
config.password = user and user.password or ""
|
||||||
config.timeout = tonumber(node.timeout)
|
config.timeout = tonumber(node.timeout)
|
||||||
config.fast_open = (node.tcp_fast_open and node.tcp_fast_open == "1") and true or false
|
config.fast_open = (node.tcp_fast_open and node.tcp_fast_open == "1") and true or false
|
||||||
config.method = node.method
|
config.method = node.method
|
||||||
|
|||||||
@@ -766,39 +766,71 @@ function gen_config_server(node)
|
|||||||
listen_port = tonumber(node.port),
|
listen_port = tonumber(node.port),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
local users = node.users or {}
|
||||||
|
local users = nil
|
||||||
|
if node.users and #node.users > 0 then
|
||||||
|
users = {}
|
||||||
|
for i, v in ipairs(node.users) do
|
||||||
|
local user = uci:get_all("passwall2_server", v) or {}
|
||||||
|
if user[".type"] == "user" then
|
||||||
|
local u = {}
|
||||||
|
if node.protocol == "mixed" or node.protocol == "socks" or node.protocol == "http" or node.protocol == "naive" then
|
||||||
|
u.username = user.username
|
||||||
|
u.password = user.password
|
||||||
|
end
|
||||||
|
if node.protocol == "shadowsocks" or node.protocol == "trojan" then
|
||||||
|
u.name = user.username
|
||||||
|
u.password = user.password
|
||||||
|
end
|
||||||
|
if node.protocol == "vmess" then
|
||||||
|
u.name = user.username
|
||||||
|
u.uuid = user.uuid
|
||||||
|
u.alterId = 0
|
||||||
|
end
|
||||||
|
if node.protocol == "vless" then
|
||||||
|
u.name = user.username
|
||||||
|
u.uuid = user.uuid
|
||||||
|
u.flow = node.flow
|
||||||
|
end
|
||||||
|
if node.protocol == "hysteria" then
|
||||||
|
u.name = user.username
|
||||||
|
u.auth_str = user.password
|
||||||
|
end
|
||||||
|
if node.protocol == "tuic" then
|
||||||
|
u.name = user.username
|
||||||
|
u.password = user.password
|
||||||
|
u.uuid = user.uuid
|
||||||
|
end
|
||||||
|
if node.protocol == "hysteria2" then
|
||||||
|
u.name = user.username
|
||||||
|
u.password = user.password
|
||||||
|
end
|
||||||
|
users[#users + 1] = u
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if #users == 0 then
|
||||||
|
users = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local protocol_table = nil
|
local protocol_table = nil
|
||||||
|
|
||||||
if node.protocol == "mixed" then
|
if node.protocol == "mixed" then
|
||||||
protocol_table = {
|
protocol_table = {
|
||||||
users = (node.auth == "1") and {
|
users = users,
|
||||||
{
|
|
||||||
username = node.username,
|
|
||||||
password = node.password
|
|
||||||
}
|
|
||||||
} or nil,
|
|
||||||
set_system_proxy = false
|
set_system_proxy = false
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
if node.protocol == "socks" then
|
if node.protocol == "socks" then
|
||||||
protocol_table = {
|
protocol_table = {
|
||||||
users = (node.auth == "1") and {
|
users = users
|
||||||
{
|
|
||||||
username = node.username,
|
|
||||||
password = node.password
|
|
||||||
}
|
|
||||||
} or nil
|
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
if node.protocol == "http" then
|
if node.protocol == "http" then
|
||||||
protocol_table = {
|
protocol_table = {
|
||||||
users = (node.auth == "1") and {
|
users = users,
|
||||||
{
|
|
||||||
username = node.username,
|
|
||||||
password = node.password
|
|
||||||
}
|
|
||||||
} or nil,
|
|
||||||
tls = (node.tls == "1") and tls or nil,
|
tls = (node.tls == "1") and tls or nil,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
@@ -806,21 +838,14 @@ function gen_config_server(node)
|
|||||||
if node.protocol == "shadowsocks" then
|
if node.protocol == "shadowsocks" then
|
||||||
protocol_table = {
|
protocol_table = {
|
||||||
method = node.method,
|
method = node.method,
|
||||||
password = node.password,
|
password = node.ss_password,
|
||||||
|
users = users,
|
||||||
multiplex = mux,
|
multiplex = mux,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
if node.protocol == "vmess" then
|
if node.protocol == "vmess" then
|
||||||
if node.uuid then
|
if users then
|
||||||
local users = {}
|
|
||||||
for i = 1, #node.uuid do
|
|
||||||
users[i] = {
|
|
||||||
name = node.uuid[i],
|
|
||||||
uuid = node.uuid[i],
|
|
||||||
alterId = 0,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
protocol_table = {
|
protocol_table = {
|
||||||
users = users,
|
users = users,
|
||||||
tls = (node.tls == "1") and tls or nil,
|
tls = (node.tls == "1") and tls or nil,
|
||||||
@@ -831,15 +856,7 @@ function gen_config_server(node)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if node.protocol == "vless" then
|
if node.protocol == "vless" then
|
||||||
if node.uuid then
|
if users then
|
||||||
local users = {}
|
|
||||||
for i = 1, #node.uuid do
|
|
||||||
users[i] = {
|
|
||||||
name = node.uuid[i],
|
|
||||||
uuid = node.uuid[i],
|
|
||||||
flow = node.flow,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
protocol_table = {
|
protocol_table = {
|
||||||
users = users,
|
users = users,
|
||||||
tls = (node.tls == "1") and tls or nil,
|
tls = (node.tls == "1") and tls or nil,
|
||||||
@@ -850,14 +867,7 @@ function gen_config_server(node)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if node.protocol == "trojan" then
|
if node.protocol == "trojan" then
|
||||||
if node.uuid then
|
if users then
|
||||||
local users = {}
|
|
||||||
for i = 1, #node.uuid do
|
|
||||||
users[i] = {
|
|
||||||
name = node.uuid[i],
|
|
||||||
password = node.uuid[i],
|
|
||||||
}
|
|
||||||
end
|
|
||||||
protocol_table = {
|
protocol_table = {
|
||||||
users = users,
|
users = users,
|
||||||
tls = (node.tls == "1") and tls or nil,
|
tls = (node.tls == "1") and tls or nil,
|
||||||
@@ -870,15 +880,12 @@ function gen_config_server(node)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if node.protocol == "naive" then
|
if node.protocol == "naive" then
|
||||||
protocol_table = {
|
if users then
|
||||||
users = {
|
protocol_table = {
|
||||||
{
|
users = users,
|
||||||
username = node.username,
|
tls = tls
|
||||||
password = node.password
|
}
|
||||||
}
|
end
|
||||||
},
|
|
||||||
tls = tls,
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if node.protocol == "hysteria" then
|
if node.protocol == "hysteria" then
|
||||||
@@ -888,13 +895,7 @@ function gen_config_server(node)
|
|||||||
up_mbps = tonumber(node.hysteria_up_mbps),
|
up_mbps = tonumber(node.hysteria_up_mbps),
|
||||||
down_mbps = tonumber(node.hysteria_down_mbps),
|
down_mbps = tonumber(node.hysteria_down_mbps),
|
||||||
obfs = node.hysteria_obfs,
|
obfs = node.hysteria_obfs,
|
||||||
users = {
|
users = users,
|
||||||
{
|
|
||||||
name = "user1",
|
|
||||||
auth = (node.hysteria_auth_type == "base64") and node.hysteria_auth_password or nil,
|
|
||||||
auth_str = (node.hysteria_auth_type == "string") and node.hysteria_auth_password or nil,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
recv_window_conn = node.hysteria_recv_window_conn and tonumber(node.hysteria_recv_window_conn) or nil, --1.14 to stream_receive_window
|
recv_window_conn = node.hysteria_recv_window_conn and tonumber(node.hysteria_recv_window_conn) or nil, --1.14 to stream_receive_window
|
||||||
recv_window_client = node.hysteria_recv_window_client and tonumber(node.hysteria_recv_window_client) or nil, --1.14 to connection_receive_window
|
recv_window_client = node.hysteria_recv_window_client and tonumber(node.hysteria_recv_window_client) or nil, --1.14 to connection_receive_window
|
||||||
max_conn_client = node.hysteria_max_conn_client and tonumber(node.hysteria_max_conn_client) or nil, --1.14 to max_concurrent_streams
|
max_conn_client = node.hysteria_max_conn_client and tonumber(node.hysteria_max_conn_client) or nil, --1.14 to max_concurrent_streams
|
||||||
@@ -904,31 +905,21 @@ function gen_config_server(node)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if node.protocol == "tuic" then
|
if node.protocol == "tuic" then
|
||||||
if node.uuid then
|
tls.alpn = (node.tuic_alpn and node.tuic_alpn ~= "default") and (function()
|
||||||
local users = {}
|
local alpn = {}
|
||||||
for i = 1, #node.uuid do
|
string.gsub(node.tuic_alpn, '[^,]+', function(w)
|
||||||
users[i] = {
|
table.insert(alpn, w)
|
||||||
name = node.uuid[i],
|
end)
|
||||||
uuid = node.uuid[i],
|
if #alpn > 0 then return alpn end
|
||||||
password = node.password
|
return nil
|
||||||
}
|
end)() or nil
|
||||||
end
|
protocol_table = {
|
||||||
tls.alpn = (node.tuic_alpn and node.tuic_alpn ~= "default") and (function()
|
users = users,
|
||||||
local alpn = {}
|
congestion_control = node.tuic_congestion_control or "cubic",
|
||||||
string.gsub(node.tuic_alpn, '[^,]+', function(w)
|
zero_rtt_handshake = (node.tuic_zero_rtt_handshake == "1") and true or false,
|
||||||
table.insert(alpn, w)
|
heartbeat = (tonumber(node.tuic_heartbeat) or 3) .. "s",
|
||||||
end)
|
tls = tls
|
||||||
if #alpn > 0 then return alpn end
|
}
|
||||||
return nil
|
|
||||||
end)() or nil
|
|
||||||
protocol_table = {
|
|
||||||
users = users,
|
|
||||||
congestion_control = node.tuic_congestion_control or "cubic",
|
|
||||||
zero_rtt_handshake = (node.tuic_zero_rtt_handshake == "1") and true or false,
|
|
||||||
heartbeat = (tonumber(node.tuic_heartbeat) or 3) .. "s",
|
|
||||||
tls = tls
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if node.protocol == "hysteria2" then
|
if node.protocol == "hysteria2" then
|
||||||
@@ -953,12 +944,7 @@ function gen_config_server(node)
|
|||||||
end
|
end
|
||||||
return o
|
return o
|
||||||
end)(node.hysteria2_obfs_type),
|
end)(node.hysteria2_obfs_type),
|
||||||
users = {
|
users = users,
|
||||||
{
|
|
||||||
name = "user1",
|
|
||||||
password = node.hysteria2_auth_password or nil,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ignore_client_bandwidth = (node.hysteria2_ignore_client_bandwidth == "1") and true or false,
|
ignore_client_bandwidth = (node.hysteria2_ignore_client_bandwidth == "1") and true or false,
|
||||||
tls = tls,
|
tls = tls,
|
||||||
realm = node.hysteria2_realms and (function()
|
realm = node.hysteria2_realms and (function()
|
||||||
@@ -978,15 +964,12 @@ function gen_config_server(node)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if node.protocol == "anytls" then
|
if node.protocol == "anytls" then
|
||||||
protocol_table = {
|
if users then
|
||||||
users = {
|
protocol_table = {
|
||||||
{
|
users = users,
|
||||||
name = (node.username and node.username ~= "") and node.username or "sekai",
|
tls = tls,
|
||||||
password = node.password
|
}
|
||||||
}
|
end
|
||||||
},
|
|
||||||
tls = tls,
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if node.protocol == "direct" then
|
if node.protocol == "direct" then
|
||||||
|
|||||||
@@ -488,17 +488,46 @@ function gen_config_server(node)
|
|||||||
{ protocol = "freedom", tag = "direct", settings = { finalRules = {{ action = "allow" }}}}, { protocol = "blackhole", tag = "blocked" }
|
{ protocol = "freedom", tag = "direct", settings = { finalRules = {{ action = "allow" }}}}, { protocol = "blackhole", tag = "blocked" }
|
||||||
}
|
}
|
||||||
|
|
||||||
if node.protocol == "vmess" or node.protocol == "vless" then
|
local users = node.users or {}
|
||||||
if node.uuid then
|
local users = nil
|
||||||
local users = {}
|
if node.users and #node.users > 0 then
|
||||||
for i = 1, #node.uuid do
|
users = {}
|
||||||
users[i] = {
|
for i, v in ipairs(node.users) do
|
||||||
id = node.uuid[i],
|
local user = uci:get_all("passwall2_server", v) or {}
|
||||||
flow = (node.protocol == "vless"
|
if user[".type"] == "user" then
|
||||||
and (node.tls == "1" or (node.decryption and node.decryption ~= "" and node.decryption ~= "none"))
|
local u = {}
|
||||||
and node.flow and node.flow ~= "") and node.flow or nil
|
if node.protocol == "socks" or node.protocol == "http" then
|
||||||
}
|
u.user = user.username
|
||||||
|
u.pass = user.password
|
||||||
|
end
|
||||||
|
if node.protocol == "shadowsocks" or node.protocol == "trojan" then
|
||||||
|
u.email = user.username
|
||||||
|
u.password = user.password
|
||||||
|
end
|
||||||
|
if node.protocol == "vmess" then
|
||||||
|
u.email = user.username
|
||||||
|
u.id = user.uuid
|
||||||
|
u.alterId = 0
|
||||||
|
end
|
||||||
|
if node.protocol == "vless" then
|
||||||
|
u.email = user.username
|
||||||
|
u.id = user.uuid
|
||||||
|
u.flow = node.flow
|
||||||
|
end
|
||||||
|
if node.protocol == "hysteria2" then
|
||||||
|
u.email = user.username
|
||||||
|
u.auth = user.password
|
||||||
|
end
|
||||||
|
users[#users + 1] = u
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
if #users == 0 then
|
||||||
|
users = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if node.protocol == "vmess" or node.protocol == "vless" then
|
||||||
|
if users then
|
||||||
settings = {
|
settings = {
|
||||||
users = users,
|
users = users,
|
||||||
decryption = (node.protocol == "vless") and ((node.decryption and node.decryption ~= "") and node.decryption or "none") or nil
|
decryption = (node.protocol == "vless") and ((node.decryption and node.decryption ~= "") and node.decryption or "none") or nil
|
||||||
@@ -507,40 +536,25 @@ function gen_config_server(node)
|
|||||||
elseif node.protocol == "socks" then
|
elseif node.protocol == "socks" then
|
||||||
settings = {
|
settings = {
|
||||||
udp = ("1" == node.udp_forward) and true or false,
|
udp = ("1" == node.udp_forward) and true or false,
|
||||||
auth = ("1" == node.auth) and "password" or "noauth",
|
auth = users and "password" or "noauth",
|
||||||
users = ("1" == node.auth) and {
|
users = users
|
||||||
{
|
|
||||||
user = node.username,
|
|
||||||
pass = node.password
|
|
||||||
}
|
|
||||||
} or nil
|
|
||||||
}
|
}
|
||||||
elseif node.protocol == "http" then
|
elseif node.protocol == "http" then
|
||||||
settings = {
|
settings = {
|
||||||
allowTransparent = false,
|
allowTransparent = false,
|
||||||
users = ("1" == node.auth) and {
|
users = users
|
||||||
{
|
|
||||||
user = node.username,
|
|
||||||
pass = node.password
|
|
||||||
}
|
|
||||||
} or nil
|
|
||||||
}
|
}
|
||||||
node.transport = "tcp"
|
node.transport = "tcp"
|
||||||
node.tcp_guise = "none"
|
node.tcp_guise = "none"
|
||||||
elseif node.protocol == "shadowsocks" then
|
elseif node.protocol == "shadowsocks" then
|
||||||
settings = {
|
settings = {
|
||||||
method = node.method,
|
method = node.method,
|
||||||
password = node.password,
|
password = node.ss_password,
|
||||||
network = node.ss_network or "TCP,UDP"
|
users = users,
|
||||||
|
network = node.ss_network or "tcp,udp"
|
||||||
}
|
}
|
||||||
elseif node.protocol == "trojan" then
|
elseif node.protocol == "trojan" then
|
||||||
if node.uuid then
|
if users then
|
||||||
local users = {}
|
|
||||||
for i = 1, #node.uuid do
|
|
||||||
users[i] = {
|
|
||||||
password = node.uuid[i],
|
|
||||||
}
|
|
||||||
end
|
|
||||||
settings = {
|
settings = {
|
||||||
users = users
|
users = users
|
||||||
}
|
}
|
||||||
@@ -548,9 +562,7 @@ function gen_config_server(node)
|
|||||||
elseif node.protocol == "hysteria2" then
|
elseif node.protocol == "hysteria2" then
|
||||||
settings = {
|
settings = {
|
||||||
version = 2,
|
version = 2,
|
||||||
users = node.hysteria2_auth_password and {
|
users = users
|
||||||
{ auth = node.hysteria2_auth_password }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
elseif node.protocol == "dokodemo-door" then
|
elseif node.protocol == "dokodemo-door" then
|
||||||
settings = {
|
settings = {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<%
|
<%
|
||||||
local api = require "luci.passwall.api"
|
local api = self.api
|
||||||
local appname = api.appname
|
local appname = api.appname
|
||||||
|
local map = self.map
|
||||||
local is_js_luci = api.is_js_luci()
|
local is_js_luci = api.is_js_luci()
|
||||||
local section_tag = is_js_luci and "div" or "fieldset"
|
local section_tag = is_js_luci and "div" or "fieldset"
|
||||||
-%>
|
-%>
|
||||||
@@ -500,15 +501,7 @@ table td, .table .td {
|
|||||||
let firstDataRow = parent.querySelector("tr:not(.cbi-section-table-titles)");
|
let firstDataRow = parent.querySelector("tr:not(.cbi-section-table-titles)");
|
||||||
if (firstDataRow && firstDataRow !== row) {
|
if (firstDataRow && firstDataRow !== row) {
|
||||||
parent.insertBefore(row, firstDataRow);
|
parent.insertBefore(row, firstDataRow);
|
||||||
let save_order_btn = document.getElementById("save_order_btn_" + group);
|
set_order(group);
|
||||||
if (save_order_btn) {
|
|
||||||
const new_order = get_node_order(group);
|
|
||||||
if (!arraysEqual(new_order, origin_group_node_order[group])) {
|
|
||||||
save_order_btn.style.display = null;
|
|
||||||
} else {
|
|
||||||
save_order_btn.style.display = "none";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "del_all":
|
case "del_all":
|
||||||
@@ -652,48 +645,24 @@ table td, .table .td {
|
|||||||
return ids;
|
return ids;
|
||||||
}
|
}
|
||||||
|
|
||||||
function save_current_page_order(group) {
|
function set_order(group) {
|
||||||
var table = document.getElementById("cbi-<%=appname%>-nodes-" + group + "-table");
|
let table = document.getElementById("cbi-<%=appname%>-nodes-" + group + "-table");
|
||||||
if (!table) {
|
if (!table) {
|
||||||
alert("<%:No table!%>");
|
alert("<%:No table!%>");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var rows = table.querySelectorAll("tr.cbi-section-table-row");
|
let rows = table.querySelectorAll("tr.cbi-section-table-row");
|
||||||
if (!rows || rows.length === 0) {
|
if (!rows || rows.length === 0) {
|
||||||
alert("<%:No nodes!%>");
|
alert("<%:No nodes!%>");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var btn = document.getElementById("save_order_btn_" + group);
|
let ids = [];
|
||||||
if (btn) {
|
|
||||||
btn.style.display = "none";
|
|
||||||
btn.disabled = true;
|
|
||||||
}
|
|
||||||
var ids = [];
|
|
||||||
rows.forEach(function(row) {
|
rows.forEach(function(row) {
|
||||||
var id = row.id.replace("cbi-<%=appname%>-", "");
|
let id = row.id.replace("cbi-<%=appname%>-", "");
|
||||||
ids.push(id);
|
ids.push(id);
|
||||||
});
|
});
|
||||||
ajax.abortAll();
|
const input_order = document.getElementsByName(`group.order.${group}`)[0];
|
||||||
XHR.get('<%=api.url("save_node_order")%>', {
|
input_order.value = ids.join(" ");
|
||||||
group: group,
|
|
||||||
ids: ids.join(",")
|
|
||||||
},
|
|
||||||
function(x, result) {
|
|
||||||
if (btn) {
|
|
||||||
btn.style.display = null;
|
|
||||||
btn.disabled = false;
|
|
||||||
}
|
|
||||||
if (x && x.status === 200) {
|
|
||||||
origin_group_node_order[group] = get_node_order(group);
|
|
||||||
alert("<%:Saved current page order successfully.%>");
|
|
||||||
if (btn) {
|
|
||||||
btn.style.display = "none";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
alert("<%:Save failed!%>");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//获取当前使用的节点
|
//获取当前使用的节点
|
||||||
@@ -947,16 +916,7 @@ table td, .table .td {
|
|||||||
forceFallback: false,
|
forceFallback: false,
|
||||||
swapThreshold: 0.65,
|
swapThreshold: 0.65,
|
||||||
onEnd: function (evt) {
|
onEnd: function (evt) {
|
||||||
//save_current_page_order(group); // 自动提交保存
|
set_order(group);
|
||||||
let save_order_btn = document.getElementById("save_order_btn_" + group);
|
|
||||||
if (save_order_btn) {
|
|
||||||
const new_order = get_node_order(group);
|
|
||||||
if (!arraysEqual(new_order, origin_group_node_order[group])) {
|
|
||||||
save_order_btn.style.display = null;
|
|
||||||
} else {
|
|
||||||
save_order_btn.style.display = "none";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
@@ -1302,9 +1262,9 @@ table td, .table .td {
|
|||||||
</tr>
|
</tr>
|
||||||
{{node-tr}}
|
{{node-tr}}
|
||||||
</table>
|
</table>
|
||||||
|
<input type="hidden" name="group.order.{{group}}">
|
||||||
<div class="cbi-section-create cbi-tblsection-create">
|
<div class="cbi-section-create cbi-tblsection-create">
|
||||||
<input class="cbi-button cbi-button-add" type="button" value="<%:Add%>" onclick="node_action('add')">
|
<input class="cbi-button cbi-button-add" type="button" value="<%:Add%>" onclick="node_action('add')">
|
||||||
<input class="cbi-button cbi-button-apply" style="display: none" type="button" id="save_order_btn_{{group}}" value="<%:Save Order%>" onclick="save_current_page_order('{{group}}')">
|
|
||||||
</div>
|
</div>
|
||||||
</<%=section_tag%>>
|
</<%=section_tag%>>
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<%
|
<%
|
||||||
local api = require "luci.passwall.api"
|
local api = self.api
|
||||||
local appname = api.appname
|
local appname = api.appname
|
||||||
local uci = api.uci
|
|
||||||
local is_js_luci = api.is_js_luci()
|
local is_js_luci = api.is_js_luci()
|
||||||
local section_tag = is_js_luci and "div" or "fieldset"
|
local section_tag = is_js_luci and "div" or "fieldset"
|
||||||
local title_tag = is_js_luci and "h3" or "legend"
|
local title_tag = is_js_luci and "h3" or "legend"
|
||||||
@@ -185,15 +184,7 @@ table td, .table .td {
|
|||||||
let firstDataRow = parent.querySelector("tr:not(.cbi-section-table-titles)");
|
let firstDataRow = parent.querySelector("tr:not(.cbi-section-table-titles)");
|
||||||
if (firstDataRow && firstDataRow !== row) {
|
if (firstDataRow && firstDataRow !== row) {
|
||||||
parent.insertBefore(row, firstDataRow);
|
parent.insertBefore(row, firstDataRow);
|
||||||
let save_order_btn = document.getElementById("save_order_btn_" + group);
|
set_order(group);
|
||||||
if (save_order_btn) {
|
|
||||||
const new_order = get_shunt_rule_order(group);
|
|
||||||
if (!arraysEqual(new_order, origin_group_shunt_rule_order[group])) {
|
|
||||||
save_order_btn.style.display = null;
|
|
||||||
} else {
|
|
||||||
save_order_btn.style.display = "none";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,48 +205,24 @@ table td, .table .td {
|
|||||||
return ids;
|
return ids;
|
||||||
}
|
}
|
||||||
|
|
||||||
function save_current_page_order(group) {
|
function set_order(group) {
|
||||||
var table = document.getElementById("cbi-<%=appname%>-shunt_rules-" + group + "-table");
|
let table = document.getElementById("cbi-<%=appname%>-shunt_rules-" + group + "-table");
|
||||||
if (!table) {
|
if (!table) {
|
||||||
alert("<%:No table!%>");
|
alert("<%:No table!%>");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var rows = table.querySelectorAll("tr.cbi-section-table-row");
|
let rows = table.querySelectorAll("tr.cbi-section-table-row");
|
||||||
if (!rows || rows.length === 0) {
|
if (!rows || rows.length === 0) {
|
||||||
alert("<%:No shunt_rules!%>");
|
alert("<%:No shunt_rules!%>");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var btn = document.getElementById("save_order_btn_" + group);
|
let ids = [];
|
||||||
if (btn) {
|
|
||||||
btn.style.display = "none";
|
|
||||||
btn.disabled = true;
|
|
||||||
}
|
|
||||||
var ids = [];
|
|
||||||
rows.forEach(function(row) {
|
rows.forEach(function(row) {
|
||||||
var id = row.id.replace("cbi-<%=appname%>-", "");
|
let id = row.id.replace("cbi-<%=appname%>-", "");
|
||||||
ids.push(id);
|
ids.push(id);
|
||||||
});
|
});
|
||||||
XHR.get('<%=api.url("save_shunt_rule_order")%>', {
|
const input_order = document.getElementsByName(`group.order.${group}`)[0];
|
||||||
group: group,
|
input_order.value = ids.join(" ");
|
||||||
ids: ids.join(",")
|
|
||||||
},
|
|
||||||
function(x, result) {
|
|
||||||
if (btn) {
|
|
||||||
btn.style.display = null;
|
|
||||||
btn.disabled = false;
|
|
||||||
}
|
|
||||||
if (x && x.status === 200) {
|
|
||||||
// origin_group_shunt_rule_order[group] = get_shunt_rule_order(group);
|
|
||||||
alert("<%:Saved current page order successfully.%>");
|
|
||||||
// if (btn) {
|
|
||||||
// btn.style.display = "none";
|
|
||||||
// }
|
|
||||||
window.location.reload();
|
|
||||||
} else {
|
|
||||||
alert("<%:Save failed!%>");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// List drag and rearrange
|
// List drag and rearrange
|
||||||
@@ -274,16 +241,7 @@ table td, .table .td {
|
|||||||
forceFallback: false,
|
forceFallback: false,
|
||||||
swapThreshold: 0.65,
|
swapThreshold: 0.65,
|
||||||
onEnd: function (evt) {
|
onEnd: function (evt) {
|
||||||
//save_current_page_order(group); // Auto save
|
set_order(group);
|
||||||
let save_order_btn = document.getElementById("save_order_btn_" + group);
|
|
||||||
if (save_order_btn) {
|
|
||||||
const new_order = get_shunt_rule_order(group);
|
|
||||||
if (!arraysEqual(new_order, origin_group_shunt_rule_order[group])) {
|
|
||||||
save_order_btn.style.display = null;
|
|
||||||
} else {
|
|
||||||
save_order_btn.style.display = "none";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
@@ -339,10 +297,10 @@ table td, .table .td {
|
|||||||
</tr>
|
</tr>
|
||||||
{{shunt_rule-tr}}
|
{{shunt_rule-tr}}
|
||||||
</table>
|
</table>
|
||||||
|
<input type="hidden" name="group.order.{{group}}">
|
||||||
<div class="cbi-section-create cbi-tblsection-create">
|
<div class="cbi-section-create cbi-tblsection-create">
|
||||||
<input type="text" id="add_name_{{group}}">
|
<input type="text" id="add_name_{{group}}">
|
||||||
<input class="cbi-button cbi-button-add" type="button" value="<%:Add%>" onclick="toPage('add')">
|
<input class="cbi-button cbi-button-add" type="button" value="<%:Add%>" onclick="toPage('add')">
|
||||||
<input class="cbi-button cbi-button-apply" style="display: none" type="button" id="save_order_btn_{{group}}" value="<%:Save Order%>" onclick="save_current_page_order('{{group}}')">
|
|
||||||
</div>
|
</div>
|
||||||
</<%=section_tag%>>
|
</<%=section_tag%>>
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ local api = self.api
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
//<![CDATA[
|
//<![CDATA[
|
||||||
let node_id = "<%=self.section%>";
|
let node_id = "<%=self.section%>";
|
||||||
let node_config_url = '<%=api.url("server_user")%>/' + node_id;
|
let node_config_url = '<%=api.url("server_config")%>/' + node_id;
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
waitForElement('select[name*="<%=self.config%>"][name*="type"]', function(el) {
|
waitForElement('select[name*="<%=self.config%>"][name*="type"]', function(el) {
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<%
|
||||||
|
local api = self.api
|
||||||
|
-%>
|
||||||
|
<script type="text/javascript">
|
||||||
|
//<![CDATA[
|
||||||
|
var _servers_status = document.getElementsByClassName('_servers_status');
|
||||||
|
for(var i = 0; i < _servers_status.length; i++) {
|
||||||
|
var id = _servers_status[i].parentElement.parentElement.parentElement.id;
|
||||||
|
id = id.substr(id.lastIndexOf("-") + 1);
|
||||||
|
XHR.get('<%=api.url("server_status")%>', {
|
||||||
|
index: i,
|
||||||
|
id: id
|
||||||
|
},
|
||||||
|
function(x, result) {
|
||||||
|
_servers_status[result.index].setAttribute("style","font-weight:bold;");
|
||||||
|
_servers_status[result.index].setAttribute("color",result.status ? "green":"red");
|
||||||
|
_servers_status[result.index].innerHTML = (result.status ? '✓' : 'X');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var edit_btn = document.getElementById("cbi-<%=self.appname%>-<%=self.sectiontype%>").getElementsByClassName("cbi-button cbi-button-edit");
|
||||||
|
for (var i = 0; i < edit_btn.length; i++) {
|
||||||
|
try {
|
||||||
|
var onclick_str = edit_btn[i].getAttribute("onclick");
|
||||||
|
var id = onclick_str.substring(onclick_str.lastIndexOf('/') + 1, onclick_str.length - 1);
|
||||||
|
var td = edit_btn[i].parentNode;
|
||||||
|
var new_div = "";
|
||||||
|
// Add `log` button
|
||||||
|
new_div += '<input class="btn cbi-button cbi-button-add" type="button" value="<%:Log%>" onclick="window.open(\'' + '<%=api.url("server_log")%>' + '?id=' + id + '\', \'_blank\')"/> ';
|
||||||
|
td.innerHTML = new_div + td.innerHTML;
|
||||||
|
}
|
||||||
|
catch(err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//]]>
|
||||||
|
</script>
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
<%
|
|
||||||
local api = require "luci.passwall.api"
|
|
||||||
-%>
|
|
||||||
<script type="text/javascript">
|
|
||||||
//<![CDATA[
|
|
||||||
var _users_status = document.getElementsByClassName('_users_status');
|
|
||||||
for(var i = 0; i < _users_status.length; i++) {
|
|
||||||
var id = _users_status[i].parentElement.parentElement.parentElement.id;
|
|
||||||
id = id.substr(id.lastIndexOf("-") + 1);
|
|
||||||
XHR.get('<%=api.url("server_user_status")%>', {
|
|
||||||
index: i,
|
|
||||||
id: id
|
|
||||||
},
|
|
||||||
function(x, result) {
|
|
||||||
_users_status[result.index].setAttribute("style","font-weight:bold;");
|
|
||||||
_users_status[result.index].setAttribute("color",result.status ? "green":"red");
|
|
||||||
_users_status[result.index].innerHTML = (result.status ? '✓' : 'X');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
var edit_btn = document.getElementById("cbi-passwall_server-user").getElementsByClassName("cbi-button cbi-button-edit");
|
|
||||||
for (var i = 0; i < edit_btn.length; i++) {
|
|
||||||
try {
|
|
||||||
var onclick_str = edit_btn[i].getAttribute("onclick");
|
|
||||||
var id = onclick_str.substring(onclick_str.lastIndexOf('/') + 1, onclick_str.length - 1);
|
|
||||||
var td = edit_btn[i].parentNode;
|
|
||||||
var new_div = "";
|
|
||||||
//添加"日志"按钮
|
|
||||||
new_div += '<input class="btn cbi-button cbi-button-add" type="button" value="<%:Log%>" onclick="window.open(\'' + '<%=api.url("server_user_log")%>' + '?id=' + id + '\', \'_blank\')"/> ';
|
|
||||||
td.innerHTML = new_div + td.innerHTML;
|
|
||||||
}
|
|
||||||
catch(err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//]]>
|
|
||||||
</script>
|
|
||||||
@@ -490,12 +490,6 @@ msgstr "节点备注"
|
|||||||
msgid "Add Mode"
|
msgid "Add Mode"
|
||||||
msgstr "添加方式"
|
msgstr "添加方式"
|
||||||
|
|
||||||
msgid "Save Order"
|
|
||||||
msgstr "保存当前顺序"
|
|
||||||
|
|
||||||
msgid "Saved current page order successfully."
|
|
||||||
msgstr "保存当前页面顺序成功。"
|
|
||||||
|
|
||||||
msgid "Drag to reorder"
|
msgid "Drag to reorder"
|
||||||
msgstr "拖动以重排"
|
msgstr "拖动以重排"
|
||||||
|
|
||||||
@@ -1670,9 +1664,15 @@ msgstr "服务器端"
|
|||||||
msgid "Server Config"
|
msgid "Server Config"
|
||||||
msgstr "服务器配置"
|
msgstr "服务器配置"
|
||||||
|
|
||||||
|
msgid "Servers Manager"
|
||||||
|
msgstr "服务器管理"
|
||||||
|
|
||||||
msgid "Users Manager"
|
msgid "Users Manager"
|
||||||
msgstr "用户管理"
|
msgstr "用户管理"
|
||||||
|
|
||||||
|
msgid "User Config"
|
||||||
|
msgstr "用户配置"
|
||||||
|
|
||||||
msgid "Logs"
|
msgid "Logs"
|
||||||
msgstr "日志"
|
msgstr "日志"
|
||||||
|
|
||||||
@@ -2109,9 +2109,15 @@ msgstr "使用自定义配置"
|
|||||||
msgid "Custom Config"
|
msgid "Custom Config"
|
||||||
msgstr "自定义配置"
|
msgstr "自定义配置"
|
||||||
|
|
||||||
|
msgid "Must be Base64 text!"
|
||||||
|
msgstr "必须是 Base64 内容!"
|
||||||
|
|
||||||
msgid "Must be JSON text!"
|
msgid "Must be JSON text!"
|
||||||
msgstr "必须是 JSON 文本内容!"
|
msgstr "必须是 JSON 文本内容!"
|
||||||
|
|
||||||
|
msgid "Must be UUID format!"
|
||||||
|
msgstr "必须是 UUID 格式!"
|
||||||
|
|
||||||
msgid "Geo View"
|
msgid "Geo View"
|
||||||
msgstr "Geo 查询"
|
msgstr "Geo 查询"
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ LUCI_NAME:=luci-theme-footstrap
|
|||||||
FOOTSTRAP_VERSION?=
|
FOOTSTRAP_VERSION?=
|
||||||
ifneq ($(FOOTSTRAP_VERSION),)
|
ifneq ($(FOOTSTRAP_VERSION),)
|
||||||
PKG_VERSION:=$(FOOTSTRAP_VERSION)
|
PKG_VERSION:=$(FOOTSTRAP_VERSION)
|
||||||
PKG_RELEASE:=15
|
PKG_RELEASE:=16
|
||||||
endif
|
endif
|
||||||
|
|
||||||
LUCI_TITLE:=Footstrap Theme
|
LUCI_TITLE:=Footstrap Theme
|
||||||
|
|||||||
@@ -684,9 +684,9 @@ function takeReturn() {
|
|||||||
return true;
|
return true;
|
||||||
} catch (e) { return false; }
|
} catch (e) { return false; }
|
||||||
}
|
}
|
||||||
const MARK = 'fs-ap';
|
const MARK = 'fs-ap'; /* the built form's own class; also how mount() knows it is already there */
|
||||||
/* how long the stock view gets to render its tabs before a missing group counts as a failure */
|
/* how long the stock view gets to render its tabs before a missing group counts as a failure */
|
||||||
const TAB_DEADLINE = 5000; /* the built form's own class; also how mount() knows it is already there */
|
const TAB_DEADLINE = 5000;
|
||||||
const TAB = 'fs-appearance'; /* the pane's data-tab, which ui.tabs' click handler matches on */
|
const TAB = 'fs-appearance'; /* the pane's data-tab, which ui.tabs' click handler matches on */
|
||||||
|
|
||||||
let _routeObserver = null, _viewObserver = null, _observedView = null, _building = false;
|
let _routeObserver = null, _viewObserver = null, _observedView = null, _building = false;
|
||||||
|
|||||||
@@ -23,8 +23,18 @@ const _fitters = [];
|
|||||||
let _rafPending = false;
|
let _rafPending = false;
|
||||||
let _ro = null, _mo = null;
|
let _ro = null, _mo = null;
|
||||||
/* one canvas for the whole document: wordFloor() measures text with it, and creating one per call
|
/* one canvas for the whole document: wordFloor() measures text with it, and creating one per call
|
||||||
* is the expensive half of that measurement */
|
* is the expensive half of that measurement. `null` = not asked yet, `false` = asked and refused —
|
||||||
|
* the same three-state slot fs-widgets.js's rasterCtx() keeps, and for the same reason: a browser
|
||||||
|
* with canvas turned off (an anti-fingerprinting extension, a WebView, memory pressure) answers
|
||||||
|
* null, and retrying that on every fit pass is a cost with no chance of a different answer. */
|
||||||
let _cx = null;
|
let _cx = null;
|
||||||
|
function textCtx() {
|
||||||
|
if (_cx === null) {
|
||||||
|
try { _cx = document.createElement('canvas').getContext('2d') || false; }
|
||||||
|
catch (e) { _cx = false; }
|
||||||
|
}
|
||||||
|
return _cx;
|
||||||
|
}
|
||||||
const SPACE_RE = /\s+/;
|
const SPACE_RE = /\s+/;
|
||||||
|
|
||||||
/* Run every fitter NOW, synchronously. A fitter must be idempotent — this fires on every
|
/* Run every fitter NOW, synchronously. A fitter must be idempotent — this fires on every
|
||||||
@@ -155,7 +165,15 @@ return baseclass.extend({
|
|||||||
wordFloor(t) {
|
wordFloor(t) {
|
||||||
const rows = t.querySelectorAll('.tr:not(.table-titles):not(.cbi-section-table-titles):not(.placeholder)');
|
const rows = t.querySelectorAll('.tr:not(.table-titles):not(.cbi-section-table-titles):not(.placeholder)');
|
||||||
if (!rows.length) return 0;
|
if (!rows.length) return 0;
|
||||||
if (!_cx) _cx = document.createElement('canvas').getContext('2d');
|
/* No 2D context, no floor to report — and 0 is the honest answer, not a failure: it says
|
||||||
|
* "this test has nothing to add", leaving the other two stack tests (does it overflow, is a
|
||||||
|
* one-token column shredded) to decide. Throwing here would cost far more than the test is
|
||||||
|
* worth: fs-select's fitTables() strips `fs-stacked` BEFORE it measures (rule 1), and this is
|
||||||
|
* the last term of that `||`, so the throw would escape the row walk with the class already
|
||||||
|
* gone — every stackable table left un-stacked, every later table in the pass unfitted, once
|
||||||
|
* a second on a polled page. */
|
||||||
|
const cx = textCtx();
|
||||||
|
if (!cx) return 0;
|
||||||
const floors = [], longest = [];
|
const floors = [], longest = [];
|
||||||
let cols = null;
|
let cols = null;
|
||||||
for (const row of rows) {
|
for (const row of rows) {
|
||||||
@@ -181,8 +199,8 @@ return baseclass.extend({
|
|||||||
else for (const w of text.split(SPACE_RE)) if (w.length > word.length) word = w;
|
else for (const w of text.split(SPACE_RE)) if (w.length > word.length) word = w;
|
||||||
if (!(word.length > (longest[i] || 0))) continue;
|
if (!(word.length > (longest[i] || 0))) continue;
|
||||||
longest[i] = word.length;
|
longest[i] = word.length;
|
||||||
_cx.font = col.font;
|
cx.font = col.font;
|
||||||
const need = _cx.measureText(word).width + (col.tracking * word.length) + col.pad;
|
const need = cx.measureText(word).width + (col.tracking * word.length) + col.pad;
|
||||||
if (!(floors[i] >= need)) floors[i] = need;
|
if (!(floors[i] >= need)) floors[i] = need;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,20 +61,39 @@ function clearViewIntervals() {
|
|||||||
* is the pair disagreeing: a tick that is running while the id it runs on has no name we know.
|
* is the pair disagreeing: a tick that is running while the id it runs on has no name we know.
|
||||||
* Then do NOTHING, once, loudly: a view's leftover interval outliving its page costs a wasted
|
* Then do NOTHING, once, loudly: a view's leftover interval outliving its page costs a wasted
|
||||||
* RPC, whereas clearing LuCI's own tick costs every live value on every later page. */
|
* RPC, whereas clearing LuCI's own tick costs every live value on every later page. */
|
||||||
|
/* …and the ALIAS ITSELF is the first thing that can go — it is the deprecation the paragraph
|
||||||
|
* above is about, so this function cannot be the one place that reads it blind. Every other
|
||||||
|
* `L.Poll` read in this file is guarded, and an unguarded one here throws where a throw costs
|
||||||
|
* most: navigate() calls this AFTER #view has been swapped for the "Loading view…" spinner and
|
||||||
|
* BEFORE data-page, pushState and the require, so a TypeError would strand every click on the
|
||||||
|
* spinner with the old URL and the old menu mark — far worse than the leaked interval this
|
||||||
|
* exists to sweep. No alias is the same answer as an unreadable timer: do NOTHING, once,
|
||||||
|
* loudly. */
|
||||||
|
if (!L.Poll) {
|
||||||
|
warnPollUnreadable('footstrap: L.Poll is gone from this luci-base, so LuCI\'s own tick cannot be '
|
||||||
|
+ 'told apart from a view\'s timers — leaving view intervals alone. fs-router.js needs '
|
||||||
|
+ 'updating for this luci-base.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
const running = (typeof L.Poll.active === 'function') ? L.Poll.active() : (L.Poll.timer != null);
|
const running = (typeof L.Poll.active === 'function') ? L.Poll.active() : (L.Poll.timer != null);
|
||||||
if (running && L.Poll.timer == null) {
|
if (running && L.Poll.timer == null) {
|
||||||
if (!_pollTimerWarned) {
|
warnPollUnreadable('footstrap: LuCI is polling but L.Poll.timer is not readable — leaving view '
|
||||||
_pollTimerWarned = true;
|
+ 'intervals alone rather than risking its tick. fs-router.js needs updating for this '
|
||||||
console.error('footstrap: LuCI is polling but L.Poll.timer is not readable — leaving view '
|
+ 'luci-base.');
|
||||||
+ 'intervals alone rather than risking its tick. fs-router.js needs updating for this '
|
|
||||||
+ 'luci-base.');
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const keep = running ? L.Poll.timer : null;
|
const keep = running ? L.Poll.timer : null;
|
||||||
_viewIntervals.forEach((id) => { if (id !== keep) window.clearInterval(id); });
|
_viewIntervals.forEach((id) => { if (id !== keep) window.clearInterval(id); });
|
||||||
}
|
}
|
||||||
|
/* ONE line per document whatever went wrong: this runs on every navigation, and a router that
|
||||||
|
* cannot read L.Poll cannot read it on the next click either — a message per click would bury the
|
||||||
|
* console the user is reading it in. */
|
||||||
let _pollTimerWarned = false;
|
let _pollTimerWarned = false;
|
||||||
|
function warnPollUnreadable(msg) {
|
||||||
|
if (_pollTimerWarned) return;
|
||||||
|
_pollTimerWarned = true;
|
||||||
|
console.error(msg);
|
||||||
|
}
|
||||||
|
|
||||||
/* --- uci cache teardown for SPA nav ---
|
/* --- uci cache teardown for SPA nav ---
|
||||||
* `uci.load()` does not answer "is this config present?" — it answers "which of these packages did
|
* `uci.load()` does not answer "is this config present?" — it answers "which of these packages did
|
||||||
@@ -806,15 +825,23 @@ function navigate(pathname, push, kbd) {
|
|||||||
* repairStaleRender() a mess that only exists because a require in flight cannot be stopped. */
|
* repairStaleRender() a mess that only exists because a require in flight cannot be stopped. */
|
||||||
if (gen !== _navGen) return null;
|
if (gen !== _navGen) return null;
|
||||||
_seen.add(className);
|
_seen.add(className);
|
||||||
/* NAME THE OWNER for the length of this require. On a first visit the require IS the render,
|
/* NAME THE OWNER for the length of this require, AND ONLY WHEN THE MODULE HAS YET TO BE
|
||||||
* so any <style> the module injects belongs to THIS page — even if a newer navigation has
|
* EVALUATED. On a first visit the require IS the render, so any <style> the module injects
|
||||||
* stamped data-page by the time it lands, which is exactly the case repairStaleRender() below
|
* belongs to THIS page — even if a newer navigation has stamped data-page by the time it
|
||||||
* exists for. Without it fs-sheets credited such a sheet to the page that superseded this one
|
* lands, which is exactly the case repairStaleRender() below exists for. Without it fs-sheets
|
||||||
* and bound it there for the life of the document (measured: luci-app-filemanager's
|
* credited such a sheet to the page that superseded this one and bound it there for the life
|
||||||
* `.cbi-button-save { display: none !important }` disabled on its own page and live on
|
* of the document (measured: luci-app-filemanager's `.cbi-button-save { display: none
|
||||||
* System -> System, across return visits). */
|
* !important }` disabled on its own page and live on System -> System, across return visits).
|
||||||
sheets.attributeTo(rsegs);
|
*
|
||||||
return RT.require(className).finally(() => sheets.attributeTo(null));
|
* `!cached` is what makes the hint survive long enough to be read, and its absence is why the
|
||||||
|
* measured case stopped being covered. A CACHED require injects nothing — the module was
|
||||||
|
* evaluated on the first visit and `require()` hands back the singleton — but it still ran
|
||||||
|
* this line, and its `.finally` fires within a microtask, so the navigation that superseded a
|
||||||
|
* cold require would set the hint to itself and clear it to null while that require was still
|
||||||
|
* in flight. The sheet then landed with no hint at all, i.e. credited to the superseding page,
|
||||||
|
* which is the exact bug. A require that cannot inject has no business naming an owner. */
|
||||||
|
if (!cached) sheets.attributeTo(rsegs, gen);
|
||||||
|
return RT.require(className).finally(() => { if (!cached) sheets.attributeTo(null, gen); });
|
||||||
}).then((view) => {
|
}).then((view) => {
|
||||||
if (view == null) return;
|
if (view == null) return;
|
||||||
if (!(view instanceof RT.view))
|
if (!(view instanceof RT.view))
|
||||||
|
|||||||
@@ -185,8 +185,21 @@ function enhance(sel) {
|
|||||||
* openclash / justclash / ssclash / dashboard / statistics included, found ZERO. That is the point:
|
* openclash / justclash / ssclash / dashboard / statistics included, found ZERO. That is the point:
|
||||||
* every table anyone here emits already carries the class, so this changes nothing that can be
|
* every table anyone here emits already carries the class, so this changes nothing that can be
|
||||||
* measured and covers the one shape that cannot be (docs/conventions.md: coverage is a contract). */
|
* measured and covers the one shape that cannot be (docs/conventions.md: coverage is a contract). */
|
||||||
const FOREIGN_TABLE = '#view .table:not(.cbi-section-table):not(.fs-dt), ' +
|
/* NO `:not(.fs-dt)`, and that is the difference between claiming a table once and KEEPING it. These
|
||||||
'#view table:not(.table):not(.cbi-section-table):not(.fs-dt)';
|
* tables are polled: L.ui.Table.update() and every hand-rolled equivalent replace the rows inside
|
||||||
|
* the element they already have. Excluding what we tagged meant the claim ran exactly once per
|
||||||
|
* element — the rows present at that moment were adopted and captioned, and every batch after it
|
||||||
|
* kept neither `.tr`/`.td` nor `data-title`, on a table that by then may carry `.fs-stacked`, where
|
||||||
|
* `#view .table.fs-dt.fs-stacked { overflow: hidden }` clips the lot with no scrollbar. That is the
|
||||||
|
* exact failure adoptMarkup() was written to prevent, arriving one poll later. LuCI's own tables
|
||||||
|
* were never exposed to it (ui.Table writes those class names and the caption itself), so this is
|
||||||
|
* third-party-only — which is the zone this whole selector exists for.
|
||||||
|
*
|
||||||
|
* Re-running is what the two functions below are already written for: both are additive, both skip
|
||||||
|
* what is already done, and neither re-decides anything (adoptMarkup() settles the "is this ours to
|
||||||
|
* rewrite?" question once, at claim time, and remembers the answer on the element). */
|
||||||
|
const FOREIGN_TABLE = '#view .table:not(.cbi-section-table), ' +
|
||||||
|
'#view table:not(.table):not(.cbi-section-table)';
|
||||||
|
|
||||||
/* The FOURTH header markup, and the one only a foreign table produces: `<table><tr><th>…`, with no
|
/* The FOURTH header markup, and the one only a foreign table produces: `<table><tr><th>…`, with no
|
||||||
* `<thead>` for the parser to imply and none of LuCI's class names. It is the exact shape the phone
|
* `<thead>` for the parser to imply and none of LuCI's class names. It is the exact shape the phone
|
||||||
@@ -255,9 +268,10 @@ function tagDataTables() {
|
|||||||
* names: a `<thead>` becomes `.thead` and a plain first row of `<th>` becomes `.tr.table-titles` —
|
* names: a `<thead>` becomes `.thead` and a plain first row of `<th>` becomes `.tr.table-titles` —
|
||||||
* the two names theme/30-tables.css hides when stacked. */
|
* the two names theme/30-tables.css hides when stacked. */
|
||||||
function adoptMarkup(t, head) {
|
function adoptMarkup(t, head) {
|
||||||
/* DECIDED ONCE, AT CLAIM TIME, and only for a table that speaks none of this vocabulary. Asking
|
/* DECIDED ONCE, AT CLAIM TIME, and only for a table that speaks none of this vocabulary — then
|
||||||
* the question every pass instead would answer "already adopted" the moment we adopted it — and
|
* READ on every pass, because the caller now revisits a table it has already claimed (see
|
||||||
* these tables are polled, so the fresh rows that arrive bare afterwards would never be taken.
|
* FOREIGN_TABLE). Asking the question afresh each pass instead would answer "already adopted"
|
||||||
|
* the moment we adopted it, and the fresh rows a poll brings in bare would never be taken.
|
||||||
* Asking it at all is what keeps the theme's hands off LuCI's own markup: the apk Software list
|
* Asking it at all is what keeps the theme's hands off LuCI's own markup: the apk Software list
|
||||||
* heads its table with `.tr.cbi-section-table-titles`, and blindly adding `table-titles` to that
|
* heads its table with `.tr.cbi-section-table-titles`, and blindly adding `table-titles` to that
|
||||||
* would be the theme rewriting a class LuCI chose. */
|
* would be the theme rewriting a class LuCI chose. */
|
||||||
|
|||||||
@@ -676,11 +676,30 @@ function appKey(segs) {
|
|||||||
* stand: the System page came back with no Save button, and stayed that way across return visits).
|
* stand: the System page came back with no Save button, and stayed that way across return visits).
|
||||||
*
|
*
|
||||||
* So the router names the owner for the duration of such a require (see fs-router.js), and this is
|
* So the router names the owner for the duration of such a require (see fs-router.js), and this is
|
||||||
* that hint. It is a plain value rather than a stack: two cold requires cannot overlap, because the
|
* that hint.
|
||||||
* second navigation supersedes the first before it starts one. */
|
*
|
||||||
|
* ONE SLOT, STAMPED WITH THE NAVIGATION THAT SET IT. The router only names an owner for a require
|
||||||
|
* that has yet to evaluate its module, so the common shape — a cold require in flight, the user
|
||||||
|
* clicking on to a page already in LuCI's class cache — no longer touches this slot at all. Two
|
||||||
|
* COLD requires can still overlap, and then the newer one wins the slot: it is the page the user is
|
||||||
|
* looking at, and crediting ITS sheet to the page it superseded would leave the visible page
|
||||||
|
* unpainted, which is the worse of the two errors. The generation stamp is what keeps that from
|
||||||
|
* getting worse still — the older require's `.finally` must not clear a slot the newer one now
|
||||||
|
* holds, or the newer page's own sheet lands unattributed.
|
||||||
|
*
|
||||||
|
* There is no way to do better from here: LuCI evaluates a view module inside `eval()` in its own
|
||||||
|
* `require()` (luci.js), so nothing observable says WHICH module is running when a <style> appears —
|
||||||
|
* `document.currentScript` is null and the injection is synchronous inside a promise chain we do
|
||||||
|
* not own. The remaining hole is therefore two cold requires overlapping, where the first one's
|
||||||
|
* sheet is credited to the second's page — the pre-existing behaviour, now narrowed to that one
|
||||||
|
* case instead of every navigation away from a cold require. */
|
||||||
let _ownerHint = null;
|
let _ownerHint = null;
|
||||||
function attributeTo(segs) {
|
let _ownerGen = -1;
|
||||||
|
function attributeTo(segs, gen) {
|
||||||
|
/* a stale require letting go of a slot somebody else now holds: leave it alone */
|
||||||
|
if (segs == null && gen !== _ownerGen) return;
|
||||||
_ownerHint = (segs == null) ? null : appKey(segs);
|
_ownerHint = (segs == null) ? null : appKey(segs);
|
||||||
|
_ownerGen = (segs == null) ? -1 : gen;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ownerKey() {
|
function ownerKey() {
|
||||||
@@ -723,6 +742,25 @@ function scopeToCurrentPage(segs) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TAKE A SHEET, AND SCOPE IT IN THE SAME BREATH.
|
||||||
|
*
|
||||||
|
* scopeToCurrentPage() runs on NAVIGATION, so it only ever sees sheets that were already here. A
|
||||||
|
* sheet that arrives afterwards is scoped by nobody until the next click — and the one case where
|
||||||
|
* that matters is precisely the case the owner hint exists for: a cold require whose page the user
|
||||||
|
* has already left injects its <style> into a document showing somebody else's page, and the sheet
|
||||||
|
* paints there until the user navigates again. Measured with a view whose module appends
|
||||||
|
* `body { outline: 3px solid rgb(9,9,9) }`: correctly credited to its own page, and still painting
|
||||||
|
* the outline on the page that superseded it.
|
||||||
|
*
|
||||||
|
* So the stamp and the switch are one act. `ownerKey()` is the page the sheet BELONGS to and
|
||||||
|
* `currentKey()` the page on screen; on every ordinary arrival they are the same string and this is
|
||||||
|
* a no-op. */
|
||||||
|
function claimOwner(el) {
|
||||||
|
const key = ownerKey();
|
||||||
|
_owner.set(el, key);
|
||||||
|
if (outlivesPage(el)) setEnabled(el, key === currentKey());
|
||||||
|
}
|
||||||
|
|
||||||
function rehostIntoThemeLayer(el, universe) {
|
function rehostIntoThemeLayer(el, universe) {
|
||||||
if (el.dataset.fsLayered) return;
|
if (el.dataset.fsLayered) return;
|
||||||
|
|
||||||
@@ -735,7 +773,7 @@ function rehostIntoThemeLayer(el, universe) {
|
|||||||
el.dataset.fsLayered = '1';
|
el.dataset.fsLayered = '1';
|
||||||
el.after(s); /* keep source order: ties inside the layer still resolve as they did */
|
el.after(s); /* keep source order: ties inside the layer still resolve as they did */
|
||||||
silence(el);
|
silence(el);
|
||||||
_owner.set(s, ownerKey()); /* the shim paints; the original is silenced for good */
|
claimOwner(s); /* the shim paints; the original is silenced for good */
|
||||||
fenceImported(s, universe.names, Date.now() + 1000); /* a cache hit lands on the first frame */
|
fenceImported(s, universe.names, Date.now() + 1000); /* a cache hit lands on the first frame */
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -752,7 +790,6 @@ function rehostIntoThemeLayer(el, universe) {
|
|||||||
* unpinned all over again and a second pass appends a second fence. The mark is the only thing
|
* unpinned all over again and a second pass appends a second fence. The mark is the only thing
|
||||||
* that says the work is done, so it has to be set for every path below, wrapped or not. */
|
* that says the work is done, so it has to be set for every path below, wrapped or not. */
|
||||||
el.dataset.fsLayered = '1';
|
el.dataset.fsLayered = '1';
|
||||||
_owner.set(el, ownerKey()); /* a <style> is re-hosted IN PLACE, so it paints itself */
|
|
||||||
|
|
||||||
/* Wrap only if the text still IS the sheet (see textIsSheet). When it is not, the sheet stays
|
/* Wrap only if the text still IS the sheet (see textIsSheet). When it is not, the sheet stays
|
||||||
* unlayered — Zone 2 exactly where it already was, which is a trade — rather than lose rules,
|
* unlayered — Zone 2 exactly where it already was, which is a trade — rather than lose rules,
|
||||||
@@ -769,6 +806,15 @@ function rehostIntoThemeLayer(el, universe) {
|
|||||||
el.textContent = '@layer theme {\n' + el.textContent + '\n}';
|
el.textContent = '@layer theme {\n' + el.textContent + '\n}';
|
||||||
}
|
}
|
||||||
try { if (el.sheet) fenceRules(el.sheet.cssRules, universe.names); } catch (e) { /* unfenced, not broken */ }
|
try { if (el.sheet) fenceRules(el.sheet.cssRules, universe.names); } catch (e) { /* unfenced, not broken */ }
|
||||||
|
/* LAST, because the line above may have re-parsed the sheet. `el.disabled` is not a content
|
||||||
|
* attribute — it is the element's view of `el.sheet.disabled`, and assigning textContent throws
|
||||||
|
* the old CSSStyleSheet away and builds a new one, which comes back ENABLED. Claiming before the
|
||||||
|
* wrap therefore switched a sheet off and then switched it back on within the same call, which is
|
||||||
|
* how a sheet belonging to a page the user had left went on painting the page that superseded it
|
||||||
|
* (measured: `body { outline: 3px solid rgb(9,9,9) }` from a still-loading view, live on
|
||||||
|
* System -> System). Ownership is recorded from the same call either way — ownerKey() is read
|
||||||
|
* here, while the router's hint still names the page whose module is evaluating. */
|
||||||
|
claimOwner(el); /* a <style> is re-hosted IN PLACE, so it paints itself */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Re-hosting needs the theme's own selectors to tell an invasive sheet from an inert one. If
|
/* Re-hosting needs the theme's own selectors to tell an invasive sheet from an inert one. If
|
||||||
|
|||||||
@@ -77,6 +77,46 @@
|
|||||||
--fs-alert-tint: var(--fs-accent-fill);
|
--fs-alert-tint: var(--fs-accent-fill);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---- A SPINNING ALERT IS A GLYPH AND A MESSAGE, SIDE BY SIDE ----
|
||||||
|
*
|
||||||
|
* The spinner is a `::before` on the element carrying `.spinning` (base/95-luci.css), and it is
|
||||||
|
* `inline-block` so that it sits in the flow of the button labels and `em.spinning` texts that
|
||||||
|
* are its usual hosts. This host is neither: `ui.changes.displayStatus()` builds the apply /
|
||||||
|
* revert / rollback dialog as `showModal('','')` + `classList.add('alert-message', 'notice',
|
||||||
|
* 'spinning')` + `dom.content(message, E('p', …))`, i.e. a BLOCK box whose content is a BLOCK
|
||||||
|
* element — so the glyph opened a line box of its own and the message went underneath it,
|
||||||
|
* against the dialog's start edge. Reported on the luci PR with a screenshot of "Aplicando
|
||||||
|
* cambios de configuración… 88s" sitting a line below its spinner.
|
||||||
|
*
|
||||||
|
* Grid rather than flex, because the caller decides how many children there are and flex would
|
||||||
|
* lay a second one out BESIDE the first: `displayStatus()` passes one `<p>` today but also
|
||||||
|
* builds `[E('h4', …), E('p', …)]` for its non-spinning states, and `addNotification(title,
|
||||||
|
* children, …classes)` takes `spinning` from anyone. Two columns — glyph, then everything else
|
||||||
|
* — puts the glyph beside the first row and stacks the remaining children under it, for any
|
||||||
|
* number of them. Measured on 25.12 at 1440px, the three shapes a caller can produce (one `<p>`,
|
||||||
|
* `h4` + `<p>`, bare text): the content used to start 24.6 / 20.6 / 19.5px BELOW the glyph's
|
||||||
|
* line and now starts on it, and the one-paragraph dialog is 57.5px tall instead of 78.1px.
|
||||||
|
*
|
||||||
|
* `align-items: center` centres the 20px glyph against the first row rather than pinning it to
|
||||||
|
* the row's top, which is where it reads as "this line is in progress".
|
||||||
|
*
|
||||||
|
* The notification variant is untouched by all this and deliberately so: ui.js writes
|
||||||
|
* `style="display:flex"` on the element itself, and an inline style beats a layered rule, so
|
||||||
|
* those keep their own flex row — where the glyph is already a flex item on the message's line. */
|
||||||
|
.alert-message.spinning {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto 1fr;
|
||||||
|
align-items: center;
|
||||||
|
/* `.alert-message`'s own `gap` above is the row gap between stacked children; the column gap
|
||||||
|
* is the glyph's own spacing and is the smaller step, so it is stated rather than inherited
|
||||||
|
* from that shorthand. */
|
||||||
|
column-gap: var(--fs-space-2);
|
||||||
|
}
|
||||||
|
/* the glyph is a grid item now, so its inline margin would double the column gap; and it is
|
||||||
|
* pinned to row 1 so that it stays beside the FIRST child however many follow */
|
||||||
|
.alert-message.spinning::before { grid-row: 1; margin-inline-end: 0; }
|
||||||
|
.alert-message.spinning > * { grid-column: 2; }
|
||||||
|
|
||||||
/* h2 is what the theme's own notices emit (the document had no heading above h4 at all); h4 is
|
/* h2 is what the theme's own notices emit (the document had no heading above h4 at all); h4 is
|
||||||
* here because a third-party alert may use it, and coverage only grows. h5/h6 joined for the
|
* here because a third-party alert may use it, and coverage only grows. h5/h6 joined for the
|
||||||
* same reason — base styled them and the theme did not, so an alert heading looked like an
|
* same reason — base styled them and the theme did not, so an alert heading looked like an
|
||||||
|
|||||||
+2
-2
@@ -10,12 +10,12 @@ include $(INCLUDE_DIR)/kernel.mk
|
|||||||
|
|
||||||
PKG_NAME:=natflow
|
PKG_NAME:=natflow
|
||||||
PKG_VERSION:=20260531
|
PKG_VERSION:=20260531
|
||||||
PKG_RELEASE:=64
|
PKG_RELEASE:=65
|
||||||
|
|
||||||
PKG_SOURCE:=$(PKG_VERSION).tar.xz
|
PKG_SOURCE:=$(PKG_VERSION).tar.xz
|
||||||
PKG_SOURCE_URL:=https://github.com/ptpt52/natflow.git
|
PKG_SOURCE_URL:=https://github.com/ptpt52/natflow.git
|
||||||
PKG_SOURCE_PROTO:=git
|
PKG_SOURCE_PROTO:=git
|
||||||
PKG_SOURCE_VERSION:=b3e749ae53becc4d31596e40d18e4875e518adc1
|
PKG_SOURCE_VERSION:=07142a20690b250747bcd0ded13586a5ee5a8020
|
||||||
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
|
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
|
||||||
PKG_MAINTAINER:=Chen Minqiang <ptpt52@gmail.com>
|
PKG_MAINTAINER:=Chen Minqiang <ptpt52@gmail.com>
|
||||||
PKG_LICENSE:=GPL-2.0
|
PKG_LICENSE:=GPL-2.0
|
||||||
|
|||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_NAME:=nexttrace
|
PKG_NAME:=nexttrace
|
||||||
PKG_VERSION:=1.7.1
|
PKG_VERSION:=1.7.2
|
||||||
PKG_RELEASE:=12
|
PKG_RELEASE:=13
|
||||||
|
|
||||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||||
PKG_SOURCE_URL:=https://codeload.github.com/nxtrace/NTrace-core/tar.gz/v$(PKG_VERSION)?
|
PKG_SOURCE_URL:=https://codeload.github.com/nxtrace/NTrace-core/tar.gz/v$(PKG_VERSION)?
|
||||||
|
|||||||
Reference in New Issue
Block a user