Move API token creation to shared header, expand docs, update README (v0.16.19)
This commit is contained in:
+4
-3
@@ -501,8 +501,8 @@ function render() {
|
||||
if (state.view === "administration") { const adminTab = state.adminTab || "users"; document.querySelectorAll("[data-admin-tab]").forEach(item => item.classList.toggle("tab-active", item.dataset.adminTab === adminTab)); document.querySelectorAll("[data-admin-panel]").forEach(panel => panel.classList.toggle("hidden", panel.dataset.adminPanel !== adminTab)); }
|
||||
$("#streaming-view").classList.toggle("hidden", state.view !== "streaming"); $("#redirects-view").classList.toggle("hidden", state.view !== "redirects"); $("#access-view").classList.toggle("hidden", state.view !== "access"); $("#documentation-view").classList.toggle("hidden", state.view !== "documentation");
|
||||
const activeAdminTab = state.view === "administration" ? document.querySelector("[data-admin-tab].tab-active")?.dataset.adminTab : null;
|
||||
const adminUsersActive = activeAdminTab === "users", adminGroupsActive = activeAdminTab === "groups";
|
||||
$("#open-create").classList.toggle("hidden", !(management || adminUsersActive || adminGroupsActive || ["streaming","redirects","access"].includes(state.view)) || !canManage()); $("#check-health").classList.toggle("hidden", state.view !== "certificates"); $("#refresh-logs").classList.toggle("hidden", state.view !== "logs");
|
||||
const adminUsersActive = activeAdminTab === "users", adminGroupsActive = activeAdminTab === "groups", adminApiActive = activeAdminTab === "api";
|
||||
$("#open-create").classList.toggle("hidden", !(management || adminUsersActive || adminGroupsActive || adminApiActive || ["streaming","redirects","access"].includes(state.view)) || !canManage()); $("#check-health").classList.toggle("hidden", state.view !== "certificates"); $("#refresh-logs").classList.toggle("hidden", state.view !== "logs");
|
||||
if (overview) {
|
||||
$("#page-title").textContent = "Dashboard";
|
||||
$("#page-subtitle").textContent = "Health, activity, and system status at a glance.";
|
||||
@@ -512,7 +512,7 @@ function render() {
|
||||
if (!management) {
|
||||
const headings = { certificates:["Certificates","Expiration, issuer, and certificate-detection status for automatic HTTPS."], logs:["Access Logs & Gateway Events","Recent requests, upstream responses, and gateway health events served through Caddy."], performance:["Performance","Live and historical request throughput across your gateway."], administration:["Administration","Users, gateway defaults, backups, and updates."], streaming:["Streaming hosts","Forward raw TCP/UDP traffic on a specific port straight to another host and port."], redirects:["Redirect hosts","Send domains to a new destination with clear, predictable rules."], access:["Access Lists","Create reusable network and login protection for your hosts."], documentation:["Documentation","Plain-language guidance and real-world Site Gateway examples."], account:["My Account","Manage your profile, password, and two-factor authentication."] };
|
||||
const heading = headings[state.view] || ["Site Gateway",""]; $("#page-title").textContent = heading[0]; $("#page-subtitle").textContent = heading[1];
|
||||
$("#open-create").textContent = state.view === "administration" ? (adminGroupsActive ? "+ Create group" : "+ Create user") : state.view === "streaming" ? "+ New streaming host" : state.view === "redirects" ? "+ New redirect host" : state.view === "access" ? "+ New Access List" : $("#open-create").textContent;
|
||||
$("#open-create").textContent = state.view === "administration" ? (adminGroupsActive ? "+ Create group" : adminApiActive ? "+ Create token" : "+ Create user") : state.view === "streaming" ? "+ New streaming host" : state.view === "redirects" ? "+ New redirect host" : state.view === "access" ? "+ New Access List" : $("#open-create").textContent;
|
||||
if (state.view === "streaming") $("#stream-empty").classList.toggle("hidden", !state.loaded || state.streams.length > 0);
|
||||
if (state.view === "streaming") { const items = state.streams; const running = items.filter(item => item.status === "running").length, disabled = items.filter(item => item.status === "disabled").length, errors = items.filter(item => item.status === "error").length; $("#running-count").textContent = running; $("#disabled-count").textContent = disabled; $("#error-count").textContent = errors; $("#running-label").textContent = running ? "Running" : "None running"; $("#disabled-label").textContent = disabled ? "Disabled" : "None disabled"; $("#error-label").textContent = errors ? "Needs attention" : "No issues"; $("#running-dot").className = `status-dot ${running ? "running" : "inactive"}`; $("#disabled-dot").className = `status-dot ${disabled ? "disabled" : "inactive"}`; $("#error-dot").className = `status-dot ${errors ? "error" : "inactive"}`; $(".port-note").classList.add("hidden"); }
|
||||
if (state.view === "redirects") $("#redirect-empty .create-trigger").textContent = "Create a redirect host";
|
||||
@@ -657,6 +657,7 @@ $("#event-category").addEventListener("change", renderLogs);
|
||||
// --- "Create" dialog: opens the right create form/dialog for the current view --------------
|
||||
function openCreate() {
|
||||
if (state.view === "administration" && state.adminTab === "groups") { openNewGroupEditor(); return; }
|
||||
if (state.view === "administration" && state.adminTab === "api") { openCreateApiTokenDialog(); return; }
|
||||
if (state.view === "administration") { $("#user-form").reset(); $("#user-error").textContent = ""; return $("#user-dialog").showModal(); }
|
||||
if (state.view === "streaming") { $("#stream-form").reset(); delete $("#stream-form").dataset.editing; $("#stream-title").textContent = "Create a streaming host"; $("#stream-form .button.primary").textContent = "Create streaming host"; $("#stream-error").textContent = ""; return $("#stream-dialog").showModal(); }
|
||||
if (state.view === "redirects") { $("#redirect-form").reset(); delete $("#redirect-form").dataset.editing; $("#redirect-error").textContent = ""; return $("#redirect-dialog").showModal(); }
|
||||
|
||||
@@ -378,7 +378,7 @@ function renderApiTokensPanel() {
|
||||
if (!panel) { panel = document.createElement("section"); panel.dataset.adminPanel = "api"; panel.className = "settings-panel hidden"; users.parentElement.append(panel); }
|
||||
if (panel.dataset.ready) return;
|
||||
panel.dataset.ready = "1";
|
||||
panel.innerHTML = '<div class="panel-heading"><div><p class="eyebrow">Programmatic access</p><h2>API access tokens</h2><p class="muted">Issue bearer tokens for scripts and integrations. A token acts as the administrator who issued it, and is shown in full only once. Changing that administrator’s password, or disabling their account, revokes every token they issued.</p></div><div class="row-actions"><button id="create-api-token" class="button primary" type="button">Create token</button></div></div><div id="api-token-summary" class="summary"></div><div id="api-token-list" class="user-grid"><p class="quiet-state padded">Open this tab to load API tokens.</p></div>';
|
||||
panel.innerHTML = '<div id="api-token-summary" class="summary"></div><div id="api-token-list" class="user-grid"><p class="quiet-state padded">Open this tab to load API tokens.</p></div>';
|
||||
tab.addEventListener("click", async () => {
|
||||
document.querySelectorAll("[data-admin-tab]").forEach(item => item.classList.toggle("tab-active", item === tab));
|
||||
document.querySelectorAll("[data-admin-panel]").forEach(item => item.classList.toggle("hidden", item !== panel));
|
||||
@@ -395,8 +395,10 @@ function showIssuedApiToken(result) {
|
||||
});
|
||||
dialog.showModal();
|
||||
}
|
||||
document.addEventListener("click", async event => {
|
||||
if (!event.target.closest("#create-api-token")) return;
|
||||
// Called from the shared global "+ Create" header button (see openCreate() in app.js) when the API
|
||||
// Access tab is active -- there is no dedicated "Create token" button in the panel itself anymore,
|
||||
// matching how Groups and Users route their create actions through the same shared button.
|
||||
async function openCreateApiTokenDialog() {
|
||||
const dialog = featureDialog("create-api-token-dialog");
|
||||
dialog.innerHTML = '<form method="dialog" class="dialog-card"><div class="dialog-heading"><div><p class="eyebrow">API access</p><h2>Create an API token</h2></div></div><p class="muted">Re-enter your administrator credentials to confirm. The token inherits your role.</p><label>Token name<input name="name" maxlength="60" required placeholder="Home Assistant integration"></label><label>Scope<select name="scope"><option value="full">Full access — read and change configuration</option><option value="read-only">Read-only — GET requests only</option></select></label><label>Expires after <span class="optional">Optional</span><input name="expiresInDays" type="number" min="1" max="3650" placeholder="Leave empty for no expiry"><small>Number of days. Leave empty for a token that never expires.</small></label><label>Administrator username<input name="username" autocomplete="username" required></label><label>Administrator password<input name="password" type="password" autocomplete="current-password" required></label><p class="error" id="api-token-error"></p><div class="dialog-actions"><button value="cancel" formnovalidate class="button secondary">Cancel</button><button value="confirm" class="button primary">Create token</button></div></form>';
|
||||
dialog.showModal();
|
||||
@@ -409,7 +411,7 @@ document.addEventListener("click", async event => {
|
||||
await loadApiTokens();
|
||||
showIssuedApiToken(result);
|
||||
} catch (error) { toast(error.message, "error"); }
|
||||
});
|
||||
}
|
||||
document.addEventListener("click", async event => {
|
||||
const button = event.target.closest('[data-token-action="revoke"]'); if (!button) return;
|
||||
const row = button.closest("[data-token-id]"); if (!row) return;
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user