Просмотр исходного кода

hysteria: also accept "hysteria2" protocol string

UI stores v1 and v2 both as "hysteria" with settings.version, but
inbounds that came in from imports / manual SQL can carry the
literal "hysteria2" string and get silently dropped everywhere we
switch on protocol.

Add Hysteria2 constant + IsHysteria helper, use it in the places
that gate on protocol (sub SQL, getLink, genHysteriaLink, clash
buildProxy, json gen, inbound.go validation, xray AddUser).

Existing "hysteria" inbounds are untouched.

closes #4081
pwnnex 2 дней назад
Родитель
Сommit
eb4791a1cd

+ 13 - 1
database/model/model.go

@@ -21,9 +21,21 @@ const (
 	Shadowsocks Protocol = "shadowsocks"
 	Mixed       Protocol = "mixed"
 	WireGuard   Protocol = "wireguard"
-	Hysteria    Protocol = "hysteria"
+	// UI stores Hysteria v1 and v2 both as "hysteria" and uses
+	// settings.version to discriminate. Imports from outside the panel
+	// can carry the literal "hysteria2" string, so IsHysteria below
+	// accepts both.
+	Hysteria  Protocol = "hysteria"
+	Hysteria2 Protocol = "hysteria2"
 )
 
+// IsHysteria returns true for both "hysteria" and "hysteria2".
+// Use instead of a bare ==model.Hysteria check: a v2 inbound stored
+// with the literal v2 string would otherwise fall through (#4081).
+func IsHysteria(p Protocol) bool {
+	return p == Hysteria || p == Hysteria2
+}
+
 // User represents a user account in the 3x-ui panel.
 type User struct {
 	Id       int    `json:"id" gorm:"primaryKey;autoIncrement"`

+ 22 - 0
database/model/model_test.go

@@ -0,0 +1,22 @@
+package model
+
+import "testing"
+
+func TestIsHysteria(t *testing.T) {
+	cases := []struct {
+		in   Protocol
+		want bool
+	}{
+		{Hysteria, true},
+		{Hysteria2, true},
+		{VLESS, false},
+		{Shadowsocks, false},
+		{Protocol(""), false},
+		{Protocol("hysteria3"), false},
+	}
+	for _, c := range cases {
+		if got := IsHysteria(c.in); got != c.want {
+			t.Errorf("IsHysteria(%q) = %v, want %v", c.in, got, c.want)
+		}
+	}
+}

+ 4 - 7
sub/subClashService.go

@@ -159,13 +159,10 @@ func (s *SubClashService) getProxies(inbound *model.Inbound, client model.Client
 }
 
 func (s *SubClashService) buildProxy(inbound *model.Inbound, client model.Client, stream map[string]any, extraRemark string) map[string]any {
-	// Hysteria (v1 / v2) doesn't ride an xray `streamSettings.network`
-	// transport and the TLS story is handled inside hysteria itself, so
-	// applyTransport / applySecurity below don't model it. Build the
-	// proxy directly. Without this, hysteria inbounds fell into the
-	// `default: return nil` branch and silently vanished from the
-	// generated Clash config.
-	if inbound.Protocol == model.Hysteria {
+	// Hysteria has its own transport + TLS model, applyTransport /
+	// applySecurity don't fit. IsHysteria also covers the literal
+	// "hysteria2" protocol string (#4081).
+	if model.IsHysteria(inbound.Protocol) {
 		return s.buildHysteriaProxy(inbound, client, extraRemark)
 	}
 

+ 1 - 1
sub/subJsonService.go

@@ -209,7 +209,7 @@ func (s *SubJsonService) getConfig(inbound *model.Inbound, client model.Client,
 			newOutbounds = append(newOutbounds, s.genVless(inbound, streamSettings, client))
 		case "trojan", "shadowsocks":
 			newOutbounds = append(newOutbounds, s.genServer(inbound, streamSettings, client))
-		case "hysteria":
+		case "hysteria", "hysteria2":
 			newOutbounds = append(newOutbounds, s.genHy(inbound, newStream, client))
 		}
 

+ 6 - 4
sub/subService.go

@@ -115,12 +115,14 @@ func (s *SubService) GetSubs(subId string, host string) ([]string, int64, xray.C
 func (s *SubService) getInboundsBySubId(subId string) ([]*model.Inbound, error) {
 	db := database.GetDB()
 	var inbounds []*model.Inbound
+	// allow "hysteria2" so imports stored with the literal v2 protocol
+	// string still surface here (#4081)
 	err := db.Model(model.Inbound{}).Preload("ClientStats").Where(`id in (
 		SELECT DISTINCT inbounds.id
 		FROM inbounds,
-			JSON_EACH(JSON_EXTRACT(inbounds.settings, '$.clients')) AS client 
+			JSON_EACH(JSON_EXTRACT(inbounds.settings, '$.clients')) AS client
 		WHERE
-			protocol in ('vmess','vless','trojan','shadowsocks','hysteria')
+			protocol in ('vmess','vless','trojan','shadowsocks','hysteria','hysteria2')
 			AND JSON_EXTRACT(client.value, '$.subId') = ? AND enable = ?
 	)`, subId, true).Find(&inbounds).Error
 	if err != nil {
@@ -171,7 +173,7 @@ func (s *SubService) getLink(inbound *model.Inbound, email string) string {
 		return s.genTrojanLink(inbound, email)
 	case "shadowsocks":
 		return s.genShadowsocksLink(inbound, email)
-	case "hysteria":
+	case "hysteria", "hysteria2":
 		return s.genHysteriaLink(inbound, email)
 	}
 	return ""
@@ -906,7 +908,7 @@ func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string) st
 }
 
 func (s *SubService) genHysteriaLink(inbound *model.Inbound, email string) string {
-	if inbound.Protocol != model.Hysteria {
+	if !model.IsHysteria(inbound.Protocol) {
 		return ""
 	}
 	var stream map[string]interface{}

+ 4 - 4
web/service/inbound.go

@@ -270,7 +270,7 @@ func (s *InboundService) AddInbound(inbound *model.Inbound) (*model.Inbound, boo
 			if client.Email == "" {
 				return inbound, false, common.NewError("empty client ID")
 			}
-		case "hysteria":
+		case "hysteria", "hysteria2":
 			if client.Auth == "" {
 				return inbound, false, common.NewError("empty client ID")
 			}
@@ -675,7 +675,7 @@ func (s *InboundService) AddInboundClient(data *model.Inbound) (bool, error) {
 			if client.Email == "" {
 				return false, common.NewError("empty client ID")
 			}
-		case "hysteria":
+		case "hysteria", "hysteria2":
 			if client.Auth == "" {
 				return false, common.NewError("empty client ID")
 			}
@@ -769,7 +769,7 @@ func (s *InboundService) DelInboundClient(inboundId int, clientId string) (bool,
 		client_key = "password"
 	case "shadowsocks":
 		client_key = "email"
-	case "hysteria":
+	case "hysteria", "hysteria2":
 		client_key = "auth"
 	}
 
@@ -877,7 +877,7 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId strin
 		case "shadowsocks":
 			oldClientId = oldClient.Email
 			newClientId = clients[0].Email
-		case "hysteria":
+		case "hysteria", "hysteria2":
 			oldClientId = oldClient.Auth
 			newClientId = clients[0].Auth
 		default:

+ 1 - 1
xray/api.go

@@ -231,7 +231,7 @@ func (x *XrayAPI) AddUser(Protocol string, inboundTag string, user map[string]an
 				Email: userEmail,
 			})
 		}
-	case "hysteria":
+	case "hysteria", "hysteria2":
 		auth, err := getRequiredUserString(user, "auth")
 		if err != nil {
 			return err