fix: address code review findings for DNS management
- CRITICAL: Change DNS zones endpoint from GET to POST to avoid leaking API token in URL query parameters - HIGH: Add sync.RWMutex to protect dnsProvider field in Server, Deployer, and proxy Manager against concurrent read/write races - HIGH: Capture old DNS provider reference synchronously before launching background cleanup goroutine - HIGH: Use getDNS()/getDNSProviderLocked() accessors instead of direct field reads in all DNS operations
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
import { e as escape_html, a as attr, s as store_get, f as ensure_array_like, b as attr_class, g as stringify, u as unsubscribe_stores } from "../../../../chunks/index.js";
|
||||
import { t } from "../../../../chunks/index2.js";
|
||||
import { I as IconTrash } from "../../../../chunks/IconTrash.js";
|
||||
import { I as IconPlus } from "../../../../chunks/IconPlus.js";
|
||||
function EmptyState($$renderer, $$props) {
|
||||
const {
|
||||
title,
|
||||
description = "",
|
||||
actionLabel = "",
|
||||
actionHref = "",
|
||||
onaction,
|
||||
icon = "projects"
|
||||
} = $$props;
|
||||
$$renderer.push(`<div class="flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-[var(--border-primary)] px-6 py-16 text-center animate-fade-in"><div class="mb-4 flex h-16 w-16 items-center justify-center rounded-2xl bg-[var(--color-brand-50)]">`);
|
||||
if (icon === "projects") {
|
||||
$$renderer.push("<!--[0-->");
|
||||
$$renderer.push(`<svg class="h-8 w-8 text-[var(--color-brand-500)]" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"></path><path d="M12 10v6"></path><path d="M9 13h6"></path></svg>`);
|
||||
} else if (icon === "instances") {
|
||||
$$renderer.push("<!--[1-->");
|
||||
$$renderer.push(`<svg class="h-8 w-8 text-[var(--color-brand-500)]" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="8" x="2" y="2" rx="2" ry="2"></rect><rect width="20" height="8" x="2" y="14" rx="2" ry="2"></rect><line x1="6" x2="6.01" y1="6" y2="6"></line><line x1="6" x2="6.01" y1="18" y2="18"></line></svg>`);
|
||||
} else if (icon === "deploys") {
|
||||
$$renderer.push("<!--[2-->");
|
||||
$$renderer.push(`<svg class="h-8 w-8 text-[var(--color-brand-500)]" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M15.59 14.37a6 6 0 0 1-5.84 7.38v-4.8m5.84-2.58a14.98 14.98 0 0 0 6.16-12.12A14.98 14.98 0 0 0 9.631 8.41m5.96 5.96a14.926 14.926 0 0 1-5.841 2.58M16.5 9a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Z"></path></svg>`);
|
||||
} else if (icon === "registries") {
|
||||
$$renderer.push("<!--[3-->");
|
||||
$$renderer.push(`<svg class="h-8 w-8 text-[var(--color-brand-500)]" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><ellipse cx="12" cy="5" rx="9" ry="3"></ellipse><path d="M3 5v14a9 3 0 0 0 18 0V5"></path><path d="M3 12a9 3 0 0 0 18 0"></path></svg>`);
|
||||
} else if (icon === "volumes") {
|
||||
$$renderer.push("<!--[4-->");
|
||||
$$renderer.push(`<svg class="h-8 w-8 text-[var(--color-brand-500)]" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><line x1="22" x2="2" y1="12" y2="12"></line><path d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"></path></svg>`);
|
||||
} else if (icon === "users") {
|
||||
$$renderer.push("<!--[5-->");
|
||||
$$renderer.push(`<svg class="h-8 w-8 text-[var(--color-brand-500)]" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M22 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path></svg>`);
|
||||
} else {
|
||||
$$renderer.push("<!--[-1-->");
|
||||
}
|
||||
$$renderer.push(`<!--]--></div> <h3 class="text-base font-semibold text-[var(--text-primary)]">${escape_html(title)}</h3> `);
|
||||
if (description) {
|
||||
$$renderer.push("<!--[0-->");
|
||||
$$renderer.push(`<p class="mt-1 max-w-sm text-sm text-[var(--text-secondary)]">${escape_html(description)}</p>`);
|
||||
} else {
|
||||
$$renderer.push("<!--[-1-->");
|
||||
}
|
||||
$$renderer.push(`<!--]--> `);
|
||||
if (actionLabel) {
|
||||
$$renderer.push("<!--[0-->");
|
||||
if (actionHref) {
|
||||
$$renderer.push("<!--[0-->");
|
||||
$$renderer.push(`<a${attr("href", actionHref)} class="mt-4 inline-flex items-center gap-2 rounded-lg bg-[var(--color-brand-600)] px-4 py-2 text-sm font-medium text-white shadow-sm transition-all duration-150 hover:bg-[var(--color-brand-700)] active:animate-press"><svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"></path><path d="M12 5v14"></path></svg> ${escape_html(actionLabel)}</a>`);
|
||||
} else if (onaction) {
|
||||
$$renderer.push("<!--[1-->");
|
||||
$$renderer.push(`<button type="button" class="mt-4 inline-flex items-center gap-2 rounded-lg bg-[var(--color-brand-600)] px-4 py-2 text-sm font-medium text-white shadow-sm transition-all duration-150 hover:bg-[var(--color-brand-700)] active:animate-press"><svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"></path><path d="M12 5v14"></path></svg> ${escape_html(actionLabel)}</button>`);
|
||||
} else {
|
||||
$$renderer.push("<!--[-1-->");
|
||||
}
|
||||
$$renderer.push(`<!--]-->`);
|
||||
} else {
|
||||
$$renderer.push("<!--[-1-->");
|
||||
}
|
||||
$$renderer.push(`<!--]--></div>`);
|
||||
}
|
||||
function _page($$renderer, $$props) {
|
||||
$$renderer.component(($$renderer2) => {
|
||||
var $$store_subs;
|
||||
let settings = {
|
||||
auth_mode: "local"
|
||||
};
|
||||
let users = [];
|
||||
let saving = false;
|
||||
let newUsername = "";
|
||||
let newPassword = "";
|
||||
let newEmail = "";
|
||||
let newRole = "viewer";
|
||||
$$renderer2.push(`<div class="space-y-6"><div><h2 class="text-lg font-semibold text-[var(--text-primary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("settingsAuth.title"))}</h2> <p class="text-sm text-[var(--text-secondary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("settingsAuth.description"))}</p></div> `);
|
||||
{
|
||||
$$renderer2.push("<!--[-1-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> `);
|
||||
{
|
||||
$$renderer2.push("<!--[-1-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> <div class="rounded-xl border border-[var(--border-primary)] bg-[var(--surface-card)] p-6 shadow-[var(--shadow-sm)]"><h3 class="text-base font-semibold text-[var(--text-primary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("settingsAuth.authMode"))}</h3> <div class="mt-4 flex gap-4"><label class="flex items-center gap-2 cursor-pointer"><input type="radio"${attr("checked", settings.auth_mode === "local", true)} value="local" class="h-4 w-4 text-[var(--color-brand-600)] focus:ring-[var(--color-brand-500)]"/> <span class="text-sm font-medium text-[var(--text-secondary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("settingsAuth.local"))}</span></label> <label class="flex items-center gap-2 cursor-pointer"><input type="radio"${attr("checked", settings.auth_mode === "oidc", true)} value="oidc" class="h-4 w-4 text-[var(--color-brand-600)] focus:ring-[var(--color-brand-500)]"/> <span class="text-sm font-medium text-[var(--text-secondary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("settingsAuth.oidc"))}</span></label></div></div> `);
|
||||
{
|
||||
$$renderer2.push("<!--[-1-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> <button${attr("disabled", saving, true)} class="inline-flex items-center gap-2 rounded-lg bg-[var(--color-brand-600)] px-4 py-2.5 text-sm font-medium text-white shadow-sm hover:bg-[var(--color-brand-700)] disabled:opacity-50 transition-colors active:animate-press">`);
|
||||
{
|
||||
$$renderer2.push("<!--[-1-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> ${escape_html(store_get($$store_subs ??= {}, "$t", t)("settingsAuth.saveSettings"))}</button> <div class="rounded-xl border border-[var(--border-primary)] bg-[var(--surface-card)] p-6 shadow-[var(--shadow-sm)]"><h3 class="text-base font-semibold text-[var(--text-primary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("settingsAuth.localUsers"))}</h3> `);
|
||||
if (users.length > 0) {
|
||||
$$renderer2.push("<!--[0-->");
|
||||
$$renderer2.push(`<div class="mt-4 overflow-hidden rounded-xl border border-[var(--border-primary)]"><table class="min-w-full divide-y divide-[var(--border-primary)]"><thead class="bg-[var(--surface-card-hover)]"><tr><th class="px-4 py-2.5 text-left text-xs font-medium uppercase text-[var(--text-tertiary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("settingsAuth.username"))}</th><th class="px-4 py-2.5 text-left text-xs font-medium uppercase text-[var(--text-tertiary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("settingsAuth.email"))}</th><th class="px-4 py-2.5 text-left text-xs font-medium uppercase text-[var(--text-tertiary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("settingsAuth.role"))}</th><th class="px-4 py-2.5 text-left text-xs font-medium uppercase text-[var(--text-tertiary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("settingsAuth.created"))}</th><th class="px-4 py-2.5"></th></tr></thead><tbody class="divide-y divide-[var(--border-secondary)]"><!--[-->`);
|
||||
const each_array_1 = ensure_array_like(users);
|
||||
for (let $$index_1 = 0, $$length = each_array_1.length; $$index_1 < $$length; $$index_1++) {
|
||||
let user = each_array_1[$$index_1];
|
||||
$$renderer2.push(`<tr class="hover:bg-[var(--surface-card-hover)] transition-colors"><td class="px-4 py-2.5 text-sm text-[var(--text-primary)]">${escape_html(user.username)}</td><td class="px-4 py-2.5 text-sm text-[var(--text-secondary)]">${escape_html(user.email || "-")}</td><td class="px-4 py-2.5"><span${attr_class(`inline-flex rounded-full px-2 py-0.5 text-xs font-semibold ${stringify(user.role === "admin" ? "bg-purple-50 text-purple-700" : "bg-gray-100 text-gray-700")}`)}>${escape_html(user.role)}</span></td><td class="px-4 py-2.5 text-sm text-[var(--text-secondary)]">${escape_html(user.created_at)}</td><td class="px-4 py-2.5 text-right"><button class="rounded-lg p-1.5 text-[var(--text-tertiary)] hover:bg-red-50 hover:text-red-600 transition-colors">`);
|
||||
IconTrash($$renderer2, { size: 16 });
|
||||
$$renderer2.push(`<!----></button></td></tr>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--></tbody></table></div>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[-1-->");
|
||||
$$renderer2.push(`<div class="mt-4">`);
|
||||
EmptyState($$renderer2, {
|
||||
title: store_get($$store_subs ??= {}, "$t", t)("empty.noUsers"),
|
||||
description: store_get($$store_subs ??= {}, "$t", t)("empty.noUsersDesc"),
|
||||
icon: "users"
|
||||
});
|
||||
$$renderer2.push(`<!----></div>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--> <div class="mt-6 border-t border-[var(--border-primary)] pt-4"><h4 class="text-sm font-semibold text-[var(--text-primary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("settingsAuth.addUser"))}</h4> <div class="mt-3 grid grid-cols-2 gap-3"><input type="text"${attr("value", newUsername)}${attr("placeholder", store_get($$store_subs ??= {}, "$t", t)("settingsAuth.username"))} class="rounded-lg border border-[var(--border-input)] bg-[var(--surface-input)] px-3 py-2 text-sm text-[var(--text-primary)] placeholder:text-[var(--text-tertiary)] focus:border-[var(--color-brand-500)] focus:outline-none focus:ring-2 focus:ring-[var(--color-brand-500)]"/> <input type="password"${attr("value", newPassword)}${attr("placeholder", store_get($$store_subs ??= {}, "$t", t)("settingsAuth.password"))} class="rounded-lg border border-[var(--border-input)] bg-[var(--surface-input)] px-3 py-2 text-sm text-[var(--text-primary)] placeholder:text-[var(--text-tertiary)] focus:border-[var(--color-brand-500)] focus:outline-none focus:ring-2 focus:ring-[var(--color-brand-500)]"/> <input type="email"${attr("value", newEmail)}${attr("placeholder", `${stringify(store_get($$store_subs ??= {}, "$t", t)("settingsAuth.email"))} (optional)`)} class="rounded-lg border border-[var(--border-input)] bg-[var(--surface-input)] px-3 py-2 text-sm text-[var(--text-primary)] placeholder:text-[var(--text-tertiary)] focus:border-[var(--color-brand-500)] focus:outline-none focus:ring-2 focus:ring-[var(--color-brand-500)]"/> `);
|
||||
$$renderer2.select(
|
||||
{
|
||||
value: newRole,
|
||||
class: "rounded-lg border border-[var(--border-input)] bg-[var(--surface-input)] px-3 py-2 text-sm text-[var(--text-primary)] focus:border-[var(--color-brand-500)] focus:outline-none focus:ring-2 focus:ring-[var(--color-brand-500)]"
|
||||
},
|
||||
($$renderer3) => {
|
||||
$$renderer3.option({ value: "viewer" }, ($$renderer4) => {
|
||||
$$renderer4.push(`${escape_html(store_get($$store_subs ??= {}, "$t", t)("settingsAuth.viewer"))}`);
|
||||
});
|
||||
$$renderer3.option({ value: "admin" }, ($$renderer4) => {
|
||||
$$renderer4.push(`${escape_html(store_get($$store_subs ??= {}, "$t", t)("settingsAuth.admin"))}`);
|
||||
});
|
||||
}
|
||||
);
|
||||
$$renderer2.push(`</div> <button class="mt-3 inline-flex items-center gap-2 rounded-lg bg-[var(--color-brand-600)] px-4 py-2.5 text-sm font-medium text-white shadow-sm hover:bg-[var(--color-brand-700)] transition-colors active:animate-press">`);
|
||||
IconPlus($$renderer2, { size: 16 });
|
||||
$$renderer2.push(`<!----> ${escape_html(store_get($$store_subs ??= {}, "$t", t)("settingsAuth.addUser"))}</button></div></div></div>`);
|
||||
if ($$store_subs) unsubscribe_stores($$store_subs);
|
||||
});
|
||||
}
|
||||
export {
|
||||
_page as default
|
||||
};
|
||||
Reference in New Issue
Block a user