Skip to content

Commit c5c6a33

Browse files
committed
build: feat 3.3.0
1 parent cb939a0 commit c5c6a33

File tree

15 files changed

+225
-56
lines changed

15 files changed

+225
-56
lines changed

.goreleaser.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ release:
197197
198198
🔧 功能调整
199199
200-
- 隧道管理:QUIC字段替换为池类型字段
200+
- 隧道管理:QUIC字段替换为池类型字段、协议屏蔽、SNI
201201
- 删除旧逻辑(回收站、SSE记录表等无用方法)
202202
- 场景创建URL拼接逻辑修复
203203
@@ -206,7 +206,6 @@ release:
206206
## 📌 注意事项
207207
208208
- 本版本需配合 NodePass Core 1.13 使用
209-
- 服务管理为全新模块,建议详细阅读相关文档
210209
- 升级后建议检查已有实例的配置参数是否正常
211210
footer: |
212211

Dockerfile

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,16 @@ RUN apk add --no-cache git gcc g++ make musl-dev sqlite-dev
3535
# 将 go.mod 和 go.sum 拷贝并拉取依赖
3636
COPY go.mod go.sum ./
3737

38-
# 设置 Go module proxy 和超时
39-
ENV GOPROXY=https://proxy.golang.org,direct
38+
# 设置 Go module proxy(使用多个镜像源提高成功率)
39+
ENV GOPROXY=https://goproxy.cn,https://goproxy.io,https://proxy.golang.org,direct
4040
ENV GOSUMDB=sum.golang.org
4141
ENV GOTIMEOUT=600s
4242

43-
# 增加网络重试和下载超时
44-
RUN go mod download -x
43+
# 下载依赖(添加重试逻辑)
44+
RUN --mount=type=cache,target=/go/pkg/mod \
45+
go mod download || \
46+
(sleep 5 && go mod download) || \
47+
(sleep 10 && go mod download)
4548

4649
# 复制 Go 后端代码
4750
COPY cmd/ ./cmd/

go.mod

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
module NodePassDash
22

3-
go 1.24.0
3+
go 1.23.0
44

5-
toolchain go1.24.10
5+
toolchain go1.23.10
66

