forked from test2/Amnezia-Web-Panel-main
Show Hysteria stop errors and add live container logs viewer.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -91,12 +91,95 @@ class HysteriaManager:
|
||||
)
|
||||
return 'Up' in out
|
||||
|
||||
def get_logs(self, protocol_type=None, tail=200):
|
||||
"""Return recent docker logs for the Hysteria container."""
|
||||
name = self._container_name(protocol_type or self.protocol)
|
||||
tail = max(20, min(int(tail or 200), 2000))
|
||||
out, err, code = self.ssh.run_sudo_command(
|
||||
f"docker logs --tail {tail} --timestamps {name} 2>&1",
|
||||
timeout=30,
|
||||
)
|
||||
text = (out or err or '').strip()
|
||||
if code != 0 and not text:
|
||||
text = f'(no logs: exit {code})'
|
||||
return text
|
||||
|
||||
def get_container_diagnostics(self, protocol_type=None):
|
||||
"""Inspect why the container is stopped / unhealthy."""
|
||||
name = self._container_name(protocol_type or self.protocol)
|
||||
fmt = (
|
||||
'{{.State.Status}}|{{.State.Running}}|{{.State.ExitCode}}|'
|
||||
'{{.State.OOMKilled}}|{{.State.Error}}|{{.State.FinishedAt}}|'
|
||||
'{{.State.StartedAt}}'
|
||||
)
|
||||
out, _, code = self.ssh.run_sudo_command(
|
||||
f"docker inspect -f '{fmt}' {name} 2>/dev/null"
|
||||
)
|
||||
diag = {
|
||||
'status': 'unknown',
|
||||
'running': False,
|
||||
'exit_code': None,
|
||||
'oom_killed': False,
|
||||
'error': '',
|
||||
'finished_at': '',
|
||||
'started_at': '',
|
||||
'recent_logs': '',
|
||||
'error_summary': '',
|
||||
}
|
||||
if code != 0 or not (out or '').strip():
|
||||
diag['status'] = 'missing'
|
||||
diag['error_summary'] = 'Container not found'
|
||||
return diag
|
||||
|
||||
parts = (out or '').strip().split('|')
|
||||
while len(parts) < 7:
|
||||
parts.append('')
|
||||
status, running, exit_code, oom, err, finished, started = parts[:7]
|
||||
diag['status'] = status or 'unknown'
|
||||
diag['running'] = str(running).lower() == 'true'
|
||||
try:
|
||||
diag['exit_code'] = int(exit_code)
|
||||
except Exception:
|
||||
diag['exit_code'] = None
|
||||
diag['oom_killed'] = str(oom).lower() == 'true'
|
||||
diag['error'] = (err or '').strip()
|
||||
diag['finished_at'] = (finished or '').strip()
|
||||
diag['started_at'] = (started or '').strip()
|
||||
diag['recent_logs'] = self.get_logs(protocol_type, tail=40)
|
||||
|
||||
# Build human-readable summary for the UI badge
|
||||
if diag['running']:
|
||||
diag['error_summary'] = ''
|
||||
elif diag['oom_killed']:
|
||||
diag['error_summary'] = 'OOM killed (out of memory)'
|
||||
elif diag['error']:
|
||||
diag['error_summary'] = diag['error']
|
||||
elif diag['exit_code'] not in (None, 0):
|
||||
# Pull last meaningful log line
|
||||
last = ''
|
||||
for line in reversed((diag['recent_logs'] or '').splitlines()):
|
||||
line = line.strip()
|
||||
if line:
|
||||
last = line
|
||||
break
|
||||
if last:
|
||||
# strip docker timestamp prefix if present
|
||||
if ' ' in last and last[0].isdigit():
|
||||
last = last.split(' ', 1)[-1]
|
||||
diag['error_summary'] = f"exit {diag['exit_code']}: {last[:180]}"
|
||||
else:
|
||||
diag['error_summary'] = f"exited with code {diag['exit_code']}"
|
||||
elif diag['status'] and diag['status'] != 'running':
|
||||
diag['error_summary'] = f"status: {diag['status']}"
|
||||
return diag
|
||||
|
||||
def get_server_status(self, protocol_type=None):
|
||||
protocol_type = protocol_type or self.protocol
|
||||
exists = self.check_protocol_installed(protocol_type)
|
||||
running = self.check_container_running(protocol_type)
|
||||
meta = self._read_metadata() if exists else {}
|
||||
clients = self._read_clients() if exists else []
|
||||
diag = {}
|
||||
|
||||
# Auto-migrate older installs (bridge NAT / userpass / missing BBR+obfs)
|
||||
if exists:
|
||||
@@ -120,6 +203,15 @@ class HysteriaManager:
|
||||
meta = self._read_metadata()
|
||||
except Exception as e:
|
||||
logger.warning('Hysteria migrate failed: %s', e)
|
||||
diag = {'error_summary': f'migrate failed: {e}'}
|
||||
|
||||
if exists:
|
||||
try:
|
||||
diag = self.get_container_diagnostics(protocol_type)
|
||||
running = bool(diag.get('running'))
|
||||
except Exception as e:
|
||||
logger.warning('Hysteria diagnostics failed: %s', e)
|
||||
diag = {'error_summary': str(e)}
|
||||
|
||||
return {
|
||||
'container_exists': exists,
|
||||
@@ -132,6 +224,10 @@ class HysteriaManager:
|
||||
'base_protocol': self.PROTOCOL,
|
||||
'instance': self._instance_index(protocol_type),
|
||||
'container_name': self._container_name(protocol_type),
|
||||
'error': diag.get('error_summary') or '',
|
||||
'exit_code': diag.get('exit_code'),
|
||||
'container_status': diag.get('status') or '',
|
||||
'recent_logs': diag.get('recent_logs') or '',
|
||||
}
|
||||
|
||||
# ===================== HELPERS =====================
|
||||
|
||||
Reference in New Issue
Block a user