mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-05-31 10:14:15 +00:00
fix(links): include TCP HTTP host header in share links
The inbound form intentionally only exposes the response side of the TCP HTTP header object (xray-core's inbound listener reads the response object, not request — see the existing comment in InboundFormModal). But the share-link generators were still reading the Host header from request.headers, so the configured value ended up in tcpSettings.header.response.headers while the link query emitted host= (empty). Fix the host lookup in both code paths: - sub/subService.go: applyShareNetworkParams (VLESS / Trojan / Shadowsocks share URLs) and applyVmessNetworkParams (the VMess base64 JSON link) now try header.response.headers first and fall back to request.headers for legacy / hand-edited configs. - frontend/src/lib/xray/inbound-link.ts mirrors the same fallback in the three TCP HTTP branches (VMess obj, VLESS params, the shared Trojan+Shadowsocks writer) so the JS-side generator used by the API docs preview stays in sync with the Go output. Also restore the request-side inputs (version / method / path / headers) under the TCP HTTP toggle in InboundFormModal. They were previously removed because xray-core ignores them on the inbound side, but they're still useful when copying the same config out to an outbound or hand-tuning the share link, and they no longer mislead users about Host — the link now derives Host from response.headers.host where the response-only form writes it.
This commit is contained in:
parent
2fea71387b
commit
8046d1519d
5 changed files with 79 additions and 13 deletions
10
frontend/package-lock.json
generated
10
frontend/package-lock.json
generated
|
|
@ -8,7 +8,7 @@
|
||||||
"name": "3x-ui-frontend",
|
"name": "3x-ui-frontend",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons": "^6.2.3",
|
"@ant-design/icons": "^6.2.5",
|
||||||
"@codemirror/lang-json": "^6.0.2",
|
"@codemirror/lang-json": "^6.0.2",
|
||||||
"@codemirror/theme-one-dark": "^6.1.3",
|
"@codemirror/theme-one-dark": "^6.1.3",
|
||||||
"@tanstack/react-query": "^5.100.14",
|
"@tanstack/react-query": "^5.100.14",
|
||||||
|
|
@ -101,14 +101,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@ant-design/icons": {
|
"node_modules/@ant-design/icons": {
|
||||||
"version": "6.2.3",
|
"version": "6.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-6.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-6.2.5.tgz",
|
||||||
"integrity": "sha512-Pl3aoAtxQeKryYnt6VvDJtOxMOtA8wrRSACe/pTjOAIG3fdHrWm6Ivb4ku9tsFjYroSXBKirvuxG4QkwBXD9gg==",
|
"integrity": "sha512-0hKtoKqTjGFOndUyJLJmC9Cg6k4rEO7rLo6xmgbNJH+/ZX1C57RVals2v1j1knHl9n7Q+sBOveTvn931wLOCKw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/colors": "^8.0.1",
|
"@ant-design/colors": "^8.0.1",
|
||||||
"@ant-design/icons-svg": "^4.4.2",
|
"@ant-design/icons-svg": "^4.4.2",
|
||||||
"@rc-component/util": "^1.10.1",
|
"@rc-component/util": "^1.11.0",
|
||||||
"clsx": "^2.1.1"
|
"clsx": "^2.1.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
"gen:zod": "cd .. && go run ./tools/openapigen"
|
"gen:zod": "cd .. && go run ./tools/openapigen"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons": "^6.2.3",
|
"@ant-design/icons": "^6.2.5",
|
||||||
"@codemirror/lang-json": "^6.0.2",
|
"@codemirror/lang-json": "^6.0.2",
|
||||||
"@codemirror/theme-one-dark": "^6.1.3",
|
"@codemirror/theme-one-dark": "^6.1.3",
|
||||||
"@tanstack/react-query": "^5.100.14",
|
"@tanstack/react-query": "^5.100.14",
|
||||||
|
|
|
||||||
|
|
@ -184,7 +184,9 @@ export function genVmessLink(input: GenVmessLinkInput): string {
|
||||||
const request = header.request;
|
const request = header.request;
|
||||||
if (request) {
|
if (request) {
|
||||||
obj.path = request.path.join(',');
|
obj.path = request.path.join(',');
|
||||||
const host = getHeaderValue(request.headers, 'host');
|
const host =
|
||||||
|
getHeaderValue(header.response?.headers, 'host')
|
||||||
|
|| getHeaderValue(request.headers, 'host');
|
||||||
if (host) obj.host = host;
|
if (host) obj.host = host;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -309,7 +311,9 @@ export function genVlessLink(input: GenVlessLinkInput): string {
|
||||||
const request = tcp.header.request;
|
const request = tcp.header.request;
|
||||||
if (request) {
|
if (request) {
|
||||||
params.set('path', request.path.join(','));
|
params.set('path', request.path.join(','));
|
||||||
const host = getHeaderValue(request.headers, 'host');
|
const host =
|
||||||
|
getHeaderValue(tcp.header.response?.headers, 'host')
|
||||||
|
|| getHeaderValue(request.headers, 'host');
|
||||||
if (host) params.set('host', host);
|
if (host) params.set('host', host);
|
||||||
params.set('headerType', 'http');
|
params.set('headerType', 'http');
|
||||||
}
|
}
|
||||||
|
|
@ -387,7 +391,9 @@ function writeNetworkParams(stream: NonNullable<Inbound['streamSettings']>, para
|
||||||
const request = tcp.header.request;
|
const request = tcp.header.request;
|
||||||
if (request) {
|
if (request) {
|
||||||
params.set('path', request.path.join(','));
|
params.set('path', request.path.join(','));
|
||||||
const host = getHeaderValue(request.headers, 'host');
|
const host =
|
||||||
|
getHeaderValue(tcp.header.response?.headers, 'host')
|
||||||
|
|| getHeaderValue(request.headers, 'host');
|
||||||
if (host) params.set('host', host);
|
if (host) params.set('host', host);
|
||||||
params.set('headerType', 'http');
|
params.set('headerType', 'http');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1758,6 +1758,48 @@ export default function InboundFormModal({
|
||||||
if (headerType !== 'http') return null;
|
if (headerType !== 'http') return null;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<Form.Item
|
||||||
|
label="Request version"
|
||||||
|
name={[
|
||||||
|
'streamSettings', 'tcpSettings', 'header',
|
||||||
|
'request', 'version',
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input placeholder="1.1" />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label="Request method"
|
||||||
|
name={[
|
||||||
|
'streamSettings', 'tcpSettings', 'header',
|
||||||
|
'request', 'method',
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input placeholder="GET" />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label="Request path"
|
||||||
|
name={[
|
||||||
|
'streamSettings', 'tcpSettings', 'header',
|
||||||
|
'request', 'path',
|
||||||
|
]}
|
||||||
|
getValueProps={(v) => ({ value: Array.isArray(v) ? v.join(',') : v })}
|
||||||
|
getValueFromEvent={(e) => {
|
||||||
|
const raw = (e?.target?.value ?? '') as string;
|
||||||
|
const parts = raw.split(',').map((s) => s.trim()).filter(Boolean);
|
||||||
|
return parts.length > 0 ? parts : ['/'];
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Input placeholder="/" />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
label="Request headers"
|
||||||
|
name={[
|
||||||
|
'streamSettings', 'tcpSettings', 'header',
|
||||||
|
'request', 'headers',
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<HeaderMapEditor mode="v2" />
|
||||||
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label="Response version"
|
label="Response version"
|
||||||
name={[
|
name={[
|
||||||
|
|
|
||||||
|
|
@ -696,8 +696,17 @@ func applyShareNetworkParams(stream map[string]any, streamNetwork string, params
|
||||||
request := header["request"].(map[string]any)
|
request := header["request"].(map[string]any)
|
||||||
requestPath, _ := request["path"].([]any)
|
requestPath, _ := request["path"].([]any)
|
||||||
params["path"] = requestPath[0].(string)
|
params["path"] = requestPath[0].(string)
|
||||||
|
host := ""
|
||||||
|
if response, ok := header["response"].(map[string]any); ok {
|
||||||
|
if respHeaders, ok := response["headers"].(map[string]any); ok {
|
||||||
|
host = searchHost(respHeaders)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if host == "" {
|
||||||
headers, _ := request["headers"].(map[string]any)
|
headers, _ := request["headers"].(map[string]any)
|
||||||
params["host"] = searchHost(headers)
|
host = searchHost(headers)
|
||||||
|
}
|
||||||
|
params["host"] = host
|
||||||
params["headerType"] = "http"
|
params["headerType"] = "http"
|
||||||
}
|
}
|
||||||
case "kcp":
|
case "kcp":
|
||||||
|
|
@ -743,8 +752,17 @@ func applyVmessNetworkParams(stream map[string]any, network string, obj map[stri
|
||||||
request := header["request"].(map[string]any)
|
request := header["request"].(map[string]any)
|
||||||
requestPath, _ := request["path"].([]any)
|
requestPath, _ := request["path"].([]any)
|
||||||
obj["path"] = requestPath[0].(string)
|
obj["path"] = requestPath[0].(string)
|
||||||
|
host := ""
|
||||||
|
if response, ok := header["response"].(map[string]any); ok {
|
||||||
|
if respHeaders, ok := response["headers"].(map[string]any); ok {
|
||||||
|
host = searchHost(respHeaders)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if host == "" {
|
||||||
headers, _ := request["headers"].(map[string]any)
|
headers, _ := request["headers"].(map[string]any)
|
||||||
obj["host"] = searchHost(headers)
|
host = searchHost(headers)
|
||||||
|
}
|
||||||
|
obj["host"] = host
|
||||||
}
|
}
|
||||||
case "kcp":
|
case "kcp":
|
||||||
applyKcpShareObj(stream, obj)
|
applyKcpShareObj(stream, obj)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue