Port-forward UX: drop tile Step 4 and misleading local 'ready' status
The Element Call tile still appended a synthetic 'Step 4: Router Setup
Needed' to the domain diagnostic checklist, and every port table carried a
'Sovran_SystemsOS Status' column with Ready / Not ready yet verdicts.
Both were misleading: port forwarding happens on the router, which this
computer cannot inspect. A local ss/firewall probe can neither prove nor
disprove that forwarding works — and the LiveKit TURN relay range binds on
demand, so it reported 'Not ready yet' even on a perfectly working system.
- server.py: add ROUTER_FORWARD_ONLY_UNITS ({livekit.service}); skip the
local probe for those units, drop the step-4 append, replace extra_ports
with router_ports (no status field), and exclude router-only ports from
both tile health and /api/ports/health so they can't raise false alarms.
- helpers.js: new shared renderPortForwardGuideHtml() — one intro naming the
internal IP, explicit instructions (same internal/external port, match the
protocol, use port-range fields for 30000-40000), a colour-coded
TCP / UDP / TCP+UDP badge per row, and a closing note that the only real
test is loading the service from a phone on mobile data.
- features.js: enable-time modal uses the shared guide and now always lists
every port to forward (the old local pre-filter hid ports the user still
had to open).
- service-detail.js: tile port section uses the same guide; the SSH/non-domain
branch keeps a small 'not open on this computer yet' hint, which is a real
local fact, separate from router forwarding.
- onboarding.js: step 3 router note reworded to match (same number for
internal/external, notes that Element Call adds UDP ports).
- domain-setup.css: styles for the protocol badges and instruction list.
Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
This commit is contained in:
co-authored by
arena-agent
parent
0e3cab78f8
commit
419680a847
@@ -514,85 +514,41 @@ async function openServiceDetailModal(unit, name, icon) {
|
||||
domainActionHtml +
|
||||
'</div>';
|
||||
|
||||
if (unit === "livekit.service" && data.extra_ports && data.extra_ports.length > 0) {
|
||||
if (data.router_ports && data.router_ports.length > 0) {
|
||||
var trimmedInternalIp = data.internal_ip ? String(data.internal_ip).trim() : "";
|
||||
var internalIp = trimmedInternalIp || "";
|
||||
var internalIpHtml = internalIp ? escHtml(internalIp) : "Could not detect";
|
||||
var forwardNote = internalIp
|
||||
? 'Forward each port below in your router to this computer’s internal IP <code class="port-req-internal-ip">' + internalIpHtml + '</code>, using the same internal and external port.'
|
||||
: 'Forward each port below in your router to this computer’s internal IP, using the same internal and external port.';
|
||||
var domainConfigured = !!(data.domain && String(data.domain).trim());
|
||||
var extraRows = "";
|
||||
data.extra_ports.forEach(function(p) {
|
||||
var statusIcon, statusClass2;
|
||||
if (!effectiveEnabled) {
|
||||
statusIcon = "⚠ Configure Element Call first";
|
||||
statusClass2 = "port-status-open";
|
||||
} else if (!domainConfigured) {
|
||||
statusIcon = "⚠ Configure domain first";
|
||||
statusClass2 = "port-status-open";
|
||||
} else if (p.status === "listening") {
|
||||
statusIcon = "✅ Ready";
|
||||
statusClass2 = "port-status-listening";
|
||||
} else if (p.status === "firewall_open") {
|
||||
statusIcon = "✅ Ready";
|
||||
statusClass2 = "port-status-open";
|
||||
} else if (p.status === "closed") {
|
||||
statusIcon = "❌ Not ready yet";
|
||||
statusClass2 = "port-status-closed";
|
||||
} else {
|
||||
statusIcon = "— Could not check";
|
||||
statusClass2 = "port-status-unknown";
|
||||
}
|
||||
extraRows += '<tr>' +
|
||||
'<td class="svc-detail-port-table-port">' + escHtml(p.port) + '</td>' +
|
||||
'<td class="svc-detail-port-table-proto">' + escHtml(p.protocol) + '</td>' +
|
||||
'<td class="svc-detail-port-table-desc">' + escHtml(p.description || "") + '</td>' +
|
||||
'<td class="svc-detail-port-table-status ' + statusClass2 + '">' + statusIcon + '</td>' +
|
||||
'</tr>';
|
||||
});
|
||||
html += '<div class="svc-detail-section">' +
|
||||
'<div class="svc-detail-section-title">Ports to Forward in Your Router</div>' +
|
||||
'<div class="svc-detail-port-note">' + forwardNote + '</div>' +
|
||||
'<table class="svc-detail-port-table">' +
|
||||
'<thead><tr><th>Port</th><th>Protocol</th><th>Used For</th><th>Sovran_SystemsOS Status</th></tr></thead>' +
|
||||
'<tbody>' + extraRows + '</tbody>' +
|
||||
'</table>' +
|
||||
'<div class="svc-detail-port-note">✅ = Sovran_SystemsOS is ready on this computer. Router-side forwarding itself can only be verified from outside your network (e.g. a phone on mobile data).</div>' +
|
||||
renderPortForwardGuideHtml(data.router_ports, {
|
||||
internalIp: trimmedInternalIp || null,
|
||||
tableClass: "svc-detail-port-table",
|
||||
introClass: "svc-detail-port-note",
|
||||
noteClass: "svc-detail-port-note",
|
||||
}) +
|
||||
'</div>';
|
||||
}
|
||||
} else if (data.port_statuses && data.port_statuses.length > 0) {
|
||||
// Non-domain services (SSH) keep local single-port checks.
|
||||
var portTableRows = "";
|
||||
data.port_statuses.forEach(function(p) {
|
||||
var statusIcon, statusClass2;
|
||||
if (p.status === "listening") {
|
||||
statusIcon = "✅ Ready";
|
||||
statusClass2 = "port-status-listening";
|
||||
} else if (p.status === "firewall_open") {
|
||||
statusIcon = "✅ Ready";
|
||||
statusClass2 = "port-status-open";
|
||||
} else if (p.status === "closed") {
|
||||
statusIcon = "❌ Not ready";
|
||||
statusClass2 = "port-status-closed";
|
||||
} else {
|
||||
statusIcon = "— Could not check";
|
||||
statusClass2 = "port-status-unknown";
|
||||
}
|
||||
portTableRows += '<tr>' +
|
||||
'<td class="svc-detail-port-table-port">' + escHtml(p.port) + '</td>' +
|
||||
'<td class="svc-detail-port-table-proto">' + escHtml(p.protocol) + '</td>' +
|
||||
'<td class="svc-detail-port-table-desc">' + escHtml(p.description || "") + '</td>' +
|
||||
'<td class="svc-detail-port-table-status ' + statusClass2 + '">' + statusIcon + '</td>' +
|
||||
'</tr>';
|
||||
// Non-domain services (e.g. SSH): show what to forward, plus a short
|
||||
// note if the service isn't actually listening on this computer yet.
|
||||
var localInternalIp = data.internal_ip ? String(data.internal_ip).trim() : "";
|
||||
var notListening = data.port_statuses.filter(function(p) {
|
||||
return p.status === "closed";
|
||||
});
|
||||
var localNote = notListening.length
|
||||
? '<div class="svc-detail-port-note port-status-closed">' +
|
||||
'⚠ Port ' + escHtml(notListening.map(function(p) { return p.port; }).join(", ")) +
|
||||
' is not open on this computer yet — enable the service below first, then forward it in your router.' +
|
||||
'</div>'
|
||||
: "";
|
||||
html += '<div class="svc-detail-section">' +
|
||||
'<div class="svc-detail-section-title">Port Requirements</div>' +
|
||||
'<div class="svc-detail-port-note">This shows whether Sovran_SystemsOS is ready to use this port on this computer. If you need access from outside your home network, forward this port in your router.</div>' +
|
||||
'<table class="svc-detail-port-table">' +
|
||||
'<thead><tr><th>Port</th><th>Protocol</th><th>Used For</th><th>Sovran_SystemsOS Status</th></tr></thead>' +
|
||||
'<tbody>' + portTableRows + '</tbody>' +
|
||||
'</table>' +
|
||||
'<div class="svc-detail-section-title">Ports to Forward in Your Router</div>' +
|
||||
'<div class="svc-detail-port-note">Only needed if you want to reach this service from <strong>outside</strong> your home network. On your local network it already works without any router changes.</div>' +
|
||||
renderPortForwardGuideHtml(data.port_statuses, {
|
||||
internalIp: localInternalIp || null,
|
||||
tableClass: "svc-detail-port-table",
|
||||
introClass: "svc-detail-port-note",
|
||||
noteClass: "svc-detail-port-note",
|
||||
}) +
|
||||
localNote +
|
||||
'</div>';
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user