diff --git a/database/model/model.go b/database/model/model.go index 720f0223..4ca39d87 100644 --- a/database/model/model.go +++ b/database/model/model.go @@ -119,11 +119,3 @@ type Client struct { CreatedAt int64 `json:"created_at,omitempty"` // Creation timestamp UpdatedAt int64 `json:"updated_at,omitempty"` // Last update timestamp } - -// VLESSSettings contains VLESS protocol-specific configuration settings. -type VLESSSettings struct { - Clients []Client `json:"clients"` // List of VLESS clients - Decryption string `json:"decryption"` // Decryption method - Encryption string `json:"encryption"` // Encryption method (usually "none" for VLESS) - Fallbacks []any `json:"fallbacks"` // Fallback configurations -} diff --git a/sub/subJsonService.go b/sub/subJsonService.go index f440ab65..86a7a405 100644 --- a/sub/subJsonService.go +++ b/sub/subJsonService.go @@ -174,12 +174,12 @@ func (s *SubJsonService) getConfig(inbound *model.Inbound, client model.Client, case "tls": if newStream["security"] != "tls" { newStream["security"] = "tls" - newStream["tslSettings"] = map[string]any{} + newStream["tlsSettings"] = map[string]any{} } case "none": if newStream["security"] != "none" { newStream["security"] = "none" - delete(newStream, "tslSettings") + delete(newStream, "tlsSettings") } } streamSettings, _ := json.MarshalIndent(newStream, "", " ") @@ -188,13 +188,9 @@ func (s *SubJsonService) getConfig(inbound *model.Inbound, client model.Client, switch inbound.Protocol { case "vmess": - newOutbounds = append(newOutbounds, s.genVnext(inbound, streamSettings, client, "")) + newOutbounds = append(newOutbounds, s.genVnext(inbound, streamSettings, client)) case "vless": - var vlessSettings model.VLESSSettings - _ = json.Unmarshal([]byte(inbound.Settings), &vlessSettings) - - newOutbounds = append(newOutbounds, - s.genVnext(inbound, streamSettings, client, vlessSettings.Encryption)) + newOutbounds = append(newOutbounds, s.genVless(inbound, streamSettings, client)) case "trojan", "shadowsocks": newOutbounds = append(newOutbounds, s.genServer(inbound, streamSettings, client)) } @@ -293,7 +289,35 @@ func (s *SubJsonService) realityData(rData map[string]any) map[string]any { return rltyData } -func (s *SubJsonService) genVnext(inbound *model.Inbound, streamSettings json_util.RawMessage, client model.Client, encryption string) json_util.RawMessage { +func (s *SubJsonService) genVnext(inbound *model.Inbound, streamSettings json_util.RawMessage, client model.Client) json_util.RawMessage { + outbound := Outbound{} + usersData := make([]UserVnext, 1) + + usersData[0].ID = client.ID + usersData[0].Email = client.Email + usersData[0].Security = client.Security + vnextData := make([]VnextSetting, 1) + vnextData[0] = VnextSetting{ + Address: inbound.Listen, + Port: inbound.Port, + Users: usersData, + } + + outbound.Protocol = string(inbound.Protocol) + outbound.Tag = "proxy" + if s.mux != "" { + outbound.Mux = json_util.RawMessage(s.mux) + } + outbound.StreamSettings = streamSettings + outbound.Settings = map[string]any{ + "vnext": vnextData, + } + + result, _ := json.MarshalIndent(outbound, "", " ") + return result +} + +func (s *SubJsonService) genVless(inbound *model.Inbound, streamSettings json_util.RawMessage, client model.Client) json_util.RawMessage { outbound := Outbound{} outbound.Protocol = string(inbound.Protocol) outbound.Tag = "proxy" @@ -301,20 +325,22 @@ func (s *SubJsonService) genVnext(inbound *model.Inbound, streamSettings json_ut outbound.Mux = json_util.RawMessage(s.mux) } outbound.StreamSettings = streamSettings - // Emit flattened settings inside Settings to match new Xray format settings := make(map[string]any) settings["address"] = inbound.Listen settings["port"] = inbound.Port settings["id"] = client.ID - if inbound.Protocol == model.VLESS { + if client.Flow != "" { settings["flow"] = client.Flow + } + + // Add encryption for VLESS outbound from inbound settings + var inboundSettings map[string]any + json.Unmarshal([]byte(inbound.Settings), &inboundSettings) + if encryption, ok := inboundSettings["encryption"].(string); ok { settings["encryption"] = encryption } - if inbound.Protocol == model.VMESS { - settings["security"] = client.Security - } - outbound.Settings = settings + outbound.Settings = settings result, _ := json.MarshalIndent(outbound, "", " ") return result } @@ -366,7 +392,17 @@ type Outbound struct { Settings map[string]any `json:"settings,omitempty"` } -// Legacy vnext-related structs removed for flattened schema +type VnextSetting struct { + Address string `json:"address"` + Port int `json:"port"` + Users []UserVnext `json:"users"` +} + +type UserVnext struct { + ID string `json:"id"` + Email string `json:"email,omitempty"` + Security string `json:"security,omitempty"` +} type ServerSetting struct { Password string `json:"password"` diff --git a/sub/subService.go b/sub/subService.go index 9f28b35b..6204fdee 100644 --- a/sub/subService.go +++ b/sub/subService.go @@ -321,9 +321,6 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string { if inbound.Protocol != model.VLESS { return "" } - var vlessSettings model.VLESSSettings - _ = json.Unmarshal([]byte(inbound.Settings), &vlessSettings) - var stream map[string]any json.Unmarshal([]byte(inbound.StreamSettings), &stream) clients, _ := s.inboundService.GetClients(inbound) @@ -338,11 +335,15 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string { port := inbound.Port streamNetwork := stream["network"].(string) params := make(map[string]string) - if vlessSettings.Encryption != "" { - params["encryption"] = vlessSettings.Encryption - } params["type"] = streamNetwork + // Add encryption parameter for VLESS from inbound settings + var settings map[string]any + json.Unmarshal([]byte(inbound.Settings), &settings) + if encryption, ok := settings["encryption"].(string); ok { + params["encryption"] = encryption + } + switch streamNetwork { case "tcp": tcp, _ := stream["tcpSettings"].(map[string]any) diff --git a/web/assets/js/model/outbound.js b/web/assets/js/model/outbound.js index 3e481dff..c727abae 100644 --- a/web/assets/js/model/outbound.js +++ b/web/assets/js/model/outbound.js @@ -686,14 +686,7 @@ class Outbound extends CommonClass { if (this.stream?.sockopt) stream = { sockopt: this.stream.sockopt.toJson() }; } - // For VMess/VLESS, emit settings as a flat object let settingsOut = this.settings instanceof CommonClass ? this.settings.toJson() : this.settings; - // Remove undefined/null keys - if (settingsOut && typeof settingsOut === 'object') { - Object.keys(settingsOut).forEach(k => { - if (settingsOut[k] === undefined || settingsOut[k] === null) delete settingsOut[k]; - }); - } return { protocol: this.protocol, settings: settingsOut, @@ -1031,21 +1024,28 @@ Outbound.VmessSettings = class extends CommonClass { } static fromJson(json = {}) { - if (ObjectUtil.isEmpty(json.address) || ObjectUtil.isEmpty(json.port)) return new Outbound.VmessSettings(); - return new Outbound.VmessSettings( - json.address, - json.port, - json.id, - json.security, - ); + if (!ObjectUtil.isArrEmpty(json.vnext)) { + const v = json.vnext[0] || {}; + const u = ObjectUtil.isArrEmpty(v.users) ? {} : v.users[0]; + return new Outbound.VmessSettings( + v.address, + v.port, + u.id, + u.security, + ); + } } toJson() { return { - address: this.address, - port: this.port, - id: this.id, - security: this.security, + vnext: [{ + address: this.address, + port: this.port, + users: [{ + id: this.id, + security: this.security + }] + }] }; } }; diff --git a/web/html/xray.html b/web/html/xray.html index 258c81c9..266f9eef 100644 --- a/web/html/xray.html +++ b/web/html/xray.html @@ -534,13 +534,11 @@ serverObj = null; switch (o.protocol) { case Protocols.VMess: - case Protocols.VLESS: - if (o.settings && o.settings.address && o.settings.port) { - return [o.settings.address + ':' + o.settings.port]; - } + serverObj = o.settings.vnext; break; + case Protocols.VLESS: case Protocols.HTTP: - case Protocols.Mixed: + case Protocols.Socks: case Protocols.Shadowsocks: case Protocols.Trojan: serverObj = o.settings.servers;