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,51 @@
import { h as head, e as escape_html, s as store_get, f as ensure_array_like, u as unsubscribe_stores } from "../../../../chunks/index.js";
import "@sveltejs/kit/internal";
import "../../../../chunks/exports.js";
import "../../../../chunks/utils.js";
import "@sveltejs/kit/internal/server";
import "../../../../chunks/root.js";
import "../../../../chunks/state.svelte.js";
import { t } from "../../../../chunks/index2.js";
import { S as Skeleton } from "../../../../chunks/Skeleton.js";
import "../../../../chunks/toast.js";
function _page($$renderer, $$props) {
$$renderer.component(($$renderer2) => {
var $$store_subs;
let $$settled = true;
let $$inner_renderer;
function $$render_inner($$renderer3) {
head("ffmenf", $$renderer3, ($$renderer4) => {
$$renderer4.title(($$renderer5) => {
$$renderer5.push(`<title>${escape_html(store_get($$store_subs ??= {}, "$t", t)("common.project"))} - ${escape_html(store_get($$store_subs ??= {}, "$t", t)("app.name"))}</title>`);
});
});
{
$$renderer3.push("<!--[0-->");
$$renderer3.push(`<div class="space-y-6"><div class="flex items-start justify-between"><div class="space-y-2">`);
Skeleton($$renderer3, { width: "4rem", height: "0.875rem" });
$$renderer3.push(`<!----> `);
Skeleton($$renderer3, { width: "12rem", height: "1.75rem" });
$$renderer3.push(`<!----> `);
Skeleton($$renderer3, { width: "16rem", height: "0.875rem" });
$$renderer3.push(`<!----></div></div> <div class="grid grid-cols-4 gap-4"><!--[-->`);
const each_array = ensure_array_like(Array(4));
for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) {
each_array[$$index];
Skeleton($$renderer3, { height: "3rem" });
}
$$renderer3.push(`<!--]--></div></div>`);
}
$$renderer3.push(`<!--]-->`);
}
do {
$$settled = true;
$$inner_renderer = $$renderer2.copy();
$$render_inner($$inner_renderer);
} while (!$$settled);
$$renderer2.subsume($$inner_renderer);
if ($$store_subs) unsubscribe_stores($$store_subs);
});
}
export {
_page as default
};
@@ -0,0 +1,43 @@
import { h as head, e as escape_html, s as store_get, a as attr, g as stringify, u as unsubscribe_stores, i as derived } from "../../../../../chunks/index.js";
import { p as page } from "../../../../../chunks/stores.js";
import "../../../../../chunks/toast.js";
import { t } from "../../../../../chunks/index2.js";
import { I as IconChevronRight } from "../../../../../chunks/IconChevronRight.js";
import { S as Skeleton } from "../../../../../chunks/Skeleton.js";
function _page($$renderer, $$props) {
$$renderer.component(($$renderer2) => {
var $$store_subs;
const projectId = derived(() => store_get($$store_subs ??= {}, "$page", page).params.id);
let $$settled = true;
let $$inner_renderer;
function $$render_inner($$renderer3) {
head("1fdiw6z", $$renderer3, ($$renderer4) => {
$$renderer4.title(($$renderer5) => {
$$renderer5.push(`<title>${escape_html(store_get($$store_subs ??= {}, "$t", t)("envEditor.title"))} - ${escape_html(store_get($$store_subs ??= {}, "$t", t)("app.name"))}</title>`);
});
});
$$renderer3.push(`<div class="space-y-6"><div><div class="flex items-center gap-1.5 text-sm text-[var(--text-tertiary)]"><a${attr("href", `/projects/${stringify(projectId())}`)} class="hover:text-[var(--text-link)] transition-colors">${escape_html(store_get($$store_subs ??= {}, "$t", t)("common.project"))}</a> `);
IconChevronRight($$renderer3, { size: 14 });
$$renderer3.push(`<!----></div> <h1 class="mt-1 text-2xl font-bold text-[var(--text-primary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("envEditor.title"))}</h1> <p class="mt-1 text-sm text-[var(--text-secondary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("envEditor.description"))}</p></div> `);
{
$$renderer3.push("<!--[0-->");
$$renderer3.push(`<div class="space-y-4">`);
Skeleton($$renderer3, { width: "16rem", height: "2.5rem" });
$$renderer3.push(`<!----> `);
Skeleton($$renderer3, { height: "12rem" });
$$renderer3.push(`<!----></div>`);
}
$$renderer3.push(`<!--]--></div>`);
}
do {
$$settled = true;
$$inner_renderer = $$renderer2.copy();
$$render_inner($$inner_renderer);
} while (!$$settled);
$$renderer2.subsume($$inner_renderer);
if ($$store_subs) unsubscribe_stores($$store_subs);
});
}
export {
_page as default
};
@@ -0,0 +1,80 @@
import { h as head, a as attr, g as stringify, e as escape_html, s as store_get, f as ensure_array_like, b as attr_class, u as unsubscribe_stores, i as derived } from "../../../../../chunks/index.js";
import { p as page } from "../../../../../chunks/stores.js";
import "../../../../../chunks/toast.js";
import { t } from "../../../../../chunks/index2.js";
import { I as IconChevronRight } from "../../../../../chunks/IconChevronRight.js";
import { S as Skeleton } from "../../../../../chunks/Skeleton.js";
function _page($$renderer, $$props) {
$$renderer.component(($$renderer2) => {
var $$store_subs;
let scopeInfos = [];
const projectId = derived(() => store_get($$store_subs ??= {}, "$page", page).params.id ?? "");
const scopeColors = {
instance: {
bg: "bg-amber-50 dark:bg-amber-900/30",
text: "text-amber-700 dark:text-amber-400"
},
stage: {
bg: "bg-blue-50 dark:bg-blue-900/30",
text: "text-blue-700 dark:text-blue-400"
},
project: {
bg: "bg-emerald-50 dark:bg-emerald-900/30",
text: "text-emerald-700 dark:text-emerald-400"
},
project_named: {
bg: "bg-violet-50 dark:bg-violet-900/30",
text: "text-violet-700 dark:text-violet-400"
},
named: {
bg: "bg-purple-50 dark:bg-purple-900/30",
text: "text-purple-700 dark:text-purple-400"
},
ephemeral: {
bg: "bg-gray-100 dark:bg-gray-800",
text: "text-gray-600 dark:text-gray-400"
}
};
function scopeColor(scope) {
return scopeColors[scope] ?? scopeColors.project;
}
head("15zmna7", $$renderer2, ($$renderer3) => {
$$renderer3.title(($$renderer4) => {
$$renderer4.push(`<title>${escape_html(store_get($$store_subs ??= {}, "$t", t)("volumeEditor.title"))} - ${escape_html(store_get($$store_subs ??= {}, "$t", t)("app.name"))}</title>`);
});
});
$$renderer2.push(`<div class="space-y-6"><div><div class="flex items-center gap-1.5 text-sm text-[var(--text-tertiary)]"><a${attr("href", `/projects/${stringify(projectId())}`)} class="hover:text-[var(--text-link)] transition-colors">${escape_html(store_get($$store_subs ??= {}, "$t", t)("common.project"))}</a> `);
IconChevronRight($$renderer2, { size: 14 });
$$renderer2.push(`<!----></div> <h1 class="mt-1 text-2xl font-bold text-[var(--text-primary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("volumeEditor.title"))}</h1> <p class="mt-1 text-sm text-[var(--text-secondary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("volumeEditor.description"))}</p></div> `);
if (scopeInfos.length > 0) {
$$renderer2.push("<!--[0-->");
$$renderer2.push(`<div class="grid grid-cols-1 gap-2 sm:grid-cols-2 lg:grid-cols-3"><!--[-->`);
const each_array = ensure_array_like(scopeInfos);
for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) {
let info = each_array[$$index];
const colors = scopeColor(info.scope);
$$renderer2.push(`<div${attr_class(`rounded-lg border border-[var(--border-primary)] p-3 ${stringify(colors.bg)}`)}><div class="flex items-center gap-2"><span${attr_class(`rounded-full px-2 py-0.5 text-xs font-semibold ${stringify(colors.text)} ${stringify(colors.bg)}`)}>${escape_html(info.scope)}</span> `);
if (info.needs_name) {
$$renderer2.push("<!--[0-->");
$$renderer2.push(`<span class="text-[10px] text-[var(--text-tertiary)]">(${escape_html(store_get($$store_subs ??= {}, "$t", t)("volumeEditor.requiresName"))})</span>`);
} else {
$$renderer2.push("<!--[-1-->");
}
$$renderer2.push(`<!--]--></div> <p class="mt-1.5 text-xs text-[var(--text-secondary)]">${escape_html(info.description)}</p> <code class="mt-1 block text-[10px] text-[var(--text-tertiary)]">${escape_html(info.path_example)}</code></div>`);
}
$$renderer2.push(`<!--]--></div>`);
} else {
$$renderer2.push("<!--[-1-->");
}
$$renderer2.push(`<!--]--> `);
{
$$renderer2.push("<!--[0-->");
Skeleton($$renderer2, { height: "12rem" });
}
$$renderer2.push(`<!--]--></div>`);
if ($$store_subs) unsubscribe_stores($$store_subs);
});
}
export {
_page as default
};
@@ -0,0 +1,45 @@
import { h as head, a as attr, g as stringify, e as escape_html, s as store_get, b as attr_class, f as ensure_array_like, u as unsubscribe_stores, i as derived } from "../../../../../../../chunks/index.js";
import { p as page } from "../../../../../../../chunks/stores.js";
import "../../../../../../../chunks/toast.js";
import { t } from "../../../../../../../chunks/index2.js";
import { I as IconChevronRight } from "../../../../../../../chunks/IconChevronRight.js";
import { S as Skeleton } from "../../../../../../../chunks/Skeleton.js";
function _page($$renderer, $$props) {
$$renderer.component(($$renderer2) => {
var $$store_subs;
const projectId = derived(() => store_get($$store_subs ??= {}, "$page", page).params.id ?? "");
const breadcrumbs = derived(() => () => {
return [];
});
head("8w09c1", $$renderer2, ($$renderer3) => {
$$renderer3.title(($$renderer4) => {
$$renderer4.push(`<title>${escape_html(store_get($$store_subs ??= {}, "$t", t)("volumeBrowser.title"))} - ${escape_html(store_get($$store_subs ??= {}, "$t", t)("app.name"))}</title>`);
});
});
$$renderer2.push(`<div class="space-y-4"><div><div class="flex items-center gap-1.5 text-sm text-[var(--text-tertiary)]"><a${attr("href", `/projects/${stringify(projectId())}`)} class="hover:text-[var(--text-link)] transition-colors">${escape_html(store_get($$store_subs ??= {}, "$t", t)("common.project"))}</a> `);
IconChevronRight($$renderer2, { size: 14 });
$$renderer2.push(`<!----> <a${attr("href", `/projects/${stringify(projectId())}/volumes`)} class="hover:text-[var(--text-link)] transition-colors">${escape_html(store_get($$store_subs ??= {}, "$t", t)("volumeEditor.title"))}</a> `);
IconChevronRight($$renderer2, { size: 14 });
$$renderer2.push(`<!----></div> <div class="mt-1 flex items-center justify-between"><h1 class="text-2xl font-bold text-[var(--text-primary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("volumeBrowser.title"))}</h1> <div class="flex items-center gap-2"><button type="button" class="inline-flex items-center gap-1.5 rounded-lg border border-[var(--border-primary)] px-3 py-2 text-xs font-medium text-[var(--text-secondary)] hover:bg-[var(--surface-card-hover)] transition-colors">📦 ${escape_html(store_get($$store_subs ??= {}, "$t", t)("volumeBrowser.downloadAll"))}</button> <label${attr_class(`inline-flex cursor-pointer items-center gap-1.5 rounded-lg bg-[var(--color-brand-600)] px-3 py-2 text-xs font-medium text-white hover:bg-[var(--color-brand-700)] transition-colors ${stringify("")}`)}>`);
{
$$renderer2.push("<!--[-1-->");
}
$$renderer2.push(`<!--]--> ${escape_html(store_get($$store_subs ??= {}, "$t", t)("volumeBrowser.upload"))} <input type="file" multiple="" class="hidden"/></label></div></div></div> <nav class="flex items-center gap-1 text-sm"><button type="button"${attr_class(`rounded px-1.5 py-0.5 text-[var(--text-link)] hover:bg-[var(--surface-card-hover)] transition-colors ${stringify("font-semibold")}`)}>/</button> <!--[-->`);
const each_array = ensure_array_like(breadcrumbs()());
for (let i = 0, $$length = each_array.length; i < $$length; i++) {
let segment = each_array[i];
IconChevronRight($$renderer2, { size: 12, class: "text-[var(--text-tertiary)]" });
$$renderer2.push(`<!----> <button type="button"${attr_class(`rounded px-1.5 py-0.5 text-[var(--text-link)] hover:bg-[var(--surface-card-hover)] transition-colors ${stringify(i === breadcrumbs()().length - 1 ? "font-semibold text-[var(--text-primary)]" : "")}`)}>${escape_html(segment)}</button>`);
}
$$renderer2.push(`<!--]--></nav> `);
{
$$renderer2.push("<!--[0-->");
Skeleton($$renderer2, { height: "16rem" });
}
$$renderer2.push(`<!--]--></div>`);
if ($$store_subs) unsubscribe_stores($$store_subs);
});
}
export {
_page as default
};
@@ -0,0 +1,4 @@
const ssr = false;
export {
ssr
};
@@ -0,0 +1,66 @@
import { f as ensure_array_like, h as head, e as escape_html, s as store_get, b as attr_class, u as unsubscribe_stores, g as stringify } from "../../../chunks/index.js";
import { t } from "../../../chunks/index2.js";
import { I as IconPlus } from "../../../chunks/IconPlus.js";
import { S as Skeleton } from "../../../chunks/Skeleton.js";
/* empty css */
function SkeletonTable($$renderer, $$props) {
const { rows = 5, cols = 4 } = $$props;
$$renderer.push(`<div class="overflow-hidden rounded-xl border border-[var(--border-primary)] bg-[var(--surface-card)] shadow-[var(--shadow-sm)]"><div class="border-b border-[var(--border-primary)] bg-[var(--surface-card-hover)] px-6 py-3"><div class="flex gap-6"><!--[-->`);
const each_array = ensure_array_like(Array(cols));
for (let i = 0, $$length = each_array.length; i < $$length; i++) {
each_array[i];
Skeleton($$renderer, { width: i === 0 ? "6rem" : "5rem", height: "0.75rem" });
}
$$renderer.push(`<!--]--></div></div> <!--[-->`);
const each_array_1 = ensure_array_like(Array(rows));
for (let i = 0, $$length = each_array_1.length; i < $$length; i++) {
each_array_1[i];
$$renderer.push(`<div class="flex gap-6 border-b border-[var(--border-secondary)] px-6 py-4 last:border-b-0"><!--[-->`);
const each_array_2 = ensure_array_like(Array(cols));
for (let j = 0, $$length2 = each_array_2.length; j < $$length2; j++) {
each_array_2[j];
Skeleton($$renderer, { width: j === 0 ? "40%" : "20%", height: "0.875rem" });
}
$$renderer.push(`<!--]--></div>`);
}
$$renderer.push(`<!--]--></div>`);
}
function _page($$renderer, $$props) {
$$renderer.component(($$renderer2) => {
var $$store_subs;
let $$settled = true;
let $$inner_renderer;
function $$render_inner($$renderer3) {
head("rqn88j", $$renderer3, ($$renderer4) => {
$$renderer4.title(($$renderer5) => {
$$renderer5.push(`<title>${escape_html(store_get($$store_subs ??= {}, "$t", t)("projects.title"))} - ${escape_html(store_get($$store_subs ??= {}, "$t", t)("app.name"))}</title>`);
});
});
$$renderer3.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)("projects.title"))}</h1> <button type="button"${attr_class(`inline-flex items-center gap-2 rounded-lg ${stringify("bg-[var(--color-brand-600)] text-white shadow-sm hover:bg-[var(--color-brand-700)]")} px-4 py-2.5 text-sm font-medium transition-all duration-150 active:animate-press`)}>`);
{
$$renderer3.push("<!--[0-->");
IconPlus($$renderer3, { size: 16 });
}
$$renderer3.push(`<!--]--> ${escape_html(store_get($$store_subs ??= {}, "$t", t)("projects.addProject"))}</button></div> `);
{
$$renderer3.push("<!--[-1-->");
}
$$renderer3.push(`<!--]--> `);
{
$$renderer3.push("<!--[0-->");
SkeletonTable($$renderer3, { rows: 4, cols: 5 });
}
$$renderer3.push(`<!--]--></div>`);
}
do {
$$settled = true;
$$inner_renderer = $$renderer2.copy();
$$render_inner($$inner_renderer);
} while (!$$settled);
$$renderer2.subsume($$inner_renderer);
if ($$store_subs) unsubscribe_stores($$store_subs);
});
}
export {
_page as default
};