3x-ui/web/html/modals/xray_outbound_modal.html

144 lines
4.9 KiB
HTML
Raw Normal View History

{{define "modals/outModal"}}
2023-12-05 17:13:36 +00:00
<a-modal id="out-modal" v-model="outModal.visible" :title="outModal.title" @ok="outModal.ok"
2026-05-04 11:20:24 +00:00
:confirm-loading="outModal.confirmLoading" :closable="true" :mask-closable="false"
:ok-button-props="{ props: { disabled: !outModal.isValid } }" :style="{ overflow: 'hidden' }"
:ok-text="outModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme">
{{template "form/outbound" .}}
2023-12-05 17:13:36 +00:00
</a-modal>
<script>
const outModal = {
title: '',
visible: false,
confirmLoading: false,
okText: '{{ i18n "sure" }}',
isEdit: false,
confirm: null,
outbound: new Outbound(),
jsonMode: false,
link: '',
cm: null,
duplicateTag: false,
isValid: true,
activeKey: '1',
2023-12-05 22:03:38 +00:00
tags: [],
2023-12-05 17:13:36 +00:00
ok() {
ObjectUtil.execute(outModal.confirm, outModal.outbound.toJson());
},
2026-05-04 11:20:24 +00:00
show({
title = '',
okText = '{{ i18n "sure" }}',
outbound,
confirm = (outbound) => {},
isEdit = false,
tags = []
}) {
2023-12-05 17:13:36 +00:00
this.title = title;
this.okText = okText;
this.confirm = confirm;
this.jsonMode = false;
this.link = '';
this.activeKey = '1';
this.visible = true;
this.outbound = isEdit ? Outbound.fromJson(outbound) : new Outbound();
this.isEdit = isEdit;
2023-12-05 22:03:38 +00:00
this.tags = tags;
2023-12-05 17:13:36 +00:00
this.check()
},
close() {
outModal.visible = false;
outModal.loading(false);
},
2026-05-04 11:20:24 +00:00
loading(loading = true) {
2023-12-05 17:13:36 +00:00
outModal.confirmLoading = loading;
},
2026-05-04 11:20:24 +00:00
check() {
if (outModal.outbound.tag == '' || outModal.tags.includes(outModal.outbound.tag)) {
2023-12-05 17:13:36 +00:00
this.duplicateTag = true;
this.isValid = false;
} else {
this.duplicateTag = false;
this.isValid = true;
}
},
toggleJson(jsonTab) {
textAreaObj = document.getElementById('outboundJson');
2026-05-04 11:20:24 +00:00
if (jsonTab) {
if (this.cm != null) {
this.cm.toTextArea();
this.cm = null;
2023-12-05 17:13:36 +00:00
}
textAreaObj.value = JSON.stringify(this.outbound.toJson(), null, 2);
this.cm = CodeMirror.fromTextArea(textAreaObj, app.cmOptions);
2026-05-04 11:20:24 +00:00
this.cm.on('change', editor => {
2023-12-05 17:13:36 +00:00
value = editor.getValue();
2026-05-04 11:20:24 +00:00
if (this.isJsonString(value)) {
2023-12-05 17:13:36 +00:00
this.outbound = Outbound.fromJson(JSON.parse(value));
this.check();
}
});
this.activeKey = '2';
} else {
2026-05-04 11:20:24 +00:00
if (this.cm != null) {
this.cm.toTextArea();
this.cm = null;
2023-12-05 17:13:36 +00:00
}
this.activeKey = '1';
}
},
isJsonString(str) {
try {
JSON.parse(str);
} catch (e) {
return false;
}
return true;
},
};
new Vue({
delimiters: ['[[', ']]'],
el: '#out-modal',
data: {
outModal: outModal,
get outbound() {
return outModal.outbound;
},
},
refactor(xhttp): split fields by direction, expand outbound coverage Audit panel xhttp config against xray-core's runtime paths and split fields per direction so each side carries only what it actually uses: - Bidirectional (must match): host, path, mode, all xPadding*, session*/seq*, uplinkData*/Key, scMaxEachPostBytes - Server-only (inbound): noSSEHeader, scMaxBufferedPosts, scStreamUpServerSecs, serverMaxHeaderBytes - Client-only (outbound): uplinkHTTPMethod, uplinkChunkSize, noGRPCHeader, scMinPostsIntervalMs, xmux The inbound previously held client-only fields and the outbound was missing every must-match field beyond host/path/mode — meaning a panel-built outbound couldn't connect to an inbound with a custom xPaddingKey/sessionKey/etc. Headers stay on the inbound for URL-share purposes only; xray's listener ignores them at runtime, but they travel through the share link's `extra` blob so the client picks them up. Renames the URL helpers (applyXhttpPadding* -> applyXhttpExtra*) since the blob now carries more than padding, and folds path/host/mode into the helper so each link generator's xhttp branch is one line. Adds two enforcement points for xray's "uplinkHTTPMethod=GET only in packet-up" rule: the GET option is disabled when mode != packet-up, and a watcher on the outbound modal auto-clears GET when the user switches modes. Hides the XMUX block behind an `enableXmux` switch on the outbound form (mirrors the QUIC Params toggle) so the section doesn't clutter the form by default; fromJson auto-flips it on for outbounds with saved xmux config. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-07 17:26:40 +00:00
watch: {
// xray-core's SplitHTTPConfig.Build() rejects "GET" as
// uplinkHTTPMethod outside packet-up mode. Clear the field
// instead of carrying an invalid combination through.
"outModal.outbound.stream.xhttp.mode"(newMode) {
const xhttp = outModal.outbound.stream && outModal.outbound.stream.xhttp;
if (xhttp && xhttp.uplinkHTTPMethod === "GET" && newMode !== "packet-up") {
xhttp.uplinkHTTPMethod = "";
}
},
},
2023-12-05 17:13:36 +00:00
methods: {
streamNetworkChange() {
2026-05-04 11:20:24 +00:00
if (this.outModal.outbound.protocol == Protocols.VLESS && !outModal.outbound
.canEnableTlsFlow()) {
2023-12-05 17:13:36 +00:00
delete this.outModal.outbound.settings.flow;
}
},
canEnableTls() {
return this.outModal.outbound.canEnableTls();
},
2026-05-04 11:20:24 +00:00
convertLink() {
2023-12-05 17:13:36 +00:00
newOutbound = Outbound.fromLink(outModal.link);
2026-05-04 11:20:24 +00:00
if (newOutbound) {
2023-12-05 17:13:36 +00:00
this.outModal.outbound = newOutbound;
this.outModal.toggleJson(true);
this.outModal.check();
2026-05-04 11:20:24 +00:00
this.$message.success('Link imported successfully...');
2023-12-05 17:13:36 +00:00
outModal.link = '';
} else {
this.$message.error('Wrong Link!');
outModal.link = '';
}
},
},
});
</script>
2026-05-04 11:20:24 +00:00
{{end}}