ux: simplify domain and router setup guidance
This commit is contained in:
@@ -1,22 +1,14 @@
|
||||
/* Sovran_SystemsOS Hub — Shared domain-prerequisite instructions.
|
||||
/* Sovran_SystemsOS Hub — Shared domain and router instructions.
|
||||
|
||||
SINGLE SOURCE OF TRUTH for the "you need a Njal.la domain + router port
|
||||
forwarding" guidance that must read identically everywhere it appears:
|
||||
This is the single source of truth for the simple setup guidance shown in:
|
||||
|
||||
• First-boot onboarding wizard (Server + Desktop role)
|
||||
— onboarding.js step 3
|
||||
• Feature-enable domain modal (Node role, and any role)
|
||||
— features.js openDomainSetupModal()
|
||||
(Lightning Wallet Connections / NWC, BTCPay Server, Haven, …)
|
||||
• Domain reconfigure / troubleshooting modal
|
||||
— features.js openDomainReconfigureModal()
|
||||
• First-boot onboarding for Server + Desktop
|
||||
• Feature setup in the Hub
|
||||
• Domain reconfiguration and troubleshooting
|
||||
|
||||
Keep these three surfaces word-for-word consistent: always edit this
|
||||
file, never fork the wording inline. Plain classic script (no modules) —
|
||||
both templates load it with a plain <script> tag. */
|
||||
Keep the wording consistent by editing it here. */
|
||||
"use strict";
|
||||
|
||||
/* Escape helper local to this file so it is self-contained on both pages. */
|
||||
function dpEsc(str) {
|
||||
return String(str)
|
||||
.replace(/&/g, "&")
|
||||
@@ -26,105 +18,71 @@ function dpEsc(str) {
|
||||
.replace(/'/g, "'");
|
||||
}
|
||||
|
||||
/* ── "What you'll need" intro ──────────────────────────────────────
|
||||
opts.serviceName — e.g. "Lightning Wallet Connections", or null for
|
||||
the multi-service (onboarding) wording.
|
||||
opts.hostExample — e.g. "lightning" → lightning.yourdomain.com */
|
||||
/* Explain only what the user needs for the step in front of them. */
|
||||
function renderDomainNeedsHtml(opts) {
|
||||
opts = opts || {};
|
||||
var serviceName = opts.serviceName || null;
|
||||
var hostExample = dpEsc(opts.hostExample || "myservice");
|
||||
var purpose = opts.purpose || "";
|
||||
var html = "";
|
||||
|
||||
if (serviceName) {
|
||||
html += "<p>To enable <strong>" + dpEsc(serviceName) + "</strong>, you'll need two things first:</p>";
|
||||
html += '<ol style="margin:8px 0 0 16px;padding:0;line-height:1.7;">';
|
||||
html += "<li><strong>A domain of your own from <a href=\"https://njal.la\" target=\"_blank\" rel=\"noopener noreferrer\" style=\"color:var(--accent-color);\">Njal.la</a></strong> "
|
||||
+ "— privacy-friendly, no personal details required, accepts Bitcoin. "
|
||||
+ dpEsc(serviceName) + " gets its own hostname: a subdomain (e.g. <code>" + hostExample + ".yourdomain.com</code>) "
|
||||
+ "or a separate domain — your choice. Subdomains are free, and one domain can have many.</li>";
|
||||
html += "<li><strong>Access to your router</strong> — you'll forward ports <strong>80</strong> and <strong>443</strong> (TCP) "
|
||||
+ "to this computer once. All domain-based services share these two ports; "
|
||||
+ "they're required for HTTPS and SSL certificates.</li>";
|
||||
html += "</ol>";
|
||||
if (purpose === "btcpay") {
|
||||
html += "<p><strong>Put your BTCPay Server online</strong></p>";
|
||||
html += "<p>Let people donate Bitcoin, pay you, or visit your online store.</p>";
|
||||
html += "<p>BTCPay Server needs a domain so people can reach it. "
|
||||
+ "Sovran_SystemsOS walks you through getting your domain from "
|
||||
+ '<a href="https://njal.la" target="_blank" rel="noopener noreferrer" style="color:var(--accent-color);">Njal.la</a>'
|
||||
+ " and connecting it. Just follow the steps below.</p>";
|
||||
} else if (serviceName) {
|
||||
html += "<p>To make <strong>" + dpEsc(serviceName) + "</strong> available outside your home, it needs a domain.</p>";
|
||||
html += "<p>Sovran_SystemsOS walks you through getting your domain from "
|
||||
+ '<a href="https://njal.la" target="_blank" rel="noopener noreferrer" style="color:var(--accent-color);">Njal.la</a>'
|
||||
+ " and connecting it. Just follow the steps below.</p>";
|
||||
} else {
|
||||
html += "<p><strong>Each service below needs two things set up:</strong></p>";
|
||||
html += '<ol style="margin:8px 0 0 16px;padding:0;line-height:1.7;">';
|
||||
html += "<li><strong>A domain of your own from <a href=\"https://njal.la\" target=\"_blank\" rel=\"noopener noreferrer\" style=\"color:var(--accent-color);\">Njal.la</a></strong> "
|
||||
+ "— privacy-friendly, no personal details required, accepts Bitcoin. "
|
||||
+ "Each service gets its own hostname: its own subdomain (e.g. <code>" + hostExample + ".yourdomain.com</code>) "
|
||||
+ "or a separate domain — your choice. Subdomains are free, and one domain can have many, "
|
||||
+ "so a single domain can serve every service.</li>";
|
||||
html += "<li><strong>Access to your router</strong> — you'll forward ports <strong>80</strong> and <strong>443</strong> (TCP) "
|
||||
+ "to this computer once. All domain-based services share these two ports; "
|
||||
+ "they're required for HTTPS and SSL certificates.</li>";
|
||||
html += "</ol>";
|
||||
html += "<p>Your domain is the address people use to reach your services on the internet.</p>";
|
||||
html += "<p>Sovran_SystemsOS walks you through getting your domain from "
|
||||
+ '<a href="https://njal.la" target="_blank" rel="noopener noreferrer" style="color:var(--accent-color);">Njal.la</a>'
|
||||
+ " and connecting your services. Just follow the steps below.</p>";
|
||||
}
|
||||
return html;
|
||||
}
|
||||
|
||||
/* ── "How to set it up at Njal.la" steps ───────────────────────────
|
||||
opts.hostExample — host part used in the examples (default "call").
|
||||
opts.pasteHint — where the curl command goes:
|
||||
"below" (single service) or "next to its service below"
|
||||
(onboarding, many services). */
|
||||
/* The shortest Njal.la steps that still tell the user exactly what to do. */
|
||||
function renderNjallaStepsHtml(opts) {
|
||||
var hostExample = dpEsc((opts && opts.hostExample) || "call");
|
||||
var pasteHint = (opts && opts.pasteHint) || "below";
|
||||
var html = "";
|
||||
|
||||
html += "<p style=\"margin-top:12px;\"><strong>How to set it up at Njal.la:</strong></p>";
|
||||
html += '<p style="margin-top:12px;"><strong>Get and connect your domain:</strong></p>';
|
||||
html += '<ol style="margin:8px 0 0 16px;padding:0;line-height:1.7;">';
|
||||
html += "<li>Create an account at <a href=\"https://njal.la\" target=\"_blank\" rel=\"noopener noreferrer\" style=\"color:var(--accent-color);\">https://njal.la</a> and buy a domain.</li>";
|
||||
html += "<li>Add a <strong>Dynamic</strong> record for the hostname:"
|
||||
+ '<ul style="margin:4px 0 0 16px;padding:0;line-height:1.7;">'
|
||||
+ "<li>In the Njal.la <strong>Name</strong> field, type ONLY the host part — the word before your domain.<br>"
|
||||
+ "(For "" + hostExample + ".yourdomain.com" you'd type just: <code>" + hostExample + "</code>.)<br>"
|
||||
+ "⚠ Do NOT type the full domain here — Njal.la adds it automatically.</li>"
|
||||
+ "<li>Dedicating a whole separate domain to this service? Leave the Name field blank or use <code>@</code>.</li>"
|
||||
+ "</ul>"
|
||||
+ "</li>";
|
||||
html += "<li>A Dynamic record has <strong>NO IP field</strong> — you don't enter an IP anywhere. "
|
||||
+ "It auto-fills once Sovran_SystemsOS runs the update command (on save, and again after every reboot).</li>";
|
||||
html += "<li>Njal.la gives you a curl command, e.g.:<br>"
|
||||
+ '<code style="font-size:0.8em;">curl "https://njal.la/update/?h=' + hostExample + '.yourdomain.com&k=abc123&auto"</code><br>'
|
||||
+ "Copy it and paste it " + pasteHint + ".</li>";
|
||||
html += '<li>Open <a href="https://njal.la" target="_blank" rel="noopener noreferrer" style="color:var(--accent-color);">Njal.la</a>, create an account, and get a domain.</li>';
|
||||
html += "<li>Open your domain and add a <strong>Dynamic</strong> record for this service. "
|
||||
+ "In the <strong>Name</strong> box, enter only the first part of the address. "
|
||||
+ "For <code>" + hostExample + ".yourdomain.com</code>, enter <code>" + hostExample + "</code>. "
|
||||
+ "If you are using the whole domain, leave the box blank or enter <code>@</code>.</li>";
|
||||
html += "<li>Copy the update command Njal.la gives you and paste it " + pasteHint + ".</li>";
|
||||
html += "</ol>";
|
||||
return html;
|
||||
}
|
||||
|
||||
/* ── "One router task" port-forwarding box ─────────────────────────
|
||||
opts.internalIp — LAN IP string, or empty/null for generic wording.
|
||||
opts.plural — true for multi-service (onboarding) wording.
|
||||
opts.includeSsh — also mention port 22 for SSH (onboarding).
|
||||
opts.extraNote — extra sentence appended at the end (optional). */
|
||||
/* One router task shared by all normal domain-based services. */
|
||||
function renderRouterPortsHtml(opts) {
|
||||
opts = opts || {};
|
||||
var ipPart = opts.internalIp
|
||||
? " to this computer’s internal IP <strong>" + dpEsc(opts.internalIp) + "</strong>"
|
||||
: " to this computer’s internal IP";
|
||||
var serviceWord = opts.plural ? "services" : "service";
|
||||
? " to this computer at <strong>" + dpEsc(opts.internalIp) + "</strong>"
|
||||
: " to this computer";
|
||||
|
||||
var html = "";
|
||||
html += "🔌 <strong>One router task:</strong> in your router’s <strong>port forwarding</strong> settings, "
|
||||
+ "forward port <strong>80 (TCP)</strong> and port <strong>443 (TCP)</strong>"
|
||||
+ ipPart + ". Use the <strong>same number for the internal and external port</strong>. "
|
||||
+ "This only needs to be done once — all domain services share these ports — "
|
||||
+ "but HTTPS and SSL certificates won’t work without them, so your " + serviceWord + " "
|
||||
+ "can’t be reached from outside your home network. "
|
||||
+ "You’ll need normal access to your router’s settings with working port forwarding — "
|
||||
+ "if your ISP blocks it (e.g. CGNAT), domain-based services can’t be reached from the internet. ";
|
||||
if (opts.includeSsh) {
|
||||
html += "Add port <strong>22 (TCP)</strong> as well if you want remote SSH access. ";
|
||||
}
|
||||
html += "🔌 <strong>One router task:</strong> in your router’s <strong>Port Forwarding</strong> settings, "
|
||||
+ "forward <strong>80 (TCP)</strong> and <strong>443 (TCP)</strong>"
|
||||
+ ipPart + ". If your router asks for outside and inside port numbers, use the same number for both. "
|
||||
+ "You only need to do this once. All your services share these two ports.";
|
||||
if (opts.extraNote) {
|
||||
html += dpEsc(opts.extraNote);
|
||||
html += " " + dpEsc(opts.extraNote);
|
||||
}
|
||||
return html;
|
||||
}
|
||||
|
||||
/* ── Async helper: fill a router-box container with the internal IP ──
|
||||
Renders generic text immediately, then upgrades to the concrete LAN IP
|
||||
if /api/network provides one. Best-effort — never blocks the UI. */
|
||||
/* Show generic wording immediately, then add the computer's address when found. */
|
||||
function renderRouterPortsBox(elId, opts) {
|
||||
var el = document.getElementById(elId);
|
||||
if (!el) return;
|
||||
@@ -142,5 +100,5 @@ function renderRouterPortsBox(elId, opts) {
|
||||
target.innerHTML = renderRouterPortsHtml(next);
|
||||
}
|
||||
})
|
||||
.catch(function() { /* generic wording already shown — fine */ });
|
||||
.catch(function() { /* Generic wording is already shown. */ });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user