77
require (
88
github.com/gin-gonic/gin v1.10.1
99
github.com/go-resty/resty/v2 v2.16.5
10+
github.com/golang-jwt/jwt/v5 v5.2.1
1011
github.com/google/uuid v1.6.0
1112
github.com/gorilla/websocket v1.5.3
1213
github.com/mattn/go-ieproxy v0.0.12
1314
github.com/mattn/go-sqlite3 v1.14.17
1415
github.com/r3labs/sse/v2 v2.10.0
1516
github.com/sirupsen/logrus v1.9.3
16-
golang.org/x/crypto v0.38.0
17+
golang.org/x/crypto v0.31.0
1718
gorm.io/driver/sqlite v1.5.4
1819
gorm.io/gorm v1.25.5
1920
)
@@ -29,7 +30,6 @@ require (
2930
github.com/go-playground/universal-translator v0.18.1 // indirect
3031
github.com/go-playground/validator/v10 v10.20.0 // indirect
3132
github.com/goccy/go-json v0.10.2 // indirect
32-
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
3333
github.com/google/go-cmp v0.6.0 // indirect
3434
github.com/jinzhu/inflection v1.0.0 // indirect
3535
github.com/jinzhu/now v1.1.5 // indirect
@@ -43,10 +43,9 @@ require (
4343
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
4444
github.com/ugorji/go/codec v1.2.12 // indirect
4545
golang.org/x/arch v0.8.0 // indirect
46-
golang.org/x/net v0.40.0 // indirect
47-
golang.org/x/sync v0.18.0 // indirect
48-
golang.org/x/sys v0.34.0 // indirect
49-
golang.org/x/text v0.25.0 // indirect
46+
golang.org/x/net v0.33.0 // indirect
47+
golang.org/x/sys v0.28.0 // indirect
48+
golang.org/x/text v0.21.0 // indirect
5049
google.golang.org/protobuf v1.34.1 // indirect
5150
gopkg.in/cenkalti/backoff.v1 v1.1.0 // indirect
5251
gopkg.in/yaml.v3 v3.0.1 // indirect

go.sum

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -87,22 +87,20 @@ golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUu
8787
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
8888
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
8989
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
90-
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
91-
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
90+
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
91+
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
9292
golang.org/x/net v0.0.0-20191116160921-f9c825593386/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
93-
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
94-
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
95-
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
96-
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
93+
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
94+
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
9795
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
9896
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
9997
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
10098
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
101-
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
102-
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
99+
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
100+
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
103101
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
104-
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
105-
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
102+
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
103+
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
106104
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
107105
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
108106
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=

internal/api/auth.go

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -117,14 +117,8 @@ func (h *AuthHandler) HandleLogin(c *gin.Context) {
117117
return
118118
}
119119

120-
// 保存 JTI 到数据库(实现 token 互踢:新登录会踢掉旧 token)
121-
if err := h.authService.SetSystemConfig(auth.ConfigKeyCurrentTokenJTI, jti); err != nil {
122-
c.JSON(http.StatusInternalServerError, auth.LoginResponse{
123-
Success: false,
124-
Error: "保存 token 失败",
125-
})
126-
return
127-
}
120+
// 保存 JTI 到内存(实现 token 互踢:新登录会踢掉旧 token,避免启动时SQLite锁)
121+
h.authService.SetCurrentJTI(jti)
128122

129123
// 检查是否是默认账号密码
130124
isDefaultCredentials := h.authService.IsDefaultCredentials()
@@ -151,8 +145,8 @@ func (h *AuthHandler) HandleLogout(c *gin.Context) {
151145
h.authService.DestroySession(sessionID)
152146
}
153147

154-
// 清除数据库中的 JTI,使所有 token 失效
155-
_ = h.authService.DeleteSystemConfig(auth.ConfigKeyCurrentTokenJTI)
148+
// 清除内存中的 JTI,使所有 token 失效
149+
h.authService.ClearCurrentJTI()
156150

157151
// 清除 cookie
158152
c.SetCookie("session", "", -1, "/", "", false, true)
@@ -548,11 +542,8 @@ func (h *AuthHandler) handleGitHubOAuth(c *gin.Context, code string) {
548542
return
549543
}
550544

551-
// 保存 JTI 到数据库(实现 token 互踢)
552-
if err := h.authService.SetSystemConfig(auth.ConfigKeyCurrentTokenJTI, jti); err != nil {
553-
c.JSON(http.StatusInternalServerError, gin.H{"error": "保存 token 失败"})
554-
return
555-
}
545+
// 保存 JTI 到内存(实现 token 互踢,避免启动时SQLite锁)
546+
h.authService.SetCurrentJTI(jti)
556547

557548
// 如果请求携带 redirect 参数或 Accept text/html,则执行页面跳转;否则返回 JSON
558549
redirectURL := c.Query("redirect")
@@ -751,11 +742,8 @@ func (h *AuthHandler) handleCloudflareOAuth(c *gin.Context, code string) {
751742
return
752743
}
753744

754-
// 保存 JTI 到数据库(实现 token 互踢)
755-
if err := h.authService.SetSystemConfig(auth.ConfigKeyCurrentTokenJTI, jti); err != nil {
756-
c.JSON(http.StatusInternalServerError, gin.H{"error": "保存 token 失败"})
757-
return
758-
}
745+
// 保存 JTI 到内存(实现 token 互踢,避免启动时SQLite锁)
746+
h.authService.SetCurrentJTI(jti)
759747

760748
// 如果请求携带 redirect 参数或 Accept text/html,则执行页面跳转;否则返回 JSON
761749
redirectURL := c.Query("redirect")

internal/api/tunnel.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -841,6 +841,13 @@ func (h *TunnelHandler) HandleGetTunnelDetails(c *gin.Context) {
841841
"sorts": tunnel.Sorts,
842842
"dial": tunnel.Dial,
843843
"dns": tunnel.Dns,
844+
"sni": tunnel.Sni,
845+
"block": func() interface{} {
846+
if tunnel.Block != nil {
847+
return *tunnel.Block
848+
}
849+
return nil
850+
}(),
844851
// endpoint 改为对象形式
845852
"endpoint": map[string]interface{}{
846853
"name": endpointName,
@@ -874,6 +881,8 @@ func (h *TunnelHandler) HandleGetTunnelDetails(c *gin.Context) {
874881
"dns": parsedConfig.Dns,
875882
"dial": parsedConfig.Dial,
876883
"listenType": parsedConfig.ListenType,
884+
"sni": parsedConfig.Sni,
885+
"block": parsedConfig.Block,
877886
},
878887

879888
// tags - GORM 自动反序列化为 *map[string]string

internal/auth/jwt.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,10 @@ func (s *Service) ValidateToken(tokenString string) (string, error) {
9494
return "", errors.New("invalid token")
9595
}
9696

97-
// 验证 JTI 是否与数据库中的当前有效 JTI 匹配(实现 token 互踢)
98-
currentJTI, err := s.GetSystemConfig(ConfigKeyCurrentTokenJTI)
97+
// 验证 JTI 是否与内存中的当前有效 JTI 匹配(实现 token 互踢)
98+
currentJTI, err := s.GetCurrentJTI()
9999
if err != nil {
100-
// 如果数据库中没有 JTI 配置,说明所有 token 都已失效
100+
// 如果内存中没有 JTI,说明所有 token 都已失效(服务重启或登出)
101101
return "", errors.New("token has been invalidated")
102102
}
103103

internal/auth/service.go

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ var (
2727

2828
// Service 认证服务
2929
type Service struct {
30-
db *gorm.DB
30+
db *gorm.DB
31+
currentJTI string // 当前有效的 JWT ID(内存存储,避免启动时SQLite锁)
32+
jtiMutex sync.RWMutex // JTI 读写锁
3133
}
3234

3335
// NewService 创建认证服务实例,需要传入GORM数据库连接
@@ -614,3 +616,27 @@ func (s *Service) ValidateOAuthState(state string) bool {
614616
}
615617
return false
616618
}
619+
620+
// SetCurrentJTI 设置当前有效的 JWT ID(内存存储)
621+
func (s *Service) SetCurrentJTI(jti string) {
622+
s.jtiMutex.Lock()
623+
defer s.jtiMutex.Unlock()
624+
s.currentJTI = jti
625+
}
626+
627+
// GetCurrentJTI 获取当前有效的 JWT ID(内存存储)
628+
func (s *Service) GetCurrentJTI() (string, error) {
629+
s.jtiMutex.RLock()
630+
defer s.jtiMutex.RUnlock()
631+
if s.currentJTI == "" {
632+
return "", errors.New("no valid token")
633+
}
634+
return s.currentJTI, nil
635+
}
636+
637+
// ClearCurrentJTI 清除当前有效的 JWT ID(登出时使用)
638+
func (s *Service) ClearCurrentJTI() {
639+
s.jtiMutex.Lock()
640+
defer s.jtiMutex.Unlock()
641+
s.currentJTI = ""
642+
}

internal/models/models.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ type Tunnel struct {
100100
Dial *string `json:"dial,omitempty" gorm:"type:text;column:dial"` //出站源IP地址
101101
PoolType *int `json:"poolType,omitempty" gorm:"type:int;column:pool_type"`
102102
Dns *string `json:"dns,omitempty" gorm:"type:text;column:dns"`
103+
Sni *string `json:"sni,omitempty" gorm:"type:text;column:sni"` //SNI服务器名称指示
104+
Block *int `json:"block,omitempty" gorm:"type:int;column:block"` //协议屏蔽:0-禁用, 1-SOCKS, 2-HTTP, 3-TLS
103105

104106
Sorts int64 `json:"sorts" gorm:"type:int;column:sorts;default:0"`
105107

internal/nodepass/parse.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ type TunnelConfig struct {
3434
PoolType string // 池类型 (0-TCP, 1-QUIC, 2-WebSocket, 3-HTTP/2)
3535
Dial string // 出站源IP地址
3636
Dns string // DNS服务器地址
37+
Sni string // SNI服务器名称指示
38+
Block string // 协议屏蔽 (0-禁用, 1-SOCKS, 2-HTTP, 3-TLS)
3739
}
3840

3941
// ParseTunnelURL 解析隧道实例 URL 并返回 Tunnel 模型
@@ -244,6 +246,13 @@ func ParseTunnelURL(rawURL string) *models.Tunnel {
244246
if poolType, err := strconv.Atoi(val); err == nil && poolType >= 0 && poolType <= 3 {
245247
tunnel.PoolType = &poolType
246248
}
249+
case "sni":
250+
tunnel.Sni = &val
251+
case "block":
252+
// 协议屏蔽 (0=禁用, 1=SOCKS, 2=HTTP, 3=TLS)
253+
if blockType, err := strconv.Atoi(val); err == nil && blockType >= 0 && blockType <= 3 {
254+
tunnel.Block = &blockType
255+
}
247256
}
248257
}
249258
}
@@ -378,6 +387,12 @@ func TunnelToMap(tunnel *models.Tunnel) map[string]interface{} {
378387
if tunnel.Dns != nil {
379388
updates["dns"] = tunnel.Dns
380389
}
390+
if tunnel.Sni != nil {
391+
updates["sni"] = tunnel.Sni
392+
}
393+
if tunnel.Block != nil {
394+
updates["block"] = tunnel.Block
395+
}
381396
return updates
382397
}
383398

@@ -456,6 +471,8 @@ func ParseTunnelConfig(rawURL string) *TunnelConfig {
456471
noTCP := query.Get("notcp")
457472
noUDP := query.Get("noudp")
458473
cfg.Dns = query.Get("dns")
474+
cfg.Sni = query.Get("sni")
475+
cfg.Block = query.Get("block")
459476

460477
// 根据notcp和noudp参数的组合来设置listenType
461478
if noTCP != "" || noUDP != "" {
@@ -584,6 +601,12 @@ func (c *TunnelConfig) BuildTunnelConfigURL() string {
584601
if c.Dns != "" {
585602
queryParams = append(queryParams, fmt.Sprintf("dns=%s", c.Dns))
586603
}
604+
if c.Sni != "" {
605+
queryParams = append(queryParams, fmt.Sprintf("sni=%s", c.Sni))
606+
}
607+
if c.Block != "" {
608+
queryParams = append(queryParams, fmt.Sprintf("block=%s", c.Block))
609+
}
587610

588611
// 根据listenType生成notcp和noudp参数
589612
if c.ListenType != "" {
@@ -766,6 +789,12 @@ func BuildTunnelURLs(tunnel models.Tunnel) string {
766789
if tunnel.PoolType != nil {
767790
queryParams = append(queryParams, fmt.Sprintf("type=%d", *tunnel.PoolType))
768791
}
792+
if tunnel.Sni != nil && *tunnel.Sni != "" {
793+
queryParams = append(queryParams, fmt.Sprintf("sni=%s", *tunnel.Sni))
794+
}
795+
if tunnel.Block != nil {
796+
queryParams = append(queryParams, fmt.Sprintf("block=%d", *tunnel.Block))
797+
}
769798

770799
if tunnel.ProxyProtocol != nil {
771800
proxyVal := "0"

0 commit comments

Comments
 (0)