Polish pass: sysops wording, QR size, NWC toolbar, logo, diagnostics placement
Eight fixes from live testing: Systems Operational modal: - Drop the "test from your phone on mobile data" (hairpin NAT) sentence from the router note — too technical for the intended audience. - Node-only role: when BTCPay Server and Lightning Wallet Connections (LNURL) are both off, the router card becomes a simple "No router setup needed yet" note explaining that ports 80/443 only matter if one of those services is turned on. When one is enabled, the card shows the same port steps and live domain/port diagnostics as the Desktop + Server role (diagnostics now poll only enabled services). Service modals: - The Domain Diagnostic Checklist now shows only the domain-active step (Domain Configured). DNS and port diagnostics live in Systems Operational, which shows the full checklist. - Node-only role: BTCPay Server and Lightning Wallet Connections modals gain a "Ports to Forward in Your Router" section with the standard 80/443 wording and this computer's LAN address. - Domain setup and reconfigure dialogs no longer contain router port-forwarding instructions (already handled during Desktop + Server onboarding and shown in Systems Operational). Lightning Wallet Connections: - Refresh and New Wallet toolbar buttons now share one height and baseline (a leftover 12px top margin on Refresh was offsetting it). - The header status chip has a proper gap between the status dot and its label. Zeus Connect / QR codes: - QR codes render at the original 240px with the white frame and pixelated upscaling, restoring scannability. Brand: - The sidebar logo loads via /static/sovran-hub-icon.svg (same as the login page) instead of an inline <use> symbol — the gradient-heavy symbol did not render reliably. The icon sprite is hidden with the browser-safe zero-size pattern instead of display:none.
This commit is contained in:
@@ -221,9 +221,19 @@
|
||||
'</div></div>';
|
||||
}
|
||||
|
||||
function isNodeRole() {
|
||||
return (typeof _currentRole !== "undefined" && _currentRole === "node");
|
||||
}
|
||||
|
||||
function hasEnabledDomainService(services) {
|
||||
return DOMAIN_UNITS.some(function (u) {
|
||||
return services.some(function (s) { return s.unit === u && s.enabled; });
|
||||
});
|
||||
}
|
||||
|
||||
function whoUsesPorts() {
|
||||
if (typeof _currentRole !== "undefined" && _currentRole === "node") {
|
||||
return 'On this <strong>Bitcoin Node</strong> install, BTCPay Server and Lightning Wallet Connections (LNURL) are the domain services that use these ports.';
|
||||
if (isNodeRole()) {
|
||||
return 'On this <strong>Bitcoin Node</strong> install, <strong>BTCPay Server</strong> and <strong>Lightning Wallet Connections (LNURL)</strong> are the domain services that use these ports.';
|
||||
}
|
||||
return 'All your domain services share ports 80 and 443 — Matrix, BTCPay Server, VaultWarden, Nextcloud, WordPress, Haven Relay, Lightning Wallet Connections, and Element Calling.';
|
||||
}
|
||||
@@ -253,14 +263,24 @@
|
||||
step(3, "Turned off", "", off ? escHtml(offNames.join(", ")) : "None") +
|
||||
'</div>';
|
||||
|
||||
/* Router ports — repo wording (domain-prereqs.js / server.py) */
|
||||
html += '<div class="sysmodal-card">' +
|
||||
'<div class="sysmodal-card-title">' + icon("g-wifi") + 'Router — Ports to Forward</div>' +
|
||||
step(1, "Port 80", "TCP — HTTP (redirect to HTTPS)", fwd) +
|
||||
step(2, "Port 443", "TCP — HTTPS", fwd) +
|
||||
'<div class="sysnote"><div class="sysnote-title">' + icon("g-alert") + 'One router task</div>' +
|
||||
'<div class="sysnote-desc">Set the internal and external port to the <strong>same number</strong>. You only need to do this once — all your services share these two ports. Test from your phone on mobile data (your home network may not support hairpin NAT / loopback).</div></div>' +
|
||||
'</div>';
|
||||
/* Router ports — repo wording (domain-prereqs.js / server.py).
|
||||
Node-only role: ports matter only once BTCPay Server or Lightning
|
||||
Wallet Connections (LNURL) is turned on — until then, no router task. */
|
||||
if (isNodeRole() && !hasEnabledDomainService(services)) {
|
||||
html += '<div class="sysmodal-card">' +
|
||||
'<div class="sysmodal-card-title">' + icon("g-wifi") + 'Router</div>' +
|
||||
'<div class="sysnote"><div class="sysnote-title">' + icon("g-check") + 'No router setup needed yet</div>' +
|
||||
'<div class="sysnote-desc">Ports 80 and 443 only need to be forwarded on your router if you turn on <strong>BTCPay Server</strong> or <strong>Lightning Wallet Connections (LNURL)</strong>. If you enable one of them, come back here — this card will show exactly what to do.</div></div>' +
|
||||
'</div>';
|
||||
} else {
|
||||
html += '<div class="sysmodal-card">' +
|
||||
'<div class="sysmodal-card-title">' + icon("g-wifi") + 'Router — Ports to Forward</div>' +
|
||||
step(1, "Port 80", "TCP — HTTP (redirect to HTTPS)", fwd) +
|
||||
step(2, "Port 443", "TCP — HTTPS", fwd) +
|
||||
'<div class="sysnote"><div class="sysnote-title">' + icon("g-alert") + 'One router task</div>' +
|
||||
'<div class="sysnote-desc">Set the internal and external port to the <strong>same number</strong>. You only need to do this once — all your services share these two ports.</div></div>' +
|
||||
'</div>';
|
||||
}
|
||||
|
||||
/* Live domain diagnostics (sequential checklist from the backend —
|
||||
same data the domain-service modals show) */
|
||||
@@ -269,10 +289,13 @@
|
||||
'<div id="sys-check-steps"><div class="sysfineprint">Checking…</div></div>' +
|
||||
'</div>';
|
||||
|
||||
/* Who uses these ports */
|
||||
html += '<div class="sysnote" style="margin-top:14px">' +
|
||||
'<div class="sysnote-title">' + icon("g-antenna") + 'Who uses these ports</div>' +
|
||||
'<div class="sysnote-desc">' + whoUsesPorts() + '</div></div>';
|
||||
/* Who uses these ports (redundant on a Node install with no domain
|
||||
services on — the router note above already covers it) */
|
||||
if (!isNodeRole() || hasEnabledDomainService(services)) {
|
||||
html += '<div class="sysnote" style="margin-top:14px">' +
|
||||
'<div class="sysnote-title">' + icon("g-antenna") + 'Who uses these ports</div>' +
|
||||
'<div class="sysnote-desc">' + whoUsesPorts() + '</div></div>';
|
||||
}
|
||||
|
||||
$sysBody.innerHTML = html;
|
||||
$sysModal.classList.add("open");
|
||||
@@ -281,7 +304,7 @@
|
||||
var checkCard = document.getElementById("sys-check-card");
|
||||
var stepsEl = document.getElementById("sys-check-steps");
|
||||
var units = DOMAIN_UNITS.filter(function (u) {
|
||||
return services.some(function (s) { return s.unit === u; });
|
||||
return services.some(function (s) { return s.unit === u && s.enabled; });
|
||||
});
|
||||
if (!units.length || !checkCard || !stepsEl) return;
|
||||
checkCard.style.display = "";
|
||||
|
||||
@@ -97,7 +97,6 @@ function openDomainSetupModal(feat, onSaved) {
|
||||
nwcWarning +
|
||||
renderDomainNeedsHtml({ serviceName: feat.name, hostExample: hostExample, purpose: purpose }) +
|
||||
renderNjallaStepsHtml({ hostExample: hostExample, pasteHint: "below" }) +
|
||||
'<div class="onboarding-port-warn" id="domain-router-box" style="margin-top:12px;"></div>' +
|
||||
'<p style="margin-top:10px;">Enter the address for this service and paste the update command from Njal.la.</p>' +
|
||||
'</div>' +
|
||||
'<div class="domain-field-group"><label class="domain-field-label" for="domain-subdomain-input">Service address (e.g. ' + domainLabelExample + '):</label><input class="domain-field-input" type="text" id="domain-subdomain-input" placeholder="' + domainPlaceholder + '" /></div>' +
|
||||
@@ -144,8 +143,6 @@ function openDomainSetupModal(feat, onSaved) {
|
||||
|
||||
$domainSetupModal.classList.add("open");
|
||||
|
||||
// Fill the router port-forwarding box with this computer's LAN IP (best-effort)
|
||||
renderRouterPortsBox("domain-router-box");
|
||||
}
|
||||
|
||||
function openDomainReconfigureModal(feat, existingDomain, onSaved) {
|
||||
@@ -193,9 +190,7 @@ function openDomainReconfigureModal(feat, existingDomain, onSaved) {
|
||||
'<span style="display:inline-block;margin-top:4px;padding:4px 10px;background:var(--card-color);border:1px solid var(--border-color);border-radius:6px;font-family:monospace;font-size:1em;font-weight:700;">' + escHtml(externalIp) + '</span></li>' +
|
||||
'<li>If the IP is wrong or the record is missing, update it</li>' +
|
||||
'<li>If you changed the DDNS curl command, paste the updated one below</li>' +
|
||||
'<li>Confirm ports <strong>80</strong> and <strong>443</strong> (TCP) are still forwarded on your router to this computer — see the reminder below:</li>' +
|
||||
'</ol>' +
|
||||
'<div class="onboarding-port-warn" id="domain-router-box" style="margin-top:12px;"></div>' +
|
||||
'</div>' +
|
||||
'<div class="domain-field-group"><label class="domain-field-label" for="domain-subdomain-input">Service domain (e.g. ' + domainLabelExample + '):</label><input class="domain-field-input" type="text" id="domain-subdomain-input" placeholder="' + domainPlaceholder + '" value="' + escHtml(currentDomain) + '" /></div>' +
|
||||
'<div class="domain-field-group"><label class="domain-field-label" for="domain-ddns-input">Njal.la Dynamic DNS Update Command:</label><input class="domain-field-input" type="text" id="domain-ddns-input" placeholder="curl "https://njal.la/update/?h=' + domainPlaceholder + '&k=abc123&auto"" /><p class="domain-field-hint">ℹ Paste the full curl command from your Njal.la dashboard\'s Dynamic record</p></div>' +
|
||||
@@ -241,8 +236,6 @@ function openDomainReconfigureModal(feat, existingDomain, onSaved) {
|
||||
|
||||
$domainSetupModal.classList.add("open");
|
||||
|
||||
// Fill the router port-forwarding box with this computer's LAN IP (best-effort)
|
||||
renderRouterPortsBox("domain-router-box");
|
||||
}
|
||||
|
||||
function closeDomainSetupModal() {
|
||||
|
||||
@@ -718,7 +718,11 @@ async function openServiceDetailModal(unit, name, icon) {
|
||||
|
||||
// Section C: Domain diagnostics (domain services)
|
||||
if (data.needs_domain) {
|
||||
var steps = data.domain_check_steps || [];
|
||||
// The Hub shows only the domain-active step here; the full DNS and
|
||||
// port diagnostics live in the Systems Operational modal.
|
||||
var steps = (data.domain_check_steps || []).filter(function (s) {
|
||||
return Number(s.step) === 1;
|
||||
});
|
||||
var stepsHtml = "";
|
||||
steps.forEach(function(step) {
|
||||
var iconLabel = "—";
|
||||
@@ -747,6 +751,17 @@ async function openServiceDetailModal(unit, name, icon) {
|
||||
domainActionHtml +
|
||||
'</div>');
|
||||
|
||||
// Node-only role: BTCPay Server and Lightning Wallet Connections are
|
||||
// the domain services — surface the router task here. (Desktop + Server
|
||||
// set this up during onboarding; Systems Operational shows it too.)
|
||||
if (typeof _currentRole !== "undefined" && _currentRole === "node" &&
|
||||
(unit === "btcpayserver.service" || unit === "albyhub.service")) {
|
||||
addSetup('<div class="svc-detail-section">' +
|
||||
'<div class="svc-detail-section-title">Ports to Forward in Your Router</div>' +
|
||||
'<div class="onboarding-port-warn" id="svc-node-router-box"></div>' +
|
||||
'</div>');
|
||||
}
|
||||
|
||||
if (data.router_ports && data.router_ports.length > 0) {
|
||||
var trimmedInternalIp = data.internal_ip ? String(data.internal_ip).trim() : "";
|
||||
addSetup('<div class="svc-detail-section">' +
|
||||
@@ -1004,6 +1019,9 @@ async function openServiceDetailModal(unit, name, icon) {
|
||||
$credsBody.innerHTML = html;
|
||||
if (isNwc) _nwcWireTabs();
|
||||
_attachCopyHandlers($credsBody);
|
||||
if (document.getElementById("svc-node-router-box")) {
|
||||
renderRouterPortsBox("svc-node-router-box");
|
||||
}
|
||||
if (_isNwcServiceUnit(unit) && (effectiveEnabled || data.enabled)) {
|
||||
await _nwcInitWalletFlow(unit, name, icon);
|
||||
var nwcRtlBtn = document.getElementById("nwc-open-rtl-btn");
|
||||
|
||||
Reference in New Issue
Block a user