Add dns-stack profile and stable IP prompt

This commit is contained in:
Aaron
2026-01-03 17:17:27 -05:00
parent a67b1a55d4
commit 1ddffee077
6 changed files with 369 additions and 29 deletions

View File

@@ -110,6 +110,13 @@ const firstbootErrorClose = document.getElementById("firstbootErrorClose");
const firstbootCopyError = document.getElementById("firstbootCopyError");
const firstbootShowRecovery = document.getElementById("firstbootShowRecovery");
const firstbootRecovery = document.getElementById("firstbootRecovery");
const networkModal = document.getElementById("networkModal");
const networkClose = document.getElementById("networkClose");
const networkReserveBtn = document.getElementById("networkReserveBtn");
const networkStaticBtn = document.getElementById("networkStaticBtn");
const networkLaterBtn = document.getElementById("networkLaterBtn");
const networkHelpBtn = document.getElementById("networkHelpBtn");
const networkProfileHint = document.getElementById("networkProfileHint");
const confirmModal = document.getElementById("confirmModal");
const confirmTitle = document.getElementById("confirmTitle");
const confirmBody = document.getElementById("confirmBody");
@@ -172,6 +179,58 @@ const firstbootUI = createFirstbootUI({
showToast,
});
const networkState = {
shown: false,
profile: null,
};
function networkKey(profile) {
const id = profile?.id || profile?.name || "profile";
return `pikit:network-setup:${id}`;
}
function markNetworkHandled(profile, message) {
if (!profile) return;
try {
localStorage.setItem(networkKey(profile), "done");
} catch (err) {
// ignore storage failures
}
if (networkModal) networkModal.classList.add("hidden");
networkState.shown = false;
if (message) showToast?.(message, "success");
}
function openNetworkModal(profile, force = false) {
if (!networkModal) return;
if (!profile?.requires_stable_ip && !force) return;
if (networkProfileHint) {
const name = profile?.name ? `${profile.name} profile` : "This profile";
networkProfileHint.textContent = `${name} needs a stable IP address so your devices can always reach DNS.`;
}
networkModal.classList.remove("hidden");
networkState.shown = true;
networkState.profile = profile;
}
function shouldShowNetworkPrompt(firstbootData) {
if (!firstbootData || firstbootData.state !== "done") return false;
const profile = firstbootData.profile || null;
if (!profile?.requires_stable_ip) return false;
if (networkState.shown) return false;
try {
return localStorage.getItem(networkKey(profile)) !== "done";
} catch (err) {
return true;
}
}
function handleFirstbootStatus(firstbootData) {
if (shouldShowNetworkPrompt(firstbootData)) {
openNetworkModal(firstbootData.profile);
}
}
const statusController = createStatusController({
heroStats,
servicesGrid,
@@ -191,6 +250,7 @@ const statusController = createStatusController({
getError: getFirstbootError,
ui: firstbootUI,
},
onFirstbootStatus: handleFirstbootStatus,
});
const { loadStatus } = statusController;
@@ -220,6 +280,30 @@ function wireDialogs() {
addServiceModal?.addEventListener("click", (e) => {
if (e.target === addServiceModal) addServiceModal.classList.add("hidden");
});
networkModal?.addEventListener("click", (e) => {
if (e.target === networkModal) {
markNetworkHandled(networkState.profile, "Network setup saved");
}
});
}
function wireNetworkSetup() {
networkClose?.addEventListener("click", () => {
markNetworkHandled(networkState.profile, "Network setup saved");
});
networkReserveBtn?.addEventListener("click", () => {
markNetworkHandled(networkState.profile, "Router reservation saved");
});
networkStaticBtn?.addEventListener("click", () => {
markNetworkHandled(networkState.profile, "Static IP reminder saved");
});
networkLaterBtn?.addEventListener("click", () => {
markNetworkHandled(networkState.profile, "Network setup saved");
});
networkHelpBtn?.addEventListener("click", () => {
if (helpModal) helpModal.classList.add("hidden");
openNetworkModal(networkState.profile || { requires_stable_ip: true }, true);
});
}
// Testing hook
@@ -348,6 +432,7 @@ function main() {
window.__pikitTest.exposeServiceForm?.();
}
wireDialogs();
wireNetworkSetup();
wireResetAndUpdates();
wireAccordions({
forceOpen: typeof window !== "undefined" && window.__pikitTest?.forceAccordionsOpen,

View File

@@ -18,6 +18,7 @@ export function createStatusController({
setUpdatesUI = null,
updatesFlagEl = null,
firstboot = null,
onFirstbootStatus = null,
}) {
let lastStatusData = null;
let lastFirstbootState = null;
@@ -88,6 +89,7 @@ export function createStatusController({
firstbootData = await firstboot.getStatus();
lastFirstbootState = firstbootData?.state || lastFirstbootState;
firstboot.ui.update(firstbootData);
onFirstbootStatus?.(firstbootData);
if (firstbootData?.state === "error" && firstboot.getError) {
const err = await firstboot.getError();
if (err?.present) {

View File

@@ -132,6 +132,42 @@
</div>
</div>
<div id="networkModal" class="modal hidden">
<div class="modal-card wide">
<div class="panel-header">
<div>
<p class="eyebrow">Network setup</p>
<h3>Keep DNS reliable</h3>
<p class="hint" id="networkProfileHint">
This profile needs a stable IP address so your devices can always reach DNS.
</p>
</div>
<button id="networkClose" class="ghost icon-btn close-btn" title="Close network setup">
&times;
</button>
</div>
<div class="help-body">
<p>
The easiest option is a router reservation. It keeps the Pi on the same IP without
changing anything on the Pi itself.
</p>
<ol>
<li>Open your routers admin page (often <code>http://192.168.0.1</code> or <code>http://10.0.0.1</code>).</li>
<li>Find <strong>DHCP reservations</strong> or <strong>Static leases</strong>.</li>
<li>Reserve the Pis current IP and MAC address.</li>
</ol>
<div class="control-actions wrap gap">
<button id="networkReserveBtn">I set a router reservation</button>
<button id="networkStaticBtn" class="ghost">Ill set a static IP on the Pi</button>
<button id="networkLaterBtn" class="ghost">Ill do this later</button>
</div>
<p class="hint">
You can reopen this from <strong>Help → Network setup</strong> at any time.
</p>
</div>
</div>
</div>
<div id="changelogModal" class="modal hidden">
<div class="modal-card wide">
<div class="panel-header sticky">
@@ -726,6 +762,14 @@
<li>Factory reset reverts passwords and firewall rules and reboots. Use only when you need a clean slate.</li>
</ul>
<h4>Network setup (DNS profiles)</h4>
<ul>
<li>DNS profiles work best with a stable IP (router reservation or static IP).</li>
<li>
<button id="networkHelpBtn" class="ghost">Open network setup</button>
</li>
</ul>
<h4>Troubleshooting</h4>
<ul>
<li>Service link fails? Make sure the service runs, port/path are right, and edit via the “…” menu.</li>