Make Hysteria UDP port selectable when 443 is busy.

Show an inline port field on the stopped card, a dedicated install port, and clearer bind-error hints so admins can switch to 8998/8443 without guessing.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
orohi
2026-07-26 02:45:24 +03:00
co-authored by Cursor
parent d480d149b6
commit 258dc9f100
3 changed files with 120 additions and 39 deletions
+30 -22
View File
@@ -156,33 +156,38 @@ class HysteriaManager:
elif diag['error']:
diag['error_summary'] = diag['error']
elif diag['exit_code'] not in (None, 0):
# Pull last meaningful log line
# Prefer bind/port conflicts over generic FATAL lines
bind_line = ''
last = ''
for line in reversed((diag['recent_logs'] or '').splitlines()):
line = self._strip_ansi(line).strip()
for raw in reversed((diag['recent_logs'] or '').splitlines()):
line = self._strip_ansi(raw).strip()
if not line:
continue
# Prefer the FATAL / bind error line
if 'address already in use' in line.lower() or 'FATAL' in line or 'failed to load' in line.lower():
# strip docker --timestamps prefix
m_ts = re.match(r'^\d{4}-\d{2}-\d{2}T\S+\s+(.*)$', line)
if m_ts:
line = m_ts.group(1).strip()
low = line.lower()
if 'address already in use' in low and not bind_line:
bind_line = line
if ('FATAL' in line or 'failed to load' in low) and not last:
last = line
break
if not last:
last = line
if last:
# strip docker timestamp prefix if present
if ' ' in last and (last[0].isdigit() or last[0] == '2'):
last = last.split(' ', 1)[-1]
last = self._strip_ansi(last)
if 'address already in use' in last.lower():
m = re.search(r'listen udp :(\d+)', last, re.I) or re.search(r':(\d+):\s*bind', last)
p = m.group(1) if m else '?'
diag['error_summary'] = (
f'UDP port {p} already in use — open Settings and choose another port'
)
else:
# keep message short for badge
short = re.sub(r'\s+', ' ', last)[:160]
diag['error_summary'] = f"exit {diag['exit_code']}: {short}"
pick = bind_line or last
if pick and 'address already in use' in pick.lower():
m = re.search(r'listen udp :(\d+)', pick, re.I) or re.search(r':(\d+):\s*bind', pick)
p = m.group(1) if m else '?'
if p == '?':
m2 = re.search(r'udp\s*:(\d+)', pick, re.I)
p = m2.group(1) if m2 else '?'
diag['error_summary'] = (
f'UDP port {p} already in use — change port below (try 8998 or 8443)'
)
diag['port_busy'] = True
elif pick:
short = re.sub(r'\s+', ' ', pick)[:160]
diag['error_summary'] = f"exit {diag['exit_code']}: {short}"
else:
diag['error_summary'] = f"exited with code {diag['exit_code']}"
elif diag['status'] and diag['status'] != 'running':
@@ -244,6 +249,7 @@ class HysteriaManager:
'exit_code': diag.get('exit_code'),
'container_status': diag.get('status') or '',
'recent_logs': diag.get('recent_logs') or '',
'port_busy': bool(diag.get('port_busy')),
}
# ===================== HELPERS =====================
@@ -460,8 +466,10 @@ iptables -C INPUT -p udp --dport {port} -j ACCEPT 2>/dev/null || iptables -I INP
def get_settings(self):
meta = self._ensure_secrets(self._read_metadata())
port = int(meta.get('port') or self.DEFAULT_PORT)
return {
'port': int(meta.get('port') or self.DEFAULT_PORT),
'port': port,
'suggested_port': 8998 if port in (443, 80) else port,
'domain': meta.get('domain') or '',
'email': meta.get('email') or '',
'protocol': self.protocol,