Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e62f0cc988 | |||
| 823a116d09 | |||
| fd414ddaf4 | |||
| 89cabf4baf |
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "site-gateway",
|
||||
"version": "0.11.78",
|
||||
"version": "0.11.82",
|
||||
"private": true,
|
||||
"description": "Site Gateway: simple self-hosted website publishing, reverse proxying, and automatic HTTPS.",
|
||||
"type": "module",
|
||||
|
||||
+12
-3
@@ -1,5 +1,5 @@
|
||||
const $ = selector => document.querySelector(selector);
|
||||
const state = { sites: [], proxies: [], redirects: [], streams: [], accessLists: [], groups: [], backups: [], settings: null, dashboard: null, certificates: null, readiness: null, logs: null, users: [], user: null, config: null, view: "overview", loaded: false, pendingDelete: null, pendingReplace: null, editing: null, iconTarget: null, passwordTarget: null, healthTimer: null };
|
||||
const state = { sites: [], proxies: [], redirects: [], streams: [], accessLists: [], groups: [], backups: [], settings: null, dashboard: null, certificates: null, readiness: null, logs: null, users: [], user: null, config: null, view: "overview", loaded: false, pendingDelete: null, pendingReplace: null, editing: null, iconTarget: null, passwordTarget: null, healthTimer: null, updateCheckTimer: null, loadedVersion: null, updateAvailable: false };
|
||||
document.querySelector("#create-form [name=domain]")?.closest("label")?.childNodes[0] && (document.querySelector("#create-form [name=domain]").closest("label").childNodes[0].textContent = "Primary domain ");
|
||||
if (!document.querySelector("#create-form [name=accessListId]")) { const anchor = document.querySelector("#create-form [name=tls]")?.closest("label"); if (anchor) { const label = document.createElement("label"); label.innerHTML = '<span>Access List <span class="optional">Optional</span></span><select name="accessListId"><option value="">Public — no Access List</option></select><small>Protect this hosted site and all of its domains.</small>'; anchor.before(label); } }
|
||||
if (!document.querySelector("#settings-access-list")) { const anchor = document.querySelector("#settings-form [name=domain]")?.closest("label"); if (anchor) { const label = document.createElement("label"); label.innerHTML = '<span>Access List <span class="optional">Optional</span></span><select id="settings-access-list" name="accessListId"><option value="">Public — no Access List</option></select><small>Protect this route and all of its domains.</small>'; anchor.after(label); } }
|
||||
@@ -24,7 +24,7 @@ async function api(url, options = {}) {
|
||||
if (!response.ok) { const body = await response.json().catch(() => ({})); throw new Error(body.error || "Request failed."); }
|
||||
return response.status === 204 ? null : response.json();
|
||||
}
|
||||
function showLogin(message = "") { state.user = null; state.users = []; state.view = "overview"; const form = $("#login-form"); form.reset(); form.elements.username.value = ""; form.elements.password.value = ""; $("#login").classList.remove("hidden"); $("#dashboard").classList.add("hidden"); $("#login-error").textContent = message; $("#mfa-login-form").reset(); $("#mfa-login-form").classList.add("hidden"); $("#login-form").classList.remove("hidden"); $("#mfa-login-error").textContent = ""; }
|
||||
function showLogin(message = "") { state.user = null; state.users = []; state.view = "overview"; const form = $("#login-form"); form.reset(); form.elements.username.value = ""; form.elements.password.value = ""; $("#login").classList.remove("hidden"); $("#dashboard").classList.add("hidden"); $("#login-error").textContent = message; $("#mfa-login-form").reset(); $("#mfa-login-form").classList.add("hidden"); $("#login-form").classList.remove("hidden"); $("#mfa-login-error").textContent = ""; setTimeout(() => form.elements.username.focus(), 0); }
|
||||
function showDashboard() { $("#login").classList.add("hidden"); $("#dashboard").classList.remove("hidden"); }
|
||||
function toast(message) { const el = $("#toast"); el.textContent = message; el.classList.add("show"); setTimeout(() => el.classList.remove("show"), 2800); }
|
||||
function escapeHtml(value) { const el = document.createElement("div"); el.textContent = value ?? ""; return el.innerHTML; }
|
||||
@@ -410,14 +410,23 @@ async function boot() {
|
||||
if (session.setupRequired) { $("#login").classList.add("hidden"); $("#dashboard").classList.add("hidden"); $("#setup-form [name=username]").value = session.user.username; if (!$("#setup-dialog").open) $("#setup-dialog").showModal(); return; }
|
||||
state.view = location.hash.slice(1) || "overview"; state.users = []; showDashboard(); state.user = session.user; $("#user-label").textContent = session.user?.displayName || session.username; document.querySelectorAll(".admin-only").forEach(element => element.classList.toggle("hidden", !canAdmin())); render(); state.config = await api("/api/config");
|
||||
$("#version-label").textContent = `v${state.config.version || "unknown"}`;
|
||||
if (!state.loadedVersion) state.loadedVersion = state.config.version;
|
||||
$("#port-range").textContent = `${state.config.minPort}–${state.config.maxPort}`; $("#port-help").textContent = `Direct LAN access range: ${state.config.minPort}–${state.config.maxPort}`;
|
||||
$("#create-form [name=port]").min = state.config.minPort; $("#create-form [name=port]").max = state.config.maxPort; await refresh(); if (state.view !== "overview") await loadFeatureView();
|
||||
if (!state.healthTimer) state.healthTimer = setInterval(() => { if (state.view === "overview" && !$("#dashboard").classList.contains("hidden")) refreshDashboard().catch(error => toast(error.message)); }, 30000);
|
||||
if (!state.updateCheckTimer) state.updateCheckTimer = setInterval(() => { if (!$("#dashboard").classList.contains("hidden")) checkForUpdate().catch(() => {}); }, 60000);
|
||||
}
|
||||
async function checkForUpdate() {
|
||||
if (state.updateAvailable || !state.loadedVersion) return;
|
||||
const config = await api("/api/config");
|
||||
if (config.version && config.version !== state.loadedVersion) { state.updateAvailable = true; $("#update-banner").classList.remove("hidden"); }
|
||||
}
|
||||
$("#update-banner-refresh").addEventListener("click", () => location.reload());
|
||||
$("#update-banner-dismiss").addEventListener("click", () => { $("#update-banner").classList.add("hidden"); state.updateAvailable = false; });
|
||||
|
||||
$("#login-form").addEventListener("submit", async event => { event.preventDefault(); $("#login-error").textContent = ""; try { const result = await api("/api/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(Object.fromEntries(new FormData(event.target))) }); if (result?.mfaRequired) { $("#login-form").classList.add("hidden"); $("#mfa-login-form").classList.remove("hidden"); $("#mfa-login-form [name=code]").focus(); return; } event.target.reset(); await boot(); } catch (error) { $("#login-error").textContent = error.message; } });
|
||||
$("#mfa-login-form").addEventListener("submit", async event => { event.preventDefault(); $("#mfa-login-error").textContent = ""; try { const response = await fetch("/api/login/mfa", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(Object.fromEntries(new FormData(event.target))) }); const body = await response.json().catch(() => ({})); if (!response.ok) throw new Error(body.error || "That code didn't match. Try again."); event.target.reset(); await boot(); } catch (error) { $("#mfa-login-error").textContent = error.message; } });
|
||||
$("#mfa-login-cancel").addEventListener("click", () => { $("#mfa-login-form").reset(); $("#mfa-login-error").textContent = ""; $("#mfa-login-form").classList.add("hidden"); $("#login-form").classList.remove("hidden"); });
|
||||
$("#mfa-login-cancel").addEventListener("click", () => { $("#mfa-login-form").reset(); $("#mfa-login-error").textContent = ""; $("#mfa-login-form").classList.add("hidden"); $("#login-form").classList.remove("hidden"); $("#login-form").elements.password.value = ""; setTimeout(() => $("#login-form").elements.password.focus(), 0); });
|
||||
$("#setup-form").addEventListener("submit", async event => { event.preventDefault(); $("#setup-error").textContent = ""; try { await api("/api/setup/admin", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(Object.fromEntries(new FormData(event.target))) }); $("#setup-dialog").close(); event.target.reset(); await boot(); showLogin("Administrator account saved. Sign in with your finalized credentials."); } catch (error) { $("#setup-error").textContent = error.message; } });
|
||||
$("#setup-dialog").addEventListener("cancel", event => event.preventDefault());
|
||||
$("#logout").addEventListener("click", async () => { await fetch("/api/logout", { method: "POST" }); showLogin(); });
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<title>Site Gateway</title>
|
||||
<meta name="description" content="Host sites, proxy services, and manage HTTPS from one simple dashboard.">
|
||||
<link rel="icon" type="image/png" href="/site-gateway-icon-approved.png">
|
||||
<link rel="stylesheet" href="/styles.css?v=0.11.78">
|
||||
<link rel="stylesheet" href="/styles.css?v=0.11.82">
|
||||
</head>
|
||||
<body>
|
||||
<div id="login" class="login-shell hidden">
|
||||
@@ -357,6 +357,7 @@
|
||||
</dialog>
|
||||
<input id="replace-files" type="file" accept=".zip,.html,text/html,application/zip" hidden>
|
||||
<div id="toast" class="toast" role="status"></div>
|
||||
<script src="/app.js?v=0.11.77" defer></script><script src="/features.js?v=0.11.77" defer></script>
|
||||
<div id="update-banner" class="update-banner hidden" role="status"><span>A new version of Site Gateway is available.</span><div class="update-banner-actions"><button id="update-banner-refresh" class="button primary">Refresh</button><button id="update-banner-dismiss" class="text-button">Dismiss</button></div></div>
|
||||
<script src="/app.js?v=0.11.82" defer></script><script src="/features.js?v=0.11.77" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user