Update On Sun Jan 18 19:40:15 CET 2026

This commit is contained in:
github-action[bot]
2026-01-18 19:40:16 +01:00
parent e52dea8cc5
commit 404d6331b3
160 changed files with 3848 additions and 8281 deletions
+1
View File
@@ -1246,3 +1246,4 @@ Update On Wed Jan 14 19:44:12 CET 2026
Update On Thu Jan 15 19:49:36 CET 2026
Update On Fri Jan 16 19:44:08 CET 2026
Update On Sat Jan 17 19:40:03 CET 2026
Update On Sun Jan 18 19:40:07 CET 2026
@@ -6,7 +6,6 @@ require (
github.com/dlclark/regexp2 v1.11.5
github.com/metacubex/mihomo v1.7.0
golang.org/x/sync v0.11.0
gopkg.in/yaml.v3 v3.0.1
)
replace github.com/metacubex/mihomo => ../../foss/golang/clash
@@ -109,4 +108,5 @@ require (
golang.org/x/time v0.10.0 // indirect
golang.org/x/tools v0.24.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
@@ -6,10 +6,9 @@ import (
"runtime"
"strings"
"gopkg.in/yaml.v3"
"cfa/native/app"
"github.com/metacubex/mihomo/common/yaml"
"github.com/metacubex/mihomo/config"
"github.com/metacubex/mihomo/hub"
"github.com/metacubex/mihomo/log"
+17 -13
View File
@@ -11,9 +11,6 @@ require (
github.com/gobwas/ws v1.4.0
github.com/gofrs/uuid/v5 v5.4.0
github.com/golang/snappy v1.0.0
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905
github.com/klauspost/compress v1.17.9 // lastest version compatible with golang1.20
github.com/mdlayher/netlink v1.7.2
github.com/metacubex/amneziawg-go v0.0.0-20251104174305-5a0e9f7e361d
github.com/metacubex/bart v0.26.0
github.com/metacubex/bbolt v0.0.0-20250725135710-010dbbbb7a5b
@@ -35,7 +32,7 @@ require (
github.com/metacubex/sing-shadowsocks v0.2.12
github.com/metacubex/sing-shadowsocks2 v0.2.7
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2
github.com/metacubex/sing-tun v0.4.11
github.com/metacubex/sing-tun v0.4.12
github.com/metacubex/sing-vmess v0.2.4
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f
github.com/metacubex/smux v0.0.0-20260105030934-d0c8756d3141
@@ -43,27 +40,34 @@ require (
github.com/metacubex/tls v0.1.0
github.com/metacubex/utls v1.8.4
github.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f
github.com/miekg/dns v1.1.63 // lastest version compatible with golang1.20
github.com/mroth/weightedrand/v2 v2.1.0
github.com/openacid/low v0.1.21
github.com/oschwald/maxminddb-golang v1.12.0 // lastest version compatible with golang1.20
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a
github.com/samber/lo v1.52.0
github.com/sirupsen/logrus v1.9.3
github.com/sirupsen/logrus v1.9.4
github.com/stretchr/testify v1.11.1
github.com/vmihailenco/msgpack/v5 v5.4.1
gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7
go.uber.org/automaxprocs v1.6.0
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
golang.org/x/crypto v0.33.0 // lastest version compatible with golang1.20
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e // lastest version compatible with golang1.20
golang.org/x/net v0.35.0 // lastest version compatible with golang1.20
golang.org/x/sync v0.11.0 // lastest version compatible with golang1.20
golang.org/x/sys v0.30.0 // lastest version compatible with golang1.20
google.golang.org/protobuf v1.34.2 // lastest version compatible with golang1.20
gopkg.in/yaml.v3 v3.0.1
)
// lastest version compatible with golang1.20
require (
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905
github.com/klauspost/compress v1.17.9
github.com/mdlayher/netlink v1.7.2
github.com/miekg/dns v1.1.63
github.com/oschwald/maxminddb-golang v1.12.0
golang.org/x/crypto v0.33.0
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e
golang.org/x/net v0.35.0
golang.org/x/sync v0.11.0
golang.org/x/sys v0.30.0
google.golang.org/protobuf v1.34.2
)
require (
github.com/RyuaNerin/go-krypto v1.3.0 // indirect
github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 // indirect
+4 -5
View File
@@ -127,8 +127,8 @@ github.com/metacubex/sing-shadowsocks2 v0.2.7 h1:hSuuc0YpsfiqYqt1o+fP4m34BQz4e6w
github.com/metacubex/sing-shadowsocks2 v0.2.7/go.mod h1:vOEbfKC60txi0ca+yUlqEwOGc3Obl6cnSgx9Gf45KjE=
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 h1:gXU+MYPm7Wme3/OAY2FFzVq9d9GxPHOqu5AQfg/ddhI=
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2/go.mod h1:mbfboaXauKJNIHJYxQRa+NJs4JU9NZfkA+I33dS2+9E=
github.com/metacubex/sing-tun v0.4.11 h1:NG5zpvYPbBXf+9GSUmDaGCDwl3hZXV677tbRAw0QtCM=
github.com/metacubex/sing-tun v0.4.11/go.mod h1:L/TjQY5JEGy8nvsuYmy/XgMFMCPiF0+AWSFCYfS6r9w=
github.com/metacubex/sing-tun v0.4.12 h1:LCi+yB7y97X3cHQGdNXQBMQNHAzpP4AWg7YhSLk+LTM=
github.com/metacubex/sing-tun v0.4.12/go.mod h1:L/TjQY5JEGy8nvsuYmy/XgMFMCPiF0+AWSFCYfS6r9w=
github.com/metacubex/sing-vmess v0.2.4 h1:Tx6AGgCiEf400E/xyDuYyafsel6sGbR8oF7RkAaus6I=
github.com/metacubex/sing-vmess v0.2.4/go.mod h1:21R5R1u90uUvBQF0owoooEu96/SAYYD56nDrwm6nFaM=
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f h1:Sr/DYKYofKHKc4GF3qkRGNuj6XA6c0eqPgEDN+VAsYU=
@@ -174,8 +174,8 @@ github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c h1:DjKMC30y6y
github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c/go.mod h1:NV/a66PhhWYVmUMaotlXJ8fIEFB98u+c8l/CQIEFLrU=
github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e h1:ur8uMsPIFG3i4Gi093BQITvwH9znsz2VUZmnmwHvpIo=
github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e/go.mod h1:+e5fBW3bpPyo+3uLo513gIUblc03egGjMM0+5GKbzK8=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@@ -226,7 +226,6 @@ golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+3 -3
View File
@@ -2,10 +2,10 @@
"manifest_version": 1,
"latest": {
"mihomo": "v1.19.19",
"mihomo_alpha": "alpha-828fd30",
"mihomo_alpha": "alpha-0c995a2",
"clash_rs": "v0.9.3",
"clash_premium": "2023-09-05-gdcc8d87",
"clash_rs_alpha": "0.9.3-alpha+sha.07c403b"
"clash_rs_alpha": "0.9.3-alpha+sha.1c4e5b1"
},
"arch_template": {
"mihomo": {
@@ -69,5 +69,5 @@
"linux-armv7hf": "clash-armv7-unknown-linux-gnueabihf"
}
},
"updated_at": "2026-01-16T22:21:55.502Z"
"updated_at": "2026-01-17T22:21:31.032Z"
}
+7
View File
@@ -17,6 +17,7 @@ builds:
- linux
- windows
- freebsd
- openbsd
goarch:
- amd64
- "386"
@@ -30,6 +31,12 @@ builds:
ignore:
- goos: darwin
goarch: "386"
# Experimental, may not work properly
- goos: openbsd
goarch: riscv64
# Broken as of Go 1.24, deprecated as of Go 1.26
- goos: windows
goarch: arm
- goos: freebsd
goarch: arm
+15
View File
@@ -2,6 +2,21 @@
All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.
## [2.55.0](https://github.com/filebrowser/filebrowser/compare/v2.54.0...v2.55.0) (2026-01-18)
### Features
* added cut, copy, paste and show command palette functions in header ([#5648](https://github.com/filebrowser/filebrowser/issues/5648)) ([785b7ab](https://github.com/filebrowser/filebrowser/commit/785b7abb7ba7a86cc0deae1052c319ff714c222c))
* update translations ([#5677](https://github.com/filebrowser/filebrowser/issues/5677)) ([e7ea1ad](https://github.com/filebrowser/filebrowser/commit/e7ea1ad27d3d17e249489d3338be40bfea15e2a1))
### Bug Fixes
* prevent context menu clicks from clearing file selection ([#5681](https://github.com/filebrowser/filebrowser/issues/5681)) ([59ca0c3](https://github.com/filebrowser/filebrowser/commit/59ca0c340afc7774747c70ede9a5a5a3c9349d6b))
* request current password when deleting users ([#5667](https://github.com/filebrowser/filebrowser/issues/5667)) ([cfa6c58](https://github.com/filebrowser/filebrowser/commit/cfa6c5864e5e7673aa9f3180e4964e0db92cc4da))
* retain file selection when closing the editor ([#5693](https://github.com/filebrowser/filebrowser/issues/5693)) ([4094fb3](https://github.com/filebrowser/filebrowser/commit/4094fb359babac70e88d0ed4bfe3bd100744aad6))
## [2.54.0](https://github.com/filebrowser/filebrowser/compare/v2.53.1...v2.54.0) (2026-01-10)
+15 -1
View File
@@ -14,6 +14,10 @@ import (
// MethodJSONAuth is used to identify json auth.
const MethodJSONAuth settings.AuthMethod = "json"
// dummyHash is used to prevent user enumeration timing attacks.
// It MUST be a valid bcrypt hash.
const dummyHash = "$2a$10$O4mEMeOL/nit6zqe.WQXauLRbRlzb3IgLHsa26Pf0N/GiU9b.wK1m"
type jsonCred struct {
Password string `json:"password"`
Username string `json:"username"`
@@ -52,7 +56,17 @@ func (a JSONAuth) Auth(r *http.Request, usr users.Store, _ *settings.Settings, s
}
u, err := usr.Get(srv.Root, cred.Username)
if err != nil || !users.CheckPwd(cred.Password, u.Password) {
hash := dummyHash
if err == nil {
hash = u.Password
}
if !users.CheckPwd(cred.Password, hash) {
return nil, os.ErrPermission
}
if err != nil {
return nil, os.ErrPermission
}
+101 -101
View File
@@ -101,28 +101,28 @@ importers:
version: 11.0.3(@vue/compiler-dom@3.5.26)(eslint@9.39.2)(rollup@4.55.1)(typescript@5.9.3)(vue-i18n@11.2.8(vue@3.5.26(typescript@5.9.3)))(vue@3.5.26(typescript@5.9.3))
'@tsconfig/node24':
specifier: ^24.0.2
version: 24.0.3
version: 24.0.4
'@types/lodash-es':
specifier: ^4.17.12
version: 4.17.12
'@types/node':
specifier: ^24.10.1
version: 24.10.6
version: 24.10.9
'@typescript-eslint/eslint-plugin':
specifier: ^8.37.0
version: 8.52.0(@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)
version: 8.53.0(@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)
'@vitejs/plugin-legacy':
specifier: ^7.2.1
version: 7.2.1(terser@5.44.1)(vite@7.3.1(@types/node@24.10.6)(terser@5.44.1)(yaml@2.8.2))
version: 7.2.1(terser@5.46.0)(vite@7.3.1(@types/node@24.10.9)(terser@5.46.0)(yaml@2.8.2))
'@vitejs/plugin-vue':
specifier: ^6.0.1
version: 6.0.3(vite@7.3.1(@types/node@24.10.6)(terser@5.44.1)(yaml@2.8.2))(vue@3.5.26(typescript@5.9.3))
version: 6.0.3(vite@7.3.1(@types/node@24.10.9)(terser@5.46.0)(yaml@2.8.2))(vue@3.5.26(typescript@5.9.3))
'@vue/eslint-config-prettier':
specifier: ^10.2.0
version: 10.2.0(eslint@9.39.2)(prettier@3.7.4)
version: 10.2.0(eslint@9.39.2)(prettier@3.8.0)
'@vue/eslint-config-typescript':
specifier: ^14.6.0
version: 14.6.0(eslint-plugin-vue@10.6.2(@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(vue-eslint-parser@10.2.0(eslint@9.39.2)))(eslint@9.39.2)(typescript@5.9.3)
version: 14.6.0(eslint-plugin-vue@10.7.0(@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(vue-eslint-parser@10.2.0(eslint@9.39.2)))(eslint@9.39.2)(typescript@5.9.3)
'@vue/tsconfig':
specifier: ^0.8.1
version: 0.8.1(typescript@5.9.3)(vue@3.5.26(typescript@5.9.3))
@@ -137,25 +137,25 @@ importers:
version: 10.1.8(eslint@9.39.2)
eslint-plugin-prettier:
specifier: ^5.5.1
version: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.39.2))(eslint@9.39.2)(prettier@3.7.4)
version: 5.5.5(eslint-config-prettier@10.1.8(eslint@9.39.2))(eslint@9.39.2)(prettier@3.8.0)
eslint-plugin-vue:
specifier: ^10.5.1
version: 10.6.2(@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(vue-eslint-parser@10.2.0(eslint@9.39.2))
version: 10.7.0(@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(vue-eslint-parser@10.2.0(eslint@9.39.2))
postcss:
specifier: ^8.5.6
version: 8.5.6
prettier:
specifier: ^3.6.2
version: 3.7.4
version: 3.8.0
terser:
specifier: ^5.43.1
version: 5.44.1
version: 5.46.0
typescript:
specifier: ^5.9.3
version: 5.9.3
vite:
specifier: ^7.2.2
version: 7.3.1(@types/node@24.10.6)(terser@5.44.1)(yaml@2.8.2)
version: 7.3.1(@types/node@24.10.9)(terser@5.46.0)(yaml@2.8.2)
vite-plugin-compression2:
specifier: ^2.3.1
version: 2.4.0(rollup@4.55.1)
@@ -1268,8 +1268,8 @@ packages:
cpu: [x64]
os: [win32]
'@tsconfig/node24@24.0.3':
resolution: {integrity: sha512-vcERKtKQKHgzt/vfS3Gjasd8SUI2a0WZXpgJURdJsMySpS5+ctgbPfuLj2z/W+w4lAfTWxoN4upKfu2WzIRYnw==}
'@tsconfig/node24@24.0.4':
resolution: {integrity: sha512-2A933l5P5oCbv6qSxHs7ckKwobs8BDAe9SJ/Xr2Hy+nDlwmLE1GhFh/g/vXGRZWgxBg9nX/5piDtHR9Dkw/XuA==}
'@types/estree@1.0.8':
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
@@ -1287,8 +1287,8 @@ packages:
'@types/lodash@4.17.13':
resolution: {integrity: sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==}
'@types/node@24.10.6':
resolution: {integrity: sha512-B8h60xgJMR/xmgyX9fncRzEW9gCxoJjdenUhke2v1JGOd/V66KopmWrLPXi5oUI4VuiGK+d+HlXJjDRZMj21EQ==}
'@types/node@24.10.9':
resolution: {integrity: sha512-ne4A0IpG3+2ETuREInjPNhUGis1SFjv1d5asp8MzEAGtOZeTeHVDOYqOgqfhvseqg/iXty2hjBf1zAOb7RNiNw==}
'@types/trusted-types@2.0.7':
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
@@ -1304,11 +1304,11 @@ packages:
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <5.9.0'
'@typescript-eslint/eslint-plugin@8.52.0':
resolution: {integrity: sha512-okqtOgqu2qmZJ5iN4TWlgfF171dZmx2FzdOv2K/ixL2LZWDStL8+JgQerI2sa8eAEfoydG9+0V96m7V+P8yE1Q==}
'@typescript-eslint/eslint-plugin@8.53.0':
resolution: {integrity: sha512-eEXsVvLPu8Z4PkFibtuFJLJOTAV/nPdgtSjkGoPpddpFk3/ym2oy97jynY6ic2m6+nc5M8SE1e9v/mHKsulcJg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
'@typescript-eslint/parser': ^8.52.0
'@typescript-eslint/parser': ^8.53.0
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <6.0.0'
@@ -1331,8 +1331,8 @@ packages:
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/project-service@8.52.0':
resolution: {integrity: sha512-xD0MfdSdEmeFa3OmVqonHi+Cciab96ls1UhIF/qX/O/gPu5KXD0bY9lu33jj04fjzrXHcuvjBcBC+D3SNSadaw==}
'@typescript-eslint/project-service@8.53.0':
resolution: {integrity: sha512-Bl6Gdr7NqkqIP5yP9z1JU///Nmes4Eose6L1HwpuVHwScgDPPuEWbUVhvlZmb8hy0vX9syLk5EGNL700WcBlbg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
@@ -1345,8 +1345,8 @@ packages:
resolution: {integrity: sha512-npgS3zi+/30KSOkXNs0LQXtsg9ekZ8OISAOLGWA/ZOEn0ZH74Ginfl7foziV8DT+D98WfQ5Kopwqb/PZOaIJGg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/scope-manager@8.52.0':
resolution: {integrity: sha512-ixxqmmCcc1Nf8S0mS0TkJ/3LKcC8mruYJPOU6Ia2F/zUUR4pApW7LzrpU3JmtePbRUTes9bEqRc1Gg4iyRnDzA==}
'@typescript-eslint/scope-manager@8.53.0':
resolution: {integrity: sha512-kWNj3l01eOGSdVBnfAF2K1BTh06WS0Yet6JUgb9Cmkqaz3Jlu0fdVUjj9UI8gPidBWSMqDIglmEXifSgDT/D0g==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/tsconfig-utils@8.37.0':
@@ -1361,8 +1361,8 @@ packages:
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/tsconfig-utils@8.52.0':
resolution: {integrity: sha512-jl+8fzr/SdzdxWJznq5nvoI7qn2tNYV/ZBAEcaFMVXf+K6jmXvAFrgo/+5rxgnL152f//pDEAYAhhBAZGrVfwg==}
'@typescript-eslint/tsconfig-utils@8.53.0':
resolution: {integrity: sha512-K6Sc0R5GIG6dNoPdOooQ+KtvT5KCKAvTcY8h2rIuul19vxH5OTQk7ArKkd4yTzkw66WnNY0kPPzzcmWA+XRmiA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
@@ -1374,8 +1374,8 @@ packages:
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <5.9.0'
'@typescript-eslint/type-utils@8.52.0':
resolution: {integrity: sha512-JD3wKBRWglYRQkAtsyGz1AewDu3mTc7NtRjR/ceTyGoPqmdS5oCdx/oZMWD5Zuqmo6/MpsYs0wp6axNt88/2EQ==}
'@typescript-eslint/type-utils@8.53.0':
resolution: {integrity: sha512-BBAUhlx7g4SmcLhn8cnbxoxtmS7hcq39xKCgiutL3oNx1TaIp+cny51s8ewnKMpVUKQUGb41RAUWZ9kxYdovuw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
@@ -1389,8 +1389,8 @@ packages:
resolution: {integrity: sha512-e9k/fneezorUo6WShlQpMxXh8/8wfyc+biu6tnAqA81oWrEic0k21RHzP9uqqpyBBeBKu4T+Bsjy9/b8u7obXQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/types@8.52.0':
resolution: {integrity: sha512-LWQV1V4q9V4cT4H5JCIx3481iIFxH1UkVk+ZkGGAV1ZGcjGI9IoFOfg3O6ywz8QqCDEp7Inlg6kovMofsNRaGg==}
'@typescript-eslint/types@8.53.0':
resolution: {integrity: sha512-Bmh9KX31Vlxa13+PqPvt4RzKRN1XORYSLlAE+sO1i28NkisGbTtSLFVB3l7PWdHtR3E0mVMuC7JilWJ99m2HxQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/typescript-estree@8.37.0':
@@ -1405,8 +1405,8 @@ packages:
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/typescript-estree@8.52.0':
resolution: {integrity: sha512-XP3LClsCc0FsTK5/frGjolyADTh3QmsLp6nKd476xNI9CsSsLnmn4f0jrzNoAulmxlmNIpeXuHYeEQv61Q6qeQ==}
'@typescript-eslint/typescript-estree@8.53.0':
resolution: {integrity: sha512-pw0c0Gdo7Z4xOG987u3nJ8akL9093yEEKv8QTJ+Bhkghj1xyj8cgPaavlr9rq8h7+s6plUJ4QJYw2gCZodqmGw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
@@ -1418,8 +1418,8 @@ packages:
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <5.9.0'
'@typescript-eslint/utils@8.52.0':
resolution: {integrity: sha512-wYndVMWkweqHpEpwPhwqE2lnD2DxC6WVLupU/DOt/0/v+/+iQbbzO3jOHjmBMnhu0DgLULvOaU4h4pwHYi2oRQ==}
'@typescript-eslint/utils@8.53.0':
resolution: {integrity: sha512-XDY4mXTez3Z1iRDI5mbRhH4DFSt46oaIFsLg+Zn97+sYrXACziXSQcSelMybnVZ5pa1P6xYkPr5cMJyunM1ZDA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
@@ -1433,8 +1433,8 @@ packages:
resolution: {integrity: sha512-LlKaciDe3GmZFphXIc79THF/YYBugZ7FS1pO581E/edlVVNbZKDy93evqmrfQ9/Y4uN0vVhX4iuchq26mK/iiA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/visitor-keys@8.52.0':
resolution: {integrity: sha512-ink3/Zofus34nmBsPjow63FP5M7IGff0RKAgqR6+CFpdk22M7aLwC9gOcLGYqr7MczLPzZVERW9hRog3O4n1sQ==}
'@typescript-eslint/visitor-keys@8.53.0':
resolution: {integrity: sha512-LZ2NqIHFhvFwxG0qZeLL9DvdNAHPGCY5dIRwBhyYeU+LfLhcStE1ImjsuTG/WaVh3XysGaeLW8Rqq7cGkPCFvw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@videojs/http-streaming@3.17.2':
@@ -1846,8 +1846,8 @@ packages:
peerDependencies:
eslint: '>=7.0.0'
eslint-plugin-prettier@5.5.4:
resolution: {integrity: sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==}
eslint-plugin-prettier@5.5.5:
resolution: {integrity: sha512-hscXkbqUZ2sPithAuLm5MXL+Wph+U7wHngPBv9OMWwlP8iaflyxpjTYZkmdgB4/vPIhemRlBEoLrH7UC1n7aUw==}
engines: {node: ^14.18.0 || >=16.0.0}
peerDependencies:
'@types/eslint': '>=8.0.0'
@@ -1860,8 +1860,8 @@ packages:
eslint-config-prettier:
optional: true
eslint-plugin-vue@10.6.2:
resolution: {integrity: sha512-nA5yUs/B1KmKzvC42fyD0+l9Yd+LtEpVhWRbXuDj0e+ZURcTtyRbMDWUeJmTAh2wC6jC83raS63anNM2YT3NPw==}
eslint-plugin-vue@10.7.0:
resolution: {integrity: sha512-r2XFCK4qlo1sxEoAMIoTTX0PZAdla0JJDt1fmYiworZUX67WeEGqm+JbyAg3M+pGiJ5U6Mp5WQbontXWtIW7TA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
'@stylistic/eslint-plugin': ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0
@@ -2351,12 +2351,12 @@ packages:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
engines: {node: '>= 0.8.0'}
prettier-linter-helpers@1.0.0:
resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==}
prettier-linter-helpers@1.0.1:
resolution: {integrity: sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==}
engines: {node: '>=6.0.0'}
prettier@3.7.4:
resolution: {integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==}
prettier@3.8.0:
resolution: {integrity: sha512-yEPsovQfpxYfgWNhCfECjG5AQaO+K3dp6XERmOepyPDVqcJm+bjyCVO3pmU+nAPe0N5dDvekfGezt/EIiRe1TA==}
engines: {node: '>=14'}
hasBin: true
@@ -2504,8 +2504,8 @@ packages:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
synckit@0.11.11:
resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==}
synckit@0.11.12:
resolution: {integrity: sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==}
engines: {node: ^14.18.0 || >=16.0.0}
systemjs@6.15.1:
@@ -2517,8 +2517,8 @@ packages:
tar-mini@0.2.0:
resolution: {integrity: sha512-+qfUHz700DWnRutdUsxRRVZ38G1Qr27OetwaMYTdg8hcPxf46U0S1Zf76dQMWRBmusOt2ZCK5kbIaiLkoGO7WQ==}
terser@5.44.1:
resolution: {integrity: sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==}
terser@5.46.0:
resolution: {integrity: sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==}
engines: {node: '>=10'}
hasBin: true
@@ -3820,7 +3820,7 @@ snapshots:
'@rollup/rollup-win32-x64-msvc@4.55.1':
optional: true
'@tsconfig/node24@24.0.3': {}
'@tsconfig/node24@24.0.4': {}
'@types/estree@1.0.8': {}
@@ -3836,7 +3836,7 @@ snapshots:
'@types/lodash@4.17.13': {}
'@types/node@24.10.6':
'@types/node@24.10.9':
dependencies:
undici-types: 7.16.0
@@ -3862,14 +3862,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)':
'@typescript-eslint/eslint-plugin@8.53.0(@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)':
dependencies:
'@eslint-community/regexpp': 4.12.2
'@typescript-eslint/parser': 8.37.0(eslint@9.39.2)(typescript@5.9.3)
'@typescript-eslint/scope-manager': 8.52.0
'@typescript-eslint/type-utils': 8.52.0(eslint@9.39.2)(typescript@5.9.3)
'@typescript-eslint/utils': 8.52.0(eslint@9.39.2)(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.52.0
'@typescript-eslint/scope-manager': 8.53.0
'@typescript-eslint/type-utils': 8.53.0(eslint@9.39.2)(typescript@5.9.3)
'@typescript-eslint/utils': 8.53.0(eslint@9.39.2)(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.53.0
eslint: 9.39.2
ignore: 7.0.5
natural-compare: 1.4.0
@@ -3908,10 +3908,10 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/project-service@8.52.0(typescript@5.9.3)':
'@typescript-eslint/project-service@8.53.0(typescript@5.9.3)':
dependencies:
'@typescript-eslint/tsconfig-utils': 8.52.0(typescript@5.9.3)
'@typescript-eslint/types': 8.52.0
'@typescript-eslint/tsconfig-utils': 8.53.0(typescript@5.9.3)
'@typescript-eslint/types': 8.53.0
debug: 4.4.3
typescript: 5.9.3
transitivePeerDependencies:
@@ -3927,10 +3927,10 @@ snapshots:
'@typescript-eslint/types': 8.49.0
'@typescript-eslint/visitor-keys': 8.49.0
'@typescript-eslint/scope-manager@8.52.0':
'@typescript-eslint/scope-manager@8.53.0':
dependencies:
'@typescript-eslint/types': 8.52.0
'@typescript-eslint/visitor-keys': 8.52.0
'@typescript-eslint/types': 8.53.0
'@typescript-eslint/visitor-keys': 8.53.0
'@typescript-eslint/tsconfig-utils@8.37.0(typescript@5.9.3)':
dependencies:
@@ -3940,7 +3940,7 @@ snapshots:
dependencies:
typescript: 5.9.3
'@typescript-eslint/tsconfig-utils@8.52.0(typescript@5.9.3)':
'@typescript-eslint/tsconfig-utils@8.53.0(typescript@5.9.3)':
dependencies:
typescript: 5.9.3
@@ -3956,11 +3956,11 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/type-utils@8.52.0(eslint@9.39.2)(typescript@5.9.3)':
'@typescript-eslint/type-utils@8.53.0(eslint@9.39.2)(typescript@5.9.3)':
dependencies:
'@typescript-eslint/types': 8.52.0
'@typescript-eslint/typescript-estree': 8.52.0(typescript@5.9.3)
'@typescript-eslint/utils': 8.52.0(eslint@9.39.2)(typescript@5.9.3)
'@typescript-eslint/types': 8.53.0
'@typescript-eslint/typescript-estree': 8.53.0(typescript@5.9.3)
'@typescript-eslint/utils': 8.53.0(eslint@9.39.2)(typescript@5.9.3)
debug: 4.4.3
eslint: 9.39.2
ts-api-utils: 2.4.0(typescript@5.9.3)
@@ -3972,7 +3972,7 @@ snapshots:
'@typescript-eslint/types@8.49.0': {}
'@typescript-eslint/types@8.52.0': {}
'@typescript-eslint/types@8.53.0': {}
'@typescript-eslint/typescript-estree@8.37.0(typescript@5.9.3)':
dependencies:
@@ -4005,12 +4005,12 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/typescript-estree@8.52.0(typescript@5.9.3)':
'@typescript-eslint/typescript-estree@8.53.0(typescript@5.9.3)':
dependencies:
'@typescript-eslint/project-service': 8.52.0(typescript@5.9.3)
'@typescript-eslint/tsconfig-utils': 8.52.0(typescript@5.9.3)
'@typescript-eslint/types': 8.52.0
'@typescript-eslint/visitor-keys': 8.52.0
'@typescript-eslint/project-service': 8.53.0(typescript@5.9.3)
'@typescript-eslint/tsconfig-utils': 8.53.0(typescript@5.9.3)
'@typescript-eslint/types': 8.53.0
'@typescript-eslint/visitor-keys': 8.53.0
debug: 4.4.3
minimatch: 9.0.5
semver: 7.7.3
@@ -4022,7 +4022,7 @@ snapshots:
'@typescript-eslint/utils@8.37.0(eslint@9.39.2)(typescript@5.9.3)':
dependencies:
'@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2)
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2)
'@typescript-eslint/scope-manager': 8.37.0
'@typescript-eslint/types': 8.37.0
'@typescript-eslint/typescript-estree': 8.37.0(typescript@5.9.3)
@@ -4031,12 +4031,12 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/utils@8.52.0(eslint@9.39.2)(typescript@5.9.3)':
'@typescript-eslint/utils@8.53.0(eslint@9.39.2)(typescript@5.9.3)':
dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2)
'@typescript-eslint/scope-manager': 8.52.0
'@typescript-eslint/types': 8.52.0
'@typescript-eslint/typescript-estree': 8.52.0(typescript@5.9.3)
'@typescript-eslint/scope-manager': 8.53.0
'@typescript-eslint/types': 8.53.0
'@typescript-eslint/typescript-estree': 8.53.0(typescript@5.9.3)
eslint: 9.39.2
typescript: 5.9.3
transitivePeerDependencies:
@@ -4052,9 +4052,9 @@ snapshots:
'@typescript-eslint/types': 8.49.0
eslint-visitor-keys: 4.2.1
'@typescript-eslint/visitor-keys@8.52.0':
'@typescript-eslint/visitor-keys@8.53.0':
dependencies:
'@typescript-eslint/types': 8.52.0
'@typescript-eslint/types': 8.53.0
eslint-visitor-keys: 4.2.1
'@videojs/http-streaming@3.17.2(video.js@8.23.4)':
@@ -4079,7 +4079,7 @@ snapshots:
global: 4.4.0
is-function: 1.0.2
'@vitejs/plugin-legacy@7.2.1(terser@5.44.1)(vite@7.3.1(@types/node@24.10.6)(terser@5.44.1)(yaml@2.8.2))':
'@vitejs/plugin-legacy@7.2.1(terser@5.46.0)(vite@7.3.1(@types/node@24.10.9)(terser@5.46.0)(yaml@2.8.2))':
dependencies:
'@babel/core': 7.28.5
'@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.28.5)
@@ -4093,15 +4093,15 @@ snapshots:
magic-string: 0.30.21
regenerator-runtime: 0.14.1
systemjs: 6.15.1
terser: 5.44.1
vite: 7.3.1(@types/node@24.10.6)(terser@5.44.1)(yaml@2.8.2)
terser: 5.46.0
vite: 7.3.1(@types/node@24.10.9)(terser@5.46.0)(yaml@2.8.2)
transitivePeerDependencies:
- supports-color
'@vitejs/plugin-vue@6.0.3(vite@7.3.1(@types/node@24.10.6)(terser@5.44.1)(yaml@2.8.2))(vue@3.5.26(typescript@5.9.3))':
'@vitejs/plugin-vue@6.0.3(vite@7.3.1(@types/node@24.10.9)(terser@5.46.0)(yaml@2.8.2))(vue@3.5.26(typescript@5.9.3))':
dependencies:
'@rolldown/pluginutils': 1.0.0-beta.53
vite: 7.3.1(@types/node@24.10.6)(terser@5.44.1)(yaml@2.8.2)
vite: 7.3.1(@types/node@24.10.9)(terser@5.46.0)(yaml@2.8.2)
vue: 3.5.26(typescript@5.9.3)
'@volar/language-core@2.4.27':
@@ -4166,20 +4166,20 @@ snapshots:
dependencies:
rfdc: 1.4.1
'@vue/eslint-config-prettier@10.2.0(eslint@9.39.2)(prettier@3.7.4)':
'@vue/eslint-config-prettier@10.2.0(eslint@9.39.2)(prettier@3.8.0)':
dependencies:
eslint: 9.39.2
eslint-config-prettier: 10.1.8(eslint@9.39.2)
eslint-plugin-prettier: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.39.2))(eslint@9.39.2)(prettier@3.7.4)
prettier: 3.7.4
eslint-plugin-prettier: 5.5.5(eslint-config-prettier@10.1.8(eslint@9.39.2))(eslint@9.39.2)(prettier@3.8.0)
prettier: 3.8.0
transitivePeerDependencies:
- '@types/eslint'
'@vue/eslint-config-typescript@14.6.0(eslint-plugin-vue@10.6.2(@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(vue-eslint-parser@10.2.0(eslint@9.39.2)))(eslint@9.39.2)(typescript@5.9.3)':
'@vue/eslint-config-typescript@14.6.0(eslint-plugin-vue@10.7.0(@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(vue-eslint-parser@10.2.0(eslint@9.39.2)))(eslint@9.39.2)(typescript@5.9.3)':
dependencies:
'@typescript-eslint/utils': 8.37.0(eslint@9.39.2)(typescript@5.9.3)
eslint: 9.39.2
eslint-plugin-vue: 10.6.2(@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(vue-eslint-parser@10.2.0(eslint@9.39.2))
eslint-plugin-vue: 10.7.0(@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(vue-eslint-parser@10.2.0(eslint@9.39.2))
fast-glob: 3.3.3
typescript-eslint: 8.37.0(eslint@9.39.2)(typescript@5.9.3)
vue-eslint-parser: 10.2.0(eslint@9.39.2)
@@ -4543,18 +4543,18 @@ snapshots:
dependencies:
eslint: 9.39.2
eslint-plugin-prettier@5.5.4(eslint-config-prettier@10.1.8(eslint@9.39.2))(eslint@9.39.2)(prettier@3.7.4):
eslint-plugin-prettier@5.5.5(eslint-config-prettier@10.1.8(eslint@9.39.2))(eslint@9.39.2)(prettier@3.8.0):
dependencies:
eslint: 9.39.2
prettier: 3.7.4
prettier-linter-helpers: 1.0.0
synckit: 0.11.11
prettier: 3.8.0
prettier-linter-helpers: 1.0.1
synckit: 0.11.12
optionalDependencies:
eslint-config-prettier: 10.1.8(eslint@9.39.2)
eslint-plugin-vue@10.6.2(@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(vue-eslint-parser@10.2.0(eslint@9.39.2)):
eslint-plugin-vue@10.7.0(@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(vue-eslint-parser@10.2.0(eslint@9.39.2)):
dependencies:
'@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2)
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2)
eslint: 9.39.2
natural-compare: 1.4.0
nth-check: 2.1.1
@@ -5015,11 +5015,11 @@ snapshots:
prelude-ls@1.2.1: {}
prettier-linter-helpers@1.0.0:
prettier-linter-helpers@1.0.1:
dependencies:
fast-diff: 1.3.0
prettier@3.7.4: {}
prettier@3.8.0: {}
pretty-bytes@7.1.0: {}
@@ -5170,7 +5170,7 @@ snapshots:
supports-preserve-symlinks-flag@1.0.0: {}
synckit@0.11.11:
synckit@0.11.12:
dependencies:
'@pkgr/core': 0.2.9
@@ -5180,7 +5180,7 @@ snapshots:
tar-mini@0.2.0: {}
terser@5.44.1:
terser@5.46.0:
dependencies:
'@jridgewell/source-map': 0.3.11
acorn: 8.15.0
@@ -5316,7 +5316,7 @@ snapshots:
transitivePeerDependencies:
- rollup
vite@7.3.1(@types/node@24.10.6)(terser@5.44.1)(yaml@2.8.2):
vite@7.3.1(@types/node@24.10.9)(terser@5.46.0)(yaml@2.8.2):
dependencies:
esbuild: 0.27.2
fdir: 6.5.0(picomatch@4.0.3)
@@ -5325,9 +5325,9 @@ snapshots:
rollup: 4.55.1
tinyglobby: 0.2.15
optionalDependencies:
'@types/node': 24.10.6
'@types/node': 24.10.9
fsevents: 2.3.3
terser: 5.44.1
terser: 5.46.0
yaml: 2.8.2
vscode-uri@3.1.0: {}
+7 -1
View File
@@ -42,8 +42,14 @@ export async function update(
});
}
export async function remove(id: number) {
export async function remove(
id: number,
currentPassword: string | null = null
) {
await fetchURL(`/api/users/${id}`, {
method: "DELETE",
body: JSON.stringify({
...(currentPassword != null ? { current_password: currentPassword } : {}),
}),
});
}
+4
View File
@@ -185,3 +185,7 @@ html[dir="rtl"] .breadcrumbs a {
.vfm-modal {
z-index: 9999999 !important;
}
body > div[style*="z-index: 9990"] {
z-index: 10000 !important;
}
+1
View File
@@ -232,6 +232,7 @@
"permissions": "الصلاحيات",
"permissionsHelp": "يمكنك تعيين المستخدم كـ \"مدير\" أو تحديد الصلاحيات بشكل منفرد.\n إذا قمت بتحديد المستخدم كـ \"مدير\"، باقي الخيارات سيتم تحديدها تلقائياً.\n إدارة المستخدمين تبقى صلاحية فريدة للـ \"مدير\" فقط.\n",
"profileSettings": "إعدادات الحساب",
"redirectAfterCopyMove": "Redirect to destination after copy/move",
"ruleExample1": "منع الوصول إلى الملفات التي تبدأ بنقطة مثل (.git، و .gitignore) في كل مجلد.\n",
"ruleExample2": "منع الوصول إلى الملف المسمى Caddyfile في نطاق الجذر.",
"rules": "المجموعات",
+1
View File
@@ -232,6 +232,7 @@
"permissions": "Разрешения",
"permissionsHelp": "Можете да зададете потребител да бъде администратор или да изберете разрешения индивидуално. Ако изберете \"Администратор\" всички други опции ще бъдат автоматично отметнати. Управлението на потребителите е привилегия на администратор.\n",
"profileSettings": "Настройки на Профила",
"redirectAfterCopyMove": "Redirect to destination after copy/move",
"ruleExample1": "предотвратете достъпа до всеки файл започващ с точка (като .git, .gitignore) във всяка папка.\n",
"ruleExample2": "блокира достъпа до файл именуван Caddyfile поставен в началото за обхвата.",
"rules": "Правила",
+1
View File
@@ -232,6 +232,7 @@
"permissions": "Permisos",
"permissionsHelp": "Pots nomenar l'usuari com a administrador o triar els permisos individualment. Si selecciones \"Administrador\", totes les altres opcions s'activaran automàticament. L'administració d'usuaris és un privilegi d'administrador.\n",
"profileSettings": "Ajustos del perfil",
"redirectAfterCopyMove": "Redirect to destination after copy/move",
"ruleExample1": "prevé l'accés a una extensió de fitxer (Com .git) en cada carpeta.\n",
"ruleExample2": "bloqueja l'accés al fitxer anomenat Caddyfile a la carpeta arrel.",
"rules": "Regles",
+1
View File
@@ -232,6 +232,7 @@
"permissions": "Oprávnění",
"permissionsHelp": "Můžete nastavit uživatele jako administrátora nebo zvolit jednotlivá oprávnění. Pokud vyberete \"Administrátor\", všechny ostatní možnosti budou automaticky zaškrtnuty. Správa uživatelů zůstává výsadou administrátora.\n",
"profileSettings": "Nastavení profilu",
"redirectAfterCopyMove": "Redirect to destination after copy/move",
"ruleExample1": "zabraňuje přístupu k jakémukoli skrytému souboru (např. .git, .gitignore) v každé složce.\n",
"ruleExample2": "blokuje přístup k souboru s názvem Caddyfile v kořenovém adresáři.",
"rules": "Pravidla",
+1
View File
@@ -232,6 +232,7 @@
"permissions": "Berechtigungen",
"permissionsHelp": "Sie können einem Benutzer Administratorrechte einräumen oder die Berechtigungen individuell festlegen. Wenn Sie \"Administrator\" auswählen, werden alle anderen Rechte automatisch vergeben. Die Nutzerverwaltung kann nur durch einen Administrator erfolgen.\n",
"profileSettings": "Profileinstellungen",
"redirectAfterCopyMove": "Nach Kopieren/Verschieben zum Zielverzeichnis wechseln",
"ruleExample1": "Verhindert den Zugang zu versteckten Dateien (dot-Files, wie .git, .gitignore) in allen Ordnern\n",
"ruleExample2": "blockiert den Zugang auf Dateien mit dem Namen Caddyfile in der Wurzel/Basis des Scopes.",
"rules": "Regeln",
+1
View File
@@ -232,6 +232,7 @@
"permissions": "Δικαιώματα",
"permissionsHelp": "Μπορείτε να ορίσετε τον χρήστη ως διαχειριστή ή να επιλέξετε τα δικαιώματα μεμονωμένα. Αν επιλέξετε \"Διαχειριστής\", όλες οι υπόλοιπες επιλογές θα είναι αυτόματα επιλεγμένες. Η διαχείριση χρηστών παραμένει προνόμιο ενός χρήστη με τον ρόλο του διαχειριστή.\n",
"profileSettings": "Ρυθμίσεις προφίλ",
"redirectAfterCopyMove": "Redirect to destination after copy/move",
"ruleExample1": "αποκλείει την πρόσβαση σε οποιοδήποτε κρυφό αρχείο (όπως .git, .gitignore) σε κάθε φάκελο.\n",
"ruleExample2": "αποκλείει την πρόσβαση στο αρχείο με το όνομα Caddyfile στον ριζικό φάκελο της εμβέλειας του κανόνα.",
"rules": "Κανόνες",
+1
View File
@@ -232,6 +232,7 @@
"permissions": "Permisos",
"permissionsHelp": "Puedes nombrar al usuario como administrador o elegir los permisos individualmente. Si seleccionas \"Administrador\", todas las otras opciones serán activadas automáticamente. La administración de usuarios es un privilegio de administrador.\n",
"profileSettings": "Ajustes del perfil",
"redirectAfterCopyMove": "Redirect to destination after copy/move",
"ruleExample1": "previene el acceso a una extensión de archivo (Como .git) en cada carpeta.\n",
"ruleExample2": "bloquea el acceso al archivo llamado Caddyfile en la carpeta raíz.",
"rules": "Reglas",
+1
View File
@@ -232,6 +232,7 @@
"permissions": "دسترسی ها",
"permissionsHelp": "شما می‌توانید کاربر را به عنوان مدیر تنظیم کنید یا دسترسی‌ها را به صورت جداگانه انتخاب کنید. اگر \"مدیر\" را انتخاب کنید، تمام گزینه‌های دیگر به طور خودکار اعمال می‌شوند. مدیریت کاربران همچنان از اختیارات مدیر است.",
"profileSettings": "تنظیمات ناحیه کاربری",
"redirectAfterCopyMove": "Redirect to destination after copy/move",
"ruleExample1": "از دسترسی به هرگونه فایل نقطه‌ای (مانند .git، .gitignore) در هر پوشه جلوگیری می‌کند.",
"ruleExample2": "دسترسی به فایلی به نام Caddyfile را در ریشه دامنه مسدود می‌کند.",
"rules": "قواعد",
+1
View File
@@ -232,6 +232,7 @@
"permissions": "Permissions",
"permissionsHelp": "Vous pouvez définir l'utilisateur·ice comme étant administrateur·ice ou encore choisir les permissions individuellement. Si vous sélectionnez \"Administrateur·ice\", toutes les autres options seront automatiquement activées. La gestion des utilisateur·ices est un privilège que seul l'administrateur·ice possède.\n",
"profileSettings": "Paramètres du profil",
"redirectAfterCopyMove": "Redirect to destination after copy/move",
"ruleExample1": "Bloque l'accès à tous les fichiers commençant par un point (comme par exemple .git, .gitignore) dans tous les dossiers.\n",
"ruleExample2": "Bloque l'accès au fichier nommé \"Caddyfile\" à la racine du dossier utilisateur·ice.",
"rules": "Règles",
+1
View File
@@ -232,6 +232,7 @@
"permissions": "הרשאות",
"permissionsHelp": "אתה יכול להגדיר את המשתמש להיות מנהל מערכת או לבחור את ההרשאות בנפרד. אם תבחר \"מנהל מערכת\", כל ההרשאות יינתנו אוטומטית. ניהול המשתמשים נשאר הרשאה של מנהל מערכת.\n",
"profileSettings": "הגדרות פרופיל",
"redirectAfterCopyMove": "Redirect to destination after copy/move",
"ruleExample1": "מנע גישה לקבצים נסתרים (כל קובץ/תיקייה שמתחיל בנקודה, לדוגמא .git)",
"ruleExample2": "חסימת גישה לקובץ בשם Caddyfile בהיקף הראשי.",
"rules": "חוקים",
+1
View File
@@ -232,6 +232,7 @@
"permissions": "Dopuštenja",
"permissionsHelp": "Korisnika možete postaviti administratorom ili odabrati dopuštenja individualno. Odabirom na \"Administrator\", sve druge opcije bit će automatski odabrane. Upravljanje korisnicima ostaje privilegija administratora.\n",
"profileSettings": "Postavke profila",
"redirectAfterCopyMove": "Redirect to destination after copy/move",
"ruleExample1": "onemogućava pristup svakoj datoteci koja započinje točkom (poput .git, .gitignore) u svakoj mapi.\n",
"ruleExample2": "blokira pristup datoteci naziva Caddyfile na korijenu opsega.",
"rules": "Pravila",
+1
View File
@@ -232,6 +232,7 @@
"permissions": "Jogosultságok",
"permissionsHelp": "A felhasználót beállíthatja rendszergazdának, vagy egyénileg is kiválaszthatja a jogosultságokat. Ha a \"Rendszergazda\" lehetőséget választja, az összes többi opció automatikusan be lesz jelölve. A felhasználók kezelése továbbra is a rendszergazda kiváltsága marad.\n",
"profileSettings": "Profilbeállítások",
"redirectAfterCopyMove": "Redirect to destination after copy/move",
"ruleExample1": "megakadályozza a hozzáférést bármely rejtett fájlhoz (pl. .git, .gitignore) bármely mappában.\n",
"ruleExample2": "blokkolja a hozzáférést a Caddyfile nevű fájlhoz a hatókör gyökerében.",
"rules": "Szabályok",
+1
View File
@@ -232,6 +232,7 @@
"permissions": "Heimildir",
"permissionsHelp": "Þú getur stillt notenda sem stjórnanda eða valið einstaklingsbundnar heimildir. Ef þú velur \"Stjórnandi\", þá verða allir aðrir valmöguleikar valdir sjálfrafa. Aðgangstýring notenda er á hendi stjórnenda. \n",
"profileSettings": "Stilla prófíl",
"redirectAfterCopyMove": "Redirect to destination after copy/move",
"ruleExample1": "kemur í veg fyrir aðgang að dot-skjali (t.d. .git, .gitignore) í öllum möppum. \n",
"ruleExample2": "kemur í veg fyrir aðgang að Caddyfile-skjalinu í root-möppu í sýn notandans. ",
"rules": "Reglur",
+1
View File
@@ -232,6 +232,7 @@
"permissions": "Permessi",
"permissionsHelp": "È possibile impostare l'utente come amministratore o scegliere i permessi singolarmente. Se si seleziona \"Amministratore\", tutte le altre opzioni saranno automaticamente assegnate. La gestione degli utenti rimane un privilegio di un amministratore.\n",
"profileSettings": "Impostazioni del profilo",
"redirectAfterCopyMove": "Redirect to destination after copy/move",
"ruleExample1": "impedisci l'accesso a qualsiasi file avente come prefisso un punto\n (ad esempio .git, .gitignore) presente in ogni cartella.\n",
"ruleExample2": "blocca l'accesso al file denominato Caddyfile nella radice del campo di applicazione.",
"rules": "Regole",
+1
View File
@@ -232,6 +232,7 @@
"permissions": "権限",
"permissionsHelp": "ユーザーを管理者に設定するか、その他の権限を個別に選択することができます。「管理者」を選択すると、他のオプションはすべて自動的にチェックされます。ユーザーを管理するには管理者権限が必要です。\n",
"profileSettings": "プロフィール設定",
"redirectAfterCopyMove": "Redirect to destination after copy/move",
"ruleExample1": ".git や .gitignore のようなドットから始まるファイルへのアクセスを禁止します。\n",
"ruleExample2": "スコープのルートにある Caddyfile という名前のファイルへのアクセスを禁止します。",
"rules": "ルール",
+1
View File
@@ -232,6 +232,7 @@
"permissions": "권한",
"permissionsHelp": "사용자를 관리자로 만들거나 권한을 부여할 수 있습니다. 관리자를 선택하면, 모든 옵션이 자동으로 선택됩니다. 사용자 관리는 현재 관리자만 할 수 있습니다.\n",
"profileSettings": "프로필 설정",
"redirectAfterCopyMove": "Redirect to destination after copy/move",
"ruleExample1": "점(.)으로 시작하는 모든 파일의 접근을 방지합니다.(예 .git, .gitignore)\n",
"ruleExample2": "Caddyfile파일의 접근을 방지합니다.",
"rules": "룰",
+1
View File
@@ -232,6 +232,7 @@
"permissions": "Atļaujas",
"permissionsHelp": "Varat iestatīt lietotāju kā administratoru vai izvēlēties atļaujas individuāli. Ja atlasīsiet “Administrators”, visas pārējās opcijas tiks automātiski atzīmētas. Lietotāju pārvaldība joprojām ir administratora privilēģija.\n",
"profileSettings": "Profila iestatījumi",
"redirectAfterCopyMove": "Redirect to destination after copy/move",
"ruleExample1": "neļauj piekļūt jebkuram dotfile failam (piemēram, .git, .gitignore) katrā mapē.\n",
"ruleExample2": "bloķē piekļuvi failam ar nosaukumu Caddyfile darbības jomas saknē.",
"rules": "Noteikumi",
+4 -3
View File
@@ -43,7 +43,7 @@
"upload": "Augšupielādēt",
"openFile": "Atvērt failu",
"discardChanges": "Izmest",
"stopSearch": "Stop searching",
"stopSearch": "Beigt meklēšanu",
"saveChanges": "Saglabāt izmaiņas",
"editAsText": "Rediģēt kā tekstu",
"increaseFontSize": "Palieliniet fonta lielumu",
@@ -232,6 +232,7 @@
"permissions": "Atļaujas",
"permissionsHelp": "Varat iestatīt lietotāju kā administratoru vai izvēlēties atļaujas individuāli. Ja atlasīsiet “Administrators”, visas pārējās opcijas tiks automātiski atzīmētas. Lietotāju pārvaldība joprojām ir administratora privilēģija.\n",
"profileSettings": "Profila iestatījumi",
"redirectAfterCopyMove": "Pārvirzīt uz galapunktu pēc kopēšanas/pārvietošanas",
"ruleExample1": "neļauj piekļūt jebkuram dotfile failam (piemēram, .git, .gitignore) katrā mapē.\n",
"ruleExample2": "bloķē piekļuvi failam ar nosaukumu Caddyfile darbības jomas saknē.",
"rules": "Noteikumi",
@@ -241,7 +242,7 @@
"settingsUpdated": "Iestatījumi atjaunināti!",
"shareDuration": "Kopīgošanas ilgums",
"shareManagement": "Kopīgošanas pārvaldība",
"shareDeleted": "Kopīgošana ir izdzēsta!",
"shareDeleted": "Kopīgojums izdzēsts!",
"singleClick": "Failu un direktoriju atvēršanai izmantojiet vienus klikšķi",
"themes": {
"default": "Sistēmas noklusējums",
@@ -259,7 +260,7 @@
"userUpdated": "Lietotājs atjaunināts!",
"username": "Lietotājvārds",
"users": "Lietotāji",
"currentPassword": "Your Current Password"
"currentPassword": "Esošā Parole"
},
"sidebar": {
"help": "Palīdzība",
+1
View File
@@ -232,6 +232,7 @@
"permissions": "Permissies",
"permissionsHelp": "U kunt de gebruiker instellen als beheerder of de machtigingen afzonderlijk kiezen. Als u \"Beheerder\" selecteert, worden alle andere opties automatisch gecontroleerd. Het beheer van gebruikers blijft een privilege van een beheerder.\n",
"profileSettings": "Profielinstellingen",
"redirectAfterCopyMove": "Redirect to destination after copy/move",
"ruleExample1": "voorkomt de toegang tot elk puntbestand (zoals .git, .gitignore) in elke map.\n",
"ruleExample2": "blokkeert de toegang tot het bestand met de naam Caddyfile in de hoofdmap van het bereik.",
"rules": "Regels",
+1
View File
@@ -232,6 +232,7 @@
"permissions": "Tilaterser",
"permissionsHelp": "Du kan angi brukeren som administrator eller velge tillatelsene individuelt. Hvis du velger «Administrator», vil alle de andre alternativene bli automatisk avkrysset. Administrasjon av brukere er fortsatt et privilegium for en administrator.\n",
"profileSettings": "Profil Innstilinger",
"redirectAfterCopyMove": "Redirect to destination after copy/move",
"ruleExample1": "forhindrer tilgang til noen dotfiler (som .git, .gitignore) i alle mapper.\n",
"ruleExample2": "blokkerer tilgangen til filen med navnet Caddyfile på roten av omfanget.",
"rules": "Regler",
+2 -1
View File
@@ -231,7 +231,8 @@
},
"permissions": "Uprawnienia",
"permissionsHelp": "Możesz ustawić użytkownika jako administratora lub wybrać uprawnienia indywidualnie. Jeśli wybierzesz „Administrator”, wszystkie pozostałe opcje zostaną automatycznie zaznaczone. Zarządzanie użytkownikami pozostaje przywilejem administratora.\n",
"profileSettings": "Twój profil",
"profileSettings": "Ustawienia profilu",
"redirectAfterCopyMove": "Przekieruj do miejsca docelowego po skopiowaniu lub przeniesieniu",
"ruleExample1": "uniemożliwia dostęp do plików poprzedzonych kropką (takich jak .git, .gitignore) we wszystkich folderach.\n",
"ruleExample2": "blokuje dostęp do pliku o nazwie Caddyfile w katalogu głównym zakresu.",
"rules": "Uprawnienia",
+1
View File
@@ -232,6 +232,7 @@
"permissions": "Permissões",
"permissionsHelp": "Pode definir o usuário como administrador ou escolher as permissões manualmente. Se selecionar a opção \"Administrador\", todas as outras opções serão automaticamente selecionadas. A gestão dos usuários é um privilégio restringido aos administradores.\n",
"profileSettings": "Configurações do usuário",
"redirectAfterCopyMove": "Redirect to destination after copy/move",
"ruleExample1": "previne o acesso a qualquer \"dotfile\" (como .git, .gitignore) em qualquer pasta\n",
"ruleExample2": "bloqueia o acesso ao arquivo chamado Caddyfile.",
"rules": "Regras",
+1
View File
@@ -232,6 +232,7 @@
"permissions": "Permissões",
"permissionsHelp": "Pode definir o utilizador como administrador ou escolher as permissões manualmente. Se selecionar a opção \"Administrador\", todas as outras opções serão automaticamente selecionadas. A gestão dos utilizadores é um privilégio restringido aos administradores.\n",
"profileSettings": "Configurações do utilizador",
"redirectAfterCopyMove": "Redirect to destination after copy/move",
"ruleExample1": "previne o acesso a qualquer \"dotfile\" (como .git, .gitignore) em qualquer pasta\n",
"ruleExample2": "bloqueia o acesso ao ficheiro chamado Caddyfile na raiz.",
"rules": "Regras",
+1
View File
@@ -232,6 +232,7 @@
"permissions": "Permisiuni",
"permissionsHelp": "Poți alege ca un utilizator să fie administrator sau să-i alegi permisiunile individual. Dacă alegi \"Administrator\", toate celelalte opțiuni vor fi bifate automat. Gestionarea utilizatorilor rămâne un privilegiu exclusiv al administratorilor.\n",
"profileSettings": "Setări profil",
"redirectAfterCopyMove": "Redirect to destination after copy/move",
"ruleExample1": "împiedică accesul la fisiere cu punct in față (.), cum ar fi .git, .gitignore în orice director.\n",
"ruleExample2": "împiedică accesul la fișierul Caddyfile din rădăcina domeniului.",
"rules": "Reguli",
+1
View File
@@ -232,6 +232,7 @@
"permissions": "Права доступа",
"permissionsHelp": "Можно настроить пользователя как администратора или выбрать разрешения индивидуально. При выборе \"Администратор\", все остальные параметры будут автоматически выбраны. Управление пользователями - привилегия администратора.\n",
"profileSettings": "Настройки профиля",
"redirectAfterCopyMove": "Redirect to destination after copy/move",
"ruleExample1": "предотвратить доступ к любому скрытому файлу (например: .git, .gitignore) в каждой папке.\n",
"ruleExample2": "блокирует доступ к файлу с именем Caddyfile в корневой области.",
"rules": "Права",
+1
View File
@@ -232,6 +232,7 @@
"permissions": "Práva",
"permissionsHelp": "Môžete nastaviť používateľa, aby bol administrátorom alebo vybrať práva jednotlivo. Ak zvolíte \"Administrator\", všetky ďalšie budú automaticky zaškrtnuté. Manažment používateľov ostáva v správe administrátora.\n",
"profileSettings": "Nastavenia profilu",
"redirectAfterCopyMove": "Redirect to destination after copy/move",
"ruleExample1": "blokuje prístup ku všetkým súborom začínajúcim bodkou (napríklad .git, .gitignore) v každom priečinku.\n",
"ruleExample2": "blokuje prístup k súborom s názvom Caddyfile v koreňovom priečinku.",
"rules": "Pravidlá",
+1
View File
@@ -232,6 +232,7 @@
"permissions": "Rättigheter",
"permissionsHelp": "Du kan ange att användaren ska vara administratör eller välja behörigheterna individuellt. Om du väljer \"administratör \" kommer alla andra alternativ att kontrolleras automatiskt. Hanteringen av användare är fortfarande ett privilegium för en administratör.\n",
"profileSettings": "Profil inställningar",
"redirectAfterCopyMove": "Redirect to destination after copy/move",
"ruleExample1": "förhindrar åtkomst till en dot-fil (till exempel. git,. gitignore) i varje mapp.\n",
"ruleExample2": "blockerar åtkomsten till filen som heter Caddyfilen i roten av scopet.",
"rules": "Regler",
+1
View File
@@ -232,6 +232,7 @@
"permissions": "İzinler",
"permissionsHelp": "Kullanıcıyı yönetici olarak ayarlayabilir veya izinleri ayrı ayrı seçebilirsiniz. \"Yönetici\"yi seçerseniz, diğer tüm seçenekler otomatik olarak kontrol edilecektir. Kullanıcıların yönetimi, bir yöneticinin yetkisi olarak kalır.\n",
"profileSettings": "Profil ayarları",
"redirectAfterCopyMove": "Redirect to destination after copy/move",
"ruleExample1": "her klasördeki herhangi bir noktalı dosyaya (.git, .gitignore gibi) erişimi engeller.\n",
"ruleExample2": "Root erişimidenki CaddyFile dosyalarına erişimi engelle.",
"rules": "Kurallar",
+1
View File
@@ -232,6 +232,7 @@
"permissions": "Дозволи",
"permissionsHelp": "Можна налаштувати користувача як адміністратора чи вибрати індивідуальні дозволи. При виборі \"Адміністратор\" всі інші параметри будуть автоматично вибрані. Керування користувачами - привілей адміністратора.\n",
"profileSettings": "Налаштування профілю",
"redirectAfterCopyMove": "Redirect to destination after copy/move",
"ruleExample1": "запобігти доступу до будь-якого прихованого файлу (наприклад: .git, .gitignore) у кожній папці.\n",
"ruleExample2": "блокує доступ до файлу з ім'ям Caddyfile у кореневій області.",
"rules": "Права",
+1
View File
@@ -232,6 +232,7 @@
"permissions": "Quyền",
"permissionsHelp": "Bạn có thể đặt người dùng làm quản trị viên hoặc chọn quyền riêng lẻ. Nếu chọn \"Người quản trị\", tất cả các tùy chọn khác sẽ tự động được chọn. Việc quản lý người dùng vẫn là đặc quyền của quản trị viên.\n",
"profileSettings": "Cài đặt hồ sơ",
"redirectAfterCopyMove": "Redirect to destination after copy/move",
"ruleExample1": "ngăn truy cập vào bất kỳ tệp ẩn nào (chẳng hạn như .git, .gitignore) trong mọi thư mục.\n",
"ruleExample2": "chặn truy cập vào tệp có tên Caddyfile trong thư mục gốc của phạm vi.",
"rules": "Quy tắc",
+3 -2
View File
@@ -43,7 +43,7 @@
"upload": "上传",
"openFile": "打开文件",
"discardChanges": "放弃更改",
"stopSearch": "Stop searching",
"stopSearch": "停止搜索",
"saveChanges": "保存更改",
"editAsText": "以文本形式编辑",
"increaseFontSize": "增大字体大小",
@@ -232,6 +232,7 @@
"permissions": "权限",
"permissionsHelp": "你可以将该用户设置为管理员或单独选择各项权限。如果你选择了“管理员”,则其他的选项会被自动选中,同时该用户可以管理其他用户。\n",
"profileSettings": "个人设置",
"redirectAfterCopyMove": "Redirect to destination after copy/move",
"ruleExample1": "阻止用户访问所有文件夹下任何以 . 开头的文件(隐藏文件, 例如: .git, .gitignore)。\n",
"ruleExample2": "阻止用户访问其目录范围的根目录下名为 Caddyfile 的文件。",
"rules": "规则",
@@ -259,7 +260,7 @@
"userUpdated": "用户已更新!",
"username": "用户名",
"users": "用户",
"currentPassword": "Your Current Password"
"currentPassword": "您当前的密码"
},
"sidebar": {
"help": "帮助",
+1
View File
@@ -232,6 +232,7 @@
"permissions": "權限",
"permissionsHelp": "您可以將該使用者設置為管理員,也可以單獨選擇各項權限。如果選擇了“管理員”,則其他的選項會被自動勾上,同時該使用者可以管理其他使用者。",
"profileSettings": "個人設定",
"redirectAfterCopyMove": "Redirect to destination after copy/move",
"ruleExample1": "封鎖使用者存取所有資料夾下任何以 . 開頭的檔案(隱藏文件, 例如: .git, .gitignore)。",
"ruleExample2": "封鎖使用者存取其目錄範圍的根目錄下名為 Caddyfile 的檔案。",
"rules": "規則",
@@ -51,6 +51,20 @@ export function copy(data: ClipboardArgs, opts?: ClipboardOpts) {
});
}
export function read() {
return new Promise<string>((resolve, reject) => {
if (
// Clipboard API requires secure context
window.isSecureContext &&
typeof navigator.clipboard !== "undefined"
) {
navigator.clipboard.readText().then(resolve).catch(reject);
} else {
reject();
}
});
}
function getPermission(name: string) {
return new Promise<void>((resolve, reject) => {
typeof navigator.permissions !== "undefined" &&
@@ -41,7 +41,30 @@
</div>
</div>
<template v-else>
<Breadcrumbs base="/files" noLink />
<div class="editor-header">
<Breadcrumbs base="/files" noLink />
<div>
<button
:disabled="isSelectionEmpty"
@click="executeEditorCommand('copy')"
>
<span><i class="material-icons">content_copy</i></span>
</button>
<button
:disabled="isSelectionEmpty"
@click="executeEditorCommand('cut')"
>
<span><i class="material-icons">content_cut</i></span>
</button>
<button @click="executeEditorCommand('paste')">
<span><i class="material-icons">content_paste</i></span>
</button>
<button @click="executeEditorCommand('openCommandPalette')">
<span><i class="material-icons">more_vert</i></span>
</button>
</div>
</div>
<div
v-show="isPreview && isMarkdownFile"
@@ -74,6 +97,7 @@ import { marked } from "marked";
import { inject, onBeforeUnmount, onMounted, ref, watchEffect } from "vue";
import { useI18n } from "vue-i18n";
import { onBeforeRouteUpdate, useRoute, useRouter } from "vue-router";
import { read, copy } from "@/utils/clipboard";
const $showError = inject<IToastError>("$showError")!;
@@ -95,6 +119,35 @@ const isMarkdownFile =
fileStore.req?.name.endsWith(".md") ||
fileStore.req?.name.endsWith(".markdown");
const isSelectionEmpty = ref(true);
const executeEditorCommand = (name: string) => {
if (name == "paste") {
read()
.then((data) => {
editor.value?.execCommand("paste", {
text: data,
});
})
.catch((e) => {
if (
document.queryCommandSupported &&
document.queryCommandSupported("paste")
) {
document.execCommand("paste");
} else {
console.warn("the clipboard api is not supported", e);
}
});
return;
}
if (name == "copy" || name == "cut") {
const selectedText = editor.value?.getCopyText();
copy({ text: selectedText });
}
editor.value?.execCommand(name);
};
onMounted(() => {
window.addEventListener("keydown", keyEvent);
window.addEventListener("beforeunload", handlePageChange);
@@ -132,6 +185,11 @@ onMounted(() => {
editor.value.setFontSize(fontSize.value);
editor.value.focus();
editor.value.getSelection().on("changeSelection", () => {
isSelectionEmpty.value =
editor.value == null || editor.value.getSelectedText().length == 0;
});
});
onBeforeUnmount(() => {
@@ -236,7 +294,6 @@ const close = () => {
};
const finishClose = () => {
fileStore.updateRequest(null);
const uri = url.removeLastDir(route.path) + "/";
router.push({ path: uri });
};
@@ -251,4 +308,32 @@ const preview = () => {
margin: 0 0.5em;
color: var(--fg);
}
.editor-header {
display: flex;
align-items: center;
justify-content: space-between;
}
.editor-header > div > button {
background: transparent;
color: var(--action);
border: none;
outline: none;
opacity: 0.8;
cursor: pointer;
}
.editor-header > div > button:hover:not(:disabled) {
opacity: 1;
}
.editor-header > div > button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.editor-header > div > button > span > i {
font-size: 1.2rem;
}
</style>
@@ -1059,6 +1059,9 @@ const handleEmptyAreaClick = (e: MouseEvent) => {
if (target.closest("item") || target.closest(".item")) return;
// Do not clear selection when clicking on context menu actions
if (target.closest(".context-menu")) return;
fileStore.selected = [];
};
</script>
@@ -71,6 +71,7 @@ import { computed, inject, onMounted, ref, watch } from "vue";
import { useRoute, useRouter } from "vue-router";
import { useI18n } from "vue-i18n";
import { StatusError } from "@/api/utils";
import { authMethod } from "@/utils/constants";
const error = ref<StatusError>();
const originalUser = ref<IUser>();
@@ -105,11 +106,7 @@ const fetchData = async () => {
try {
if (isNew.value) {
const {
authMethod,
defaults,
createUserDir: _createUserDir,
} = await settings.get();
const { defaults, createUserDir: _createUserDir } = await settings.get();
isCurrentPasswordRequired.value = authMethod == "json";
createUserDir.value = _createUserDir;
user.value = {
@@ -146,7 +143,7 @@ const deleteUser = async (e: Event) => {
return false;
}
try {
await api.remove(user.value.id);
await api.remove(user.value.id, currentPassword.value);
router.push({ path: "/settings/users" });
$showSuccess(t("settings.userDeleted"));
} catch (err) {
+4 -4
View File
@@ -24,8 +24,8 @@ require (
github.com/spf13/viper v1.21.0
github.com/stretchr/testify v1.11.1
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce
golang.org/x/crypto v0.46.0
golang.org/x/image v0.34.0
golang.org/x/crypto v0.47.0
golang.org/x/image v0.35.0
golang.org/x/text v0.33.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v3 v3.0.1
@@ -73,9 +73,9 @@ require (
go.etcd.io/bbolt v1.4.3 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
golang.org/x/net v0.47.0 // indirect
golang.org/x/net v0.48.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.39.0 // indirect
golang.org/x/sys v0.40.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
+8 -8
View File
@@ -266,8 +266,8 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -279,8 +279,8 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.34.0 h1:33gCkyw9hmwbZJeZkct8XyR11yH889EQt/QH4VmXMn8=
golang.org/x/image v0.34.0/go.mod h1:2RNFBZRB+vnwwFil8GkMdRvrJOFd1AzdZI6vOY+eJVU=
golang.org/x/image v0.35.0 h1:LKjiHdgMtO8z7Fh18nGY6KDcoEtVfsgLDPeLyguqb7I=
golang.org/x/image v0.35.0/go.mod h1:MwPLTVgvxSASsxdLzKrl8BRFuyqMyGhLwmC+TO1Sybk=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -319,8 +319,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -360,8 +360,8 @@ golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
+19 -1
View File
@@ -103,7 +103,25 @@ var userGetHandler = withSelfOrAdmin(func(w http.ResponseWriter, r *http.Request
return renderJSON(w, r, u)
})
var userDeleteHandler = withSelfOrAdmin(func(_ http.ResponseWriter, _ *http.Request, d *data) (int, error) {
var userDeleteHandler = withSelfOrAdmin(func(_ http.ResponseWriter, r *http.Request, d *data) (int, error) {
if r.Body == nil {
return http.StatusBadRequest, fberrors.ErrEmptyRequest
}
var body struct {
CurrentPassword string `json:"current_password"`
}
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
return http.StatusBadRequest, err
}
if d.settings.AuthMethod == auth.MethodJSONAuth {
if !users.CheckPwd(body.CurrentPassword, d.user.Password) {
return http.StatusBadRequest, fberrors.ErrCurrentPasswordIncorrect
}
}
err := d.store.Users.Delete(d.raw.(uint))
if err != nil {
return errToStatus(err), err
+2 -2
View File
@@ -5,10 +5,10 @@
include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mk
PKG_VERSION:=2025.10
PKG_VERSION:=2026.01
PKG_RELEASE:=1
PKG_HASH:=b4f032848e56cc8f213ad59f9132c084dbbb632bc29176d024e58220e0efdf4a
PKG_HASH:=b60d5865cefdbc75da8da4156c56c458e00de75a49b80c1a2e58a96e30ad0d54
PKG_MAINTAINER:=Tobias Maedel <openwrt@tbspace.de>
@@ -1,134 +0,0 @@
From: Jonas Karlman <jonas@kwiboo.se>
To: Kever Yang <kever.yang@rock-chips.com>,
Simon Glass <sjg@chromium.org>,
Philipp Tomsich <philipp.tomsich@vrull.eu>,
Tom Rini <trini@konsulko.com>, Jonas Karlman <jonas@kwiboo.se>
Cc: Quentin Schulz <quentin.schulz@cherry.de>, u-boot@lists.denx.de
Subject: [PATCH v3 10/10] board: rockchip: Add Radxa ROCK 4D
Date: Sun, 31 Aug 2025 11:20:31 +0000 [thread overview]
Message-ID: <20250831112046.2642363-11-jonas@kwiboo.se> (raw)
In-Reply-To: <20250831112046.2642363-1-jonas@kwiboo.se>
The Radxa ROCK 4D is a compact single-board computer (SBC) featuring
numerous top-tier functions, features, and expansion options.
Equipped with the Rockchip RK3576 or RK3576J SoC, the ROCK 4D boasts an
octa-core CPU (4x Cortex-A72 + 4x Cortex-A53), Mali-G52 GPU, and a
powerful 6 TOPS NPU, making it ideal for AI and multimedia tasks.
Features tested on a Radxa ROCK 4D v1.112:
- SPI Flash boot
- Ethernet
- PCIe/NVMe
- USB host
ROCK 4D boards with SPI Flash is configured to boot from FSPI0->UFS->USB,
or directly from USB when the MASKROM button is pressed, booting
directly from SD-card is not possible on these boards.
Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
---
v3: Drop the reset-gpios prop rename
v2: Add comment about the reset-gpios prop rename
---
arch/arm/dts/rk3576-rock-4d-u-boot.dtsi | 10 ++++
configs/rock-4d-rk3576_defconfig | 68 +++++++++++++++++++++++
doc/board/rockchip/rockchip.rst | 1 +
4 files changed, 85 insertions(+)
create mode 100644 arch/arm/dts/rk3576-rock-4d-u-boot.dtsi
create mode 100644 configs/rock-4d-rk3576_defconfig
--- /dev/null
+++ b/arch/arm/dts/rk3576-rock-4d-u-boot.dtsi
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+
+#include "rk3576-u-boot.dtsi"
+
+&sfc0 {
+ flash@0 {
+ bootph-pre-ram;
+ bootph-some-ram;
+ };
+};
--- /dev/null
+++ b/configs/rock-4d-rk3576_defconfig
@@ -0,0 +1,68 @@
+CONFIG_ARM=y
+CONFIG_SKIP_LOWLEVEL_INIT=y
+CONFIG_COUNTER_FREQUENCY=24000000
+CONFIG_ARCH_ROCKCHIP=y
+CONFIG_SF_DEFAULT_SPEED=50000000
+CONFIG_SF_DEFAULT_MODE=0x2000
+CONFIG_DEFAULT_DEVICE_TREE="rockchip/rk3576-rock-4d"
+CONFIG_ROCKCHIP_RK3576=y
+CONFIG_ROCKCHIP_SPI_IMAGE=y
+CONFIG_SYS_LOAD_ADDR=0x40c00800
+CONFIG_SF_DEFAULT_BUS=5
+CONFIG_DEBUG_UART_BASE=0x2AD40000
+CONFIG_DEBUG_UART_CLOCK=24000000
+CONFIG_SPL_SPI_FLASH_SUPPORT=y
+CONFIG_SPL_SPI=y
+CONFIG_PCI=y
+CONFIG_DEBUG_UART=y
+CONFIG_DEFAULT_FDT_FILE="rockchip/rk3576-rock-4d.dtb"
+# CONFIG_DISPLAY_CPUINFO is not set
+CONFIG_SPL_MAX_SIZE=0x40000
+# CONFIG_SPL_RAW_IMAGE_SUPPORT is not set
+CONFIG_SPL_SPI_LOAD=y
+CONFIG_SYS_SPI_U_BOOT_OFFS=0x60000
+CONFIG_CMD_MEMINFO=y
+CONFIG_CMD_MEMINFO_MAP=y
+CONFIG_CMD_ADC=y
+CONFIG_CMD_GPIO=y
+CONFIG_CMD_GPT=y
+CONFIG_CMD_I2C=y
+CONFIG_CMD_MISC=y
+CONFIG_CMD_MMC=y
+CONFIG_CMD_PCI=y
+CONFIG_CMD_USB=y
+# CONFIG_CMD_SETEXPR is not set
+CONFIG_CMD_RNG=y
+CONFIG_CMD_REGULATOR=y
+# CONFIG_SPL_DOS_PARTITION is not set
+CONFIG_OF_SPL_REMOVE_PROPS="clock-names interrupt-parent assigned-clocks assigned-clock-rates assigned-clock-parents"
+CONFIG_ROCKCHIP_GPIO=y
+CONFIG_SYS_I2C_ROCKCHIP=y
+CONFIG_LED=y
+CONFIG_LED_GPIO=y
+CONFIG_SUPPORT_EMMC_RPMB=y
+CONFIG_MMC_DW=y
+CONFIG_MMC_DW_ROCKCHIP=y
+CONFIG_SPI_FLASH_SFDP_SUPPORT=y
+CONFIG_SPI_FLASH_MACRONIX=y
+CONFIG_PHY_REALTEK=y
+CONFIG_DWC_ETH_QOS=y
+CONFIG_DWC_ETH_QOS_ROCKCHIP=y
+CONFIG_NVME_PCI=y
+CONFIG_PCIE_DW_ROCKCHIP=y
+CONFIG_PHY_ROCKCHIP_INNO_USB2=y
+CONFIG_PHY_ROCKCHIP_NANENG_COMBOPHY=y
+CONFIG_PHY_ROCKCHIP_USBDP=y
+CONFIG_DM_PMIC=y
+CONFIG_PMIC_RK8XX=y
+CONFIG_REGULATOR_RK8XX=y
+CONFIG_BAUDRATE=1500000
+CONFIG_DEBUG_UART_SHIFT=2
+CONFIG_SYS_NS16550_MEM32=y
+CONFIG_ROCKCHIP_SFC=y
+CONFIG_SYSRESET_PSCI=y
+CONFIG_USB=y
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_DWC3=y
+CONFIG_USB_DWC3_GENERIC=y
+CONFIG_ERRNO_STR=y
--- a/doc/board/rockchip/rockchip.rst
+++ b/doc/board/rockchip/rockchip.rst
@@ -137,6 +137,7 @@ List of mainline supported Rockchip boar
* rk3576
- Firefly ROC-RK3576-PC (roc-pc-rk3576)
- Generic RK3576 (generic-rk3576)
+ - Radxa ROCK 4D (rock-4d-rk3576)
* rk3588
- ArmSoM Sige7 (sige7-rk3588)
@@ -1,6 +1,6 @@
--- a/Makefile
+++ b/Makefile
@@ -2315,26 +2315,7 @@ endif
@@ -2324,26 +2324,7 @@ endif
# Check dtc and pylibfdt, if DTC is provided, else build them
PHONY += scripts_dtc
scripts_dtc: scripts_basic
@@ -1,14 +0,0 @@
--- a/tools/binman/control.py
+++ b/tools/binman/control.py
@@ -9,8 +9,9 @@ from collections import OrderedDict
import glob
try:
import importlib.resources as importlib_resources
-except ImportError: # pragma: no cover
- # for Python 3.6
+ # for Python 3.6, 3.7 and 3.8
+ importlib_resources.files
+except (ImportError, AttributeError):
import importlib_resources
import os
import re
@@ -29,7 +29,7 @@ Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
#include <misc.h>
#include <spl.h>
#include <asm/armv8/mmu.h>
@@ -200,6 +201,16 @@ int arch_cpu_init(void)
@@ -213,6 +214,16 @@ int arch_cpu_init(void)
#define RK3588_OTP_CPU_CODE_OFFSET 0x02
#define RK3588_OTP_SPECIFICATION_OFFSET 0x06
@@ -46,7 +46,7 @@ Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
int checkboard(void)
{
@@ -245,3 +256,213 @@ int checkboard(void)
@@ -258,3 +269,213 @@ int checkboard(void)
return 0;
}
@@ -69,7 +69,7 @@ Signed-off-by: Jonas Karlman <jonas@kwiboo.se>
CONFIG_SPL_MAX_SIZE=0x40000
--- a/doc/board/rockchip/rockchip.rst
+++ b/doc/board/rockchip/rockchip.rst
@@ -148,7 +148,7 @@ List of mainline supported Rockchip boar
@@ -154,7 +154,7 @@ List of mainline supported Rockchip boar
- FriendlyElec NanoPi R6C (nanopi-r6c-rk3588s)
- FriendlyElec NanoPi R6S (nanopi-r6s-rk3588s)
- GameForce Ace (gameforce-ace-rk3588s)
@@ -1,6 +1,6 @@
--- a/arch/arm/mach-rockchip/rk3588/rk3588.c
+++ b/arch/arm/mach-rockchip/rk3588/rk3588.c
@@ -343,6 +343,7 @@ int ft_system_setup(void *blob, struct b
@@ -356,6 +356,7 @@ int ft_system_setup(void *blob, struct b
if (ip_state[0] & FAIL_CPU_CLUSTER2)
ip_state[0] |= FAIL_CPU_CLUSTER2;
@@ -8,7 +8,7 @@
/* policy: always fail one big core cluster on rk3582/rk3583 */
if (!(ip_state[0] & (FAIL_CPU_CLUSTER1 | FAIL_CPU_CLUSTER2)))
ip_state[0] |= FAIL_CPU_CLUSTER2;
@@ -362,6 +363,7 @@ int ft_system_setup(void *blob, struct b
@@ -375,6 +376,7 @@ int ft_system_setup(void *blob, struct b
/* policy: always fail one rkvenc core on rk3582/rk3583 */
if (!(ip_state[2] & (FAIL_RKVENC0 | FAIL_RKVENC1)))
ip_state[2] |= FAIL_RKVENC1;
@@ -1,23 +1,4 @@
--- /dev/null
+++ b/arch/arm/dts/rk3568-fastrhino-r66s-u-boot.dtsi
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include "rk356x-u-boot.dtsi"
+
+/ {
+ chosen {
+ u-boot,spl-boot-order = "same-as-spl", &sdmmc0;
+ };
+};
+
+&usb_host0_xhci {
+ dr_mode = "peripheral";
+ maximum-speed = "high-speed";
+ phys = <&usb2phy0_otg>;
+ phy-names = "usb2-phy";
+};
--- /dev/null
+++ b/arch/arm/dts/rk3568-fastrhino-r68s-u-boot.dtsi
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0+
@@ -37,84 +18,6 @@
+ phy-names = "usb2-phy";
+};
--- /dev/null
+++ b/configs/fastrhino-r66s-rk3568_defconfig
@@ -0,0 +1,75 @@
+CONFIG_ARM=y
+CONFIG_SKIP_LOWLEVEL_INIT=y
+CONFIG_COUNTER_FREQUENCY=24000000
+CONFIG_ARCH_ROCKCHIP=y
+CONFIG_DEFAULT_DEVICE_TREE="rockchip/rk3568-fastrhino-r66s"
+CONFIG_ROCKCHIP_RK3568=y
+CONFIG_SPL_SERIAL=y
+CONFIG_DEBUG_UART_BASE=0xFE660000
+CONFIG_DEBUG_UART_CLOCK=24000000
+CONFIG_SYS_LOAD_ADDR=0xc00800
+CONFIG_DEBUG_UART=y
+CONFIG_FIT=y
+CONFIG_FIT_VERBOSE=y
+CONFIG_SPL_FIT_SIGNATURE=y
+CONFIG_SPL_LOAD_FIT=y
+CONFIG_LEGACY_IMAGE_FORMAT=y
+CONFIG_DEFAULT_FDT_FILE="rockchip/rk3568-fastrhino-r66s.dtb"
+# CONFIG_DISPLAY_CPUINFO is not set
+CONFIG_DISPLAY_BOARDINFO_LATE=y
+CONFIG_SPL_MAX_SIZE=0x40000
+CONFIG_SPL_PAD_TO=0x7f8000
+# CONFIG_SPL_RAW_IMAGE_SUPPORT is not set
+CONFIG_SPL_ATF=y
+CONFIG_CMD_GPIO=y
+CONFIG_CMD_GPT=y
+CONFIG_CMD_I2C=y
+CONFIG_CMD_MMC=y
+CONFIG_CMD_USB=y
+CONFIG_CMD_ROCKUSB=y
+CONFIG_CMD_USB_MASS_STORAGE=y
+# CONFIG_CMD_SETEXPR is not set
+CONFIG_CMD_PMIC=y
+CONFIG_CMD_REGULATOR=y
+# CONFIG_SPL_DOS_PARTITION is not set
+CONFIG_SPL_OF_CONTROL=y
+CONFIG_OF_LIVE=y
+CONFIG_OF_SPL_REMOVE_PROPS="clock-names interrupt-parent assigned-clocks assigned-clock-rates assigned-clock-parents"
+CONFIG_ENV_IS_IN_MMC=y
+CONFIG_SPL_REGMAP=y
+CONFIG_SPL_SYSCON=y
+CONFIG_SPL_CLK=y
+# CONFIG_USB_FUNCTION_FASTBOOT is not set
+CONFIG_ROCKCHIP_GPIO=y
+CONFIG_SYS_I2C_ROCKCHIP=y
+CONFIG_MISC=y
+CONFIG_SUPPORT_EMMC_RPMB=y
+CONFIG_MMC_DW=y
+CONFIG_MMC_DW_ROCKCHIP=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_SDMA=y
+CONFIG_MMC_SDHCI_ROCKCHIP=y
+CONFIG_PHY_ROCKCHIP_INNO_USB2=y
+CONFIG_PHY_ROCKCHIP_NANENG_COMBOPHY=y
+CONFIG_SPL_PINCTRL=y
+CONFIG_DM_PMIC=y
+CONFIG_PMIC_RK8XX=y
+CONFIG_REGULATOR_RK8XX=y
+CONFIG_PWM_ROCKCHIP=y
+CONFIG_SPL_RAM=y
+CONFIG_BAUDRATE=1500000
+CONFIG_DEBUG_UART_SHIFT=2
+CONFIG_SYS_NS16550_MEM32=y
+CONFIG_SYSRESET=y
+CONFIG_USB=y
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_GENERIC=y
+CONFIG_USB_OHCI_HCD=y
+CONFIG_USB_OHCI_GENERIC=y
+CONFIG_USB_DWC3=y
+CONFIG_USB_DWC3_GENERIC=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_DOWNLOAD=y
+CONFIG_USB_FUNCTION_ROCKUSB=y
+CONFIG_ERRNO_STR=y
--- /dev/null
+++ b/configs/fastrhino-r68s-rk3568_defconfig
@@ -0,0 +1,78 @@
+CONFIG_ARM=y
+124
View File
@@ -0,0 +1,124 @@
name: Publish Mita Docker Images
on:
release:
types:
- published
workflow_dispatch:
inputs:
tag:
description: "Tag name"
env:
REGISTRY_IMAGE: ghcr.io/enfein/mita
jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: true
matrix:
platform:
- linux/amd64
- linux/arm64
steps:
- name: Get commit to build
id: ref
run: |-
if [[ -z "${{ github.event.inputs.tag }}" ]]; then
ref="${{ github.ref_name }}"
else
ref="${{ github.event.inputs.tag }}"
fi
echo "ref=$ref"
echo "ref=$ref" >> $GITHUB_OUTPUT
- name: Checkout
uses: actions/checkout@v6
with:
ref: ${{ steps.ref.outputs.ref }}
fetch-depth: 0
- name: Prepare
run: |
platform=${{ matrix.platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Setup QEMU
uses: docker/setup-qemu-action@v3
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY_IMAGE }}
- name: Build and push by digest
id: build
uses: docker/build-push-action@v6
with:
platforms: ${{ matrix.platform }}
context: .
file: deployments/docker/mita/Dockerfile
build-args: |
BUILDKIT_CONTEXT_KEEP_GIT_DIR=1
labels: ${{ steps.meta.outputs.labels }}
outputs: type=image,name=${{ env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true
- name: Export digest
run: |
mkdir -p /tmp/digests
digest="${{ steps.build.outputs.digest }}"
touch "/tmp/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v4
with:
name: digests-${{ env.PLATFORM_PAIR }}
path: /tmp/digests/*
if-no-files-found: error
retention-days: 1
merge:
runs-on: ubuntu-latest
needs:
- build
steps:
- name: Get commit to build
id: ref
run: |-
if [[ -z "${{ github.event.inputs.tag }}" ]]; then
ref="${{ github.ref_name }}"
else
ref="${{ github.event.inputs.tag }}"
fi
echo "ref=$ref"
echo "ref=$ref" >> $GITHUB_OUTPUT
latest="latest"
echo "latest=$latest"
echo "latest=$latest" >> $GITHUB_OUTPUT
- name: Download digests
uses: actions/download-artifact@v5
with:
path: /tmp/digests
pattern: digests-*
merge-multiple: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Create manifest list and push
working-directory: /tmp/digests
run: |
docker buildx imagetools create \
-t "${{ env.REGISTRY_IMAGE }}:${{ steps.ref.outputs.latest }}" \
-t "${{ env.REGISTRY_IMAGE }}:${{ steps.ref.outputs.ref }}" \
$(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *)
- name: Inspect image
run: |
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.ref.outputs.latest }}
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.ref.outputs.ref }}
@@ -1,14 +1,13 @@
FROM golang:1.23-alpine AS builder
RUN apk update && apk add --no-cache git
RUN git clone https://github.com/enfein/mieru.git /build
WORKDIR /build
RUN GOOS=linux CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o mita cmd/mita/mita.go
COPY . .
ENV CGO_ENABLED=0
RUN GOOS=linux go build -trimpath -ldflags="-s -w" -o mita cmd/mita/mita.go
FROM alpine AS base
COPY --from=builder /build/mita /usr/local/bin/
WORKDIR /app
COPY . .
RUN chmod +x ./start.sh && adduser -H -D -g "" mita && \
RUN adduser -H -D -g "" mita && \
mkdir -p /etc/mita && \
chown -R mita:mita /etc/mita && \
chmod 775 /etc/mita && \
@@ -18,4 +17,3 @@ RUN chmod +x ./start.sh && adduser -H -D -g "" mita && \
mkdir -p /var/run/mita && \
chown -R mita:mita /var/run/mita && \
chmod 775 /var/run/mita
CMD ["./start.sh"]
@@ -1,16 +0,0 @@
{
"portBindings": [
{
"portRange": "27017-27019",
"protocol": "UDP"
}
],
"users": [
{
"name": "uname1",
"password": "pwd1"
}
],
"loggingLevel": "INFO",
"mtu": 1400
}
@@ -1,16 +0,0 @@
services:
mita:
build: .
image: mita
container_name: mita
stop_grace_period: 5s
restart: always
logging:
driver: json-file
options:
max-size: 10m
max-file: 10
ports:
- 27017-27019:27017-27019/udp
environment:
- TZ=Asia/Shanghai
-12
View File
@@ -1,12 +0,0 @@
#!/bin/sh
set -e
mita run &
sleep 2
mita apply config ./conf/config.json
mita start
mita describe config
wait -n
+17 -13
View File
@@ -11,9 +11,6 @@ require (
github.com/gobwas/ws v1.4.0
github.com/gofrs/uuid/v5 v5.4.0
github.com/golang/snappy v1.0.0
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905
github.com/klauspost/compress v1.17.9 // lastest version compatible with golang1.20
github.com/mdlayher/netlink v1.7.2
github.com/metacubex/amneziawg-go v0.0.0-20251104174305-5a0e9f7e361d
github.com/metacubex/bart v0.26.0
github.com/metacubex/bbolt v0.0.0-20250725135710-010dbbbb7a5b
@@ -35,7 +32,7 @@ require (
github.com/metacubex/sing-shadowsocks v0.2.12
github.com/metacubex/sing-shadowsocks2 v0.2.7
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2
github.com/metacubex/sing-tun v0.4.11
github.com/metacubex/sing-tun v0.4.12
github.com/metacubex/sing-vmess v0.2.4
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f
github.com/metacubex/smux v0.0.0-20260105030934-d0c8756d3141
@@ -43,27 +40,34 @@ require (
github.com/metacubex/tls v0.1.0
github.com/metacubex/utls v1.8.4
github.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f
github.com/miekg/dns v1.1.63 // lastest version compatible with golang1.20
github.com/mroth/weightedrand/v2 v2.1.0
github.com/openacid/low v0.1.21
github.com/oschwald/maxminddb-golang v1.12.0 // lastest version compatible with golang1.20
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a
github.com/samber/lo v1.52.0
github.com/sirupsen/logrus v1.9.3
github.com/sirupsen/logrus v1.9.4
github.com/stretchr/testify v1.11.1
github.com/vmihailenco/msgpack/v5 v5.4.1
gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7
go.uber.org/automaxprocs v1.6.0
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
golang.org/x/crypto v0.33.0 // lastest version compatible with golang1.20
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e // lastest version compatible with golang1.20
golang.org/x/net v0.35.0 // lastest version compatible with golang1.20
golang.org/x/sync v0.11.0 // lastest version compatible with golang1.20
golang.org/x/sys v0.30.0 // lastest version compatible with golang1.20
google.golang.org/protobuf v1.34.2 // lastest version compatible with golang1.20
gopkg.in/yaml.v3 v3.0.1
)
// lastest version compatible with golang1.20
require (
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905
github.com/klauspost/compress v1.17.9
github.com/mdlayher/netlink v1.7.2
github.com/miekg/dns v1.1.63
github.com/oschwald/maxminddb-golang v1.12.0
golang.org/x/crypto v0.33.0
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e
golang.org/x/net v0.35.0
golang.org/x/sync v0.11.0
golang.org/x/sys v0.30.0
google.golang.org/protobuf v1.34.2
)
require (
github.com/RyuaNerin/go-krypto v1.3.0 // indirect
github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 // indirect
+4 -5
View File
@@ -127,8 +127,8 @@ github.com/metacubex/sing-shadowsocks2 v0.2.7 h1:hSuuc0YpsfiqYqt1o+fP4m34BQz4e6w
github.com/metacubex/sing-shadowsocks2 v0.2.7/go.mod h1:vOEbfKC60txi0ca+yUlqEwOGc3Obl6cnSgx9Gf45KjE=
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 h1:gXU+MYPm7Wme3/OAY2FFzVq9d9GxPHOqu5AQfg/ddhI=
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2/go.mod h1:mbfboaXauKJNIHJYxQRa+NJs4JU9NZfkA+I33dS2+9E=
github.com/metacubex/sing-tun v0.4.11 h1:NG5zpvYPbBXf+9GSUmDaGCDwl3hZXV677tbRAw0QtCM=
github.com/metacubex/sing-tun v0.4.11/go.mod h1:L/TjQY5JEGy8nvsuYmy/XgMFMCPiF0+AWSFCYfS6r9w=
github.com/metacubex/sing-tun v0.4.12 h1:LCi+yB7y97X3cHQGdNXQBMQNHAzpP4AWg7YhSLk+LTM=
github.com/metacubex/sing-tun v0.4.12/go.mod h1:L/TjQY5JEGy8nvsuYmy/XgMFMCPiF0+AWSFCYfS6r9w=
github.com/metacubex/sing-vmess v0.2.4 h1:Tx6AGgCiEf400E/xyDuYyafsel6sGbR8oF7RkAaus6I=
github.com/metacubex/sing-vmess v0.2.4/go.mod h1:21R5R1u90uUvBQF0owoooEu96/SAYYD56nDrwm6nFaM=
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f h1:Sr/DYKYofKHKc4GF3qkRGNuj6XA6c0eqPgEDN+VAsYU=
@@ -174,8 +174,8 @@ github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c h1:DjKMC30y6y
github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c/go.mod h1:NV/a66PhhWYVmUMaotlXJ8fIEFB98u+c8l/CQIEFLrU=
github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e h1:ur8uMsPIFG3i4Gi093BQITvwH9znsz2VUZmnmwHvpIo=
github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e/go.mod h1:+e5fBW3bpPyo+3uLo513gIUblc03egGjMM0+5GKbzK8=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@@ -226,7 +226,6 @@ golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-510
View File
@@ -1,510 +0,0 @@
# NodePass Development Guide
## Architecture Overview
NodePass is a Go-based TCP/UDP tunneling solution with a **tri-modal architecture** (Server/Client/Master) built on separation of control and data channels.
### Core Components
- **`cmd/nodepass/`**: Entry point with URL-based configuration parsing
- `main.go`: Simple entry that invokes `start()` with version injection
- `core.go`: URL parser, logger initialization, TLS mode selection, core factory (`createCore()`)
- **`internal/`**: Three operational modes sharing `common.go` base (~1970 lines):
- `server.go`: Accepts tunnel connections via `tunnelHandshake()`, binds target addresses, supports bidirectional data flow
- `client.go`: Initiates tunnel connections, supports single-end forwarding (`singleStart()`) and dual-end handshake (`commonStart()`)
- `master.go`: RESTful API server with instance management, SSE events, gob persistence (~2165 lines)
- `common.go`: Shared functionality - DNS caching, buffer pools, slot management, connection routing
- **External packages** (NodePassProject org on GitHub):
- `pool`: TCP connection pooling with auto-scaling (min/max capacity)
- `quic`: QUIC transport with 0-RTT support
- `npws`: WebSocket transport adapter
- `conn`: Utilities (`DataExchange`, `StatConn` for traffic accounting, `RateLimiter` for bandwidth control)
- `logs`: Structured logger with levels (none/debug/info/warn/error/event)
- `cert`: TLS certificate generation and management
### Data Flow Modes
1. **Server Receives Mode** (Reverse): Server binds target address locally → signals client → client connects back → data flows: External → Server → Client → Target
2. **Server Sends Mode** (Forward): Server connects to remote target → client signals server → server creates outgoing connection → data flows: Client → Server → Remote Target
3. **Client Single-End Forwarding**: Client binds tunnel address locally (e.g., `127.0.0.1:8080`) → direct forwarding to target without server coordination (no control channel)
Mode selection is **automatic** via `initTargetListener()` success/failure. Server tries binding target address; if successful = mode 1 (reverse), if fails = mode 2 (forward). Client tries binding tunnel address; if successful = single-end, if fails = dual-end. Force with `mode` query parameter (`0`=auto, `1`=reverse/single, `2`=forward/dual).
## URL-Based Configuration
All configuration through URL scheme: `<mode>://<auth>@<tunnel>/<target>?<params>`
**URL Structure Examples:**
```
server://password@0.0.0.0:10101/127.0.0.1:8080?tls=1&max=512
client://password@server.com:10101/localhost:8080?min=64&type=1
master://0.0.0.0:9090/api?log=debug&tls=2&crt=/path/cert.pem&key=/path/key.pem
```
**Critical query parameters:**
- `log`: Log level - `none`|`debug`|`info`(default)|`warn`|`error`|`event`
- `tls`: Encryption mode - `0` (plain TCP/UDP), `1` (self-signed cert in memory), `2` (custom cert with `crt`/`key` files)
- Mode 0: No encryption, fastest but insecure
- Mode 1: Auto-generated self-signed cert, no verification, protects against passive sniffing
- Mode 2: Custom certificate with validation, requires both `crt` and `key` parameters pointing to PEM files
- **Note**: QUIC transport (`type=1`) requires minimum `tls=1`
- `type`: Pool transport protocol - `0` (TCP pool, default), `1` (QUIC with 0-RTT), `2` (WebSocket)
- `mode`: Force run mode - `0` (auto-detect via binding), `1` (server=reverse/client=single-end), `2` (server=forward/client=dual-end)
- `dns`: DNS cache TTL duration (default `5m`, accepts Go duration syntax like `30s`, `10m`, `1h`)
- `min`: Client minimum pool capacity (default `64`)
- `max`: Server maximum pool capacity (default `1024`)
- `rate`: Bandwidth limit in **Mbps * 8** (e.g., `rate=100` = 100Mbps = 12.5MB/s; internal unit is bytes/sec, computed as rate*125000)
- `slot`: Max concurrent connections - TCP+UDP combined (default `65536`, `0`=unlimited)
- `proxy`: PROXY protocol version - `0` (disabled), `1` (v1 text format), `2` (v2 binary format)
- `read`: Connection read timeout (default `0` = infinite, accepts Go duration like `30s`, `5m`)
- `dial`: Local bind IP for outgoing connections (default `auto` = system routing, or specific IP like `192.168.1.100`)
- Automatic fallback to system routing if specified IP fails (logged as "fallback to system auto")
- `notcp`: Disable TCP forwarding - `0` (enabled), `1` (disabled)
- `noudp`: Disable UDP forwarding - `0` (enabled), `1` (disabled)
**Password field usage:** The `@` password portion in URLs (e.g., `mykey@server:10101`) becomes `tunnelKey` for authentication - it's NOT a system password, just a shared secret for tunnel validation. Server compares incoming `tunnelKey` via XOR+base64 encoding in handshake.
Examples in `docs/en/examples.md`, full configuration reference in `docs/en/configuration.md`.
## Development Workflow
### Building
```bash
# Development build
cd cmd/nodepass
go build -ldflags "-X main.version=dev"
# Release build (via goreleaser)
goreleaser build --snapshot --clean
# Docker build (multi-stage, scratch-based final image)
docker build --build-arg VERSION=dev -t nodepass:dev .
```
Build produces single static binary with no external dependencies. The `-ldflags "-X main.version=..."` injects version into `main.version` variable displayed in `exit()` banner.
### Testing Patterns
**No test suite exists** - all testing is manual via URL invocations. Common test scenarios:
```bash
# Server mode (binds :10101 for tunnel, forwards to local 8080)
nodepass "server://:10101/127.0.0.1:8080?log=debug&tls=1"
# Client mode (connects to server:10101, creates local listener on :8080)
nodepass "client://server:10101/127.0.0.1:8080?min=128&log=debug"
# Master API mode (launches API server on :10101 with /api prefix)
nodepass "master://:10101/api?log=debug&tls=1"
# Test QUIC transport with bandwidth limiting
nodepass "server://:10101/127.0.0.1:8080?type=1&tls=1&rate=100"
# Test multi-target load balancing (comma-separated targets)
nodepass "client://server:10101/target1.com:80,target2.com:80,target3.com:80?mode=2"
```
**Debugging tips:**
- Use `log=debug` to see connection lifecycle events, pool operations, handshake details
- Check `DataExchange` log messages for connection completion status and byte counts
- Monitor pool capacity with `Active()` and `Capacity()` calls logged periodically
- TLS handshake failures appear as "access denied" warnings - verify `tunnelKey` matches
- DNS resolution issues trigger fallback to cached addresses with warning logs
### Environment Tuning
Performance constants in `common.go` (lines 93-105) are environment-configurable via `NP_*` prefix:
```bash
# Increase semaphore limit for high concurrency (default 65536)
export NP_SEMAPHORE_LIMIT=131072
# Larger TCP buffer for high-bandwidth links (default 16384)
export NP_TCP_DATA_BUF_SIZE=32768
# Extend handshake timeout for slow networks (default 5s)
export NP_HANDSHAKE_TIMEOUT=10s
# Pool connection acquisition timeout (default 5s)
export NP_POOL_GET_TIMEOUT=10s
# Pool scaling intervals (defaults: min=100ms, max=1s)
export NP_MIN_POOL_INTERVAL=50ms
export NP_MAX_POOL_INTERVAL=2s
# Health check report frequency (default 5s)
export NP_REPORT_INTERVAL=10s
# Service restart cooldown (default 3s)
export NP_SERVICE_COOLDOWN=5s
# Graceful shutdown timeout (default 5s)
export NP_SHUTDOWN_TIMEOUT=10s
# TLS certificate reload interval for mode 2 (default 1h)
export NP_RELOAD_INTERVAL=30m
```
All duration values accept Go duration syntax (`s`, `m`, `h`). Changes require restart to take effect.
## Code Conventions
### Logging
Use structured logging with `logger` from `logs.Logger`. Six levels: none/debug/info/warn/error/event. Format strings with `%v` placeholders:
```go
logger.Debug("TLS cert reloaded: %v", crtFile)
logger.Info("Server started: server://%v@%v/%v", key, tunnel, target)
logger.Warn("tunnelHandshake: access denied: %v", remoteAddr)
logger.Error("Certificate load failed: %v", err)
logger.Event("Traffic stats: TCP RX=%d TX=%d", tcpRX, tcpTX)
```
**Never use `fmt.Printf`** except in `exit()` help banner. All user-facing output goes through logger.
### Error Handling
Wrap errors with context using `fmt.Errorf` with `%w` verb for error chain preservation:
```go
return fmt.Errorf("start: initTunnelListener failed: %w", err)
return fmt.Errorf("tunnelHandshake: decode failed: %w", err)
```
Functions return `error` as last return value. Restart logic uses `err != nil && err != io.EOF` pattern - `io.EOF` signals graceful shutdown, other errors trigger restart after `serviceCooldown`.
### Connection Pool Interface
All transport types (`pool.ServerPool`, `quic.ServerPool`, `npws.ServerPool`) implement unified `TransportPool` interface (defined in `common.go` line 92):
```go
type TransportPool interface {
// IncomingGet retrieves connection from server pool by ID with timeout
IncomingGet(timeout time.Duration) (string, net.Conn, error)
// OutgoingGet retrieves connection from client pool for given ID with timeout
OutgoingGet(id string, timeout time.Duration) (net.Conn, error)
// Flush signals pool to drop all connections and reset state
Flush()
// Close terminates pool and all managed connections
Close()
// Ready reports if pool has reached minimum capacity
Ready() bool
// Active returns current active connection count
Active() int
// Capacity returns maximum pool capacity
Capacity() int
// Interval returns current auto-scaling interval
Interval() time.Duration
// AddError increments error counter for health monitoring
AddError()
// ErrorCount returns cumulative error count
ErrorCount() int
// ResetError clears error counter
ResetError()
}
```
Connection IDs are generated via FNV hash: `hash := fnv.New64a(); hash.Write([]byte); id := hex.EncodeToString(hash.Sum(nil))`. Server generates IDs for incoming connections, client receives IDs via control channel.
### Buffer Pool Management
**Critical**: Always return buffers to prevent memory leaks. Pools are initialized in constructor with `sync.Pool`:
```go
tcpBufferPool: &sync.Pool{
New: func() any {
buf := make([]byte, tcpDataBufSize)
return &buf
},
}
```
Usage pattern:
```go
buffer := c.getTCPBuffer() // Acquire from pool
defer c.putTCPBuffer(buffer) // ALWAYS return via defer
// Use buffer for I/O operations...
```
UDP buffers follow identical pattern with `getUDPBuffer()`/`putUDPBuffer()`. Buffer sizes configurable via `NP_TCP_DATA_BUF_SIZE` (default 16384) and `NP_UDP_DATA_BUF_SIZE` (default 16384).
### Slot Management
Connection slots limit concurrent connections via atomic counters. Check before accepting connections:
```go
if !c.tryAcquireSlot(isUDP) {
logger.Warn("Slot limit reached: %d", c.slotLimit)
conn.Close()
return
}
defer c.releaseSlot(isUDP)
```
Slots are combined TCP+UDP count. `slotLimit=0` disables limit. Slot tracking uses `atomic.AddInt32()` for thread-safe counters.
### Context Management
Each mode initializes context in `start()` method:
```go
func (c *Common) initContext() {
c.ctx, c.cancel = context.WithCancel(context.Background())
}
```
Graceful shutdown via `shutdown(ctx, stopFunc)` helper:
1. Calls `stopFunc()` to close listeners/pools
2. Waits for `ctx.Done()` or `shutdownTimeout` (default 5s)
3. Logs completion/timeout status
Restart loop pattern in `Run()` methods:
```go
for ctx.Err() == nil {
if err := c.start(); err != nil && err != io.EOF {
c.logger.Error("Client error: %v", err)
c.stop()
select {
case <-ctx.Done():
return
case <-time.After(serviceCooldown): // 3s default
}
logInfo("Client restart")
}
}
```
Use `contextCheckInterval` (50ms) in tight loops: `select { case <-ctx.Done(): return; case <-time.After(contextCheckInterval): }`
### Traffic Accounting
All connections wrapped in `conn.StatConn` for automatic byte counting and rate limiting:
```go
targetConn = &conn.StatConn{
Conn: targetConn,
RX: &c.tcpRX, // Points to Common's atomic uint64 counter
TX: &c.tcpTX, // Points to Common's atomic uint64 counter
Rate: c.rateLimiter, // Optional rate limiter (nil if rate=0)
}
```
Counters updated atomically on every Read/Write. Master mode reads counters to compute traffic deltas. `DataExchange()` from `conn` package handles bidirectional copy with automatic accounting:
```go
conn.DataExchange(connA, connB, readTimeout, buffer1, buffer2)
```
Rate limiting initialized via `initRateLimiter()` if `rateLimit > 0` (rate in bytes/sec = query param * 125000).
## Master Mode Specifics
### Instance Management
Instances stored in `sync.Map` (concurrent-safe), persisted to `gob/nodepass.gob` using `gob` encoding. State file layout:
- API key (auto-generated 32-byte hex on first start)
- Instance map serialization with all fields except those tagged `gob:"-"`
Key `Instance` struct fields:
```go
type Instance struct {
ID string // 8-char hex identifier
Alias string // User-friendly name
Type string // "server" or "client"
Status string // "running", "stopped", "error"
URL string // Original user-provided URL
Config string // Computed URL with all defaults filled
Restart bool // Auto-restart policy
Meta Meta // Metadata with peer info and tags
cmd *exec.Cmd // Running subprocess (not serialized)
stopped chan struct{} // Shutdown coordination (not serialized)
// Traffic baseline tracking (not serialized)
TCPRXBase/TCPTXBase/UDPRXBase/UDPTXBase uint64
}
```
Instance lifecycle:
1. **Create**: `POST /instances` with URL → generates ID → spawns subprocess → stores in `sync.Map` → persists to gob
2. **Monitor**: Periodic goroutine reads `/proc/<pid>/status` for traffic stats, computes deltas from baseline
3. **Update**: `PATCH /instances/{id}` with actions: `start`, `stop`, `restart`, `reset-traffic`, `toggle-restart`
4. **Delete**: `DELETE /instances/{id}` → stops subprocess → removes from map → re-persists gob
Subprocess management uses `exec.CommandContext()` with instance-specific context. Logs captured via custom `InstanceLogWriter` that parses structured logs and emits SSE events.
### SSE Events
Real-time updates via `/events` endpoint (Server-Sent Events). Event types and payloads:
- `initial`: Full instance list on connection (sent once per subscriber)
- `create`: New instance created (includes full Instance object)
- `update`: Instance state changed (includes full Instance object with updated fields)
- `delete`: Instance removed (includes ID only)
- `shutdown`: Master shutting down (no payload)
- `log`: Instance log line (includes `instance.id` and `logs` fields)
Subscribers stored in `sync.Map` with unique IDs. Event broadcasting via `notifyChannel` (buffered channel). Connection management pattern:
```go
subscriber := &Subscriber{id: generateID(), channel: make(chan *InstanceEvent, 100)}
m.subscribers.Store(subscriber.id, subscriber)
defer m.subscribers.Delete(subscriber.id)
for {
select {
case event := <-subscriber.channel:
fmt.Fprintf(w, "event: %s\ndata: %s\n\n", event.Type, jsonData)
flusher.Flush()
case <-r.Context().Done():
return
}
}
```
### API Authentication
Auto-generated API key on first start. Special instance ID `********` (8 asterisks) reserved for key operations:
- `GET /instances/********`: Retrieve current API key
- `PATCH /instances/********` with `{"action": "restart"}`: Regenerate API key
Protected endpoints check `X-API-Key` header. Public endpoints: `/openapi.json`, `/docs` (Swagger UI).
Key validation pattern:
```go
if apiKey := r.Header.Get("X-API-Key"); apiKey != m.apiKey {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
```
### TCPing Functionality
Built-in connectivity testing via `GET /tcping?target=host:port`. Concurrent limit enforced via buffered semaphore (`tcpingSem chan struct{}` with capacity 10). Returns JSON:
```json
{
"target": "example.com:443",
"connected": true,
"latency": 42,
"error": null
}
```
Timeout handling: 1s for semaphore acquisition, 5s for TCP dial. Latency measured in milliseconds.
## Integration Points
### External Package Boundaries
- **`github.com/NodePassProject/pool`**: TCP connection pooling with dynamic scaling
- **`github.com/NodePassProject/quic`**: QUIC-based transport (0-RTT support)
- **`github.com/NodePassProject/npws`**: WebSocket transport wrapper
- **`github.com/NodePassProject/conn`**: Connection helpers (`DataExchange`, `StatConn`, `RateLimiter`)
- **`github.com/NodePassProject/cert`**: TLS certificate generation/management
When modifying transport behavior, coordinate with corresponding package version in `go.mod`.
### DNS Caching
Custom DNS resolution via `dnsCacheEntry` stored in `sync.Map` with TTL. Functions: `getTunnelTCPAddr()`, `getTargetTCPAddr()`.
### Handshake Protocol
**Server-side handshake** (`server.go` lines 208-279):
1. Creates HTTP server with `HandlerFunc` on `tunnelListener`
2. Validates incoming HTTP GET request to path `/`
3. Extracts `Authorization` header and verifies Bearer token using HMAC-SHA256:
- Client sends: `Authorization: Bearer <HMAC-SHA256(tunnelKey)>`
- Server verifies via `hmac.Equal()` constant-time comparison
4. Extracts client IP from `RemoteAddr()` (strips port if present)
5. Responds with JSON config containing:
```json
{
"flow": "<dataFlow>", // Direction: "+" or "-"
"max": <maxPoolCapacity>, // Server pool capacity
"tls": "<tlsCode>", // TLS mode: "0", "1", or "2"
"type": "<poolType>" // Transport: "0" (TCP), "1" (QUIC), "2" (WS)
}
```
6. Closes HTTP server after successful handshake
7. Recreates `tunnelListener` for subsequent pool connections
**Client-side handshake** (`client.go` lines 231-273):
1. Constructs HTTP GET request to `http://<tunnelAddr>/`
2. Sets `Host` header to `tunnelName` for DNS-based routing
3. Generates HMAC-SHA256 token: `hex.EncodeToString(hmac.New(sha256.New, []byte(tunnelKey)).Sum(nil))`
4. Sends `Authorization: Bearer <token>` header
5. Receives JSON response and decodes config
6. Updates local configuration:
- `dataFlow`: Controls connection direction
- `maxPoolCapacity`: Adopts server's pool size
- `tlsCode`: Applies server's TLS settings to data connections
- `poolType`: Switches transport type if needed
7. Logs loaded configuration for debugging
**Authentication mechanism**: HMAC-SHA256 provides cryptographic authentication without transmitting the raw `tunnelKey`. Token generation in `common.go` lines 248-256 uses standard library `crypto/hmac` and `crypto/sha256`.
### Load Balancing & Failover
Multi-target support via comma-separated addresses in URL path. `dialWithRotation()` (`common.go` lines 385-450) implements:
- Round-robin distribution using atomic counter
- Automatic failover on connection errors
- Single-target fast path optimization
- Dynamic DNS resolution per attempt
Example: `client://server:10101/target1:80,target2:80,target3:80` rotates across three backends.
## Common Pitfalls
1. **TLS Mode vs Pool Type**: `tls` parameter applies to data channel, `type` parameter selects transport (QUIC requires `tls=1` minimum)
2. **URL Password Field**: Used as `tunnelKey` for authentication - not actual password
3. **Buffer Pool Management**: Always return buffers via `putTCPBuffer()`/`putUDPBuffer()` to prevent leaks
4. **Signal Channel Buffering**: `signalChan` has `semaphoreLimit` capacity - blocks if full
5. **Instance Config vs URL**: Master stores both user-provided URL and computed config string with all defaults
## Key File References
- **`internal/common.go`** (1970 lines): Core shared functionality
- Lines 29-85: `Common` struct definition with all shared fields
- Lines 93-122: Environment-configurable performance constants
- Lines 140-165: Buffer pool management (`getTCPBuffer`, `putTCPBuffer`, `getUDPBuffer`, `putUDPBuffer`)
- Lines 168-200: Slot management (`tryAcquireSlot`, `releaseSlot`)
- Lines 250-270: Handshake encoding/decoding (`xor`, `encode`, `decode`)
- Lines 385-450: Load balancing with failover (`dialWithRotation`)
- Lines 722-726: Rate limiter initialization
- Lines 1229, 1568: `DataExchange` calls for bidirectional traffic
- **`internal/server.go`** (320 lines): Server mode implementation
- Lines 32-62: Server constructor with pool initialization
- Lines 65-106: Run loop with restart logic
- Lines 109-183: Start sequence and mode detection
- Lines 194-320: Tunnel handshake with concurrent connection acceptance
- **`internal/client.go`** (273 lines): Client mode implementation
- Lines 33-61: Client constructor
- Lines 111-132: Mode detection logic (single-end vs dual-end)
- Lines 135-210: Pool initialization per transport type
- Lines 218-273: Tunnel handshake with config reception
- **`internal/master.go`** (2165 lines): Master API server
- Lines 67-90: Master struct definition
- Lines 91-124: Instance struct with traffic tracking
- Lines 138-145: InstanceEvent for SSE
- Lines 330+: RESTful handlers and instance management
- **`cmd/nodepass/core.go`** (165 lines): Entry point and configuration
- Lines 17-35: URL parsing and core creation
- Lines 38-59: Logger initialization
- Lines 62-75: Core factory (`createCore`)
- Lines 78-143: TLS configuration with three modes
+1 -1
View File
@@ -15,7 +15,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v6.1.0
with:
go-version: '1.25.0'
go-version: '1.25.6'
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6.4.0
with:
-12
View File
@@ -10,20 +10,8 @@ builds:
- linux
- windows
goarch:
- 386
- arm
- amd64
- arm64
- mips
- mipsle
- mips64
- mips64le
goarm:
- 6
- 7
gomips:
- hardfloat
- softfloat
flags:
- -trimpath
ldflags:
+1 -1
View File
@@ -39,4 +39,4 @@ If you experience or witness unacceptable behavior, please report it to **team@m
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.1.
This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.1.
+1 -1
View File
@@ -321,4 +321,4 @@ Current maintainers handle:
Thank you for contributing to NodePass! Your contributions help make universal TCP/UDP tunneling more accessible and reliable for everyone.
For questions about contributing, please reach out through our community channels or create a GitHub issue.
For questions about contributing, please reach out through our community channels or create a GitHub issue.
+29 -57
View File
@@ -11,88 +11,62 @@
![GitHub last commit](https://img.shields.io/github/last-commit/NodePassProject/nodepass)
<a href="https://apps.apple.com/us/app/nodepass/id6747930492"><img src="https://nodepass.eu/assets/appstore.png" width="120"></a>
English | [简体中文](README_zh.md)
</div>
**NodePass** is an open-source, lightweight, enterprise-grade TCP/UDP network tunneling solution featuring an all-in-one architecture with separation of control and data channels, along with flexible and high-performance instance control. It supports zero-configuration deployment, intelligent connection pooling, tiered TLS encryption, and seamless protocol conversion. Designed for DevOps professionals and system administrators to effortlessly handle complex network scenarios.
## 💎 Key Features
## Key Features
- **🌐 Universal Functionality**
- **Universal Functionality**
- Basic TCP/UDP tunneling and protocol conversion across diverse networks.
- Compatible with port mapping, NAT traversal, and traffic relay.
- Cross-platform, multi-architecture, single binary or container.
- **🚀 Connection Pool**
- **Connection Pool**
- Supports TCP, QUIC, WebSocket, HTTP/2 pooling transport methods.
- Eliminates handshake delays, boosts performance with 0-RTT support.
- Auto-scaling with real-time capacity adjustment.
- **🧬 Innovative Architecture**
- **Innovative Architecture**
- Integrated S/C/M architecture, flexible mode switching.
- Full decoupling of control/data channels.
- API-instance management, multi-instance collaboration.
- **🔐 Multi-level Security**
- **Multi-level Security**
- Three TLS modes: plaintext, self-signed, strict validation.
- Covers development to enterprise security needs.
- Hot-reload certificates with zero downtime.
- **⚙️ Minimal Configuration**
- **Minimal Configuration**
- No config files required, ready to use via CLI.
- Optimized for CI/CD and containers.
- Advanced parameters like timeouts and rate limits.
- **📈 Performance**
- **Performance**
- Intelligent scheduling, auto-tuning, ultra-low resource usage.
- Stable under high concurrency and heavy load.
- Load balancing, health checks, self-healing and more.
- **💡 Visualization**
- **Visualization**
- Rich cross-platform visual frontends.
- One-click deployment scripts, easy management.
- Real-time monitoring, API-instance management, traffic stats.
## 📋 Quick Start
### 📥 Installation
- **Pre-built Binaries**: Download from [releases page](https://github.com/NodePassProject/nodepass/releases).
- **Container Image**: `docker pull ghcr.io/NodePassProject/nodepass:latest`
### 🚀 Basic Usage
**Server Mode**
```bash
nodepass "server://:10101/127.0.0.1:8080?log=debug&tls=1"
```
**Client Mode**
```bash
nodepass "client://server:10101/127.0.0.1:8080?min=128"
```
**Master Mode (API)**
```bash
nodepass "master://:10101/api?log=debug&tls=1"
```
## 📚 Documentation
## Documentation
Explore the complete documentation to learn more about NodePass:
- [Installation Guide](/docs/en/installation.md)
- [Usage Instructions](/docs/en/usage.md)
- [Configuration Options](/docs/en/configuration.md)
- [API Reference](/docs/en/api.md)
- [Examples](/docs/en/examples.md)
- [How It Works](/docs/en/how-it-works.md)
- [Troubleshooting](/docs/en/troubleshooting.md)
- [Installation Guide](/docs/installation.md)
- [Usage Instructions](/docs/usage.md)
- [Configuration Options](/docs/configuration.md)
- [API Reference](/docs/api.md)
- [Examples](/docs/examples.md)
- [How It Works](/docs/how-it-works.md)
- [Troubleshooting](/docs/troubleshooting.md)
See also [DeepWiki](https://deepwiki.com/NodePassProject/nodepass) for AI-powered documentation.
## 🌱 Ecosystem
## Ecosystem
The [NodePassProject](https://github.com/NodePassProject) organization develops various frontend applications and auxiliary tools to enhance the NodePass experience:
@@ -106,33 +80,31 @@ The [NodePassProject](https://github.com/NodePassProject) organization develops
- **[nodepass-core](https://github.com/NodePassProject/nodepass-core)**: Development branch, featuring previews of new functionalities and performance optimizations, suitable for advanced users and developers.
## 💬 Discussion
- Follow our [Telegram Channel](https://t.me/NodePassChannel) for updates and community support.
- Join our [Discord](https://discord.gg/2cnXcnDMGc) and [Telegram Group](https://t.me/NodePassGroup) to share experiences and ideas.
## 📄 License
## License
- Project **NodePass** is licensed under the [BSD 3-Clause License](LICENSE), which applies to the source code only.
- The **NodePass** name, logo, and official project identity are not covered by the code license and may not be used without explicit authorization.
## ⚖️ Disclaimer
## Disclaimer
This project is provided "as is" without any warranties. Users assume all risks and must comply with local laws for legal use only. Developers are not liable for any direct, indirect, incidental, or consequential damages. Secondary development requires commitment to legal use and self-responsibility for legal compliance. Developers reserve the right to modify software features and this disclaimer at any time. Final interpretation rights belong to developers.
## 🔗 Donation
## Donation
**Cryptocurrency:**
- EVM-compatible Address: `0x2ea4Ea9425BEe897ED74fC5512bd13ABC7100000`
- EVM: `0x2ea4Ea9425BEe897ED74fC5512bd13ABC7100000`
- TRX: `TCqbhDHoBFKRVwibe4tb7xNjtgnmkJXGR6`
- SOL: `BwZuvh13BQWhYSh31dEZiy8avqZwMFfdSwfawTiMv7Bw`
**NFT collection:**
- Support **NodePass** in a unique way by checking out our NFT collection on [OpenSea](https://opensea.io/collection/nodepass).
- Support **NodePass** in a unique way by collecting our NFTs on [OpenSea](https://opensea.io/collection/nodepass).
## 🤝 Sponsors
## Sponsors
<table>
<tr>
@@ -153,6 +125,6 @@ This project is provided "as is" without any warranties. Users assume all risks
</tr>
</table>
## Stargazers
## Stargazers
[![Stargazers over time](https://starchart.cc/NodePassProject/nodepass.svg?variant=adaptive)](https://starchart.cc/NodePassProject/nodepass)
[![Stargazers over time](https://starchart.cc/NodePassProject/nodepass.svg?variant=adaptive)](https://starchart.cc/NodePassProject/nodepass)
-158
View File
@@ -1,158 +0,0 @@
<div align="center">
<img src="https://nodepass.eu/assets/np-gopher.png" alt="nodepass" width="300">
[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge.svg)](https://github.com/avelino/awesome-go#networking)
[![GitHub release](https://img.shields.io/github/v/release/NodePassProject/nodepass)](https://github.com/NodePassProject/nodepass/releases)
[![GitHub downloads](https://img.shields.io/github/downloads/NodePassProject/nodepass/total.svg)](https://github.com/NodePassProject/nodepass/releases)
[![Go Report Card](https://goreportcard.com/badge/github.com/NodePassProject/nodepass)](https://goreportcard.com/report/github.com/NodePassProject/nodepass)
[![License](https://img.shields.io/badge/License-BSD_3--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)
[![Go Reference](https://pkg.go.dev/badge/github.com/NodePassProject/nodepass.svg)](https://pkg.go.dev/github.com/NodePassProject/nodepass)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/NodePassProject/nodepass)
![GitHub last commit](https://img.shields.io/github/last-commit/NodePassProject/nodepass)
<a href="https://apps.apple.com/cn/app/nodepass/id6747930492"><img src="https://nodepass.eu/assets/appstore.png" width="120"></a>
[English](README.md) | 简体中文
</div>
**NodePass** 是一款开源、轻量的企业级 TCP/UDP 网络隧道解决方案,采用多合一架构设计,通过控制通道与数据通道分离,实现灵活、高性能的实例管控。支持零配置文件部署,内置智能连接池、分级 TLS 加密和无缝协议转换。专为 DevOps 工程师和系统管理员打造,助力轻松应对复杂网络场景。
## 💎 核心功能
- **🌐 通用网络隧道**
- 基础 TCP/UDP 隧道,具备协议转换能力,适配多种网络结构。
- 完整适配端口映射、内网穿透、流量中转等多场景应用需求。
- 多平台、多架构支持,支持独立二进制文件、容器灵活部署。
- **🚀 内置连接池**
- 提供 TCP、QUIC、WebSocket、HTTP/2 多种池化传输方式。
- 消除连接的握手等待,通过 0-RTT 支持显著提升性能体验。
- 支持实时容量自适应,动态调整连接池规模。
- **🧬 创新架构设计**
- Server-Client-Master 多模式整合架构设计,灵活切换。
- 将 S/C 控制通道与数据通道完全解耦,相互独立、各司其职。
- 主控-实例的管理方式,支持动态扩容、多实例协作和集中控制。
- **🔐 多级安全策略**
- 三种 TLS 模式:明文、自签名、严格验证,适配不同安全等级。
- 满足从开发测试到企业级高安全部署的全场景需求。
- 支持证书文件的热重载,免停运、无缝处理证书更新问题。
- **⚙️ 极简配置方式**
- 无需配置文件,仅命令行参数即可运行,适合自动化和快速迭代。
- 适配 CI/CD 流程与容器环境,极大提升部署和运维效率。
- 支持超时、限速等高级参数调优,灵活适应不同运行环境。
- **📈 高性能优化**
- 智能流量调度与自动连接调优,极低资源占用。
- 高并发、高负载状态下卓越的系统稳定性能。
- 负载均衡、健康检查、故障自愈,确保持续高可用。
- **💡 可视化管理**
- 配套跨平台、多样化的管理前端应用,具备可视化配置能力。
- 主流平台支持一键部署脚本,支撑灵活配置和辅助管理。
- 具备实时隧道监控、实例管理、主控管理、流量统计等丰富功能。
## 📋 快速开始
### 📥 安装方法
- **预编译二进制文件**: 从[发布页面](https://github.com/NodePassProject/nodepass/releases)下载。
- **容器镜像**: `docker pull ghcr.io/NodePassProject/nodepass:latest`
### 🚀 基本用法
**服务端模式**
```bash
nodepass "server://:10101/127.0.0.1:8080?log=debug&tls=1"
```
**客户端模式**
```bash
nodepass "client://server:10101/127.0.0.1:8080?min=128"
```
**主控模式 (API)**
```bash
nodepass "master://:10101/api?log=debug&tls=1"
```
## 📚 文档
探索完整文档以了解更多关于NodePass的信息:
- [安装指南](/docs/zh/installation.md)
- [使用说明](/docs/zh/usage.md)
- [配置选项](/docs/zh/configuration.md)
- [API参考](/docs/zh/api.md)
- [使用示例](/docs/zh/examples.md)
- [工作原理](/docs/zh/how-it-works.md)
- [故障排除](/docs/zh/troubleshooting.md)
参阅 [DeepWiki](https://deepwiki.com/NodePassProject/nodepass) 以获取 AI 驱动的文档。
## 🌱 生态系统
[NodePassProject](https://github.com/NodePassProject) 组织开发了各种前端应用和辅助工具来增强 NodePass 体验:
- **[NodePassDash](https://github.com/NodePassProject/NodePassDash)**: 现代化的 NodePass 管理界面,提供主控管理、实例管理、流量统计、历史记录等功能。
- **[NodePanel](https://github.com/NodePassProject/NodePanel)**: 轻量化的前端面板,提供可视化的隧道管理功能,在 Vercel 或 Cloudflare Pages 轻松部署。
- **[npsh](https://github.com/NodePassProject/npsh)**: 简单易用的 NodePass 一键脚本合集,包括 API 主控、Dash 面板的安装部署、灵活配置和辅助管理。
- **[NodePass-ApplePlatforms](https://github.com/NodePassProject/NodePass-ApplePlatforms)**: 面向服务的 iOS/macOS 应用,为 Apple 用户提供原生体验。
- **[nodepass-core](https://github.com/NodePassProject/nodepass-core)**: 开发分支,包含新功能预览和性能优化测试,适合高级用户和开发者。
## 💬 讨论
- 关注我们的 [Telegram 频道](https://t.me/NodePassChannel) 获取最新更新和社区支持。
- 加入我们的 [Discord](https://discord.gg/2cnXcnDMGc) 和 [Telegram 群组](https://t.me/NodePassGroup) 分享经验和想法。
## 📄 许可协议
- **NodePass** 项目根据 [BSD 3-Clause 许可证](LICENSE)授权,该许可仅适用于源代码本身。
- **NodePass** 项目名称、Logo 及官方身份标识不包含在代码许可中,未经明确授权不得使用。
## ⚖️ 免责声明
本项目以"现状"提供,开发者不提供任何明示或暗示的保证。用户使用风险自担,需遵守当地法律法规,仅限合法用途。开发者对任何直接、间接、偶然或后果性损害概不负责。进行二次开发须承诺合法使用并自负法律责任。开发者保留随时修改软件功能及本声明的权利。最终解释权归开发者所有。
## 🔗 捐赠
**加密货币:**
- EVM 兼容地址: `0x2ea4Ea9425BEe897ED74fC5512bd13ABC7100000`
**数字藏品:**
- 以独特方式支持 **NodePass**,查看我们在 [OpenSea](https://opensea.io/collection/nodepass) 上的 NFT 收藏。
## 🤝 赞助商
<table>
<tr>
<td width="240" align="center">
<a href="https://whmcs.as211392.com"><img src="https://nodepass.eu/assets/dreamcloud.png"></a>
</td>
<td width="240" align="center">
<a href="https://t.me/xiao_bai_xue_zhang"><img src="https://nodepass.eu/assets/xuezhang.png"></a>
</td>
</tr>
<tr>
<td width="240" align="center">
<a href="https://sharon.io"><img src="https://nodepass.eu/assets/sharon.png"></a>
</td>
<td width="240" align="center">
<a href="https://vps.town"><img src="https://nodepass.eu/assets/vpstown.png"></a>
</td>
</tr>
</table>
## ⭐ Star 趋势
[![Stargazers over time](https://starchart.cc/NodePassProject/nodepass.svg?variant=adaptive)](https://starchart.cc/NodePassProject/nodepass)
+1 -1
View File
@@ -163,4 +163,4 @@ We appreciate security researchers who help improve NodePass security. Contribut
---
**Note**: This security policy applies to the NodePass core project. For security issues in ecosystem projects (NodePassDash, NodePanel, etc.), please refer to their respective repositories in the [NodePassProject](https://github.com/NodePassProject) organization.
**Note**: This security policy applies to the NodePass core project. For security issues in ecosystem projects (NodePassDash, NodePanel, etc.), please refer to their respective repositories in the [NodePassProject](https://github.com/NodePassProject) organization.
+6 -25
View File
@@ -8,12 +8,10 @@ import (
"runtime"
"time"
"github.com/NodePassProject/cert"
"github.com/NodePassProject/logs"
"github.com/NodePassProject/nodepass/internal"
)
// start 启动核心逻辑
func start(args []string) error {
if len(args) != 2 {
return fmt.Errorf("start: empty URL command")
@@ -35,7 +33,6 @@ func start(args []string) error {
return nil
}
// initLogger 初始化日志记录器
func initLogger(level string) *logs.Logger {
logger := logs.NewLogger(logs.Info, true)
switch level {
@@ -58,7 +55,6 @@ func initLogger(level string) *logs.Logger {
return logger
}
// createCore 创建核心
func createCore(parsedURL *url.URL, logger *logs.Logger) (interface{ Run() }, error) {
switch parsedURL.Scheme {
case "server":
@@ -74,10 +70,8 @@ func createCore(parsedURL *url.URL, logger *logs.Logger) (interface{ Run() }, er
}
}
// getTLSProtocol 获取TLS配置
func getTLSProtocol(parsedURL *url.URL, logger *logs.Logger) (string, *tls.Config) {
// 生成基本TLS配置
tlsConfig, err := cert.NewTLSConfig(version)
tlsConfig, err := internal.NewTLSConfig()
if err != nil {
logger.Error("Generate TLS config failed: %v", err)
logger.Warn("TLS code-0: nil cert")
@@ -88,11 +82,9 @@ func getTLSProtocol(parsedURL *url.URL, logger *logs.Logger) (string, *tls.Confi
switch parsedURL.Query().Get("tls") {
case "1":
// 使用内存自签证书
logger.Info("TLS code-1: RAM cert with TLS 1.3")
return "1", tlsConfig
case "2":
// 使用自定义证书
crtFile, keyFile := parsedURL.Query().Get("crt"), parsedURL.Query().Get("key")
cert, err := tls.LoadX509KeyPair(crtFile, keyFile)
if err != nil {
@@ -101,13 +93,11 @@ func getTLSProtocol(parsedURL *url.URL, logger *logs.Logger) (string, *tls.Confi
return "1", tlsConfig
}
// 缓存证书并设置自动重载
cachedCert := cert
lastReload := time.Now()
tlsConfig = &tls.Config{
MinVersion: tls.VersionTLS13,
GetCertificate: func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
// 定期重载证书
if time.Since(lastReload) >= internal.ReloadInterval {
newCert, err := tls.LoadX509KeyPair(crtFile, keyFile)
if err != nil {
@@ -130,31 +120,22 @@ func getTLSProtocol(parsedURL *url.URL, logger *logs.Logger) (string, *tls.Confi
return "2", tlsConfig
default:
if poolType := parsedURL.Query().Get("type"); poolType == "1" || poolType == "3" {
// 流池类型不支持明文传输
logger.Info("TLS code-1: RAM cert with TLS 1.3 for stream pool")
return "1", tlsConfig
}
// 不使用加密
logger.Warn("TLS code-0: unencrypted")
return "0", nil
}
}
// exit 退出程序并显示帮助信息
func exit(err error) {
errMsg := "none"
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v [%d]\n", err, os.Getpid())
errMsg = err.Error()
}
fmt.Printf(`
%*s
%*s
server://password@host/host?<query> │
client://password@host/host?<query> │
master://hostname:port/path?<query> │
fmt.Fprintf(os.Stderr,
"nodepass-%s %s/%s pid=%d error=%s\nvisit https://github.com/NodePassProject for more information\n",
version, runtime.GOOS, runtime.GOARCH, os.Getpid(), errMsg)
`, 36, fmt.Sprintf("nodepass-%s", version), 36, fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH))
os.Exit(1)
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-888
View File
@@ -1,888 +0,0 @@
# 使用示例
本页提供了NodePass在各种部署场景中的实际示例。这些示例涵盖了常见用例,可以根据您的具体需求进行调整。
## 基本服务器设置与TLS选项
### 示例1:无TLS加密
当速度比安全性更重要时(例如,在受信任网络中):
```bash
nodepass "server://0.0.0.0:10101/127.0.0.1:8080?log=debug&tls=0"
```
这会启动一个NodePass服务器,它:
- 在所有接口的10101端口上监听隧道连接
- 将流量转发到localhost:8080
- 使用debug日志记录详细信息
- 不对数据通道使用加密(最快性能)
### 示例2:自签名证书
为了平衡安全性和易于设置(推荐大多数情况):
```bash
nodepass "server://0.0.0.0:10101/127.0.0.1:8080?log=debug&tls=1"
```
此配置:
- 自动生成自签名证书
- 提供加密而无需证书管理
- 保护数据流量免受被动窃听
- 适用于内部或测试环境
### 示例3:自定义域名证书
对于需要验证证书的生产环境:
```bash
nodepass "server://0.0.0.0:10101/127.0.0.1:8080?log=debug&tls=2&crt=/path/to/cert.pem&key=/path/to/key.pem"
```
这一设置:
- 使用您提供的TLS证书和私钥
- 提供具有证书验证的最高安全级别
- 适合生产环境和面向公众的服务
- 允许客户端验证服务器的身份
## 连接到NodePass服务器
### 示例4:基本客户端连接
使用默认设置连接到NodePass服务器:
```bash
nodepass client://server.example.com:10101/127.0.0.1:8080
```
此客户端:
- 连接到server.example.com:10101的NodePass服务器
- 将接收到的流量转发到localhost:8080
- 自动采用服务器的TLS安全策略
- 使用默认的info日志级别
### 示例5:带调试日志的客户端
用于故障排除连接问题:
```bash
nodepass client://server.example.com:10101/127.0.0.1:8080?log=debug
```
这启用了详细输出,有助于识别:
- 连接建立问题
- 信号处理
- 数据传输详情
- 错误情况
### 示例6:运行模式控制
通过明确的模式设置控制操作行为:
```bash
# 强制服务器以反向模式运行(服务器接收流量)
nodepass "server://0.0.0.0:10101/0.0.0.0:8080?mode=1&tls=1"
# 强制客户端以单端转发模式运行(高性能本地代理)
nodepass "client://127.0.0.1:1080/remote.example.com:8080?mode=1"
# 强制客户端以双端握手模式运行(需要服务器协调)
nodepass "client://server.example.com:10101/127.0.0.1:8080?mode=2&log=debug"
```
这些配置:
- **服务器 mode=1**:强制反向模式,服务器本地绑定目标地址
- **客户端 mode=1**:强制单端转发模式,使用直接连接实现高性能
- **客户端 mode=2**:强制双端握手模式,适用于需要服务器协调的场景
- 当自动检测不符合部署需求时使用模式控制
## 通过防火墙访问数据库
### 示例7:数据库隧道
启用对防火墙后的数据库服务器的安全访问:
```bash
# 服务器端(位于安全网络外部)使用TLS加密
nodepass server://:10101/127.0.0.1:5432?tls=1
# 客户端(位于防火墙内部)
nodepass client://server.example.com:10101/127.0.0.1:5432
```
此配置:
- 创建到PostgreSQL数据库(端口5432)的加密隧道
- 允许安全访问数据库而不直接将其暴露于互联网
- 使用自签名证书加密所有数据库流量
- 使远程数据库在客户端上显示为本地服务
## 安全的微服务通信
### 示例8:服务间通信
启用微服务之间的安全通信:
```bash
# 服务A(消费API)使用自定义证书
nodepass "server://0.0.0.0:10101/127.0.0.1:8081?log=warn&tls=2&crt=/path/to/service-a.crt&key=/path/to/service-a.key"
# 服务B(提供API)
nodepass client://service-a:10101/127.0.0.1:8082
```
此设置:
- 在两个微服务之间创建安全通道
- 使用自定义证书进行服务身份验证
- 将日志限制为仅警告和错误
- 使服务A的API在服务B上显示为本地服务
## 协议屏蔽和流量过滤
### 示例9:屏蔽代理协议
阻止SOCKS和HTTP代理使用你的隧道:
```bash
# 屏蔽SOCKS和HTTP代理协议的服务器
nodepass "server://0.0.0.0:10101/app.backend.com:8080?block=12&tls=1"
# 连接到受保护服务器的客户端
nodepass "client://server.example.com:10101/127.0.0.1:8080"
```
此配置:
- 屏蔽所有SOCKS4/4a/5代理连接(`block`包含`1`
- 屏蔽所有HTTP代理方法如CONNECT、GET、POST`block`包含`2`
- 仅允许应用特定协议通过隧道
- 用于防止在应用隧道上滥用代理
### 示例10:屏蔽TLS嵌套场景
当外层已提供安全保护时,防止嵌套TLS加密:
```bash
# 使用TLS加密并屏蔽内部TLS连接的服务器
nodepass "server://0.0.0.0:10101/0.0.0.0:8080?tls=1&block=3"
# 客户端自动继承TLS设置
nodepass "client://server.example.com:10101/127.0.0.1:8080"
```
此设置:
- 使用TLS加密隧道本身(`tls=1`
- 屏蔽加密隧道内的TLS握手(`block=3`
- 防止不必要的双重加密开销
- 有助于识别应用尝试添加冗余TLS的错误配置
### 示例11:综合安全策略
执行严格的安全策略,仅允许应用流量:
```bash
# 具有综合协议屏蔽的生产服务器
nodepass "server://0.0.0.0:10101/secure-app.internal:443?tls=2&crt=/path/to/cert.pem&key=/path/to/key.pem&block=123&slot=500"
# 强制加密的客户端
nodepass "client://prod-server.example.com:10101/127.0.0.1:8443?log=warn"
```
此配置:
- 使用经过验证的自定义证书以获得最大安全性(`tls=2`
- 屏蔽SOCKS代理(`block`包含`1`
- 屏蔽HTTP代理(`block`包含`2`
- 屏蔽嵌套TLS连接(`block`包含`3`
- 将并发连接限制为500以控制资源
- 仅记录警告和错误以减少噪音
### 示例12:开发环境的选择性协议屏蔽
在开发环境中允许HTTP流量同时屏蔽代理:
```bash
# 仅屏蔽SOCKS协议的开发服务器
nodepass "server://127.0.0.1:10101/localhost:3000?block=1&log=debug"
# 开发客户端
nodepass "client://127.0.0.1:10101/localhost:8080"
```
此设置:
- 屏蔽SOCKS协议但允许HTTP请求
- 适用于需要HTTP方法的Web应用测试
- 防止开发人员隧道传输SOCKS代理流量
- 启用调试日志记录以进行故障排除
## 带宽速率限制
### 示例13:带速率限制的文件传输服务器
控制文件传输服务的带宽使用:
```bash
# 服务端:限制文件传输带宽为100 Mbps
nodepass "server://0.0.0.0:10101/127.0.0.1:8080?log=info&tls=1&rate=100"
# 客户端:连接时限制为50 Mbps
nodepass "client://fileserver.example.com:10101/127.0.0.1:3000?log=info&rate=50"
```
此配置:
- 限制服务器带宽为100 Mbps以防止网络拥塞
- 客户端进一步限制下载速度为50 Mbps以实现公平共享
- 允许文件传输的同时为其他服务保留带宽
- 使用TLS加密确保文件传输安全
### 示例14:物联网传感器数据收集的保守限制
对于带宽有限或按流量计费的物联网设备:
```bash
# 服务器:接受物联网数据,限制为5 Mbps
nodepass "server://0.0.0.0:10101/127.0.0.1:1883?log=warn&rate=5"
# 物联网设备客户端:发送传感器数据,限制为2 Mbps
nodepass "client://iot-gateway.example.com:10101/127.0.0.1:1883?log=error&rate=2"
```
此设置:
- 限制服务器为5 Mbps用于从多个物联网设备收集传感器数据
- 单个物联网客户端限制为2 Mbps以防止单一设备消耗所有带宽
- 最小日志记录(warn/error)以减少物联网设备的资源使用
- 高效适用于MQTT或其他物联网协议
### 示例15:开发环境速率控制
在带宽约束下测试应用程序:
```bash
# 模拟慢速网络条件进行测试
nodepass "client://api.example.com:443/127.0.0.1:8080?log=debug&rate=1"
# 带监控的高速开发服务器
nodepass "server://0.0.0.0:10101/127.0.0.1:3000?log=debug&rate=500"
```
此配置:
- 客户端模拟1 Mbps连接用于测试慢速网络场景
- 开发服务器限制为500 Mbps并提供详细日志记录用于调试
- 帮助识别不同带宽约束下的性能问题
## 物联网设备管理
### 示例16:物联网网关
创建物联网设备的中央访问点:
```bash
# 中央管理服务器
nodepass "server://0.0.0.0:10101/127.0.0.1:8888?log=info&tls=1"
# 物联网设备
nodepass client://mgmt.example.com:10101/127.0.0.1:80
```
此配置:
- 使分布式物联网设备能够安全连接到中央服务器
- 使用自签名证书提供足够的安全性
- 允许嵌入式设备安全地暴露其本地Web界面
- 通过单一端点集中设备管理
## 多网卡系统与源IP控制
### 示例17:指定网络接口选择
在多网卡系统上控制出站连接使用的网络接口:
```bash
# 服务器为出站连接使用特定源IP(适用于策略路由)
nodepass "server://0.0.0.0:10101/remote.backend.com:8080?dial=10.1.0.100&mode=2&tls=1"
# 客户端为目标连接使用特定源IP(适用于防火墙规则)
nodepass "client://server.example.com:10101/127.0.0.1:8080?dial=192.168.1.50&mode=2"
```
此配置:
- 强制出站连接使用特定的本地IP地址
- 适用于具有多个网络接口的系统(例如,独立的公网/内网)
- 通过源IP启用基于策略的路由
- 如果指定地址失败,自动回退到系统选择的IP
- 支持IPv4和IPv6地址
### 示例18:网络分段和VLAN路由
通过特定网络段或VLAN引导流量:
```bash
# 服务器通过管理网络路由流量(10.0.0.0/8)
nodepass "server://0.0.0.0:10101/mgmt.backend.local:8080?dial=10.200.1.10&mode=2&log=info"
# 服务器通过生产网络路由流量(172.16.0.0/12
nodepass "server://0.0.0.0:10102/prod.backend.local:8080?dial=172.16.50.20&mode=2&log=info"
# 客户端使用自动源IP选择(默认行为)
nodepass "client://server.example.com:10101/127.0.0.1:8080?dial=auto"
```
此设置:
- 在网络层分离管理和生产流量
- 确保流量基于源IP遵循指定的网络路径
- 符合要求基于源的路由的网络安全策略
- 自动回退防止配置错误导致的连接失败
- `dial=auto`(默认)让系统选择合适的源IP
**源IP控制使用场景**
- **多网卡服务器**:具有不同网络的多个网卡的系统
- **策略路由**:需要特定源IP的网络策略
- **防火墙合规**:匹配按源地址过滤的防火墙规则
- **负载分配**:在多个网络链路之间分配出站流量
- **网络测试**:模拟来自特定网络位置的流量
## DNS缓存TTL配置
### 示例19:稳定的企业网络
为稳定的内部服务使用较长的TTL
```bash
# 服务端:为稳定的内部主机名使用1小时缓存TTL
nodepass "server://0.0.0.0:10101/internal-api.corp.local:8080?dns=1h&mode=2&tls=1"
# 客户端:使用相同的TTL以保持一致行为
nodepass "client://tunnel.corp.local:10101/127.0.0.1:8080?dns=1h"
```
此配置:
- 为稳定的内部服务使用1小时DNS缓存TTL
- 减少企业网络中的DNS查询开销
- 通过最小化DNS查找提高连接性能
- 适用于DNS稳定的生产环境
### 示例20:动态DNS环境
为频繁变化的DNS记录使用较短的TTL:
```bash
# 服务端:为动态DNS使用30秒缓存TTL
nodepass "server://0.0.0.0:10101/dynamic.example.com:8080?dns=30s&tls=1&log=info"
# 客户端:为负载均衡场景使用短TTL
nodepass "client://server.example.com:10101/127.0.0.1:8080?dns=30s"
```
此设置:
- 为动态环境使用30秒DNS缓存TTL
- 为负载均衡服务实现更快的故障转移
- 确保连接使用当前的DNS记录
- 适合IP频繁变化的云环境
### 示例21:开发和测试
为开发环境禁用缓存:
```bash
# 开发服务器:不使用DNS缓存以立即更新
nodepass "server://0.0.0.0:10101/dev.backend.local:8080?dns=0&tls=0&log=debug"
# 测试客户端:不使用缓存以立即查看DNS更改
nodepass "client://dev-server.local:10101/127.0.0.1:8080?dns=0&log=debug"
```
此配置:
- 禁用DNS缓存(dns=0)以立即更新
- 每次连接都执行新的DNS查找
- 在开发期间DNS记录频繁变化时很有用
- 帮助在测试期间识别DNS相关问题
### 示例22:混合环境的自定义TTL
使用适中的TTL平衡性能和新鲜度:
```bash
# 生产API10分钟缓存以平衡性能
nodepass "server://0.0.0.0:10101/api.example.com:8080?dns=10m&tls=1&mode=2"
# 暂存环境:2分钟缓存以更快更新
nodepass "server://0.0.0.0:10102/staging.example.com:8080?dns=2m&tls=1&mode=2"
# 客户端:默认5分钟缓存
nodepass "client://server.example.com:10101/127.0.0.1:8080"
```
此设置:
- 生产环境使用10分钟TTL以获得良好性能
- 暂存环境使用2分钟TTL以更快地更新DNS
- 客户端使用默认5分钟TTL
- 每个环境针对其使用场景进行优化
**DNS缓存TTL使用场景**
- **企业网络**:为稳定的内部主机名使用长TTL(1h)
- **动态DNS**:为频繁变化的记录使用短TTL(30s-1m)
- **负载均衡**:短TTL实现更快的故障转移
- **性能优化**:较长的TTL降低连接延迟
- **高可用性**:适中的TTL平衡新鲜度和性能
## 高可用性与负载均衡
### 示例23:多后端服务器负载均衡
使用目标地址组实现流量均衡分配和自动故障转移:
```bash
# 服务端:配置3个后端Web服务器
nodepass "server://0.0.0.0:10101/web1.internal:8080,web2.internal:8080,web3.internal:8080?mode=2&tls=1&log=info"
# 客户端:连接到服务端
nodepass "client://server.example.com:10101/127.0.0.1:8080?log=info"
```
此配置:
- 流量自动轮询分配到3个后端服务器,实现负载均衡
- 当某个后端服务器故障时,自动切换到其他可用服务器
- 故障服务器恢复后自动重新接入流量
- 使用TLS加密确保隧道安全
### 示例24:数据库主从切换
为数据库配置主从实例,实现高可用访问:
```bash
# 客户端:配置主从数据库地址(单端转发模式)
nodepass "client://127.0.0.1:3306/db-primary.local:3306,db-secondary.local:3306?mode=1&log=warn"
```
此设置:
- 优先连接主数据库,主库故障时自动切换到从库
- 单端转发模式提供高性能本地代理
- 应用程序无需修改,透明地实现故障转移
- 仅记录警告和错误,减少日志输出
### 示例25API网关后端池
为API网关配置多个后端服务实例:
```bash
# 服务端:配置4个API服务实例
nodepass "server://0.0.0.0:10101/api1.backend:8080,api2.backend:8080,api3.backend:8080,api4.backend:8080?mode=2&tls=1&rate=200&slot=5000"
# 客户端:从API网关连接
nodepass "client://apigateway.example.com:10101/127.0.0.1:8080?rate=100&slot=2000"
```
此配置:
- 4个API服务实例形成后端池,轮询分配请求
- 服务端限制带宽200 Mbps,最大5000并发连接
- 客户端限制带宽100 Mbps,最大2000并发连接
- 单个实例故障不影响整体服务可用性
### 示例26:地域分布式服务
配置多地域服务节点,优化网络延迟:
```bash
# 服务端:配置多地域节点
nodepass "server://0.0.0.0:10101/us-west.service:8080,us-east.service:8080,eu-central.service:8080?mode=2&log=debug"
```
此设置:
- 配置3个不同地域的服务节点
- 轮询算法自动分配流量到各个地域
- Debug日志帮助分析流量分布和故障情况
- 适用于全球分布式应用场景
**目标地址组最佳实践:**
- **地址数量**:建议配置2-5个地址,过多会增加故障检测时间
- **健康检查**:确保后端服务有自己的健康检查机制
- **端口一致性**:所有地址使用相同端口或明确指定每个地址的端口
- **监控告警**:配置监控系统跟踪故障转移事件
- **测试验证**:部署前在测试环境验证故障转移和负载均衡行为
## PROXY协议集成
### 示例27:负载均衡器与PROXY协议集成
启用PROXY协议支持,与负载均衡器和反向代理集成:
```bash
# 服务端:为HAProxy/Nginx集成启用PROXY协议v1
nodepass "server://0.0.0.0:10101/127.0.0.1:8080?log=info&tls=1&proxy=1"
# 客户端:启用PROXY协议以保留客户端连接信息
nodepass "client://tunnel.example.com:10101/127.0.0.1:3000?log=info&proxy=1"
```
此配置:
- 在数据传输开始前发送PROXY协议v1头部
- 通过隧道保留原始客户端IP和端口信息
- 使后端服务能够看到真实的客户端连接详情
- 兼容HAProxy、Nginx和其他支持PROXY协议的服务
- 有助于维护准确的访问日志和基于IP的访问控制
### 示例28:Web应用的反向代理支持
使NodePass后的Web应用能够接收原始客户端信息:
```bash
# 为Web应用启用PROXY协议的NodePass服务器
nodepass "server://0.0.0.0:10101/127.0.0.1:8080?log=warn&tls=2&crt=/path/to/cert.pem&key=/path/to/key.pem&proxy=1"
# 后端Web服务器(如Nginx)配置以处理PROXY协议
# 在nginx.conf中:
# server {
# listen 8080 proxy_protocol;
# real_ip_header proxy_protocol;
# set_real_ip_from 127.0.0.1;
# ...
# }
```
此设置:
- Web应用接收原始客户端IP地址而不是NodePass隧道IP
- 启用正确的访问日志记录、分析和安全控制
- 支持连接审计的合规性要求
- 适用于支持PROXY协议的Web服务器(Nginx、HAProxy等)
### 示例29:数据库访问与客户端IP保留
为数据库访问日志记录和安全维护客户端IP信息:
```bash
# 启用PROXY协议的数据库代理服务器
nodepass "server://0.0.0.0:10101/127.0.0.1:5432?log=error&proxy=1"
# 通过隧道连接的应用客户端
nodepass "client://dbproxy.example.com:10101/127.0.0.1:5432?proxy=1"
```
优势:
- 数据库日志显示原始应用服务器IP而不是隧道IP
- 启用基于IP的数据库访问控制正常工作
- 维护安全和合规的审计轨迹
- 兼容支持PROXY协议的数据库(适当配置的PostgreSQL)
**PROXY协议重要说明:**
- 目标服务必须支持PROXY协议v1才能正确处理头部
- PROXY头部仅对TCP连接发送,不支持UDP流量
- 头部包含:协议(TCP4/TCP6)、源IP、目标IP、源端口、目标端口
- 如果目标服务不支持PROXY协议,连接可能失败或行为异常
- 在生产环境部署前,请在非生产环境中充分测试启用PROXY协议的配置
## 容器部署
### 示例30:容器化NodePass
在Docker环境中部署NodePass
```bash
# 为容器创建网络
docker network create nodepass-net
# 部署使用自签名证书的NodePass服务器
docker run -d --name nodepass-server \
--network nodepass-net \
-p 10101:10101 \
ghcr.io/NodePassProject/nodepass "server://0.0.0.0:10101/web-service:80?log=info&tls=1"
# 部署Web服务作为目标
docker run -d --name web-service \
--network nodepass-net \
nginx:alpine
# 部署NodePass客户端
docker run -d --name nodepass-client \
-p 8080:8080 \
ghcr.io/NodePassProject/nodepass client://nodepass-server:10101/127.0.0.1:8080?log=info
# 通过http://localhost:8080访问Web服务
```
此配置:
- 在服务之间创建容器化隧道
- 使用Docker网络连接容器
- 仅向主机公开必要端口
- 提供对内部Web服务的安全访问
## 主控API管理
### 示例31:集中化管理
为多个NodePass实例设置中央控制器:
```bash
# 使用自签名证书启动主控API服务
nodepass "master://0.0.0.0:9090?log=info&tls=1"
```
然后您可以通过API调用管理实例:
```bash
# 创建服务器实例
curl -X POST http://localhost:9090/api/v1/instances \
-H "Content-Type: application/json" \
-d '{"url":"server://0.0.0.0:10101/0.0.0.0:8080?tls=1"}'
# 创建客户端实例
curl -X POST http://localhost:9090/api/v1/instances \
-H "Content-Type: application/json" \
-d '{"url":"client://localhost:10101/127.0.0.1:8081"}'
# 列出所有运行实例
curl http://localhost:9090/api/v1/instances
# 控制实例(用实际实例ID替换{id})
curl -X PUT http://localhost:9090/api/v1/instances/{id} \
-H "Content-Type: application/json" \
-d '{"action":"restart"}'
```
此设置:
- 为所有NodePass实例提供中央管理界面
- 允许动态创建和控制隧道
- 提供用于自动化和集成的RESTful API
- 包含内置的Swagger UI,位于http://localhost:9090/api/v1/docs
### 示例32:自定义API前缀
为主控模式使用自定义API前缀:
```bash
# 使用自定义API前缀启动
nodepass "master://0.0.0.0:9090/admin?log=info&tls=1"
# 使用自定义前缀创建实例
curl -X POST http://localhost:9090/admin/v1/instances \
-H "Content-Type: application/json" \
-d '{"url":"server://0.0.0.0:10101/0.0.0.0:8080?tls=1"}'
```
这允许:
- 与现有API网关集成
- 用于安全或组织目的的自定义URL路径
- 在http://localhost:9090/admin/v1/docs访问Swagger UI
### 示例33:实时连接和流量监控
通过主控API监控实例的连接数和流量统计:
```bash
# 获取实例详细信息,包括连接数统计
curl -H "X-API-Key: your-api-key" http://localhost:9090/api/v1/instances/{id}
# 响应示例(包含TCPS和UDPS字段)
{
"id": "a1b2c3d4",
"alias": "网站代理",
"type": "server",
"status": "running",
"url": "server://0.0.0.0:10101/127.0.0.1:8080",
"restart": true,
"pool": 64,
"ping": 25,
"tcps": 12,
"udps": 5,
"tcprx": 1048576,
"tcptx": 2097152,
"udprx": 512000,
"udptx": 256000
}
# 使用SSE实时监控所有实例状态变化
curl -H "X-API-Key: your-api-key" \
-H "Accept: text/event-stream" \
http://localhost:9090/api/v1/events
```
此监控设置提供:
- **实时连接数跟踪**:TCPS和UDPS字段显示当前活动连接数
- **性能分析**:通过连接数和流量数据评估系统负载
- **容量规划**:基于历史连接数据进行资源规划
- **故障诊断**:异常的连接数变化可能指示网络问题
## 连接池类型
### 示例34: 基于QUIC的流多路复用隧道
使用QUIC协议进行连接池管理,在高延迟网络中提供更优性能:
```bash
# 服务器端:启用QUIC连接池
nodepass "server://0.0.0.0:10101/remote.example.com:8080?type=1&mode=2&tls=1&log=debug"
# 客户端:自动从服务器接收连接池类型配置
nodepass "client://server.example.com:10101/127.0.0.1:8080?mode=2&min=128&log=debug"
```
此配置:
- 使用QUIC协议进行基于UDP的多路复用流
- 单个QUIC连接承载多个并发数据流
- 强制使用TLS 1.3加密(自动启用)
- 在丢包场景中性能更好(无队头阻塞)
- 通过0-RTT支持改善连接建立
- 客户端在握手时自动接收服务器的连接池类型配置
### 示例35: 使用自定义TLS证书的QUIC连接池
在生产环境部署带有验证证书的QUIC隧道:
```bash
# 服务器端:使用自定义证书的QUIC连接池
nodepass "server://0.0.0.0:10101/backend.internal:8080?type=1&mode=2&tls=2&crt=/etc/nodepass/cert.pem&key=/etc/nodepass/key.pem"
# 客户端:自动接收连接池类型配置并进行证书验证
nodepass "client://tunnel.example.com:10101/127.0.0.1:8080?mode=2&min=64&log=info"
```
此设置:
- 使用验证证书实现最高安全性
- QUIC协议提供强制TLS 1.3加密
- 适用于生产环境
- 客户端进行完整证书验证
- 连接池类型配置自动从服务器下发
### 示例36: WebSocket连接池穿透HTTP代理
在企业防火墙后使用WebSocket连接池:
```bash
# 服务器端:启用WebSocket连接池(需要TLS
nodepass "server://0.0.0.0:10101/internal.backend:8080?type=2&mode=2&tls=1&log=info"
# 客户端:自动接收WebSocket配置
nodepass "client://wss.tunnel.com:10101/127.0.0.1:8080?mode=2&min=64"
```
此配置:
- 使用WebSocket协议可以穿透HTTP代理和CDN
- **需要TLS加密** - 最少`tls=1`,生产环境建议使用带证书的`tls=2`
- 使用标准HTTPS端口,容易通过防火墙
- 与现有Web基础设施兼容
- 支持全双工通信
- 适合企业环境中仅允许HTTP/HTTPS流量的场景
- 客户端自动采用服务器的连接池类型配置
- **注意**:WebSocket连接池不支持不加密模式(tls=0)
### 示例37: 高并发环境的HTTP/2连接池
使用HTTP/2连接池实现高效的多路复用流和协议优化:
```bash
# 服务器端:启用HTTP/2连接池(需要TLS)
nodepass "server://0.0.0.0:10101/backend.internal:8080?type=3&mode=2&tls=1&log=info"
# 客户端:自动接收HTTP/2配置
nodepass "client://h2.tunnel.com:10101/127.0.0.1:8080?mode=2&min=64"
```
此配置:
- 使用HTTP/2协议在单个TLS连接上实现多路复用流
- **需要TLS加密** - 最少`tls=1`,生产环境建议使用带证书的`tls=2`
- HPACK头部压缩减少带宽使用
- 高效解析的二进制帧协议
- 每个流的流量控制实现最优资源利用
- 与HTTP/2感知的代理和负载均衡器配合使用
- 适合HTTP/HTTPS仅支持策略的环境
- 客户端自动采用服务器的连接池类型配置
- 适用于受益于流多路复用的高并发场景
### 示例38: 移动/高延迟网络的QUIC连接池
针对移动网络或卫星连接进行优化:
```bash
# 服务器端:带自适应池大小的QUIC连接池
nodepass "server://0.0.0.0:10101/api.backend:443?type=1&mode=2&max=512&tls=1&log=info"
# 客户端:自动接收连接池类型,配置较大最小连接池用于移动网络
nodepass "client://mobile.tunnel.com:10101/127.0.0.1:8080?mode=2&min=256&log=warn"
```
此配置:
- QUIC的基于UDP传输在NAT环境中表现更好
- 更大的连接池大小补偿网络切换
- 流多路复用减少连接开销
- 更好地处理丢包和抖动
- 0-RTT重连在网络变化后实现更快恢复
- 客户端自动采用服务器的连接池类型配置
### 示例39: 连接池类型性能对比
TCP、QUIC、WebSocket和HTTP/2连接池的并排比较:
```bash
# 传统TCP连接池(默认)
nodepass "server://0.0.0.0:10101/backend.example.com:8080?type=0&mode=2&tls=1&log=event"
nodepass "client://server.example.com:10101/127.0.0.1:8080?mode=2&min=128&log=event"
# QUIC连接池(现代方法)
nodepass "server://0.0.0.0:10102/backend.example.com:8080?type=1&mode=2&tls=1&log=event"
nodepass "client://server.example.com:10102/127.0.0.1:8081?mode=2&min=128&log=event"
# WebSocket连接池(代理穿透)
nodepass "server://0.0.0.0:10103/backend.example.com:8080?type=2&mode=2&tls=1&log=event"
nodepass "client://server.example.com:10103/127.0.0.1:8082?mode=2&min=128&log=event"
# HTTP/2连接池(多路复用流)
nodepass "server://0.0.0.0:10104/backend.example.com:8080?type=3&mode=2&tls=1&log=event"
nodepass "client://server.example.com:10104/127.0.0.1:8083?mode=2&min=128&log=event"
```
**TCP连接池优势**
- 与网络基础设施更广泛兼容
- 已建立的协议,行为可预测
- 在某些企业环境中支持更好
**QUIC连接池优势**
- 通过0-RTT连接恢复降低延迟
- 跨流无队头阻塞
- 更好的拥塞控制和丢失恢复
- 改善NAT穿透能力
- 单个UDP套接字减少资源使用
**WebSocket连接池优势**
- 可以穿透HTTP代理和CDN
- 使用标准HTTP/HTTPS端口
- 与现有Web基础设施集成
- 适合企业防火墙环境
**HTTP/2连接池优势**
- 在单个TCP连接上高效的流多路复用
- HPACK头部压缩减少带宽
- 高效解析的二进制协议
- 每个流的流量控制实现资源优化
- 与HTTP/2感知的基础设施配合使用
- 适合HTTP/HTTPS仅支持策略的环境
### 示例40: 实时应用的QUIC连接池
为游戏、VoIP或视频流配置QUIC隧道:
```bash
# 服务器端:为实时流量优化的QUIC设置
nodepass "server://0.0.0.0:10101/gameserver.local:7777?type=1&mode=2&tls=1&read=30s&slot=10000"
# 客户端:自动从服务器接收连接池类型配置
nodepass "client://game.tunnel.com:10101/127.0.0.1:7777?mode=2&min=64&read=30s"
```
此设置:
- QUIC的流级别流量控制防止流之间的干扰
- 在有损网络中比TCP连接池延迟更低
- 30秒读取超时快速检测陈旧连接
- 大槽位限制支持许多并发玩家/流
- 减少连接建立开销
- 客户端自动采用服务器的连接池类型配置
**连接池类型使用场景总结**
- **TCP连接池**:标准企业环境、最大兼容性、稳定网络
- **QUIC连接池**:移动网络、高延迟链路、实时应用、复杂NAT环境
- **WebSocket连接池**:HTTP代理穿透、企业防火墙限制、Web基础设施集成
- **HTTP/2连接池**:HTTP/HTTPS仅支持策略、高并发Web流量、与HTTP/2感知基础设施集成
## 下一步
现在您已经了解了各种使用示例,您可能想要:
- 了解[配置选项](/docs/zh/configuration.md)以进行微调
- 理解NodePass内部[工作原理](/docs/zh/how-it-works.md)
- 查看[故障排除指南](/docs/zh/troubleshooting.md)了解常见问题
-680
View File
@@ -1,680 +0,0 @@
# NodePass 工作原理
本页解释了 NodePass 的内部架构和数据流机制,提供了不同组件如何交互以创建高效、安全的隧道的深入见解。
## 架构概述
NodePass 创建了一个具有独立控制和数据通道的网络架构:
1. **控制通道(隧道)**
- 客户端和服务器之间的未加密 TCP 连接
- 专门用于信号传输和协调
- 在隧道生命周期内维持持久连接
2. **数据通道(目标)**
- 可配置的 TLS 加密选项:
- **模式 0**:未加密数据传输(最快,安全性最低)
- **模式 1**:自签名证书加密(良好安全性,无验证)
- **模式 2**:验证证书加密(最高安全性,需要有效证书)
- 按需为每个连接或数据报创建
- 用于实际应用数据传输
3. **服务端模式操作**
- 在隧道端点监听控制连接
- 当流量到达目标端点时,通过控制通道向客户端发送信号
- 在需要时使用指定的 TLS 模式建立数据通道
- 支持双向数据流:可以从服务端或客户端发起连接
4. **客户端模式操作**
- 连接到服务端的控制通道
- **握手阶段**:服务端验证隧道密钥后,向客户端下发配置信息:
- 数据流向模式(决定客户端接收或发送流量)
- 最大连接池容量(由服务端统一管理和分配)
- TLS安全级别(确保客户端使用正确的加密模式)
- 监听指示传入连接的信号
- 使用服务端指定的 TLS 安全级别创建数据连接
- 在安全通道和本地目标之间转发数据
- 支持双向数据流:根据目标地址自动选择数据流方向
5. **客户端单端转发模式**
- 当隧道地址为本地地址时(如127.0.0.1)自动启用
- 客户端直接在本地监听端口,无需服务端的控制通道协调
- 对于TCP和UDP协议使用直接连接建立方式
- 适用于纯本地转发场景,减少网络开销和延迟
- 支持TCP和UDP协议的高性能单端转发,采用优化的连接处理
5. **协议支持**
- **TCP**:具有持久连接的全双工流式传输,在客户端单端转发模式下优化了直接连接建立
- **UDP**:具有可配置缓冲区大小和超时的数据报转发
## 数据传输流
NodePass 通过其隧道架构建立双向数据流,支持 TCP 和 UDP 协议。系统支持三种数据流模式:
### 数据流模式说明
- **服务端接收模式**:服务端在目标地址监听,客户端在本地监听,数据从目标地址流向客户端本地
- **服务端发送模式**:服务端连接到远程目标地址,客户端在本地监听,数据从客户端本地流向远程目标
- **客户端单端转发模式**:客户端在本地直接监听并转发到目标地址,无需服务端协调,使用直接连接建立实现优化转发
数据流模式根据隧道地址和目标地址自动确定:
- 如果隧道地址是本地地址(localhost、127.0.0.1等),启用客户端单端转发模式
- 如果目标地址是本地地址,使用服务端接收模式
- 如果目标地址是远程地址,使用服务端发送模式
### 服务端流程(服务端接收模式)
1. **连接初始化**
```
[目标客户端] → [目标监听器] → [服务器:目标连接已创建]
```
- 对于 TCP:客户端建立到目标监听器的持久连接
- 对于 UDP:服务器在绑定到目标地址的 UDP 套接字上接收数据报
2. **信号生成**
```
[服务端] → [生成唯一连接 ID] → [通过未加密的 TCP 隧道向客户端发送信号]
```
- 对于 TCP:生成 `{"action":"tcp","remote":"target_addr","id":"connection_id"}` 信号
- 对于 UDP:生成 `{"action":"udp","remote":"client_addr","id":"connection_id"}` 信号
3. **连接准备**
```
[服务端] → [在池中创建具有配置的 TLS 模式的远程连接] → [等待客户端连接]
```
- 两种协议都使用相同的具有唯一连接 ID 的连接池机制
- 根据指定模式(0、1 或 2)应用 TLS 配置
4. **数据交换**
```
[目标连接] ⟷ [交换/传输] ⟷ [远程连接]
```
- 对于 TCP:使用 `conn.DataExchange()` 进行持续的双向数据流传输
- 对于 UDP:使用可配置的缓冲区大小转发单个数据报
### 客户端流程
1. **信号接收**
```
[客户端] → [从 TCP 隧道读取信号] → [解析连接 ID]
```
- 客户端根据 `action` 字段区分不同类型的信号
2. **连接建立**
```
[客户端] → [从池中检索连接] → [连接到远程端点]
```
- 此阶段的连接管理与协议无关
3. **本地连接**
```
[客户端] → [连接到本地目标] → [建立本地连接]
```
- 对于 TCP:建立到本地目标的持久 TCP 连接
- 对于 UDP:创建用于与本地目标交换数据报的 UDP 套接字
4. **数据交换**
```
[远程连接] ⟷ [交换/传输] ⟷ [本地目标连接]
```
- 对于 TCP:使用 `conn.DataExchange()` 进行持续的双向数据流传输
- 对于 UDP:读取单个数据报,转发它,使用超时等待响应,然后返回响应
### 客户端单端转发流程
1. **模式识别**
```
[客户端] → [检测隧道地址为本地地址] → [启用单端转发模式]
```
- 自动检测隧道地址是否为localhost、127.0.0.1等本地地址
- 启用单端转发模式,跳过服务端控制通道建立
2. **本地监听**
```
[客户端] → [在隧道端口启动监听器] → [等待本地连接]
```
- 直接在指定的隧道端口启动TCP或UDP监听器
- 无需连接到远程服务端,实现零延迟启动
3. **直接连接建立**
```
[客户端] → [创建到目标地址的直接连接] → [建立目标连接]
```
- 对于TCP:为每个隧道连接直接建立到目标地址的TCP连接
- 对于UDP:创建用于与目标地址交换数据报的UDP套接字
- 消除连接池开销,提供更简单、更直接的转发路径
4. **优化转发**
```
[本地连接] → [直接目标连接] → [数据交换] → [连接清理]
```
- 对于TCP:直接连接建立后进行高效数据交换
- 对于UDP:直接转发数据报到目标地址,延迟最小
- 简化的数据路径,确保可靠高效的转发
### 特定协议特性
- **TCP 交换**
- 用于全双工通信的持久连接
- 连接终止前的持续数据流传输
- 具有自动重连的错误处理
- **客户端单端转发优化**:为每个隧道连接直接建立连接,确保可靠高效的转发
- **UDP 交换**
- 具有可配置缓冲区大小的一次性数据报转发 (`UDP_DATA_BUF_SIZE`)
- 响应等待的读取超时控制 (`read`参数或默认0)
- 针对低延迟、无状态通信进行了优化
- **客户端单端转发优化**:直接转发机制,实现最低延迟
## 信号通信机制
NodePass 通过 TCP 隧道使用基于 JSON 的信号协议:
### 信号类型
1. **刷新信号**
- 格式:`{"action":"flush"}`
- 目的:刷新连接池并重置错误计数
- 时机:当连接池健康检查失败时发送
2. **PING 信号**
- 格式:`{"action":"ping"}`
- 目的:检查客户端连接状态并请求 PONG 响应
- 时机:定期健康检查时发送
3. **PONG 信号**
- 格式:`{"action":"pong"}`
- 目的:响应 PING 信号并报告系统状态
- 时机:收到 PING 信号时发送
4. **验证信号**
- 格式:`{"action":"verify","id":"connection_id","fp":"tls_fingerprint"}`
- 目的:验证 TLS 证书指纹
- 时机:建立 TLS 连接后发送
5. **TCP 启动信号**
- 格式:`{"action":"tcp","remote":"remote_addr","id":"connection_id"}`
- 目的:请求客户端为特定 ID 建立 TCP 连接
- 时机:当接收到目标服务的新 TCP 连接时发送
6. **UDP 启动信号**
- 格式:`{"action":"udp","remote":"remote_addr","id":"connection_id"}`
- 目的:请求客户端处理特定 ID 的 UDP 流量
- 时机:当在目标端口接收到 UDP 数据时发送
### 信号流程
1. **信号生成**
- 服务端为特定事件创建 JSON 格式的信号
- 信号以换行符终止,以便正确解析
2. **信号传输**
- 服务端将信号写入 TCP 隧道连接
- 使用互斥锁防止对隧道的并发写入
3. **信号接收**
- 客户端使用缓冲读取器从隧道读取信号
- 信号被解析为 JSON 格式
4. **信号处理**
- 客户端将有效信号放入缓冲通道 (signalChan)
- 专用 goroutine 处理来自通道的信号
- 信号量模式防止信号溢出
5. **信号执行**
- 根据 `action` 字段分派到相应的处理逻辑
- 连接启动信号触发相应的处理方法建立连接
### 信号弹性
- 具有可配置容量的缓冲通道防止在高负载下信号丢失
- 信号量实现确保受控并发
- 对格式错误或意外信号的错误处理
## 连接池架构
NodePass 实现了一个高效的连接池系统来管理网络连接,这是其性能优势的核心设计。NodePass支持三种连接池传输协议:传统的基于TCP的连接池、现代的基于QUIC的UDP连接池和基于WebSocket的连接池。
### 传输协议选择
NodePass通过`type`参数提供三种连接池传输选项:
1. **基于TCP的连接池 (type=0, 默认)**
- 由`pool`库管理的传统TCP连接
- 客户端和服务器之间的多个独立TCP连接
- 在单独的TCP连接上进行标准TLS加密
- 经过充分测试且广泛兼容的方法
2. **基于QUIC的连接池 (type=1)**
- 由`quic`库管理的基于UDP的多路复用流
- 单个QUIC连接承载多个并发流
- 强制使用TLS 1.3加密并支持0-RTT
- 在高延迟和移动网络中性能优越
3. **基于WebSocket的连接池 (type=2)**
- 通过HTTP升级建立的WebSocket连接
- 可以穿透HTTP代理和CDN
- 使用标准HTTPS端口
- 适合企业环境和防火墙限制场景
### QUIC连接池架构
当启用`type=1`时,NodePass使用QUIC协议进行连接池管理,具有以下特性:
**流多路复用**
- 单个UDP连接承载多个双向流
- 每个流代表一个独立的隧道连接
- 流是独立的:一个流的丢包不影响其他流
- 流级别的流量控制防止队头阻塞
**连接建立**
- 服务器监听UDP端口并接受QUIC连接
- 客户端与服务器建立单个QUIC连接
- 服务器为每个传入连接生成唯一的流ID
- 客户端使用提供的流ID按需打开新流
**流生命周期**
1. **流创建**(服务器端):
- 服务器接受来自授权客户端的新QUIC连接
- 对于每个目标连接,服务器打开双向流
- 服务器生成4字节随机流ID
- 流ID发送给客户端用于关联
2. **流检索**(客户端):
- 客户端通过控制通道信号接收流ID
- 客户端从QUIC连接中检索相应的流
- 流被包装为`net.Conn`以保持兼容性
- 流用于与目标端点进行数据交换
3. **流终止**
- 数据交换完成后关闭流
- 优雅关闭并正确清理
- QUIC连接保持活跃以供未来的流使用
**动态管理**
- 基于流创建成功率调整连接池容量
- 流创建间隔根据连接池利用率自适应
- 在最小/最大边界内自动流容量扩缩
- 保活机制维护QUIC连接健康
**安全特性**
- 所有QUIC连接强制使用TLS 1.3加密
- 支持三种TLS模式:
- 模式0/1InsecureSkipVerify用于测试/开发
- 模式2:生产环境完整证书验证
- 服务器端可用客户端IP限制
- ALPN协议协商("np-quic"
**性能优势**
- 减少连接开销:所有流使用单个UDP套接字
- 0-RTT连接恢复实现更快重连
- 具有流级别优先级的更好拥塞控制
- 与多个TCP连接相比改善NAT穿透
- 在丢包场景中降低延迟(无队头阻塞)
### WebSocket连接池架构
当启用`type=2`时,NodePass使用WebSocket协议进行连接池管理,具有以下特性:
**HTTP升级机制**
- 通过标准HTTP升级请求建立WebSocket连接
- 兼容HTTP/1.1代理和CDN
- 使用标准的80(ws)或443(wss)端口
- 支持自定义HTTP头部用于身份验证和路由
**连接建立**
- 服务器在TCP端口上监听HTTP请求
- 客户端发起HTTP升级请求到WebSocket
- 握手完成后,连接升级为全双工WebSocket
- 服务器为每个传入连接分配唯一的连接ID
**连接生命周期**
1. **连接创建**(服务器端):
- 服务器接受来自授权客户端的HTTP升级请求
- 验证WebSocket握手参数(Origin、协议等)
- 升级连接并将其添加到连接池
- 生成连接ID发送给客户端用于关联
2. **连接检索**(客户端):
- 客户端通过控制通道接收连接ID
- 客户端从WebSocket连接池中检索对应连接
- 连接被包装为`net.Conn`以保持兼容性
- 连接用于与目标端点进行数据交换
3. **连接终止**
- 数据交换完成后发送WebSocket关闭帧
- 优雅关闭并正确清理底层TCP连接
- 支持关闭原因码和描述信息
**动态管理**
- 基于连接创建成功率调整连接池容量
- 连接创建间隔根据连接池利用率自适应
- 在最小/最大边界内自动连接容量扩缩
- Ping/Pong帧维护连接健康状态
**安全特性**
- 支持WSSWebSocket Secure)通过TLS加密
- WebSocket连接池需要启用TLS加密
- 两种TLS模式支持:
- 模式1:自签名证书的WSS
- 模式2:生产环境完整证书验证的WSS
- Origin验证防止跨站WebSocket劫持
- 支持自定义身份验证头部
**穿透优势**
- 使用标准HTTP/HTTPS端口,易于穿透防火墙
- 兼容企业HTTP代理和负载均衡器
- 可通过CDN和反向代理部署
- 与HTTP流量混合,降低检测和封锁风险
- 需要启用TLS加密以确保安全
### 设计哲学
连接池的设计遵循"预热优于冷启动"的原则,通过预先建立连接消除网络延迟。这种设计理念借鉴了现代高性能服务器的最佳实践,将连接建立的成本分摊到系统启动阶段,而非在关键路径上承担这一开销。
三种连接池类型共享这一理念,但实现方式不同:
- **TCP连接池**:预先建立多个独立TCP连接
- **QUIC连接池**:在单个QUIC连接上预先创建多个流
- **WebSocket连接池**:预先建立多个WebSocket连接
### 池设计
1. **池类型**
- **客户端池**:预先建立到远程端点的连接/流,采用主动式连接管理
- **服务器池**:管理来自客户端的传入连接/流,采用被动式连接接收
2. **池组件**
- **连接/流存储**:线程安全的连接ID到net.Conn对象的映射,支持高并发访问
- **ID通道**:用于可用连接ID的缓冲通道,实现无锁的快速分配
- **容量管理**:基于使用模式的动态调整,实现智能扩缩容
- 最小容量由客户端设置,确保客户端具备基础连接保障
- 最大容量由服务端在握手时统一下发,实现全局资源协调
- **间隔控制**:连接/流创建之间的基于时间的限流,防止网络资源过载
- **连接工厂**:可定制的连接创建函数
- TCP模式:标准TCP拨号和TLS握手
- QUIC模式:流管理和多路复用
- WebSocket模式:HTTP升级和握手处理
### 先进性设计
1. **零延迟连接**
- 预建立的连接池消除了TCP三次握手的延迟
- TLS握手在连接池初始化时完成,避免运行时加密协商开销
- 连接预热策略确保池中始终有可用的热连接
- **QUIC增强**0-RTT支持进一步降低重连延迟
2. **智能负载感知**
- 基于实时连接使用率的动态池管理
- 预测性连接创建,根据历史使用模式提前准备连接
- 自适应超时和重试机制,应对网络波动
### 连接生命周期
1. **连接创建**
- 连接/流创建数量不超过配置的容量,确保资源可控性
- 每个连接都分配一个唯一ID,支持精确的连接跟踪和管理
- ID和连接存储在池中,采用写时复制和延迟删除策略
- **TCP模式**:创建带有可选TLS的独立TCP连接
- **QUIC模式**:在共享QUIC连接上打开双向流
- **WebSocket模式**:通过HTTP升级建立WebSocket连接
2. **连接获取**
- 客户端使用连接ID检索连接,支持精确匹配和快速查找
- 服务端从池中检索下一个可用连接,采用轮询或最少使用策略
- 在返回前验证连接有效性,包括网络状态和TLS会话检查
3. **连接使用**
- 获取时从池中移除连接,避免重复使用冲突
- 用于端点之间的数据交换,采用高效的零拷贝传输
- 采用一次性使用模型,确保连接状态的干净性
4. **连接终止**
- 使用后立即关闭连接,避免资源泄漏
- 正确释放系统资源,包括文件描述符和内存缓冲区
- 错误处理确保异常情况下的干净终止
### 会话管理与状态维护
1. **有状态的UDP处理**
- 将无状态的UDP协议转换为有状态的会话处理
- 智能的会话超时管理,平衡资源使用和响应性
- 会话复用机制,减少连接建立开销
2. **TCP连接管理**
- 连接池管理,实现高效的资源利用
- 连接池条目的一次性使用模型,确保状态的干净性
- 连接健康监控和自动清理
3. **跨协议统一管理**
- 统一的连接生命周期管理,简化系统复杂性
- 协议无关的监控和统计,提供一致的观测体验
- 灵活的协议转换能力,支持异构网络环境
## 信号通信与协调机制
NodePass 的信号系统体现了分布式系统设计的精髓:
### 信号设计原理
1. **事件驱动架构**
- 基于事件的异步通信模式,避免阻塞等待
- 发布-订阅模式的信号分发,支持多订阅者
- 信号的优先级管理,确保关键事件的及时处理
2. **可靠性保障**
- 信号的持久化机制,防止关键信号丢失
- 重试和确认机制,确保信号的可靠传递
- 信号的幂等性设计,避免重复执行的副作用
3. **性能优化**
- 批量信号处理,减少系统调用开销
- 信号压缩和合并,优化网络带宽使用
- 异步信号处理,避免阻塞主处理流程
### 分布式协调
1. **一致性保证**
- 分布式锁机制,确保关键操作的原子性
- 状态同步协议,保持多节点间的数据一致性
- 冲突解决策略,处理并发操作的竞争条件
2. **故障处理**
- 节点故障检测,及时发现和隔离故障节点
- 自动故障转移,保证服务的连续性
- 状态恢复机制,支持故障后的快速恢复
### 池管理
1. **容量控制**
- 最小容量保证:确保始终有足够的预热连接可用
- 最大容量限制:防止过度资源消耗,保护系统稳定性
- 基于需求模式的动态缩放,响应流量变化
2. **间隔控制**
- 最小间隔限制:防止连接创建风暴,保护网络资源
- 最大间隔限制:确保及时响应连接需求
- 自适应基于时间的限流以优化资源使用
3. **动态池适应**
连接池采用双重自适应机制以确保最佳性能:
**A. 容量调整**
- 池容量根据实时使用模式动态调整,实现智能扩缩容
- 基于连接创建成功率的反馈调节:低成功率时收缩容量以减少资源浪费
- 高成功率时扩展容量以满足增长需求
- 渐进缩放防止系统震荡,提供平滑的性能过渡
- 严格遵守配置的容量边界,确保系统可控性
**B. 间隔调整**
- 创建间隔根据池空闲连接数实时调整
- 低空闲率时加速连接创建,确保供应充足
- 高空闲率时放缓创建节奏,避免资源浪费
- 防止在低需求期间对网络资源造成压力
- 在池耗尽的高需求期间加速连接创建,保证服务质量
4. **性能优化策略**
- **预测性扩容**:基于历史使用模式预测未来需求
- **分层连接管理**:不同优先级的连接采用不同的管理策略
- **批量操作优化**:连接的批量创建和销毁,减少系统调用开销
- **连接亲和性**:基于地理位置或网络拓扑的智能连接分配
## 数据交换机制
NodePass 的数据交换机制体现了现代网络编程的最佳实践:
### 高性能数据传输
1. **零拷贝架构**
- 数据在内核空间直接传输,避免用户空间的多次拷贝
- 减少CPU开销和内存带宽占用
- 支持大文件和高吞吐量场景的优化传输
2. **异步I/O模型**
- 非阻塞的事件驱动架构,最大化并发处理能力
- 基于epoll/kqueue的高效事件循环
- 智能的读写缓冲区管理,平衡内存使用和性能
3. **流量统计与监控**
- 实时的字节级流量统计,支持精确的带宽控制
- 分协议的流量分析,便于性能调优
- 连接级别的性能指标,支持细粒度监控
- 实时跟踪TCP和UDP活动连接数,便于容量规划和性能分析
### 协议优化
1. **TCP优化**
- 智能的TCP_NODELAY配置,减少小包延迟
- Keep-alive机制确保长连接的可靠性
- 拥塞控制算法的自适应选择
2. **UDP优化**
- 会话式UDP处理,支持有状态的数据报交换
- 智能超时管理,平衡响应性和资源使用
- 数据报去重和乱序处理
## 主控API架构
在主控模式下,NodePass提供RESTful API进行集中管理,体现了云原生架构的设计理念:
### 架构设计哲学
主控模式采用"统一管理,分布式执行"的架构模式,将控制平面与数据平面分离。这种设计使得系统具备了企业级的可管理性和可观测性,同时保持了数据传输的高性能。
### API组件
1. **HTTP/HTTPS服务器**
- 在配置的地址和端口上监听,支持灵活的网络部署
- 可选的TLS加密,与隧道服务器使用相同安全模式,确保管理通道的安全性
- 可配置的API前缀路径,支持反向代理和API网关集成
2. **实例管理**
- 基于内存的高性能实例注册表,支持快速查询和更新
- 基于UID的实例标识,确保全局唯一性
- 每个实例的状态跟踪(运行中、已停止等),支持实时状态监控
3. **RESTful端点**
- 标准CRUD操作,遵循REST设计原则
- 实例控制操作(启动、停止、重启),支持远程生命周期管理
- 健康状态报告,提供实时的系统健康信息
- OpenAPI规范支持,便于API文档生成和客户端开发
### 实例生命周期管理
1. **实例创建**
- 基于URL的配置,类似于命令行,降低学习成本
- 基于实例类型的动态初始化,支持多种部署模式
- 实例创建前的参数验证,确保配置正确性
2. **实例控制**
- 启动/停止/重启能力,支持远程运维操作
- 可配置超时的优雅关闭,确保数据完整性
- 终止时的资源清理,防止资源泄漏
3. **API安全**
- API连接的TLS加密选项,保护管理通信安全
- 与隧道服务端相同的安全模式,统一安全策略
- 证书管理支持,简化HTTPS部署
## 系统架构的先进性
### 分层解耦设计
NodePass 采用了现代软件架构的分层设计原则:
1. **传输层分离**
- 控制通道与数据通道的彻底分离,避免控制信息干扰数据传输
- 不同协议的独立优化,TCP和UDP各自采用最优策略
- 多路复用支持,单一隧道承载多个应用连接
2. **安全层可插拔**
- 模块化的TLS实现,支持不同安全级别的灵活选择
- 证书管理的自动化,减少运维复杂性
- 密钥轮换机制,增强长期安全性
3. **管理层云原生**
- API优先的设计理念,所有功能均可通过API访问
- 容器化友好的配置方式,支持现代DevOps实践
- 无状态设计,便于水平扩展
### 性能优化理念
1. **延迟优化**
- 预连接池消除冷启动延迟
- 智能路由减少网络跳数
- 批量处理减少系统调用开销
2. **吞吐量优化**
- 零拷贝数据传输最大化带宽利用
- 并发连接管理支持高并发场景
- 自适应缓冲区大小优化内存使用
3. **资源优化**
- 智能连接复用减少资源消耗
- 动态容量调整适应负载变化
- 垃圾回收优化减少暂停时间
### 可靠性保障
1. **故障隔离**
- 连接级别的故障隔离,单点故障不影响整体服务
- 自动重连机制,透明处理网络波动
- 优雅降级策略,在资源不足时保证核心功能
2. **状态管理**
- 分布式状态同步,确保多实例间的一致性
- 持久化关键状态,支持故障恢复
- 版本化配置管理,支持回滚操作
## NodePass 架构创新总结
### 技术创新点
1. **连接池预热技术**
- 革命性地消除了网络隧道的冷启动延迟
- 将传统的"按需建连"转变为"预热待用"
- 显著提升了首次连接的响应速度
2. **分离式架构设计**
- 控制平面与数据平面的彻底分离
- 信令通道与数据通道的独立优化
- 实现了高性能与高可管理性的完美结合
3. **自适应资源管理**
- 基于实时负载的智能扩缩容
- 预测性的资源分配策略
- 故障自愈的弹性系统设计
### 行业领先优势
1. **性能优势**
- 零延迟连接建立,业界领先的响应速度
- 高并发处理能力,支持企业级应用场景
- 智能路由优化,最短路径数据传输
2. **可靠性优势**
- 多层次的故障隔离和恢复机制
- 分布式架构的高可用保障
- 优雅降级的服务质量保证
3. **安全性优势**
- 端到端的加密保护
- 多层次的安全防护体系
- 符合企业级安全标准
### 适用场景与价值
1. **企业级应用**
- 微服务架构的服务网格
- 混合云环境的网络连接
- 跨地域的服务访问
2. **开发运维**
- 本地开发环境的快速搭建
- 测试环境的灵活配置
- 生产环境的流量管理
3. **网络优化**
- 网络延迟的大幅降低
- 带宽利用率的显著提升
- 连接稳定性的可靠保障
NodePass 通过其创新的架构设计和技术实现,为现代网络应用提供了一个高性能、高可靠、高安全的隧道解决方案,代表了网络隧道技术的发展方向。
## 下一步
- 有关部署NodePass的实际示例,请参阅[示例页面](/docs/zh/examples.md)
- 要根据您的特定需求优化NodePass,请探索[配置选项](/docs/zh/configuration.md)
- 如果遇到任何问题,请查看[故障排除指南](/docs/zh/troubleshooting.md)
-111
View File
@@ -1,111 +0,0 @@
# 安装指南
本指南提供了使用不同方法安装 NodePass 的详细说明。选择最适合您环境和需求的安装方式。
## 系统要求
- Go 1.25或更高版本(从源代码构建时需要)
- 服务器和客户端端点之间的网络连接
- 绑定1024以下端口可能需要管理员权限
## 安装方法
### 方式1:预编译二进制文件
开始使用 NodePass 的最简单方法是为您的平台下载预编译的二进制文件。
1. 访问 GitHub 上的[发布页面](https://github.com/NodePassProject/nodepass/releases)
2. 下载适合您操作系统的二进制文件(Windows、macOS、Linux)
3. 如有必要,解压缩档案
4. 使二进制文件可执行(Linux/macOS)
```bash
chmod +x nodepass
```
5. 将二进制文件移动到PATH中的位置:
- Linux/macOS`sudo mv nodepass /usr/local/bin/`
- Windows:将位置添加到PATH环境变量
### 方式2:使用Go安装
如果您的系统上已安装Go,可以使用`go install`命令:
```bash
go install github.com/NodePassProject/nodepass/cmd/nodepass@latest
```
此命令下载源代码,编译它,并将二进制文件安装到您的Go bin目录中(通常是`$GOPATH/bin`)。
### 方式3:从源代码构建
对于最新的开发版本或自定义构建:
```bash
# 克隆仓库
git clone https://github.com/NodePassProject/nodepass.git
# 导航到项目目录
cd nodepass
# 构建二进制文件
go build -o nodepass ./cmd/nodepass
# 可选:安装到GOPATH/bin
go install ./cmd/nodepass
```
### 方式4:使用容器镜像
NodePass在GitHub容器注册表中提供容器镜像,非常适合容器化环境:
```bash
# 拉取容器镜像
docker pull ghcr.io/NodePassProject/nodepass:latest
# 服务器模式运行
docker run -d --name nodepass-server -p 10101:10101 -p 8080:8080 \
ghcr.io/NodePassProject/nodepass server://0.0.0.0:10101/0.0.0.0:8080
# 客户端模式运行
docker run -d --name nodepass-client \
-e NP_MIN_POOL_INTERVAL=200ms \
-e NP_SEMAPHORE_LIMIT=512 \
-p 8080:8080 \
ghcr.io/NodePassProject/nodepass "client://nodepass-server:10101/127.0.0.1:8080?min=32&max=512"
```
### 方式5:使用管理脚本(仅限Linux)
对于Linux系统,我们提供了一键脚本:
```bash
bash <(curl -sSL https://run.nodepass.eu/np.sh)
```
- 本脚本提供了简单易用的 master 模式,即 API 模式的安装、配置和管理功能。
- 详情请参阅[https://github.com/NodePassProject/npsh](https://github.com/NodePassProject/npsh)
## 验证安装
安装后,通过检查版本来验证NodePass是否正确安装:
```bash
nodepass
```
## 下一步
安装NodePass后,您可以:
- 了解基本[使用方法](/docs/zh/usage.md)
- 探索[配置选项](/docs/zh/configuration.md)
- 尝试一些[使用示例](/docs/zh/examples.md)
## 安装问题故障排除
如果在安装过程中遇到任何问题:
- 确保您的系统满足最低要求
- 检查是否具有安装软件的正确权限
- 对于Go相关问题,使用`go version`验证您的Go安装
- 对于容器相关问题,确保Docker正确安装并运行
- 查看我们的[故障排除指南](/docs/zh/troubleshooting.md)获取更多帮助
-587
View File
@@ -1,587 +0,0 @@
# 故障排除指南
本指南帮助您诊断并解决使用NodePass时可能遇到的常见问题。对于每个问题,我们提供可能的原因和逐步解决方案。
## 连接问题
### 无法建立隧道连接
**症状**:客户端无法连接到服务器的隧道端点,或连接立即断开。
**可能的原因和解决方案**
1. **网络连接问题**
- 使用`ping``telnet`验证与服务器地址的基本连接
- 检查指定的端口是否可达:`telnet server.example.com 10101`
- 确保没有防火墙阻止隧道端口(通常为10101)
2. **服务器未运行**
- 在Linux/macOS上使用`ps aux | grep nodepass`验证NodePass服务器是否运行
- 检查服务器日志中的任何启动错误
- 尝试重启服务器进程
3. **地址错误**
- 仔细检查客户端命令中的隧道地址格式
- 确保使用了正确的主机名/IP和端口
- 如果使用DNS名称,验证它们是否解析为正确的IP地址
4. **TLS配置不匹配**
- 如果服务器需要TLS但客户端不支持,连接将失败
- 检查服务器日志中的TLS握手错误
- 如果使用TLS模式2,确保证书配置正确
### 数据未通过隧道流动
**症状**:隧道连接已建立,但应用程序数据未到达目的地。
**可能的原因和解决方案**
1. **目标服务未运行**
- 验证目标服务在服务器和客户端两侧是否运行
- 检查是否可以在本地直接连接到该服务
2. **端口冲突**
- 确保目标端口没有被其他应用程序占用
- 使用`netstat -tuln`检查端口使用情况
3. **协议不匹配**
- 验证您是否在隧道传输正确的协议(TCP与UDP)
- 某些应用程序需要特定的协议支持
4. **目标地址错误**
- 仔细检查服务器和客户端命令中的目标地址
- 对于服务器端目标,确保它们可从服务器访问
- 对于客户端目标,确保它们可从客户端访问
### 连接稳定性问题
**症状**:隧道最初工作但频繁断开或变得无响应。
**可能的原因和解决方案**
1. **网络不稳定**
- 检查您的网络中是否有数据包丢失或高延迟
- 考虑为生产部署使用更稳定的网络连接
2. **资源限制**
- 监控客户端和服务器的CPU和内存使用情况
- 如果资源耗尽,调整池参数(参见性能部分)
- 在Linux/macOS上使用`ulimit -n`检查文件描述符限制
3. **超时配置**
- 如果使用具有慢响应时间的UDP,调整`NP_UDP_DIAL_TIMEOUT`
- 增加URL中的`read`参数用于长时间传输(默认:0
- 考虑为不稳定网络条件调整`NP_TCP_DIAL_TIMEOUT`
4. **服务器过载**
- 检查服务器日志中的连接过载迹象
- 调整`max`参数和`NP_SEMAPHORE_LIMIT`以处理负载
- 考虑用多个NodePass实例水平扩展
## 证书问题
### TLS握手失败
**症状**:连接尝试因TLS握手错误而失败。
**可能的原因和解决方案**
1. **无效证书**
- 验证证书有效性:`openssl x509 -in cert.pem -text -noout`
- 确保证书没有过期
- 检查证书是否针对正确的域名/IP颁发
2. **证书文件丢失或无法访问**
- 确认证书和密钥的文件路径正确
- 验证文件权限允许NodePass进程读取它们
- 通过文本编辑器打开证书检查文件是否损坏
3. **证书信任问题**
- 如果使用自定义CA,确保它们被正确信任
- 对于自签名证书,确认使用TLS模式1
- 对于验证证书,确保CA链完整
4. **密钥格式问题**
- 确保私钥格式正确(通常为PEM)
- 检查私钥是否有密码保护(不直接支持)
### 证书更新问题
**症状**:证书更新后,安全连接开始失败。
**可能的原因和解决方案**
1. **新证书未加载**
- 重启NodePass强制加载新证书
- 检查`RELOAD_INTERVAL`是否设置正确以自动检测变更
2. **证书链不完整**
- 确保证书文件中包含完整的证书链
- 验证链顺序:首先是您的证书,然后是中间证书
3. **密钥不匹配**
- 验证新证书是否与私钥匹配:
```bash
openssl x509 -noout -modulus -in cert.pem | openssl md5
openssl rsa -noout -modulus -in key.pem | openssl md5
```
- 如果输出不同,证书和密钥不匹配
## 性能优化
### 高延迟
**症状**:连接工作但有明显延迟。
**可能的原因和解决方案**
1. **池配置**
- 增加`min`参数以准备更多连接
- 减少`MIN_POOL_INTERVAL`以更快创建连接
- 如果连接队列堆积,调整`NP_SEMAPHORE_LIMIT`
2. **网络路径**
- 检查网络拥塞或高延迟链路
- 考虑将NodePass部署在更靠近客户端或服务器的位置
- 使用traceroute识别潜在瓶颈
3. **TLS开销**
- 如果需要极低延迟且安全性不太重要,考虑使用TLS模式0
- 为了平衡,使用带会话恢复的TLS模式1
4. **资源竞争**
- 确保主机系统有足够的CPU和内存
- 检查是否有其他进程竞争资源
- 考虑为高流量部署使用专用主机
### CPU使用率高
**症状**NodePass进程消耗过多CPU资源。
**可能的原因和解决方案**
1. **池抖动**
- 如果池不断创建和销毁连接,调整时间
- 增加`MIN_POOL_INTERVAL`以减少连接创建频率
- 为`min``max`连接池参数找到良好平衡
2. **过度日志记录**
- 在生产环境中将日志级别从debug降低到info或warn
- 检查日志是否写入缓慢设备
3. **TLS开销**
- TLS握手需要大量CPU;考虑会话缓存
- 如果证书验证不太重要,使用TLS模式1而不是模式2
4. **流量体积**
- 高吞吐量可能导致CPU饱和
- 考虑跨多个NodePass实例分配流量
- 对于非常高的吞吐量,可能需要垂直扩展(更多CPU核心)
### 内存泄漏
**症状**:NodePass内存使用随时间持续增长。
**可能的原因和解决方案**
1. **连接泄漏**
- 确保`NP_SHUTDOWN_TIMEOUT`足够长以正确关闭连接
- 检查自定义脚本或管理代码中的错误处理
- 使用系统工具如`netstat`监控连接数量
2. **池大小问题**
- 如果`max`参数非常大,内存使用会更高
- 监控实际池使用情况与配置容量
- 根据实际并发连接需求调整容量
3. **调试日志**
- 在高流量场景中,大量调试日志可能消耗内存
- 在生产环境中使用适当的日志级别
## UDP特定问题
### UDP数据丢失
**症状**:UDP数据包无法通过隧道可靠转发。
**可能的原因和解决方案**
1. **缓冲区大小限制**
- 如果UDP数据包较大,增加`UDP_DATA_BUF_SIZE`
- 默认8192字节对某些应用程序可能太小
2. **超时问题**
- 如果响应较慢,增加`NP_UDP_DIAL_TIMEOUT`
- 调整`read`参数以获得更长的会话超时
- 对于响应时间变化的应用程序,找到最佳平衡点
3. **高数据包率**
- UDP一次处理一个数据报;非常高的速率可能导致问题
- 考虑为高流量UDP应用增加池容量
4. **协议期望**
- 一些UDP应用期望特定的数据包顺序或时序行为
- NodePass提供尽力转发,但无法保证超出网络提供的UDP属性
### UDP连接跟踪
**症状**:UDP会话过早断开或无法建立。
**可能的原因和解决方案**
1. **连接映射**
- 验证客户端配置是否符合服务器期望
- 检查防火墙是否超时UDP会话跟踪
2. **应用UDP超时**
- 一些应用有内置UDP会话超时
- 可能需要调整应用特定的keepalive设置
## DNS问题
### DNS解析失败
**症状**:连接失败,显示"no such host"或DNS查找错误。
**解决方案**
1. **验证系统DNS配置**
- 验证解析功能:`nslookup example.com`
- 检查系统的DNS设置(NodePass使用系统解析器)
- 确保网络连接正常工作
2. **网络连接问题**
- 检查防火墙是否阻止UDP端口53
- 验证域名可达性
- 使用其他域名测试以隔离问题
### DNS缓存问题
**症状**:解析返回过期IP,连接到错误端点。
**解决方案**
1. **调整缓存TTL**(默认5分钟)
- 动态环境:`dns=1m`
- 稳定环境:`dns=30m`
2. **负载均衡场景**
- 使用较短TTL`dns=30s`
- 或直接使用IP地址避免DNS缓存
### DNS性能优化
**症状**:连接延迟高,启动缓慢。
**解决方案**
1. **优化缓存TTL**
- 稳定环境增加TTL`dns=1h`
- 动态环境减少TTL`dns=1m`
- 在新鲜度和性能之间取得平衡
2. **减少DNS查询**
- 性能关键场景直接使用IP地址
- 为稳定主机名增加TTL
- 尽可能预解析地址
## 主控API问题
### API可访问性问题
**症状**:无法连接到主控API端点。
**可能的原因和解决方案**
1. **端点配置**
- 验证主控命令中的API地址和端口
- 检查API服务器是否绑定到正确的网络接口
2. **TLS配置**
- 如果使用HTTPS(TLS模式1或2),确保客户端工具支持TLS
- 对于测试,使用`curl -k`跳过证书验证
3. **自定义前缀问题**
- 如果使用自定义API前缀,确保所有请求中都包含它
- 检查API客户端和脚本中的URL格式
### 实例管理失败
**症状**:无法通过API创建、控制或删除实例。
**可能的原因和解决方案**
1. **JSON格式问题**
- 验证请求体是有效的JSON
- 检查API请求中的必填字段
2. **URL解析问题**
- 确保实例URL格式正确,必要时进行URL编码
- 验证URL参数使用正确格式
3. **实例状态冲突**
- 无法删除运行中的实例,必须先停止
- 在执行操作前先用GET检查当前实例状态
4. **权限问题**
- 确保NodePass主控具有创建进程的足够权限
- 检查任何引用的证书或密钥的文件系统权限
## 数据恢复
### 主控状态文件损坏
**症状**:主控模式启动失败,显示状态文件损坏错误,或实例数据丢失。
**可能的原因和解决方案**
1. **使用自动备份文件恢复**
- NodePass每小时自动创建备份文件 `nodepass.gob.backup`
- 停止NodePass主控服务
- 将备份文件复制为主文件:`cp nodepass.gob.backup nodepass.gob`
- 重新启动主控服务
2. **手动状态文件恢复**
```bash
# 停止NodePass服务
pkill nodepass
# 备份损坏的文件(可选)
mv nodepass.gob nodepass.gob.corrupted
# 使用备份文件
cp nodepass.gob.backup nodepass.gob
# 重新启动服务
nodepass "master://0.0.0.0:9090?log=info"
```
3. **备份文件也损坏时**
- 删除损坏的状态文件:`rm nodepass.gob*`
- 重新启动主控,将创建新的状态文件
- 需要重新配置所有实例和设置
4. **预防性备份建议**
- 定期备份 `nodepass.gob` 到外部存储
- 调整备份频率:设置环境变量 `export NP_RELOAD_INTERVAL=30m`
- 监控状态文件大小,异常增长可能表示问题
**最佳实践**
- 在生产环境中,建议将 `nodepass.gob` 定期备份到不同的存储位置
- 使用配置管理工具保存实例配置的文本形式备份
## 连接池类型问题
### QUIC连接池连接失败
**症状**:启用`type=1`时QUIC连接池隧道无法建立。
**可能的原因和解决方案**
1. **UDP端口被阻止**
- 验证服务器和客户端的UDP端口可访问
- 检查防火墙规则:`sudo ufw allow 10101/udp`Linux示例)
- 使用`nc -u server.example.com 10101`测试UDP连接性
- 某些ISP或网络会阻止或限制UDP流量
2. **TLS配置问题**
- QUIC需要启用TLS(至少`tls=1`
- 如果设置了`type=1`但禁用TLS,系统会自动启用`tls=1`
- 生产环境使用`tls=2`和有效证书
- 检查QUIC连接的证书有效性
3. **客户端-服务器连接池类型不匹配**
- 服务器和客户端必须使用相同的`type`设置
- 带`type=1`的服务器需要带`type=1`的客户端
- 带`type=0`的服务器需要带`type=0`的客户端
- 检查日志中的"QUIC connection not available"错误
4. **模式兼容性**
- QUIC仅在双端握手模式(mode=2)下工作
- 单端转发模式(mode=1)不可用
- 如果模式不兼容,系统将回退到TCP连接池
### WebSocket连接池连接失败
**症状**:启用`type=2`时WebSocket连接池隧道无法建立。
**可能的原因和解决方案**
1. **HTTP/WebSocket端口被阻止**
- 验证TCP端口支持WebSocket协议访问
- 检查防火墙规则和代理配置
- 某些代理或CDN可能干扰WebSocket升级
- 使用WebSocket客户端工具测试连接性
2. **TLS配置问题**
- WebSocket SecureWSS)需要启用TLS(至少`tls=1`
- **WebSocket连接池不支持不加密模式** - type=2不允许使用tls=0
- 如果设置了`type=2`但禁用TLS,系统将自动强制使用`tls=1`
- 生产环境使用`tls=2`和有效证书
- 检查WSS连接的证书有效性
3. **客户端-服务器连接池类型不匹配**
- 服务器和客户端必须使用相同的`type`设置
- 带`type=2`的服务器需要带`type=2`的客户端
- 配置在握手时自动下发
- 检查日志中的"WebSocket connection not available"错误
### HTTP/2连接池连接失败
**症状**:启用`type=3`时HTTP/2连接池隧道无法建立。
**可能的原因和解决方案**
1. **TCP端口或HTTP/2协议被阻止**
- 验证TCP端口支持HTTP/2协议访问
- 检查防火墙规则和网络策略
- 某些网络可能阻止或检查HTTPS流量
- 使用支持HTTP/2的客户端工具测试连接性
2. **TLS配置问题**
- HTTP/2需要启用TLS(至少`tls=1`
- 如果设置了`type=3`但禁用TLS,系统将自动强制使用`tls=1`
- 生产环境使用`tls=2`和有效证书
- HTTP/2需要TLS 1.3且支持ALPN(应用层协议协商)
- 检查证书有效性和ALPN配置
3. **客户端-服务器连接池类型不匹配**
- 服务器和客户端必须使用相同的`type`设置
- 带`type=3`的服务器需要带`type=3`的客户端
- 配置在握手时自动下发
- 检查日志中的"HTTP/2 connection not available"错误
4. **模式兼容性**
- HTTP/2连接池仅在双端握手模式(mode=2)下工作
- 单端转发模式(mode=1)不可用
- 如果模式不兼容,系统将回退到TCP连接池
5. **HTTP/2协议协商失败**
- 验证ALPN扩展已启用并协商"h2"协议
- 某些较旧的TLS实现可能不支持ALPN
- 检查日志中的协议协商错误
- 确保两端都支持基于TLS的HTTP/2
### QUIC连接池性能问题
**症状**:QUIC连接池隧道性能低于预期或不如TCP连接池。
**可能的原因和解决方案**
1. **网络路径问题**
- 某些网络会降低UDP流量的优先级或进行流量整形
- 检查网络中间设备是否干扰QUIC
- 考虑使用TCP连接池(`type=0`)进行对比测试
- 监控丢包率 - QUIC在低丢包率下性能更好
2. **连接池容量配置**
- 增加`min``max`参数以获得更高吞吐量
- QUIC流共享单个UDP连接 - 需要足够容量
- 使用`log=debug`监控流利用率
- 在流数量和资源使用之间取得平衡
3. **证书开销**
- TLS 1.3握手(QUIC强制要求)可能增加初始延迟
- 使用0-RTT恢复实现更快重连
- 确保正确的证书链以避免验证延迟
4. **应用兼容性**
- 某些应用程序在QUIC流上可能无法达到最佳性能
- 使用TCP和QUIC连接池进行对比测试
- 对于需要严格顺序的应用考虑使用TCP连接池
### WebSocket连接池性能问题
**症状**:WebSocket连接池隧道性能低于预期。
**可能的原因和解决方案**
1. **代理/CDN开销**
- 通过代理的WebSocket连接可能增加延迟
- 检查中间代理是否缓冲流量
- 考虑使用TCP连接池(`type=0`)或QUIC连接池(`type=1`)进行对比测试
- 直接连接通常比代理连接性能更好
2. **帧开销**
- WebSocket协议为每条消息添加帧开销
- 较大的消息大小减少相对开销
- 监控帧大小并根据需要调整应用行为
- 在延迟和吞吐量之间取得平衡
3. **TLS握手开销**
- WSS需要为每个连接进行TLS握手
- 使用连接池来分摊握手成本
- 增加`min``max`参数以获得更好的性能
### QUIC流耗尽
**症状**:使用QUIC时出现"流不足"错误或连接超时。
**可能的原因和解决方案**
1. **连接池容量过低**
- 增加服务器端的`max`参数
- 增加客户端的`min`参数
- 在日志中监控活动流数量
- 默认容量对于高并发场景可能不足
2. **流泄漏**
- 检查应用是否正确关闭连接
- 监控流数量随时间的逐渐增长
- 重启实例以清除泄漏的流
- 检查应用代码的连接处理
3. **QUIC连接断开**
- 检查保活设置(通过`NP_REPORT_INTERVAL`配置)
- 监控"QUIC connection not available"错误
- NAT超时可能断开UDP连接 - 调整NAT设置
- 如果网络延迟高,增加连接超时
### 连接池类型选择
**何时使用QUIC连接池**`type=1`):
- 移动网络或频繁变化的网络条件
- 高延迟连接(卫星、长距离)
- NAT密集环境,UDP穿透性能更好
- 受益于流独立性的实时应用
- 0-RTT重连提供价值的场景
**何时使用WebSocket连接池**`type=2`):
- 需要穿透HTTP代理或CDN的场景
- 企业环境中仅允许HTTP/HTTPS流量
- 防火墙阻止原始TCP连接的环境
- 需要与现有网络基础设施兼容
- Web代理或VPN替代方案
**何时使用TCP连接池**`type=0`):
- 阻止或严重限制UDP流量的网络
- 需要严格TCP语义的应用
- 具有UDP限制的企业环境
- 最大兼容性要求
- 测试显示TCP性能更好时
**对比测试**
```bash
# 测试TCP连接池性能
nodepass "server://0.0.0.0:10101/backend:8080?type=0&mode=2&log=event"
nodepass "client://server:10101/127.0.0.1:8080?mode=2&log=event"
# 测试QUIC连接池性能
nodepass "server://0.0.0.0:10102/backend:8080?type=1&mode=2&log=event"
nodepass "client://server:10102/127.0.0.1:8081?mode=2&log=event"
# 测试WebSocket连接池性能
nodepass "server://0.0.0.0:10103/backend:8080?type=2&mode=2&log=event"
nodepass "client://server:10103/127.0.0.1:8082?mode=2&log=event"
```
监控流量统计并根据观察到的性能进行选择。
## 下一步
如果您遇到本指南未涵盖的问题:
- 查看[项目仓库](https://github.com/NodePassProject/nodepass)中的已知问题
- 将日志级别增加到`debug`以获取更详细信息
- 查看[工作原理](/docs/zh/how-it-works.md)部分以更好地理解内部机制
- 考虑加入社区讨论,从其他用户处获取帮助
-392
View File
@@ -1,392 +0,0 @@
# 使用说明
NodePass创建一个带有未加密TCP控制通道的隧道,并为数据交换提供可配置的TLS加密选项。本指南涵盖三种操作模式并说明如何有效地使用每种模式。
## 命令行语法
NodePass命令的一般语法是:
```bash
nodepass "<core>://<tunnel_addr>/<target_addr>?log=<level>&tls=<mode>&crt=<cert_file>&key=<key_file>&dns=<duration>&min=<min_pool>&max=<max_pool>&mode=<run_mode>&type=<pool_type>&dial=<source_ip>&read=<timeout>&rate=<mbps>&slot=<limit>&proxy=<mode>&notcp=<0|1>&noudp=<0|1>"
```
其中:
- `<core>`:指定运行模式(`server``client``master`
- `<tunnel_addr>`:用于控制通道通信的隧道端点地址
- `<target_addr>`:支持双向流的业务数据目标地址(或主控模式下的API前缀)
### 查询参数
通用查询参数:
- `log=<level>`:日志详细级别(`none``debug``info``warn``error``event`
- `dns=<duration>`DNS缓存TTL持续时间(默认:`5m`,支持时间单位如`1h``30m``15s`等)
- `min=<min_pool>`:最小连接池容量(默认:64,由客户端设置)
- `max=<max_pool>`:最大连接池容量(默认:1024,由服务端设置并下发给客户端)
- `mode=<run_mode>`:运行模式控制(`0``1``2`- 控制操作行为
- `type=<pool_type>`:连接池类型(`0`为TCP连接池,`1`为QUIC UDP连接池,`2`为WebSocket/WSS连接池,`3`为HTTP/2连接池,默认:0,仅服务端配置)
- `dial=<source_ip>`:出站连接的源IP地址(默认:`auto`,支持IPv4和IPv6
- `read=<timeout>`:数据读取超时时长(默认:0,支持时间单位如30s、5m、1h等)
- `rate=<mbps>`:带宽速率限制,单位Mbps(默认:0表示无限制)
- `slot=<limit>`:最大并发连接数限制(默认:65536,0表示无限制)
- `proxy=<mode>`PROXY协议支持(默认:`0``1`启用PROXY协议v1头部传输)
- `notcp=<0|1>`TCP支持控制(默认:`0`启用,`1`禁用)
- `noudp=<0|1>`UDP支持控制(默认:`0`启用,`1`禁用)
TLS相关参数(仅适用于server/master模式):
- `tls=<mode>`:数据通道的TLS安全级别(`0``1``2`
- `crt=<cert_file>`:证书文件路径(当`tls=2`时)
- `key=<key_file>`:私钥文件路径(当`tls=2`时)
连接池类型(仅服务端模式):
- `type=<mode>`:连接池类型(`0`为TCP连接池,`1`为QUIC UDP连接池,`2`为WebSocket/WSS连接池,默认:0
- 服务端配置在握手时自动下发给客户端
- 客户端无需指定type参数
## 运行模式
NodePass提供三种互补的运行模式,以适应各种部署场景。
### 服务端模式
服务端模式建立隧道控制通道,并支持双向数据流转发。
```bash
nodepass "server://<tunnel_addr>/<target_addr>?log=<level>&tls=<mode>&crt=<cert_file>&key=<key_file>&dns=<duration>&type=<pool_type>&max=<max_pool>&mode=<run_mode>&dial=<source_ip>&read=<timeout>&rate=<mbps>&slot=<limit>&proxy=<mode>&notcp=<0|1>&noudp=<0|1>"
```
#### 参数
- `tunnel_addr`:TCP隧道端点地址(控制通道),客户端将连接到此处(例如, 10.1.0.1:10101)
- `target_addr`:业务数据的目标地址,支持双向数据流模式(例如, 10.1.0.1:8080)
- `log`:日志级别(debug, info, warn, error, event)
- `dns`:DNS缓存TTL持续时间(默认:5m,支持时间单位如`1h``30m``15s`等)
- `type`:连接池类型 (0, 1, 2, 3)
- `0`:使用基于TCP的连接池(默认)
- `1`:使用基于QUIC的UDP连接池,支持流多路复用(需要TLS,至少`tls=1`
- `2`:使用基于WebSocket/WSS的连接池
- `3`:使用基于HTTP/2的连接池,支持多路复用流(需要TLS,至少`tls=1`
- 配置在握手时自动下发给客户端
- `tls`:目标数据通道的TLS加密模式 (0, 1, 2)
- `0`:无TLS加密(明文TCP/UDP
- `1`:自签名证书(自动生成)
- `2`:自定义证书(需要`crt``key`参数)
- `crt`:证书文件路径(当`tls=2`时必需)
- `key`:私钥文件路径(当`tls=2`时必需)
- `max`:最大连接池容量(默认:1024
- `mode`:数据流方向的运行模式控制
- `0`:自动检测(默认)- 首先尝试本地绑定,如果不可用则回退
- `1`:强制反向模式 - 服务器本地绑定目标地址并接收流量
- `2`:强制正向模式 - 服务器连接到远程目标地址
- `dial`:连接目标的出站源IP地址(默认:`auto`为系统选择的IP
- `read`:数据读取超时时间(默认:0,支持时间单位如 30s、30m、1h 等)
- `rate`:带宽速率限制(默认:0,表示无限制)
- `slot`:最大并发连接数限制(默认:65536,0表示无限制)
- `proxy`PROXY协议支持(默认:`0``1`在数据传输前启用PROXY协议v1头部)
- `notcp`TCP支持控制(默认:`0`启用,`1`禁用)
- `noudp`UDP支持控制(默认:`0`启用,`1`禁用)
#### 服务端模式工作原理
服务端模式通过`mode`参数支持自动模式检测或强制模式选择:
**模式0:自动检测**(默认)
- 首先尝试本地绑定`target_addr`
- 如果成功,以反向模式运行(服务端接收流量)
- 如果绑定失败,以正向模式运行(服务端发送流量)
**模式1:反向模式**(服务端接收流量)
1. 在`tunnel_addr`上监听TCP隧道连接(控制通道)
2. 绑定并在`target_addr`上监听传入的TCP和UDP流量
3. 当`target_addr`收到连接时,通过控制通道向已连接的客户端发送信号
4. 为每个连接创建具有指定TLS加密级别的数据通道
**模式2:正向模式**(服务端发送流量)
1. 在`tunnel_addr`上监听TCP隧道连接(控制通道)
2. 等待客户端在其本地监听,并通过隧道接收连接
3. 建立到远程`target_addr`的连接并转发数据
#### 示例
```bash
# 自动模式检测,无TLS加密
nodepass "server://10.1.0.1:10101/10.1.0.1:8080?log=debug&tls=0"
# 强制反向模式,自签名证书
nodepass "server://10.1.0.1:10101/10.1.0.1:8080?log=debug&tls=1&mode=1"
# 强制正向模式,自定义证书
nodepass "server://10.1.0.1:10101/192.168.1.100:8080?log=debug&tls=2&mode=2&crt=/path/to/cert.pem&key=/path/to/key.pem"
# QUIC连接池,自动启用TLS
nodepass "server://10.1.0.1:10101/192.168.1.100:8080?log=debug&type=1&mode=2"
# WebSocket连接池,使用自定义证书
nodepass "server://10.1.0.1:10101/192.168.1.100:8080?log=debug&type=2&tls=2&mode=2&crt=/path/to/cert.pem&key=/path/to/key.pem"
# HTTP/2连接池,自动启用TLS
nodepass "server://10.1.0.1:10101/192.168.1.100:8080?log=debug&type=3&mode=2&tls=1"
```
### 客户端模式
客户端模式连接到NodePass服务端并支持双向数据流转发。
```bash
nodepass "client://<tunnel_addr>/<target_addr>?log=<level>&dns=<duration>&min=<min_pool>&mode=<run_mode>&dial=<source_ip>&read=<timeout>&rate=<mbps>&slot=<limit>&proxy=<mode>&notcp=<0|1>&noudp=<0|1>"
```
#### 参数
- `tunnel_addr`:要连接的NodePass服务端隧道端点地址(例如, 10.1.0.1:10101)
- `target_addr`:业务数据的目标地址,支持双向数据流模式(例如, 127.0.0.1:8080)
- `log`:日志级别(debug, info, warn, error, event)
- `dns`:DNS缓存TTL持续时间(默认:5m,支持时间单位如`1h``30m``15s`等)
- `min`:最小连接池容量(默认:64
- `mode`:客户端行为的运行模式控制
- `0`:自动检测(默认)- 首先尝试本地绑定,如果失败则回退到握手模式
- `1`:强制单端转发模式 - 带连接池的本地代理
- `2`:强制双端握手模式 - 需要服务器协调
- `dial`:连接目标的出站源IP地址(默认:`auto`为系统选择的IP
- `read`:数据读取超时时间(默认:0,支持时间单位如30s、5m、1h等)
- `rate`:带宽速率限制,单位Mbps(默认:0表示无限制)
- `slot`:最大并发连接数限制(默认:65536,0表示无限制)
- `proxy`PROXY协议支持(默认:`0``1`在数据传输前启用PROXY协议v1头部)
- `notcp`TCP支持控制(默认:`0`启用,`1`禁用)
- `noudp`UDP支持控制(默认:`0`启用,`1`禁用)
**注意**:连接池类型配置在握手时自动从服务器接收。客户端无需指定`type`参数。
#### 客户端模式工作原理
客户端模式通过`mode`参数支持自动模式检测或强制模式选择:
**模式0:自动检测**(默认)
- 首先尝试本地绑定`tunnel_addr`
- 如果成功,以单端转发模式运行
- 如果绑定失败,以双端握手模式运行
**模式1:单端转发模式**
1. 在本地隧道地址上监听TCP和UDP连接
2. 使用连接池技术预建立到目标地址的TCP连接,消除连接延迟
3. 直接将接收到的流量转发到目标地址,实现高性能转发
4. 无需与服务端握手,实现点对点的直接转发
5. 适用于本地代理和简单转发场景
**模式2:双端握手模式**
- **客户端接收流量**(当服务端发送流量时)
1. 连接到服务端的TCP隧道端点(控制通道)
2. 在本地监听端口,等待通过隧道传入的连接
3. 建立到本地`target_addr`的连接并转发数据
- **客户端发送流量**(当服务端接收流量时)
1. 连接到服务端的TCP隧道端点(控制通道)
2. 通过控制通道监听来自服务端的信号
3. 当收到信号时,使用服务端指定的TLS安全级别建立数据连接
4. 建立到`target_addr`的连接并转发流量
#### 示例
```bash
# 自动模式检测 - 本地代理监听1080端口,转发到目标服务器
nodepass "client://127.0.0.1:1080/target.example.com:8080?log=debug"
# 强制单端转发模式 - 高性能本地代理
nodepass "client://127.0.0.1:1080/target.example.com:8080?mode=1&log=debug"
# 强制双端握手模式 - 连接到NodePass服务端并采用其TLS安全策略
nodepass "client://server.example.com:10101/127.0.0.1:8080?mode=2"
# 使用调试日志和自定义连接池容量连接
nodepass "client://server.example.com:10101/192.168.1.100:8080?log=debug&min=128"
# 强制模式的资源受限配置
nodepass "client://server.example.com:10101/127.0.0.1:8080?mode=2&min=16&log=info"
# 资源受限配置 - 小型连接池
nodepass "client://server.example.com:10101/127.0.0.1:8080?min=16&log=info"
# 客户端自动从服务器接收连接池类型配置(无需type参数)
nodepass "client://server.example.com:10101/127.0.0.1:8080?mode=2&min=128&log=debug"
# 实时应用客户端(从服务器获取连接池类型配置)
nodepass "client://server.example.com:10101/127.0.0.1:7777?mode=2&min=64&read=30s"
```
### 主控模式 (API)
主控模式运行RESTful API服务器,用于集中管理NodePass实例。
```bash
nodepass "master://<api_addr>[<prefix>]?log=<level>&tls=<mode>&crt=<cert_file>&key=<key_file>"
```
#### 参数
- `api_addr`:API服务监听的地址(例如,0.0.0.0:9090
- `prefix`:可选的API前缀路径(例如,/management)。默认为`/api`
- `log`:日志级别(debug, info, warn, error, event)
- `tls`API服务的TLS加密模式(0, 1, 2)
- `0`:无TLS加密(HTTP
- `1`:自签名证书(带自动生成证书的HTTPS)
- `2`:自定义证书(带提供证书的HTTPS
- `crt`:证书文件路径(当`tls=2`时必需)
- `key`:私钥文件路径(当`tls=2`时必需)
#### 主控模式工作原理
在主控模式下,NodePass
1. 运行一个RESTful API服务器,允许动态管理NodePass实例
2. 提供用于创建、启动、停止和监控客户端和服务端实例的端点
3. 包含用于轻松API探索的Swagger UI,位于`{prefix}/v1/docs`
4. 自动继承通过API创建的实例的TLS和日志设置
#### API端点
所有端点都是相对于配置的前缀(默认:`/api`):
**受保护的端点(需要API Key):**
- `GET {prefix}/v1/instances` - 列出所有实例
- `POST {prefix}/v1/instances` - 创建新实例,JSON请求体: `{"url": "server://0.0.0.0:10101/0.0.0.0:8080"}`
- `GET {prefix}/v1/instances/{id}` - 获取实例详情
- `PATCH {prefix}/v1/instances/{id}` - 更新实例,JSON请求体: `{"action": "start|stop|restart"}`
- `DELETE {prefix}/v1/instances/{id}` - 删除实例
- `GET {prefix}/v1/events` - 服务端发送事件流(SSE
- `GET {prefix}/v1/info` - 获取系统信息
**公共端点(无需API Key):**
- `GET {prefix}/v1/openapi.json` - OpenAPI规范
- `GET {prefix}/v1/docs` - Swagger UI文档
#### 示例
```bash
# 启动HTTP主控服务(使用默认API前缀/api)
nodepass "master://0.0.0.0:9090?log=info"
# 启动带有自定义API前缀的主控服务(/management
nodepass "master://0.0.0.0:9090/management?log=info"
# 启动HTTPS主控服务(自签名证书)
nodepass "master://0.0.0.0:9090/admin?log=info&tls=1"
# 启动HTTPS主控服务(自定义证书)
nodepass "master://0.0.0.0:9090?log=info&tls=2&crt=/path/to/cert.pem&key=/path/to/key.pem"
```
## 管理NodePass实例
### 通过API创建和管理
NodePass主控模式提供RESTful API来管理实例,所有API请求都需要使用API Key进行身份验证。
#### API Key获取
启动主控模式后,系统会自动生成API Key并在日志中显示:
```bash
# 启动主控模式
nodepass "master://0.0.0.0:9090?log=info"
# 日志输出中会显示:
# INFO: API Key created: abc123def456...
```
#### API请求示例
所有受保护的API端点都需要在请求头中包含`X-API-Key`
```bash
# 获取API Key (假设为: abc123def456789)
# 通过API创建实例(使用默认前缀)
curl -X POST http://localhost:9090/api/v1/instances \
-H "X-API-Key: abc123def456789" \
-d '{"url":"server://0.0.0.0:10101/0.0.0.0:8080?tls=1"}'
# 使用自定义前缀
curl -X POST http://localhost:9090/admin/v1/instances \
-H "X-API-Key: abc123def456789" \
-d '{"url":"server://0.0.0.0:10101/0.0.0.0:8080?tls=1"}'
# 列出所有运行实例
curl http://localhost:9090/api/v1/instances \
-H "X-API-Key: abc123def456789"
# 控制实例(用实际实例ID替换{id})
curl -X PATCH http://localhost:9090/api/v1/instances/{id} \
-H "X-API-Key: abc123def456789" \
-d '{"action":"restart"}'
```
#### 公共端点
以下端点不需要API Key身份验证:
- `GET {prefix}/v1/openapi.json` - OpenAPI规范
- `GET {prefix}/v1/docs` - Swagger UI文档
## 双向数据流说明
NodePass支持灵活的双向数据流配置:
### 客户端单端转发模式
- **客户端**:在本地隧道地址监听,使用连接池技术直接转发到目标地址
- **连接池优化**:预建立TCP连接,消除连接延迟,提供高性能转发
- **无需服务端**:独立运行,不依赖服务端握手
- **使用场景**:本地代理、简单端口转发、测试环境、高性能转发
### 服务端接收模式
- **服务端**:在target_addr监听传入连接,通过隧道转发到客户端
- **客户端**:连接到本地target_addr提供服务
- **使用场景**:将内网服务暴露给外网访问
### 服务端发送模式
- **服务端**:连接到远程target_addr获取数据,通过隧道发送到客户端
- **客户端**:在本地监听,接收来自服务端的连接
- **使用场景**:通过隧道代理访问远程服务
系统会根据隧道地址和目标地址自动选择合适的操作模式:
- 如果客户端的隧道地址为本地地址,启用单端转发模式
- 如果目标地址是本地地址,使用服务端接收模式
- 如果目标地址是远程地址,使用服务端发送模式
## 隧道密钥(Tunnel Key
NodePass使用隧道密钥来验证客户端和服务端之间的连接。密钥可以通过两种方式指定:
### 密钥获取规则
1. **显式密钥**:在URL中指定用户名部分作为密钥
```bash
# 使用"mypassword"作为隧道密钥
nodepass server://mypassword@10.1.0.1:10101/10.1.0.1:8080
nodepass client://mypassword@10.1.0.1:10101/127.0.0.1:8080
```
2. **端口派生密钥**:如果未指定用户名,系统将使用端口号的十六进制值作为密钥
```bash
# 端口10101的十六进制值为"2775",将作为隧道密钥
nodepass server://10.1.0.1:10101/10.1.0.1:8080
nodepass client://10.1.0.1:10101/127.0.0.1:8080
```
### 握手流程
客户端与服务端的握手过程如下:
1. **客户端连接**:客户端连接到服务端的隧道地址
2. **密钥验证**:客户端发送加密的隧道密钥
3. **服务端验证**:服务端解密并验证密钥是否匹配
4. **配置同步**:验证成功后,服务端发送隧道配置信息,包括:
- 数据流向模式
- 最大连接池容量
- TLS安全模式
5. **连接确立**:握手完成,开始数据传输
这种设计确保了只有拥有正确密钥的客户端才能建立隧道连接,同时允许服务端统一管理连接池容量。
## 下一步
- 了解[配置选项](/docs/zh/configuration.md)以微调NodePass
- 探索常见部署场景的[使用示例](/docs/zh/examples.md)
- 理解NodePass内部[工作原理](/docs/zh/how-it-works.md)
- 如果遇到问题,请查看[故障排除指南](/docs/zh/troubleshooting.md)
+7 -8
View File
@@ -1,15 +1,14 @@
module github.com/NodePassProject/nodepass
go 1.25.0
go 1.25.6
require (
github.com/NodePassProject/cert v1.0.1
github.com/NodePassProject/conn v1.0.16
github.com/NodePassProject/logs v1.0.3
github.com/NodePassProject/nph2 v1.0.4
github.com/NodePassProject/npws v1.0.6
github.com/NodePassProject/pool v1.0.50
github.com/NodePassProject/quic v1.0.14
github.com/NodePassProject/conn v1.1.0
github.com/NodePassProject/logs v1.1.0
github.com/NodePassProject/nph2 v1.1.0
github.com/NodePassProject/npws v1.1.0
github.com/NodePassProject/pool v1.1.0
github.com/NodePassProject/quic v1.1.0
)
require (
+12 -14
View File
@@ -1,17 +1,15 @@
github.com/NodePassProject/cert v1.0.1 h1:BDy2tTOudy6yk7hvcmScAJMw4NrpCdSCsbuu7hHsIuw=
github.com/NodePassProject/cert v1.0.1/go.mod h1:wP7joOJeQAIlIuOUmhHPwMExjuwGa4XApMWQYChGSrk=
github.com/NodePassProject/conn v1.0.16 h1:ojHfyBveZMcyOikdUs1SOW4yKp92NOBnNhfNenLYljc=
github.com/NodePassProject/conn v1.0.16/go.mod h1:xfQ7ZLUxrtdLsljGHYYCToW+Hdg6DAbmL1Cs94n5h6E=
github.com/NodePassProject/logs v1.0.3 h1:CDUZVQ477vmmFQHazrQCWM0gJPNINm0C2N3FzC4jVyw=
github.com/NodePassProject/logs v1.0.3/go.mod h1:TwtPXOzLtb8iH+fdduQjEEywICXivsM39cy9AinMSks=
github.com/NodePassProject/nph2 v1.0.4 h1:szvp7hlvMRt1g/g1hF++0DfgM++5yqWWAPlfMDh226c=
github.com/NodePassProject/nph2 v1.0.4/go.mod h1:fzNbTk0zh+0gXer2aKpPXF69o4/BOd15Ys2gvicwWR0=
github.com/NodePassProject/npws v1.0.6 h1:LZ+ZqqZoZ7OnmdtEps/WHuq2S7Qda4nPtr+15tm0024=
github.com/NodePassProject/npws v1.0.6/go.mod h1:cyVS2X3/8f07yOkVEl53D+ozHLljJvHQx/s6xoLl/s8=
github.com/NodePassProject/pool v1.0.50 h1:Xmvb0hSHq24U06moHICr8CkqEHB4ZHBbY9Z+a7fSejQ=
github.com/NodePassProject/pool v1.0.50/go.mod h1:joQFk1oocg56QpJ1QK/2g5Jv/AyqYUQgPXMG1gWe8iA=
github.com/NodePassProject/quic v1.0.14 h1:zx5/cmQ8Tp1ntwCVAmtosODfBWTXTHHuprZdAspSjc8=
github.com/NodePassProject/quic v1.0.14/go.mod h1:JEHezuTn+AWN5NURNl2efa5lVqG6VQeutTBKhf9Y0T8=
github.com/NodePassProject/conn v1.1.0 h1:LfHdsIbYzbhrySJlapp3CD/d/KAY9J0k55K/euuB+Oc=
github.com/NodePassProject/conn v1.1.0/go.mod h1:aKRzuZAT55Ru04quTq4tKie/lNzO+VCsC9A6hX30fYQ=
github.com/NodePassProject/logs v1.1.0 h1:cjL5OHBMCoMYbpifK6YwtkE0vNrhQYiYuwEGLvAChm8=
github.com/NodePassProject/logs v1.1.0/go.mod h1:MyIUuNN1UhermLCUXhb9ddzijpa9AIu4zqOjyVZvIOI=
github.com/NodePassProject/nph2 v1.1.0 h1:cIdC4Mwk9G3n6TAh81sMuKYIt2J1/RZ8VBdNfWYeVAo=
github.com/NodePassProject/nph2 v1.1.0/go.mod h1:y4Jmq33RIgNBQqtgPm5+IYZse2+cxrlnsi0P4waKzJk=
github.com/NodePassProject/npws v1.1.0 h1:wi6F7UZgWgycWdnDfLznW4aaqSYdln5ZyN4B+/pf0f4=
github.com/NodePassProject/npws v1.1.0/go.mod h1:6jLkh7nz+/C5f2uc2RIOhm0HUzXoarXdSJw+P1i4izQ=
github.com/NodePassProject/pool v1.1.0 h1:xH+tBMnI1c8vW4glS2zKUaN9oxgNBiXttmnLlyYNCWg=
github.com/NodePassProject/pool v1.1.0/go.mod h1:SCM9h8wQniTxL+yK2+rbD/lcvU2QTEwPaZaC6+/DiWY=
github.com/NodePassProject/quic v1.1.0 h1:Y9c0n2uaOkvugToYwQz9vj1zKz2PrXNLiPWgUCfBVKc=
github.com/NodePassProject/quic v1.1.0/go.mod h1:ZRFcnZBHsdbl+VMoQtIomA+9W7tFWK9VRB3PelqD6no=
github.com/coder/websocket v1.8.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g=
github.com/coder/websocket v1.8.14/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+13 -35
View File
@@ -1,8 +1,8 @@
// 内部包,实现客户端模式功能
package internal
import (
"context"
"crypto/tls"
"encoding/json"
"fmt"
"io"
@@ -22,10 +22,8 @@ import (
"github.com/NodePassProject/quic"
)
// Client 实现客户端模式功能
type Client struct{ Common }
// NewClient 创建新的客户端实例
func NewClient(parsedURL *url.URL, logger *logs.Logger) (*Client, error) {
client := &Client{
Common: Common{
@@ -55,7 +53,6 @@ func NewClient(parsedURL *url.URL, logger *logs.Logger) (*Client, error) {
return client, nil
}
// Run 管理客户端生命周期
func (c *Client) Run() {
logInfo := func(prefix string) {
c.logger.Info("%v: client://%v@%v/%v?dns=%v&sni=%v&lbs=%v&min=%v&mode=%v&dial=%v&read=%v&rate=%v&slot=%v&proxy=%v&block=%v&notcp=%v&noudp=%v",
@@ -67,13 +64,10 @@ func (c *Client) Run() {
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
// 启动客户端服务并处理重启
go func() {
for ctx.Err() == nil {
// 启动客户端
if err := c.start(); err != nil && err != io.EOF {
c.logger.Error("Client error: %v", err)
// 重启客户端
c.stop()
select {
case <-ctx.Done():
@@ -85,11 +79,9 @@ func (c *Client) Run() {
}
}()
// 监听系统信号以优雅关闭
<-ctx.Done()
stop()
// 执行关闭过程
shutdownCtx, cancel := context.WithTimeout(context.Background(), shutdownTimeout)
defer cancel()
if err := c.shutdown(shutdownCtx, c.stop); err != nil {
@@ -99,22 +91,19 @@ func (c *Client) Run() {
}
}
// start 启动客户端服务
func (c *Client) start() error {
// 初始化上下文
c.initContext()
// 运行模式判断
switch c.runMode {
case "1": // 单端模式
case "1":
if err := c.initTunnelListener(); err == nil {
return c.singleStart()
} else {
return fmt.Errorf("start: initTunnelListener failed: %w", err)
}
case "2": // 双端模式
case "2":
return c.commonStart()
default: // 自动判断
default:
if err := c.initTunnelListener(); err == nil {
c.runMode = "1"
return c.singleStart()
@@ -125,7 +114,6 @@ func (c *Client) start() error {
}
}
// singleStart 启动单端转发模式
func (c *Client) singleStart() error {
if err := c.singleControl(); err != nil {
return fmt.Errorf("singleStart: singleControl failed: %w", err)
@@ -133,27 +121,22 @@ func (c *Client) singleStart() error {
return nil
}
// commonStart 启动双端握手模式
func (c *Client) commonStart() error {
// 发起隧道握手
c.logger.Info("Pending tunnel handshake...")
c.handshakeStart = time.Now()
if err := c.tunnelHandshake(); err != nil {
return fmt.Errorf("commonStart: tunnelHandshake failed: %w", err)
}
// 初始化连接池
if err := c.initTunnelPool(); err != nil {
return fmt.Errorf("commonStart: initTunnelPool failed: %w", err)
}
// 设置控制连接
c.logger.Info("Getting tunnel pool ready...")
if err := c.setControlConn(); err != nil {
return fmt.Errorf("commonStart: setControlConn failed: %w", err)
}
// 判断数据流向
if c.dataFlow == "+" {
if err := c.initTargetListener(); err != nil {
return fmt.Errorf("commonStart: initTargetListener failed: %w", err)
@@ -161,7 +144,6 @@ func (c *Client) commonStart() error {
go c.commonLoop()
}
// 启动共用控制
if err := c.commonControl(); err != nil {
return fmt.Errorf("commonStart: commonControl failed: %w", err)
}
@@ -169,7 +151,6 @@ func (c *Client) commonStart() error {
return nil
}
// initTunnelPool 初始化隧道连接池
func (c *Client) initTunnelPool() error {
switch c.poolType {
case "0":
@@ -243,20 +224,19 @@ func (c *Client) initTunnelPool() error {
return nil
}
// tunnelHandshake 与隧道服务端进行握手
func (c *Client) tunnelHandshake() error {
scheme := "http"
if c.serverPort == "443" {
scheme = "https"
}
// 构建请求
req, _ := http.NewRequest(http.MethodGet, scheme+"://"+c.tunnelAddr+"/", nil)
req, _ := http.NewRequest(http.MethodGet, "https://"+c.tunnelAddr+"/", nil)
req.Host = c.serverName
req.Header.Set("Authorization", "Bearer "+c.generateAuthToken())
// 发送请求
client := &http.Client{}
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
},
}
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("tunnelHandshake: %w", err)
@@ -267,7 +247,6 @@ func (c *Client) tunnelHandshake() error {
return fmt.Errorf("tunnelHandshake: status %d", resp.StatusCode)
}
// 解析配置
var config struct {
Flow string `json:"flow"`
Max int `json:"max"`
@@ -278,7 +257,6 @@ func (c *Client) tunnelHandshake() error {
return fmt.Errorf("tunnelHandshake: %w", err)
}
// 更新配置
c.dataFlow = config.Flow
c.maxPoolCapacity = config.Max
c.tlsCode = config.TLS
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+15 -35
View File
@@ -1,4 +1,3 @@
// 内部包,实现服务端模式功能
package internal
import (
@@ -17,7 +16,6 @@ import (
"syscall"
"time"
"github.com/NodePassProject/cert"
"github.com/NodePassProject/logs"
"github.com/NodePassProject/nph2"
"github.com/NodePassProject/npws"
@@ -25,10 +23,8 @@ import (
"github.com/NodePassProject/quic"
)
// Server 实现服务端模式功能
type Server struct{ Common }
// NewServer 创建新的服务端实例
func NewServer(parsedURL *url.URL, tlsCode string, tlsConfig *tls.Config, logger *logs.Logger) (*Server, error) {
server := &Server{
Common: Common{
@@ -60,7 +56,6 @@ func NewServer(parsedURL *url.URL, tlsCode string, tlsConfig *tls.Config, logger
return server, nil
}
// Run 管理服务端生命周期
func (s *Server) Run() {
logInfo := func(prefix string) {
s.logger.Info("%v: server://%v@%v/%v?dns=%v&lbs=%v&max=%v&mode=%v&type=%v&dial=%v&read=%v&rate=%v&slot=%v&proxy=%v&block=%v&notcp=%v&noudp=%v",
@@ -72,13 +67,10 @@ func (s *Server) Run() {
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
// 启动服务端并处理重启
go func() {
for ctx.Err() == nil {
// 启动服务端
if err := s.start(); err != nil && err != io.EOF {
s.logger.Error("Server error: %v", err)
// 重启服务端
s.stop()
select {
case <-ctx.Done():
@@ -90,11 +82,9 @@ func (s *Server) Run() {
}
}()
// 监听系统信号以优雅关闭
<-ctx.Done()
stop()
// 执行关闭过程
shutdownCtx, cancel := context.WithTimeout(context.Background(), shutdownTimeout)
defer cancel()
if err := s.shutdown(shutdownCtx, s.stop); err != nil {
@@ -104,31 +94,26 @@ func (s *Server) Run() {
}
}
// start 启动服务端
func (s *Server) start() error {
// 初始化上下文
s.initContext()
// 初始化隧道监听器
if err := s.initTunnelListener(); err != nil {
return fmt.Errorf("start: initTunnelListener failed: %w", err)
}
// 关闭UDP监听器
if s.tunnelUDPConn != nil {
s.tunnelUDPConn.Close()
}
// 运行模式判断
switch s.runMode {
case "1": // 反向模式
case "1":
if err := s.initTargetListener(); err != nil {
return fmt.Errorf("start: initTargetListener failed: %w", err)
}
s.dataFlow = "-"
case "2": // 正向模式
case "2":
s.dataFlow = "+"
default: // 自动判断
default:
if err := s.initTargetListener(); err == nil {
s.runMode = "1"
s.dataFlow = "-"
@@ -138,37 +123,31 @@ func (s *Server) start() error {
}
}
// 接受隧道握手
s.logger.Info("Pending tunnel handshake...")
s.handshakeStart = time.Now()
if err := s.tunnelHandshake(); err != nil {
return fmt.Errorf("start: tunnelHandshake failed: %w", err)
}
// 初始化连接池
if err := s.initTunnelPool(); err != nil {
return fmt.Errorf("start: initTunnelPool failed: %w", err)
}
// 设置控制连接
s.logger.Info("Getting tunnel pool ready...")
if err := s.setControlConn(); err != nil {
return fmt.Errorf("start: setControlConn failed: %w", err)
}
// 判断数据流向
if s.dataFlow == "-" {
go s.commonLoop()
}
// 启动共用控制
if err := s.commonControl(); err != nil {
return fmt.Errorf("start: commonControl failed: %w", err)
}
return nil
}
// initTunnelPool 初始化隧道连接池
func (s *Server) initTunnelPool() error {
switch s.poolType {
case "0":
@@ -213,40 +192,33 @@ func (s *Server) initTunnelPool() error {
return nil
}
// tunnelHandshake 与客户端进行HTTP握手
func (s *Server) tunnelHandshake() error {
var clientIP string
done := make(chan struct{})
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Connection", "close")
// 验证请求
if r.Method != http.MethodGet {
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
return
}
// 验证路径
if r.URL.Path != "/" {
http.Error(w, "Not Found", http.StatusNotFound)
return
}
// 验证令牌
auth := r.Header.Get("Authorization")
if !strings.HasPrefix(auth, "Bearer ") || !s.verifyAuthToken(strings.TrimPrefix(auth, "Bearer ")) {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// 记录客户端地址
clientIP = r.RemoteAddr
if host, _, err := net.SplitHostPort(clientIP); err == nil {
clientIP = host
}
// 发送配置
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]any{
"flow": s.dataFlow,
@@ -261,16 +233,24 @@ func (s *Server) tunnelHandshake() error {
close(done)
})
server := &http.Server{Handler: handler}
go server.Serve(s.tunnelListener)
tlsConfig := s.tlsConfig
if tlsConfig == nil {
tlsConfig, _ = NewTLSConfig()
}
server := &http.Server{
Handler: handler,
TLSConfig: tlsConfig,
ErrorLog: s.logger.StdLogger(),
}
go server.ServeTLS(s.tunnelListener, "", "")
select {
case <-done:
server.Close()
s.clientIP = clientIP
if s.tlsCode == "1" {
if newTLSConfig, err := cert.NewTLSConfig(""); err == nil {
if newTLSConfig, err := NewTLSConfig(); err == nil {
newTLSConfig.MinVersion = tls.VersionTLS13
s.tlsConfig = newTLSConfig
s.logger.Info("TLS code-1: RAM cert regenerated with TLS 1.3")
+2 -2
View File
@@ -5,12 +5,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=filebrowser
PKG_VERSION:=2.54.0
PKG_VERSION:=2.55.0
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/filebrowser/filebrowser/tar.gz/v${PKG_VERSION}?
PKG_HASH:=0752cc7444e2f327cb4beefe6eb40493bfd2f5a077daa2e5dbf6ace013cfc34d
PKG_HASH:=8a857d5ae7eac27c3a957610f0671943ba780ef947f15b83dfa7776f92495dbe
PKG_LICENSE:=Apache-2.0
PKG_LICENSE_FILES:=LICENSE
@@ -0,0 +1,588 @@
msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Project-Id-Version: \n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
"Last-Translator: https://github.com/ophub/luci-app-amlogic\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: ru_RU\n"
"X-Generator: Poedit 2.3.1\n"
msgid "Choose local file:"
msgstr "Выберите локальный файл:"
msgid "Couldn't open file:"
msgstr "Не удалось открыть файл:"
msgid "The file Will download automatically."
msgstr "Файл будет загружен автоматически."
msgid "Create upload file error."
msgstr "Ошибка создания загружаемого файла."
msgid "Download"
msgstr "Скачать"
msgid "Download file"
msgstr "Скачать файл"
msgid "File name"
msgstr "Имя файла"
msgid "File saved to"
msgstr "Файл сохранён в"
msgid "FileTransfer"
msgstr "Передача файлов"
msgid "Install"
msgstr "Установить"
msgid "Attributes"
msgstr "Атрибуты"
msgid "Modify time"
msgstr "Время изменения"
msgid "No specify upload file."
msgstr "Не указан файл для загрузки."
msgid "Path on Route:"
msgstr "Путь на маршрутизаторе:"
msgid "Remove"
msgstr "Удалить"
msgid "Size"
msgstr "Размер"
msgid "Upload"
msgstr "Загрузить"
msgid "Upload file list"
msgstr "Список загруженных файлов"
msgid "There are config file in the upload directory, and you can restore the config. "
msgstr "В каталоге загрузки есть файл конфигурации — вы можете восстановить настройки."
msgid "There are kernel files in the upload directory, and you can replace the kernel."
msgstr "В каталоге загрузки находятся файлы ядра — вы можете заменить ядро."
msgid "There are openwrt firmware file in the upload directory, and you can update the openwrt."
msgstr "В каталоге загрузки есть файл прошивки OpenWrt — вы можете обновить OpenWrt."
msgid "Update plugins first, then update the kernel or firmware."
msgstr "Сначала обновите плагины, затем ядро или прошивку."
msgid "After uploading [Firmware], [Kernel], [IPK] or [Backup Config], the operation buttons will be displayed."
msgstr "После загрузки [прошивки], [ядра], [IPK-пакета] или [резервной конфигурации] появятся соответствующие кнопки управления."
msgid "After uploading firmware (.img/.img.gz/.img.xz/.7z suffix) or kernel files (3 kernel files), the update button will be displayed."
msgstr "После загрузки файлов прошивки (с расширениями .img/.img.gz/.img.xz/.7z) или файлов ядра (3 файла), появится кнопка обновления."
msgid "Amlogic Service"
msgstr "Сервис Amlogic"
msgid "Restore Config / Replace OpenWrt Kernel"
msgstr "Восстановить конфигурацию / Заменить ядро OpenWrt"
msgid "Manually Upload Update"
msgstr "Обновление вручную"
msgid "Install OpenWrt"
msgstr "Установить OpenWrt"
msgid "Select the device model:"
msgstr "Выберите модель устройства:"
msgid "Select List"
msgstr "Выбрать из списка"
msgid "Enter the dtb file name"
msgstr "Введите имя файла DTB"
msgid "Enter the soc name:"
msgstr "Введите имя SoC:"
msgid "Enter the uboot_overload name:"
msgstr "Введите имя uboot_overload:"
msgid "Invalid value."
msgstr "Недопустимое значение."
msgid "Install OpenWrt:"
msgstr "Установка OpenWrt:"
msgid "Install OpenWrt to EMMC, Please select the device model, Or enter the dtb file name."
msgstr "Установка OpenWrt на eMMC. Пожалуйста, выберите модель устройства или введите имя файла DTB."
msgid "Tip: Writing is in progress, and it will automatically restart after completion."
msgstr "Подсказка: идёт запись — после завершения устройство автоматически перезагрузится."
msgid "You have chosen:"
msgstr "Вы выбрали:"
msgid "Start install?"
msgstr "Начать установку?"
msgid "Installing..."
msgstr "Установка..."
msgid "Install Failed"
msgstr "Установка не удалась"
msgid "Successful Install"
msgstr "Установка завершена успешно"
msgid "Update"
msgstr "Обновить"
msgid "Updating..."
msgstr "Обновление..."
msgid "Update Failed"
msgstr "Обновление не удалось"
msgid "Successful Update"
msgstr "Обновление завершено успешно"
msgid "Update OpenWrt firmware"
msgstr "Обновить прошивку OpenWrt"
msgid "kernel"
msgstr "ядро"
msgid "Replace OpenWrt Kernel"
msgstr "Заменить ядро OpenWrt"
msgid "Tip: The kernel is being replaced, and it will automatically restart after completion."
msgstr "Подсказка: идёт замена ядра — после завершения устройство автоматически перезагрузится."
msgid "Supports management of Amlogic s9xxx, Allwinner (V-Plus Cloud), and Rockchip (BeikeYun, Chainedbox L1 Pro) boxes."
msgstr "Поддерживает управление устройствами на базе Amlogic s9xxx, Allwinner (V-Plus Cloud) и Rockchip (BeikeYun, Chainedbox L1 Pro)."
msgid "Supported functions:"
msgstr "Поддерживаемые функции:"
msgid "Provide services such as install to EMMC, Update Firmware and Kernel, Backup and Recovery Config, Snapshot management, etc."
msgstr "Позволяет устанавливать OpenWrt на eMMC, обновлять прошивку и ядро, выполнять резервное копирование и восстановление конфигурации, а также управлять снапшотами (мгновенными снимками системы)."
msgid "Supported Boxes:"
msgstr "Поддерживаемые устройства:"
msgid "Amlogic s922x --- [ Beelink, Beelink-Pro, Ugoos-AM6-Plus, ODROID-N2, Khadas-VIM3, Ali-CT2000 ]"
msgstr "Amlogic s922x — [Beelink, Beelink-Pro, Ugoos-AM6-Plus, ODROID-N2, Khadas-VIM3, Ali-CT2000]"
msgid "Amlogic s905x3 -- [ X96-Max+, HK1-Box, H96-Max-X3, Ugoos-X3, TX3, X96-Air, A95XF3-Air ]"
msgstr "Amlogic s905x3 — [X96-Max+, HK1-Box, H96-Max-X3, Ugoos-X3, TX3, X96-Air, A95XF3-Air]"
msgid "Amlogic s905x2 -- [ X96Max-4G, X96Max-2G, MECOOL-KM3-4G, Tanix-Tx5-Max, A95X-F2 ]"
msgstr "Amlogic s905x2 — [X96Max-4G, X96Max-2G, MECOOL-KM3-4G, Tanix-Tx5-Max, A95X-F2]"
msgid "Amlogic s912 ---- [ H96-Pro-Plus, Octopus-Planet, A1, A2, Z6-Plus, TX92, X92, TX8-MAX, TX9-Pro ]"
msgstr "Amlogic s912 — [H96-Pro-Plus, Octopus-Planet, A1, A2, Z6-Plus, TX92, X92, TX8-MAX, TX9-Pro]"
msgid "Amlogic s905x --- [ HG680P, B860H, TBee, T95, TX9, XiaoMI-3S, X96 ]"
msgstr "Amlogic s905x — [HG680P, B860H, TBee, T95, TX9, XiaoMI-3S, X96]"
msgid "Amlogic s905w --- [ X96-Mini, TX3-Mini, W95, X96W/FunTV, MXQ-Pro-4K ]"
msgstr "Amlogic s905w — [X96-Mini, TX3-Mini, W95, X96W/FunTV, MXQ-Pro-4K]"
msgid "Amlogic s905d --- [ Phicomm-N1, MECOOL-KI-Pro, SML-5442TW ]"
msgstr "Amlogic s905d — [Phicomm-N1, MECOOL-KI-Pro, SML-5442TW]"
msgid "Amlogic s905l --- [ UNT402A, M201-S ]"
msgstr "Amlogic s905l — [UNT402A, M201-S]"
msgid "Amlogic s905l2 -- [ MGV2000, MGV3000, Wojia-TV-IPBS9505, M301A, E900v21E ]"
msgstr "Amlogic s905l2 — [MGV2000, MGV3000, Wojia-TV-IPBS9505, M301A, E900v21E]"
msgid "Amlogic s905l3 -- [ CM211-1, CM311-1, HG680-LC, M401A, UNT400G1, UNT402A, ZXV10-BV310 ]"
msgstr "Amlogic s905l3 — [CM211-1, CM311-1, HG680-LC, M401A, UNT400G1, UNT402A, ZXV10-BV310]"
msgid "Amlogic s905l3a - [ E900V22C/D, CM311-1a-YST, M401A, M411A, UNT403A, UNT413A, IP112H ]"
msgstr "Amlogic s905l3a — [E900V22C/D, CM311-1a-YST, M401A, M411A, UNT403A, UNT413A, IP112H]"
msgid "Amlogic s905l3b - [ CM211-1, CM311-1, E900V22D, E900V21E, E900V22E, M302A/M304A ]"
msgstr "Amlogic s905l3b — [CM211-1, CM311-1, E900V22D, E900V21E, E900V22E, M302A/M304A]"
msgid "Amlogic s905 ---- [ Beelink-Mini-MX-2G, Sunvell-T95M, MXQ-Pro+4K, SumaVision-Q5 ]"
msgstr "Amlogic s905 — [Beelink-Mini-MX-2G, Sunvell-T95M, MXQ-Pro+4K, SumaVision-Q5]"
msgid "Allwinner H6 ---- [ V-Plus Cloud ]"
msgstr "Allwinner H6 — [V-Plus Cloud]"
msgid "Rockchip -------- [ BeikeYun, L1-Pro, FastRhino R66S/R68S, Radxa 5B/E25 ]"
msgstr "Rockchip — [BeikeYun, L1-Pro, FastRhino R66S/R68S, Radxa 5B/E25]"
msgid "Used in KVM ----- [ Can be used in KVM virtual machine of Armbian system. ]"
msgstr "Используется в KVM — [можно использовать в виртуальной машине KVM на базе Armbian]."
msgid "KVM dual system switching"
msgstr "Переключение между двумя системами в KVM"
msgid "You can freely switch between KVM dual partitions, using OpenWrt systems in different partitions."
msgstr "Вы можете свободно переключаться между двумя разделами KVM, используя OpenWrt из разных разделов."
msgid "Switch System"
msgstr "Переключить систему"
msgid "System is switching..."
msgstr "Переключение системы..."
msgid "Waiting for system switching..."
msgstr "Ожидание завершения переключения..."
msgid "System switchover succeeded, restarting..."
msgstr "Переключение успешно, перезагрузка..."
msgid "Are you sure you want to switch systems?"
msgstr "Вы уверены, что хотите переключить систему?"
msgid "Install Ipk"
msgstr "Установить IPK"
msgid "Plugin Settings"
msgstr "Настройки плагина"
msgid "Backup Firmware Config"
msgstr "Резервное копирование конфигурации"
msgid "Backup Config:"
msgstr "Резервная копия конфигурации:"
msgid "Download Backup"
msgstr "Скачать резервную копию"
msgid "Backup OpenWrt config (openwrt_config.tar.gz). Use this file to restore the config in [Manually Upload Update]."
msgstr "Создать резервную копию конфигурации OpenWrt (openwrt_config.tar.gz). Используйте этот файл для восстановления в разделе [Обновление вручную]."
msgid "Edit List:"
msgstr "Редактировать список:"
msgid "Open List"
msgstr "Открыть список"
msgid "Backup Configuration - Custom List"
msgstr "Резервная копия — пользовательский список"
msgid "Write one configuration item per line, and directories should end with a /."
msgstr "По одному элементу на строку. Имена каталогов должны оканчиваться на «/»."
msgid "Restore Backup:"
msgstr "Восстановить из резервной копии:"
msgid "Upload Backup"
msgstr "Загрузить резервную копию"
msgid "Restore"
msgstr "Восстановить"
msgid "Restore Config"
msgstr "Восстановить конфигурацию"
msgid "Restore configuration"
msgstr "Восстановление конфигурации"
msgid "Config File"
msgstr "Файл конфигурации"
msgid "Tip: The config is being restored, and it will automatically restart after completion."
msgstr "Подсказка: идёт восстановление конфигурации — после завершения устройство автоматически перезагрузится."
msgid "Snapshot Management"
msgstr "Управление снапшотами"
msgid "Create Snapshot"
msgstr "Создать снапшот"
msgid "Creating..."
msgstr "Создание..."
msgid "Created successfully"
msgstr "Создано успешно"
msgid "Creation failed"
msgstr "Ошибка создания"
msgid "Initialize Snapshot"
msgstr "Инициализировать снапшот"
msgid "Update Snapshot"
msgstr "Обновить снапшот"
msgid "Restore Snap"
msgstr "Восстановить снапшот"
msgid "Restoring..."
msgstr "Восстановление..."
msgid "Restore Failed"
msgstr "Восстановление не удалось"
msgid "Successfully Restored"
msgstr "Восстановлено успешно"
msgid "Delete Snap"
msgstr "Удалить снапшот"
msgid "You selected a snapshot:"
msgstr "Вы выбрали снапшот:"
msgid "Confirm delete?"
msgstr "Подтвердить удаление?"
msgid "Confirm recovery and restart OpenWrt?"
msgstr "Подтвердить восстановление и перезагрузку OpenWrt?"
msgid "Delete Failed"
msgstr "Удаление не удалось"
msgid "Successfully Deleted"
msgstr "Удалено успешно"
msgid "Create a snapshot of the current system configuration, or restore to a snapshot."
msgstr "Создать снапшот текущей конфигурации системы или восстановиться из снапшота."
msgid "Currently OpenWrt does not support the snapshot function."
msgstr "В текущей версии OpenWrt функция снапшотов не поддерживается."
msgid "Please use this plugin to reinstall or upgrade OpenWrt to enable the snapshot function."
msgstr "Пожалуйста, используйте этот плагин для переустановки или обновления OpenWrt, чтобы включить поддержку снапшотов."
msgid "Deleting..."
msgstr "Удаление..."
msgid "Online Download Update"
msgstr "Обновление через интернет"
msgid "Config Source"
msgstr "Источник конфигурации"
msgid "You can customize the github.com download repository of OpenWrt files and kernels in [Online Download Update]."
msgstr "Вы можете настроить репозиторий на github.com для загрузки файлов OpenWrt и ядер в разделе [Обновление через интернет]."
msgid "Tip: The same files as the current OpenWrt system's BOARD (such as rock5b) and kernel (such as 5.10) will be downloaded."
msgstr "Подсказка: будут загружены файлы, соответствующие текущей платформе (например, rock5b) и версии ядра (например, 5.10)."
msgid "OpenWrt download repository:"
msgstr "Репозиторий OpenWrt:"
msgid "Set the OpenWrt files download repository on github.com in [Online Download Update]."
msgstr "Укажите репозиторий на github.com для загрузки файлов OpenWrt в разделе [Обновление через интернет]."
msgid "OpenWrt download tags keyword:"
msgstr "Ключевое слово тега OpenWrt:"
msgid "Set the OpenWrt files download tags keyword for github.com in [Online Download Update]."
msgstr "Укажите ключевое слово тега для загрузки файлов OpenWrt на github.com в разделе [Обновление через интернет]."
msgid "OpenWrt files suffix:"
msgstr "Расширение файлов OpenWrt:"
msgid "Set the OpenWrt files download suffix for github.com in [Online Download Update]."
msgstr "Укажите расширение файлов OpenWrt, загружаемых с github.com, в разделе [Обновление через интернет]."
msgid "Kernel download repository:"
msgstr "Репозиторий ядра:"
msgid "Set the kernel files download repository on github.com in [Online Download Update]."
msgstr "Укажите репозиторий на github.com для загрузки файлов ядра в разделе [Обновление через интернет]."
msgid "Kernel download tags:"
msgstr "Теги ядра:"
msgid "Set the kernel files download tags on github.com in [Online Download Update]."
msgstr "Укажите теги для загрузки файлов ядра на github.com в разделе [Обновление через интернет]."
msgid "kernel_rk3588 [Rockchip RK3588 Kernel]"
msgstr "kernel_rk3588 [Ядро для Rockchip RK3588]"
msgid "kernel_rk35xx [Rockchip RK35xx Kernel]"
msgstr "kernel_rk35xx [Ядро для Rockchip RK35xx]"
msgid "kernel_stable [Mainline Stable Kernel]"
msgstr "kernel_stable [Стабильное ядро из mainline]"
msgid "kernel_flippy [Mainline Stable Kernel by Flippy]"
msgstr "kernel_flippy [Стабильное ядро от Flippy]"
msgid "kernel_h6 [Allwinner H6 Kernel]"
msgstr "kernel_h6 [Ядро для Allwinner H6]"
msgid "kernel_beta [Beta Kernel]"
msgstr "kernel_beta [Бета-версия ядра]"
msgid "Set version branch:"
msgstr "Ветка версии:"
msgid "Set the version branch of the OpenWrt files and kernel selected in [Online Download Update]."
msgstr "Укажите ветку версии для файлов OpenWrt и ядра в разделе [Обновление через интернет]."
msgid "Keep config update:"
msgstr "Сохранять конфигурацию при обновлении:"
msgid "Set whether to keep the current config during [Online Download Update] and [Manually Upload Update]."
msgstr "Выберите, сохранять ли текущую конфигурацию при обновлении через интернет или вручную."
msgid "Auto write bootloader:"
msgstr "Автоматически записывать bootloader:"
msgid "[Recommended choice] Set whether to auto write bootloader during install and update OpenWrt."
msgstr "[Рекомендуется] Указывает, записывать ли автоматически bootloader при установке или обновлении OpenWrt."
msgid "Set the file system type:"
msgstr "Тип файловой системы:"
msgid "[Default ext4] Set the file system type of the shared partition (/mnt/mmcblk*p4) when install OpenWrt."
msgstr "[По умолчанию ext4] Укажите тип файловой системы для общего раздела (/mnt/mmcblk*p4) при установке OpenWrt."
msgid "Save Config:"
msgstr "Сохранить конфигурацию:"
msgid "Save"
msgstr "Сохранить"
msgid "Check All Components Update"
msgstr "Проверка обновлений"
msgid "Provide OpenWrt Firmware, Kernel and Plugin online check, download and update service."
msgstr "Предоставляет онлайн-проверку, загрузку и обновление прошивки OpenWrt, ядра и плагинов."
msgid "Only update Amlogic Service"
msgstr "Обновить только сервис Amlogic"
msgid "Update system kernel only"
msgstr "Обновить только ядро системы"
msgid "Complete system update"
msgstr "Полное обновление системы"
msgid "Check Update"
msgstr "Проверить обновления"
msgid "Checking..."
msgstr "Проверка..."
msgid "Current Version"
msgstr "Текущая версия"
msgid "Latest Version"
msgstr "Последняя версия"
msgid "Rescue Kernel"
msgstr "Аварийное восстановление ядра"
msgid "When a kernel update fails and causes the OpenWrt system to be unbootable, the kernel can be restored by mutual recovery from eMMC/NVMe/sdX."
msgstr "Если обновление ядра привело к невозможности загрузки OpenWrt, его можно восстановить с другого носителя (eMMC/NVMe/sdX)."
msgid "Rescue the original system kernel"
msgstr "Восстановить исходное ядро системы"
msgid "Rescuing..."
msgstr "Восстановление..."
msgid "Current Device:"
msgstr "Текущее устройство:"
msgid "Display the PLATFORM classification of the device."
msgstr "Отображает класс PLATFORM устройства."
msgid "Update plugins first, then update the kernel or firmware. More options can be configured in [Plugin Settings]."
msgstr "Сначала обновите плагины, затем ядро или прошивку. Дополнительные настройки доступны в [Настройках плагина]."
msgid "Collecting data..."
msgstr "Сбор данных..."
msgid "Server Logs"
msgstr "Журнал операций"
msgid "Display the execution log of the current operation."
msgstr "Отображает журнал выполнения текущей операции."
msgid "Stop Refresh Log"
msgstr "Остановить обновление журнала"
msgid "Start Refresh Log"
msgstr "Начать обновление журнала"
msgid "Clean Log"
msgstr "Очистить журнал"
msgid "Download Log"
msgstr "Скачать журнал"
msgid "CPU Settings"
msgstr "Настройки CPU"
msgid "CPU Freq"
msgstr "Частота CPU"
msgid "CPU Freq Settings"
msgstr "Настройки частоты CPU"
msgid "Set CPU Scaling Governor to Max Performance or Balance Mode"
msgstr "Выберите режим управления частотой CPU: максимальная производительность или баланс."
msgid "CPU Scaling Governor:"
msgstr "Режим управления частотой:"
msgid "ondemand"
msgstr "Ondemand — автоматический баланс"
msgid "performance"
msgstr "Performance — максимальная производительность"
msgid "schedutil"
msgstr "Schedutil — умный планировщик"
msgid "CPU Freq from 48000 to 716000 (Khz)"
msgstr "Диапазон частоты CPU: от 48000 до 716000 КГц"
msgid "Min Freq:"
msgstr "Мин. частота:"
msgid "Max Freq:"
msgstr "Макс. частота:"
msgid "CPU Switching Threshold:"
msgstr "Порог переключения частоты CPU:"
msgid "Kernel make a decision on whether it should increase the frequency (%)"
msgstr "При загрузке CPU выше указанного процента (%), ядро увеличит частоту."
msgid "CPU Switching Sampling rate:"
msgstr "Период проверки частоты CPU:"
msgid "The sampling rate determines how frequently the governor checks to tune the CPU (ms)"
msgstr "Частота проверки загрузки CPU (мс). Примечание: слишком частое переключение может вызвать дрожание задержек в сети."
msgid "Microarchitectures:"
msgstr "Микроархитектура:"
msgid "Loading"
msgstr "Загрузка"
msgid "PowerOff"
msgstr "Выключить"
msgid "Shut down your router device."
msgstr "Выключить устройство маршрутизатора."
msgid "Perform PowerOff"
msgstr "Выполнить выключение"
msgid "Are you sure you want to shut down?"
msgstr "Вы уверены, что хотите выключить устройство?"
msgid "Device is shutting down..."
msgstr "Устройство выключается..."
msgid "Waiting for the device to shut down..."
msgstr "Ожидание завершения выключения..."
msgid "The device has been turned off"
msgstr "Устройство выключено"
@@ -335,7 +335,7 @@ if api.is_finded("smartdns") then
o = s:taboption("DNS", Value, "group_domestic", translate("Domestic group name"))
o.placeholder = "local"
o:depends("dns_shunt", "smartdns")
o.description = translate("You only need to configure domestic DNS packets in SmartDNS and set it redirect or as Dnsmasq upstream, and fill in the domestic DNS group name here.")
o.description = translate("You only need to configure domestic DNS packets in SmartDNS, and fill in the domestic DNS group name here.")
end
o = s:taboption("DNS", ListValue, "direct_dns_mode", translate("Direct DNS") .. " " .. translate("Request protocol"))
@@ -273,7 +273,7 @@ function gen_outbound(flag, node, tag, proxy_table)
port = string.gsub(node.hysteria2_hop, ":", "-"),
interval = (function()
local v = tonumber((node.hysteria2_hop_interval or "30s"):match("^%d+"))
return (v and v >= 5) and (v .. "s") or "30s"
return (v and v >= 5) and v or 30
end)()
} or nil,
maxIdleTimeout = (function()
@@ -124,8 +124,8 @@ msgstr "DNS分流"
msgid "Domestic group name"
msgstr "国内分组名"
msgid "You only need to configure domestic DNS packets in SmartDNS and set it redirect or as Dnsmasq upstream, and fill in the domestic DNS group name here."
msgstr "您只需要在SmartDNS配置好国内DNS分组,并设置重定向或作为Dnsmasq上游,此处填入国内DNS分组名。"
msgid "You only need to configure domestic DNS packets in SmartDNS, and fill in the domestic DNS group name here."
msgstr "您只需要在SmartDNS配置好国内DNS分组,并此处填入国内DNS分组名。"
msgid "Filter Mode"
msgstr "过滤模式"

Some files were not shown because too many files have changed in this diff Show More