Let Xray pick standard 443 or a custom listen port (v3.1.2).

Default to custom 8443 so 443 stays free unless the user chooses standard.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
orohimaru2
2026-08-09 19:23:49 +03:00
co-authored by Cursor
parent 4720d8f2bd
commit d915b91f01
8 changed files with 125 additions and 35 deletions
+70 -7
View File
@@ -801,6 +801,21 @@
<div class="form-hint" id="xrayPortsWarning" style="margin-bottom: var(--space-md); padding: var(--space-sm) var(--space-md); border-radius: var(--radius-sm); background: rgba(234,179,8,0.12); border: 1px solid rgba(234,179,8,0.35); color: var(--text);">
{{ _('xray_ports_warning_cf') }}
</div>
<div class="form-group">
<label class="form-label">{{ _('xray_listen_port') }} *</label>
<div style="display:flex; flex-direction:column; gap:8px; margin-bottom:8px;">
<label style="display:flex; align-items:flex-start; gap:8px; cursor:pointer;">
<input type="radio" name="xrayPortMode" value="standard" onchange="updateXrayPortUi()">
<span>{{ _('xray_port_standard') }}</span>
</label>
<label style="display:flex; align-items:flex-start; gap:8px; cursor:pointer;">
<input type="radio" name="xrayPortMode" value="custom" checked onchange="updateXrayPortUi()">
<span>{{ _('xray_port_custom') }}</span>
</label>
</div>
<input class="form-input" type="number" id="installXrayPort" value="8443" min="1" max="65535" oninput="syncXrayPortToInstall()">
<div class="form-hint" id="xrayPortHint">{{ _('xray_port_hint') }}</div>
</div>
<div class="form-group">
<label class="form-label">{{ _('xray_domain') }} *</label>
<input class="form-input" type="text" id="installXrayDomain" placeholder="vpn.example.com" oninput="updateXrayDnsHint()">
@@ -2444,6 +2459,47 @@
return el ? el.value : 'cloudflare';
}
function getXrayPortMode() {
const el = document.querySelector('input[name="xrayPortMode"]:checked');
return el ? el.value : 'custom';
}
function syncXrayPortToInstall() {
const portInput = document.getElementById('installPort');
const xrPort = document.getElementById('installXrayPort');
if (!portInput || !xrPort) return;
if (getXrayPortMode() === 'standard') {
portInput.value = '443';
xrPort.value = '443';
} else {
const v = parseInt(xrPort.value, 10);
portInput.value = (v >= 1 && v <= 65535) ? String(v) : '8443';
}
}
function updateXrayPortUi() {
const mode = getXrayPortMode();
const xrPort = document.getElementById('installXrayPort');
const hint = document.getElementById('xrayPortHint');
if (xrPort) {
if (mode === 'standard') {
xrPort.value = '443';
xrPort.disabled = true;
} else {
xrPort.disabled = false;
if (!xrPort.value || xrPort.value === '443') {
xrPort.value = currentInstallAnother
? String(nextSuggestedPort(currentInstallProto, 8443))
: '8443';
}
}
}
if (hint) {
hint.textContent = mode === 'standard' ? _('xray_port_hint_standard') : _('xray_port_hint');
}
syncXrayPortToInstall();
}
function updateXrayAcmeUi() {
const method = getXrayAcmeMethod();
const cfGroup = document.getElementById('xrayCfTokenGroup');
@@ -2451,10 +2507,6 @@
if (cfGroup) cfGroup.style.display = method === 'cloudflare' ? '' : 'none';
if (warn) warn.textContent = method === 'cloudflare' ? _('xray_ports_warning_cf') : _('xray_ports_warning');
updateXrayDnsHint();
const portHint = document.getElementById('installPortHint');
if (portHint && protoBase(currentInstallProto) === 'xray' && !currentInstallAnother) {
portHint.textContent = method === 'cloudflare' ? _('port_xray_hint_cf') : _('port_xray_hint');
}
}
function updateXrayDnsHint() {
@@ -2503,15 +2555,18 @@
portInput.disabled = true;
portHint.textContent = _('dns_internal_hint');
} else if (base === 'xray') {
portLabel.textContent = _('port') + ' (TCP)';
if (portGroup) portGroup.style.display = 'none';
portInput.disabled = false;
portInput.value = currentInstallAnother ? nextSuggestedPort(currentInstallProto, 443) : '443';
portHint.textContent = currentInstallAnother ? _('port_next_instance_hint') : _('port_xray_hint');
if (xrayOpts) xrayOpts.style.display = 'block';
const xrDomain = document.getElementById('installXrayDomain');
const xrEmail = document.getElementById('installXrayEmail');
if (xrDomain && !xrDomain.value && SERVER_SSL_DOMAIN) xrDomain.value = SERVER_SSL_DOMAIN;
if (xrEmail && !xrEmail.value && SERVER_SSL_EMAIL) xrEmail.value = SERVER_SSL_EMAIL;
const stdRadio = document.querySelector('input[name="xrayPortMode"][value="standard"]');
const customRadio = document.querySelector('input[name="xrayPortMode"][value="custom"]');
if (customRadio) customRadio.checked = true;
if (stdRadio) stdRadio.checked = false;
updateXrayPortUi();
updateXrayAcmeUi();
} else if (base === 'telemt') {
portLabel.textContent = _('port') + ' (TCP)';
@@ -2599,10 +2654,16 @@
const xrEmail = (document.getElementById('installXrayEmail')?.value || '').trim();
const acme = getXrayAcmeMethod();
const cfToken = (document.getElementById('installXrayCfToken')?.value || '').trim();
syncXrayPortToInstall();
const xrPort = parseInt(document.getElementById('installPort')?.value, 10);
if (!xrDomain || !xrEmail) {
showToast(_('xray_domain') + ' / ' + _('xray_email'), 'error');
return;
}
if (!(xrPort >= 1 && xrPort <= 65535)) {
showToast(_('xray_listen_port'), 'error');
return;
}
if (acme === 'cloudflare' && !cfToken) {
showToast(_('xray_cf_token'), 'error');
return;
@@ -2667,6 +2728,8 @@
params.hysteria_domain = document.getElementById('installHysteriaDomain').value.trim();
params.hysteria_email = document.getElementById('installHysteriaEmail').value.trim();
} else if (protoBase(currentInstallProto) === 'xray') {
syncXrayPortToInstall();
params.port = document.getElementById('installPort').value;
params.xray_domain = (document.getElementById('installXrayDomain')?.value || '').trim();
params.xray_email = (document.getElementById('installXrayEmail')?.value || '').trim();
params.xray_acme_method = getXrayAcmeMethod();