Compare commits

...

6 Commits

6 changed files with 12 additions and 35 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "site-gateway",
"version": "0.11.36",
"version": "0.11.42",
"private": true,
"description": "Site Gateway: simple self-hosted website publishing, reverse proxying, and automatic HTTPS.",
"type": "module",
-3
View File
@@ -1,7 +1,4 @@
const $ = selector => document.querySelector(selector);
const summaryBar = document.querySelector("#management-summary");
const redirectView = document.querySelector("#redirects-view");
if (summaryBar && redirectView) redirectView.parentElement.insertBefore(summaryBar, redirectView);
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 };
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); } }
+2 -1
View File
@@ -11,7 +11,8 @@ function renderStreams() {
const status = item.status === "running" ? "running" : item.status === "error" ? "error" : "disabled";
const upstream = item.enabled === false || item.upstream?.status === "unmonitored" ? "Monitoring paused" : !item.upstream || item.upstream.status === "pending" ? "Target check pending" : item.upstream.status === "healthy" ? `Target reachable · ${item.upstream.responseMs} ms` : `Target unreachable · ${extendedEscape(item.upstream.error || "check failed")}`;
const protocols = [item.tcp !== false ? "TCP" : null, item.udp ? "UDP" : null].filter(Boolean).map(value => `<span class="chip">${value}</span>`).join("");
return `<article class="site-card stream-card" data-stream-id="${item.id}" data-kind="stream"><div class="card-top"><div class="site-icon">${featureIcon(item,"SH")}</div><div class="menu-wrap"><button class="icon-button menu-button" aria-label="Streaming host options" aria-expanded="false">•••</button><div class="menu"><button data-stream-action="edit">Edit streaming host</button><button data-stream-action="icon">Change icon</button><button data-stream-action="toggle">${item.enabled === false ? "Enable" : "Disable"}</button><button data-stream-action="delete" class="danger-text">Delete streaming host</button></div></div></div><h2>${extendedEscape(item.name)}</h2><p class="address">Port ${item.port}</p><p class="gateway-address">→ ${extendedEscape(item.target)}</p><p class="upstream-copy ${item.upstream?.status === "unhealthy" ? "bad" : ""}">${upstream}</p><div class="card-footer"><span class="status-pill"><span class="status-dot ${status}"></span>${status === "error" ? "Needs attention" : status[0].toUpperCase() + status.slice(1)}</span><div class="card-actions">${protocols}</div></div></article>`;
const toggle = `<button class="toggle ${item.enabled === false ? "" : "on"}" data-stream-action="toggle" aria-label="${item.enabled === false ? "Enable" : "Disable"} ${extendedEscape(item.name)}"><span></span></button>`;
return `<article class="site-card stream-card" data-stream-id="${item.id}" data-kind="stream"><div class="card-top"><div class="site-icon">${featureIcon(item,"SH")}</div><div class="menu-wrap"><button class="icon-button menu-button" aria-label="Streaming host options" aria-expanded="false">•••</button><div class="menu"><button data-stream-action="edit">Edit streaming host</button><button data-stream-action="icon">Change icon</button><button data-stream-action="delete" class="danger-text">Delete streaming host</button></div></div></div><h2>${extendedEscape(item.name)}</h2><p class="address">Port ${item.port}</p><p class="gateway-address">→ ${extendedEscape(item.target)}</p><p class="upstream-copy ${item.upstream?.status === "unhealthy" ? "bad" : ""}">${upstream}</p><div class="card-footer"><span class="status-pill"><span class="status-dot ${status}"></span>${status === "error" ? "Needs attention" : status[0].toUpperCase() + status.slice(1)}</span><div class="card-actions">${toggle}${protocols}</div></div></article>`;
}).join("");
}
+3 -25
View File
File diff suppressed because one or more lines are too long
+4 -4
View File
@@ -130,9 +130,9 @@ dialog{max-height:calc(100vh - 28px);overflow:auto}
#access-assignment-summary>strong{margin-top:20px;padding-top:16px;border-top:1px solid var(--line)}.access-create-guidance{margin-top:18px;padding:16px;border:1px solid var(--line);border-radius:12px;background:var(--panel2)}.access-create-guidance strong{display:block}.access-create-guidance p{margin:6px 0 0;color:var(--muted);line-height:1.45}
.danger-actions{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.danger-actions .button{margin:0}
.docs-intro{--text:#f4f7fb;--muted:#aebbd0;margin-bottom:22px;padding:24px;border:1px solid var(--line);border-radius:16px;background:linear-gradient(145deg,rgba(20,33,54,.95),rgba(13,23,39,.95));color:var(--text)}.docs-intro h2{margin:0 0 10px}.docs-intro p:last-child{margin:0;color:var(--muted);max-width:850px;line-height:1.6}
.docs-layout{display:grid;grid-template-columns:210px minmax(0,1fr);gap:22px;align-items:start}.docs-nav{position:sticky;top:18px;height:calc(100vh - 170px);overflow:auto;display:grid;align-content:start;gap:6px;padding:14px;border:1px solid var(--line);border-radius:14px;background:rgba(16,26,43,.9)}.docs-nav .eyebrow{margin:4px 8px 8px}.docs-nav button{padding:9px 10px;border:0;border-radius:8px;background:transparent;color:var(--muted);text-align:left;cursor:pointer;font:inherit}.docs-nav button:hover{background:rgba(98,230,167,.1);color:var(--text)}.docs #docs-content{display:grid;gap:16px}.docs #docs-content article{scroll-margin-top:18px;padding:24px;border:1px solid var(--line);border-radius:16px;background:rgba(16,26,43,.76);line-height:1.6}.docs #docs-content article h2{margin:0 0 12px}.docs #docs-content article h3{margin:20px 0 5px;color:var(--green);font-size:.86rem}.docs #docs-content article p{color:var(--muted)}.docs .doc-search{display:block;margin-top:18px}.docs .doc-search input{margin-top:8px}@media(max-width:800px){.docs-layout{grid-template-columns:1fr}.docs-nav{position:static;display:flex;flex-wrap:wrap}.docs-nav .eyebrow{width:100%}}
.docs-intro{padding:32px 34px}.docs-search-panel{max-width:880px;margin-top:24px;padding:18px 20px;border:1px solid var(--line);border-radius:14px;background:rgba(7,15,28,.42)}.docs-search-panel .doc-search{margin:0}.docs-search-panel .doc-search span{display:block;margin-bottom:8px;font-weight:750;color:var(--text)}.docs-search-panel small{display:block;margin-top:8px;color:var(--muted)}.docs #docs-content article ul{margin:10px 0 0;padding-left:22px;color:var(--muted)}.docs #docs-content article li{margin:8px 0}
.docs-intro{margin-bottom:22px;padding:24px;border:1px solid var(--line);border-radius:16px;background:var(--bg);color:var(--text)}.docs-intro h2{margin:0 0 10px}.docs-intro p:last-child{margin:0;color:var(--muted);max-width:850px;line-height:1.6}
.docs-layout{display:grid;grid-template-columns:210px minmax(0,1fr);gap:22px;align-items:start}.docs-nav{position:sticky;top:18px;height:calc(100vh - 170px);overflow:auto;display:grid;align-content:start;gap:6px;padding:14px;border:1px solid var(--line);border-radius:14px;background:var(--panel)}.docs-nav .eyebrow{margin:4px 8px 8px}.docs-nav button{padding:9px 10px;border:0;border-radius:8px;background:transparent;color:var(--muted);text-align:left;cursor:pointer;font:inherit}.docs-nav button:hover{background:rgba(98,230,167,.1);color:var(--text)}.docs #docs-content{display:grid;gap:16px}.docs #docs-content article{scroll-margin-top:18px;padding:24px;border:1px solid var(--line);border-radius:16px;background:var(--panel);line-height:1.6}.docs #docs-content article h2{margin:0 0 12px}.docs #docs-content article h3{margin:20px 0 5px;color:var(--green);font-size:.86rem}.docs #docs-content article p{color:var(--muted)}.docs .doc-search{display:block;margin-top:18px}.docs .doc-search input{margin-top:8px}@media(max-width:800px){.docs-layout{grid-template-columns:1fr}.docs-nav{position:static;display:flex;flex-wrap:wrap}.docs-nav .eyebrow{width:100%}}
.docs-intro{padding:32px 34px}.docs-search-panel{max-width:880px;margin-top:24px;padding:18px 20px;border:1px solid var(--line);border-radius:14px;background:var(--panel2)}.docs-search-panel .doc-search{margin:0}.docs-search-panel .doc-search span{display:block;margin-bottom:8px;font-weight:750;color:var(--text)}.docs-search-panel small{display:block;margin-top:8px;color:var(--muted)}.docs #docs-content article ul{margin:10px 0 0;padding-left:22px;color:var(--muted)}.docs #docs-content article li{margin:8px 0}
.docs-intro{width:100%;box-sizing:border-box;text-align:center}.docs-intro>p:not(.eyebrow){margin-left:auto;margin-right:auto}.docs-search-panel{max-width:none;text-align:left}.docs-search-panel .doc-search input{width:100%;box-sizing:border-box}
/* Manual uses a full-width header, then a two-column reading layout. */
.docs{display:block}.docs-intro{grid-column:1/-1}.docs-layout{width:100%}
@@ -240,7 +240,7 @@ select{appearance:none!important;-webkit-appearance:none!important;background-re
:root .site-icon,:root[data-theme="light"] .site-icon,:root .site-card.proxy .site-icon,:root[data-theme="light"] .site-card.proxy .site-icon{background:var(--panel2);color:var(--green);border:1px solid var(--line)}
:root .retention-load-more{display:block;margin:10px auto 0;padding:8px 14px;border:1px solid var(--line);border-radius:9px;background:var(--panel2);color:var(--text);font-size:.72rem;font-weight:700}:root .retention-load-more:hover{border-color:var(--green);color:var(--green)}
:root .retention-load-more{display:none!important}
#create-dialog .dialog-heading .close-dialog,#settings-dialog .dialog-heading .close-dialog{display:none}
#create-dialog .dialog-heading .close-dialog,#settings-dialog .dialog-heading .close-dialog,#proxy-dialog .dialog-heading .close-dialog,#redirect-dialog .dialog-heading .close-dialog,#stream-dialog .dialog-heading .close-dialog{display:none}
#app .site-card.access-card .site-icon{background:#193d38;color:var(--green)}:root[data-theme="light"] #app .site-card.access-card .site-icon{background:#e2f5ed;color:#138a5b}
#app .user-avatar{background:#193d38;color:var(--green)}:root[data-theme="light"] #app .user-avatar{background:#e2f5ed;color:#138a5b}
#app .user-avatar img{filter:grayscale(1) sepia(1) saturate(5) hue-rotate(95deg) brightness(1.1)}
+2 -1
View File
@@ -1014,7 +1014,8 @@ app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.get(["/", "/index.html"], (req, res) => {
const html = fs.readFileSync(path.join(publicDir, "index.html"), "utf8")
.replace(/\/(app|features)\.js\?v=[^"']+/g, `/$1.js?v=${appVersion}`);
.replace(/\/(app|features)\.js\?v=[^"']+/g, `/$1.js?v=${appVersion}`)
.replace(/\/styles\.css\?v=[^"']+/g, `/styles.css?v=${appVersion}`);
res.type("html").send(html);
});
app.use(express.static(publicDir));