Allow changing Hysteria UDP port when 443 is already in use.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
orohi
2026-07-26 02:35:55 +03:00
co-authored by Cursor
parent 7323f08c37
commit d480d149b6
8 changed files with 277 additions and 11 deletions
+93 -1
View File
@@ -747,6 +747,33 @@
</div>
</div>
<!-- ===== Hysteria Settings Modal ===== -->
<div class="modal-backdrop" id="hysteriaSettingsModal">
<div class="modal">
<div class="modal-header">
<h2 class="modal-title" id="hysteriaSettingsTitle">{{ _('hysteria_settings_title') }}</h2>
<button class="modal-close" onclick="closeModal('hysteriaSettingsModal')">×</button>
</div>
<input type="hidden" id="hysteriaSetProto" value="hysteria" />
<div class="form-group">
<label class="form-label">{{ _('hysteria_domain') }}</label>
<input class="form-input" type="text" id="hysteriaSetDomain" disabled>
</div>
<div class="form-group">
<label class="form-label">{{ _('port') }} (UDP)</label>
<input class="form-input" type="number" id="hysteriaSetPort" min="1" max="65535" value="8998">
<div class="form-hint">{{ _('hysteria_port_change_hint') }}</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" onclick="closeModal('hysteriaSettingsModal')">{{ _('cancel') }}</button>
<button class="btn btn-primary" onclick="saveHysteriaSettings()" id="hysteriaSaveBtn">
<span id="hysteriaSaveBtnText">{{ _('save') }}</span>
<div class="spinner hidden" id="hysteriaSaveSpinner"></div>
</button>
</div>
</div>
</div>
<!-- ===== NGINX Site Modal ===== -->
<div class="modal-backdrop" id="siteConfigModal">
<div class="modal" style="max-width:680px;">
@@ -1562,6 +1589,14 @@
<button class="btn btn-secondary btn-sm" onclick="showProtocolBackups('${proto}')" style="flex:1">${_('backup')}</button>
<button class="btn btn-danger btn-sm" onclick="uninstallProtocol('${proto}')">${_('uninstall')}</button>
`;
} else if (protoBase(proto) === 'hysteria') {
actionsEl.innerHTML = `
<button class="btn btn-secondary btn-sm" onclick="selectProtocolForConns('${proto}')" style="flex:1">${_('connections')}</button>
<button class="btn btn-secondary btn-sm" onclick="openHysteriaSettings('${proto}')">${_('hysteria_change_port')}</button>
<button class="btn btn-secondary btn-sm" onclick="showProtocolBackups('${proto}')">${_('backup')}</button>
<button class="btn btn-danger btn-sm" onclick="uninstallProtocol('${proto}')">${_('uninstall')}</button>
`;
showConnectionsSection();
} else {
actionsEl.innerHTML = `
<button class="btn btn-secondary btn-sm" onclick="selectProtocolForConns('${proto}')" style="flex:1">${_('connections')}</button>
@@ -1625,6 +1660,13 @@
<button class="btn btn-secondary btn-sm" onclick="showProtocolBackups('${proto}')">${_('backup')}</button>
<button class="btn btn-danger btn-sm" onclick="uninstallProtocol('${proto}')">${_('uninstall')}</button>
`;
} else if (protoBase(proto) === 'hysteria') {
actionsEl.innerHTML = `
<button class="btn btn-primary btn-sm" onclick="openHysteriaSettings('${proto}')" style="flex:1">${_('hysteria_change_port')}</button>
<button class="btn btn-secondary btn-sm" onclick="showContainerLogs('${proto}')">${_('logs_btn')}</button>
<button class="btn btn-secondary btn-sm" onclick="openInstallModal('${proto}')">${_('reinstall')}</button>
<button class="btn btn-danger btn-sm" onclick="uninstallProtocol('${proto}')">${_('uninstall')}</button>
`;
} else {
actionsEl.innerHTML = `
<button class="btn btn-primary btn-sm" onclick="openInstallModal('${proto}')" style="flex:1">${_('reinstall')}</button>
@@ -1972,7 +2014,7 @@
updateNginxDnsHint();
} else if (base === 'hysteria') {
portLabel.textContent = _('port') + ' (UDP)';
portInput.value = currentInstallAnother ? nextSuggestedPort(currentInstallProto, 443) : '443';
portInput.value = currentInstallAnother ? nextSuggestedPort(currentInstallProto, 8998) : '8998';
portInput.disabled = false;
portHint.textContent = currentInstallAnother ? _('port_next_instance_hint') : _('hysteria_port_hint');
hysteriaOpts.style.display = 'block';
@@ -2219,6 +2261,56 @@
}
}
// ========== Hysteria Settings (port) ==========
async function openHysteriaSettings(proto = 'hysteria') {
try {
const data = await apiCall(`/api/servers/${SERVER_ID}/hysteria/settings?protocol=${encodeURIComponent(proto)}`);
document.getElementById('hysteriaSetProto').value = proto;
document.getElementById('hysteriaSettingsTitle').textContent =
`${_('hysteria_settings_title')}${getProtoTitle(proto)}`;
document.getElementById('hysteriaSetDomain').value = data.domain || '';
// Prefer live/status port, then settings, then 8998
const livePort = (currentProtocolStatus[proto] || {}).port;
document.getElementById('hysteriaSetPort').value = livePort || data.port || 8998;
openModal('hysteriaSettingsModal');
} catch (err) {
showToast(_('error') + ': ' + err.message, 'error');
}
}
async function saveHysteriaSettings() {
const btn = document.getElementById('hysteriaSaveBtn');
const text = document.getElementById('hysteriaSaveBtnText');
const spinner = document.getElementById('hysteriaSaveSpinner');
const proto = document.getElementById('hysteriaSetProto').value || 'hysteria';
const port = parseInt(document.getElementById('hysteriaSetPort').value, 10);
if (!port || port < 1 || port > 65535) {
showToast(_('error') + ': invalid port', 'error');
return;
}
if (port === 80) {
showToast(_('error') + ': port 80 reserved', 'error');
return;
}
btn.disabled = true;
text.textContent = _('saving') || 'Saving...';
spinner.classList.remove('hidden');
try {
const res = await apiCall(`/api/servers/${SERVER_ID}/hysteria/settings`, 'POST', {
protocol: proto, port,
});
showToast(res.message || _('hysteria_settings_saved'), 'success');
closeModal('hysteriaSettingsModal');
setTimeout(() => checkServer(), 800);
} catch (err) {
showToast(_('error') + ': ' + err.message, 'error');
} finally {
btn.disabled = false;
text.textContent = _('save');
spinner.classList.add('hidden');
}
}
// ========== Connection Management ==========
function selectProtocolForConns(proto) {
document.getElementById('connectionsSection').style.display = '';