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:
2026-04-02 14:54:15 +03:00
parent c730cfaa45
commit 670948f113
243 changed files with 15971 additions and 535 deletions
@@ -0,0 +1,135 @@
import { b as attr_class, g as stringify, e as escape_html, s as store_get, u as unsubscribe_stores, i as derived, h as head, a as attr, f as ensure_array_like } from "../../../../chunks/index.js";
import { c as cleanupStaleContainer, b as bulkCleanupStaleContainers } from "../../../../chunks/api.js";
import { I as IconTrash } from "../../../../chunks/IconTrash.js";
import { I as IconLoader } from "../../../../chunks/IconLoader.js";
import { t } from "../../../../chunks/index2.js";
import { I as IconAlert } from "../../../../chunks/IconAlert.js";
import { S as SkeletonCard } from "../../../../chunks/SkeletonCard.js";
import { t as toasts } from "../../../../chunks/toast.js";
function ConfirmDialog($$renderer, $$props) {
$$renderer.component(($$renderer2) => {
var $$store_subs;
const {
open,
title,
message,
confirmLabel = "Confirm",
confirmVariant = "primary",
onconfirm,
oncancel
} = $$props;
const confirmClass = derived(() => confirmVariant === "danger" ? "bg-[var(--color-danger)] hover:bg-[var(--color-danger-dark)] focus-visible:outline-[var(--color-danger)]" : "bg-[var(--color-brand-600)] hover:bg-[var(--color-brand-700)] focus-visible:outline-[var(--color-brand-600)]");
const iconBgClass = derived(() => confirmVariant === "danger" ? "bg-[var(--color-danger-light)]" : "bg-[var(--color-brand-50)]");
const iconColorClass = derived(() => confirmVariant === "danger" ? "text-[var(--color-danger)]" : "text-[var(--color-brand-600)]");
if (open) {
$$renderer2.push("<!--[0-->");
$$renderer2.push(`<div class="fixed inset-0 z-40 bg-[var(--surface-overlay)] animate-fade-in" role="presentation"></div> <div class="fixed inset-0 z-50 flex items-center justify-center p-4"><div class="w-full max-w-md rounded-2xl bg-[var(--surface-card)] p-6 shadow-xl animate-scale-in"><div class="flex items-start gap-4"><div${attr_class(`flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-full ${stringify(iconBgClass())}`)}>`);
IconAlert($$renderer2, { size: 20, class: iconColorClass() });
$$renderer2.push(`<!----></div> <div class="flex-1"><h3 class="text-lg font-semibold text-[var(--text-primary)]">${escape_html(title)}</h3> <p class="mt-2 text-sm text-[var(--text-secondary)] leading-relaxed">${escape_html(message)}</p></div></div> <div class="mt-6 flex justify-end gap-3"><button type="button" class="rounded-lg px-4 py-2 text-sm font-medium text-[var(--text-secondary)] hover:bg-[var(--surface-card-hover)] transition-colors active:animate-press">${escape_html(store_get($$store_subs ??= {}, "$t", t)("common.cancel"))}</button> <button type="button"${attr_class(`rounded-lg px-4 py-2 text-sm font-medium text-white ${stringify(confirmClass())} shadow-sm transition-colors focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 active:animate-press`)}>${escape_html(confirmLabel)}</button></div></div></div>`);
} else {
$$renderer2.push("<!--[-1-->");
}
$$renderer2.push(`<!--]-->`);
if ($$store_subs) unsubscribe_stores($$store_subs);
});
}
function _page($$renderer, $$props) {
$$renderer.component(($$renderer2) => {
var $$store_subs;
let containers = [];
let confirmSingleId = "";
let confirmBulk = false;
let cleaningIds = /* @__PURE__ */ new Set();
let bulkCleaning = false;
async function handleConfirmCleanup() {
const id = confirmSingleId;
confirmSingleId = "";
cleaningIds = /* @__PURE__ */ new Set([...cleaningIds, id]);
try {
await cleanupStaleContainer(id);
containers = containers.filter((c) => c.id !== id);
toasts.success(store_get($$store_subs ??= {}, "$t", t)("stale.cleanedUp"));
} catch (e) {
toasts.error(e instanceof Error ? e.message : store_get($$store_subs ??= {}, "$t", t)("stale.cleanupFailed"));
} finally {
const next = new Set(cleaningIds);
next.delete(id);
cleaningIds = next;
}
}
async function handleConfirmBulkCleanup() {
confirmBulk = false;
bulkCleaning = true;
try {
const result = await bulkCleanupStaleContainers();
containers = [];
toasts.success(store_get($$store_subs ??= {}, "$t", t)("stale.bulkCleanedUp", { count: String(result.deleted) }));
} catch (e) {
toasts.error(e instanceof Error ? e.message : store_get($$store_subs ??= {}, "$t", t)("stale.cleanupFailed"));
} finally {
bulkCleaning = false;
}
}
head("r4vrn", $$renderer2, ($$renderer3) => {
$$renderer3.title(($$renderer4) => {
$$renderer4.push(`<title>${escape_html(store_get($$store_subs ??= {}, "$t", t)("stale.title"))} - ${escape_html(store_get($$store_subs ??= {}, "$t", t)("app.name"))}</title>`);
});
});
$$renderer2.push(`<div class="space-y-6"><div class="flex items-center justify-between"><h1 class="text-2xl font-bold text-[var(--text-primary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("stale.title"))}</h1> `);
if (containers.length > 0) {
$$renderer2.push("<!--[0-->");
$$renderer2.push(`<button type="button"${attr("disabled", bulkCleaning, true)} class="inline-flex items-center gap-2 rounded-lg border border-[var(--color-danger)] px-4 py-2.5 text-sm font-medium text-[var(--color-danger)] transition-colors hover:bg-[var(--color-danger-light)] disabled:opacity-50 active:animate-press">`);
if (bulkCleaning) {
$$renderer2.push("<!--[0-->");
IconLoader($$renderer2, { size: 16 });
} else {
$$renderer2.push("<!--[-1-->");
}
$$renderer2.push(`<!--]--> `);
IconTrash($$renderer2, { size: 16 });
$$renderer2.push(`<!----> ${escape_html(store_get($$store_subs ??= {}, "$t", t)("stale.cleanupAll"))}</button>`);
} else {
$$renderer2.push("<!--[-1-->");
}
$$renderer2.push(`<!--]--></div> `);
{
$$renderer2.push("<!--[0-->");
$$renderer2.push(`<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3"><!--[-->`);
const each_array = ensure_array_like(Array(3));
for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) {
each_array[$$index];
SkeletonCard($$renderer2);
}
$$renderer2.push(`<!--]--></div>`);
}
$$renderer2.push(`<!--]--></div> `);
ConfirmDialog($$renderer2, {
open: confirmSingleId !== "",
title: store_get($$store_subs ??= {}, "$t", t)("stale.cleanup"),
message: store_get($$store_subs ??= {}, "$t", t)("stale.confirmCleanup"),
confirmLabel: store_get($$store_subs ??= {}, "$t", t)("stale.cleanup"),
confirmVariant: "danger",
onconfirm: handleConfirmCleanup,
oncancel: () => {
confirmSingleId = "";
}
});
$$renderer2.push(`<!----> `);
ConfirmDialog($$renderer2, {
open: confirmBulk,
title: store_get($$store_subs ??= {}, "$t", t)("stale.cleanupAll"),
message: store_get($$store_subs ??= {}, "$t", t)("stale.confirmBulkCleanup"),
confirmLabel: store_get($$store_subs ??= {}, "$t", t)("stale.cleanupAll"),
confirmVariant: "danger",
onconfirm: handleConfirmBulkCleanup,
oncancel: () => {
confirmBulk = false;
}
});
$$renderer2.push(`<!---->`);
if ($$store_subs) unsubscribe_stores($$store_subs);
});
}
export {
_page as default
};
@@ -0,0 +1,4 @@
const ssr = false;
export {
ssr
};