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,62 @@
export { matchers } from './matchers.js';
export const nodes = [
() => import('./nodes/0'),
() => import('./nodes/1'),
() => import('./nodes/2'),
() => import('./nodes/3'),
() => import('./nodes/4'),
() => import('./nodes/5'),
() => import('./nodes/6'),
() => import('./nodes/7'),
() => import('./nodes/8'),
() => import('./nodes/9'),
() => import('./nodes/10'),
() => import('./nodes/11'),
() => import('./nodes/12'),
() => import('./nodes/13'),
() => import('./nodes/14'),
() => import('./nodes/15'),
() => import('./nodes/16'),
() => import('./nodes/17'),
() => import('./nodes/18'),
() => import('./nodes/19')
];
export const server_loads = [];
export const dictionary = {
"/": [3],
"/containers/stale": [4],
"/deploy": [5],
"/events": [6],
"/login": [7],
"/projects": [8],
"/projects/[id]": [9],
"/projects/[id]/env": [10],
"/projects/[id]/volumes": [11],
"/projects/[id]/volumes/[volId]/browse": [12],
"/proxies": [13],
"/proxies/create": [14],
"/proxies/[id]/edit": [15],
"/settings": [16,[2]],
"/settings/auth": [17,[2]],
"/settings/credentials": [18,[2]],
"/settings/registries": [19,[2]]
};
export const hooks = {
handleError: (({ error }) => { console.error(error) }),
reroute: (() => {}),
transport: {}
};
export const decoders = Object.fromEntries(Object.entries(hooks.transport).map(([k, v]) => [k, v.decode]));
export const encoders = Object.fromEntries(Object.entries(hooks.transport).map(([k, v]) => [k, v.encode]));
export const hash = false;
export const decode = (type, value) => decoders[type](value);
export { default as root } from '../root.js';
@@ -0,0 +1 @@
export const matchers = {};
@@ -0,0 +1,3 @@
import * as universal from "../../../../src/routes/+layout.ts";
export { universal };
export { default as component } from "../../../../src/routes/+layout.svelte";
@@ -0,0 +1 @@
export { default as component } from "../../../../node_modules/@sveltejs/kit/src/runtime/components/svelte-5/error.svelte";
@@ -0,0 +1 @@
export { default as component } from "../../../../src/routes/projects/[id]/env/+page.svelte";
@@ -0,0 +1 @@
export { default as component } from "../../../../src/routes/projects/[id]/volumes/+page.svelte";
@@ -0,0 +1,3 @@
import * as universal from "../../../../src/routes/projects/[id]/volumes/[volId]/browse/+page.ts";
export { universal };
export { default as component } from "../../../../src/routes/projects/[id]/volumes/[volId]/browse/+page.svelte";
@@ -0,0 +1,3 @@
import * as universal from "../../../../src/routes/proxies/+page.ts";
export { universal };
export { default as component } from "../../../../src/routes/proxies/+page.svelte";
@@ -0,0 +1,3 @@
import * as universal from "../../../../src/routes/proxies/create/+page.ts";
export { universal };
export { default as component } from "../../../../src/routes/proxies/create/+page.svelte";
@@ -0,0 +1,3 @@
import * as universal from "../../../../src/routes/proxies/[id]/edit/+page.ts";
export { universal };
export { default as component } from "../../../../src/routes/proxies/[id]/edit/+page.svelte";
@@ -0,0 +1 @@
export { default as component } from "../../../../src/routes/settings/+page.svelte";
@@ -0,0 +1 @@
export { default as component } from "../../../../src/routes/settings/auth/+page.svelte";
@@ -0,0 +1 @@
export { default as component } from "../../../../src/routes/settings/credentials/+page.svelte";
@@ -0,0 +1 @@
export { default as component } from "../../../../src/routes/settings/registries/+page.svelte";
@@ -0,0 +1 @@
export { default as component } from "../../../../src/routes/settings/+layout.svelte";
@@ -0,0 +1 @@
export { default as component } from "../../../../src/routes/+page.svelte";
@@ -0,0 +1,3 @@
import * as universal from "../../../../src/routes/containers/stale/+page.ts";
export { universal };
export { default as component } from "../../../../src/routes/containers/stale/+page.svelte";
@@ -0,0 +1 @@
export { default as component } from "../../../../src/routes/deploy/+page.svelte";
@@ -0,0 +1,3 @@
import * as universal from "../../../../src/routes/events/+page.ts";
export { universal };
export { default as component } from "../../../../src/routes/events/+page.svelte";
@@ -0,0 +1 @@
export { default as component } from "../../../../src/routes/login/+page.svelte";
@@ -0,0 +1 @@
export { default as component } from "../../../../src/routes/projects/+page.svelte";
@@ -0,0 +1 @@
export { default as component } from "../../../../src/routes/projects/[id]/+page.svelte";
+64
View File
@@ -0,0 +1,64 @@
export { matchers } from './matchers.js';
export const nodes = [
() => import('./nodes/0'),
() => import('./nodes/1'),
() => import('./nodes/2'),
() => import('./nodes/3'),
() => import('./nodes/4'),
() => import('./nodes/5'),
() => import('./nodes/6'),
() => import('./nodes/7'),
() => import('./nodes/8'),
() => import('./nodes/9'),
() => import('./nodes/10'),
() => import('./nodes/11'),
() => import('./nodes/12'),
() => import('./nodes/13'),
() => import('./nodes/14'),
() => import('./nodes/15'),
() => import('./nodes/16'),
() => import('./nodes/17'),
() => import('./nodes/18'),
() => import('./nodes/19'),
() => import('./nodes/20')
];
export const server_loads = [];
export const dictionary = {
"/": [3],
"/containers/stale": [4],
"/deploy": [5],
"/dns": [6],
"/events": [7],
"/login": [8],
"/projects": [9],
"/projects/[id]": [10],
"/projects/[id]/env": [11],
"/projects/[id]/volumes": [12],
"/projects/[id]/volumes/[volId]/browse": [13],
"/proxies": [14],
"/proxies/create": [15],
"/proxies/[id]/edit": [16],
"/settings": [17,[2]],
"/settings/auth": [18,[2]],
"/settings/credentials": [19,[2]],
"/settings/registries": [20,[2]]
};
export const hooks = {
handleError: (({ error }) => { console.error(error) }),
reroute: (() => {}),
transport: {}
};
export const decoders = Object.fromEntries(Object.entries(hooks.transport).map(([k, v]) => [k, v.decode]));
export const encoders = Object.fromEntries(Object.entries(hooks.transport).map(([k, v]) => [k, v.encode]));
export const hash = false;
export const decode = (type, value) => decoders[type](value);
export { default as root } from '../root.js';
@@ -0,0 +1 @@
export const matchers = {};
@@ -0,0 +1,3 @@
import * as universal from "../../../../src/routes/+layout.ts";
export { universal };
export { default as component } from "../../../../src/routes/+layout.svelte";
@@ -0,0 +1 @@
export { default as component } from "../../../../node_modules/@sveltejs/kit/src/runtime/components/svelte-5/error.svelte";
@@ -0,0 +1 @@
export { default as component } from "../../../../src/routes/projects/[id]/+page.svelte";
@@ -0,0 +1 @@
export { default as component } from "../../../../src/routes/projects/[id]/env/+page.svelte";
@@ -0,0 +1 @@
export { default as component } from "../../../../src/routes/projects/[id]/volumes/+page.svelte";
@@ -0,0 +1,3 @@
import * as universal from "../../../../src/routes/projects/[id]/volumes/[volId]/browse/+page.ts";
export { universal };
export { default as component } from "../../../../src/routes/projects/[id]/volumes/[volId]/browse/+page.svelte";
@@ -0,0 +1,3 @@
import * as universal from "../../../../src/routes/proxies/+page.ts";
export { universal };
export { default as component } from "../../../../src/routes/proxies/+page.svelte";
@@ -0,0 +1,3 @@
import * as universal from "../../../../src/routes/proxies/create/+page.ts";
export { universal };
export { default as component } from "../../../../src/routes/proxies/create/+page.svelte";
@@ -0,0 +1,3 @@
import * as universal from "../../../../src/routes/proxies/[id]/edit/+page.ts";
export { universal };
export { default as component } from "../../../../src/routes/proxies/[id]/edit/+page.svelte";
@@ -0,0 +1 @@
export { default as component } from "../../../../src/routes/settings/+page.svelte";
@@ -0,0 +1 @@
export { default as component } from "../../../../src/routes/settings/auth/+page.svelte";
@@ -0,0 +1 @@
export { default as component } from "../../../../src/routes/settings/credentials/+page.svelte";
@@ -0,0 +1 @@
export { default as component } from "../../../../src/routes/settings/+layout.svelte";
@@ -0,0 +1 @@
export { default as component } from "../../../../src/routes/settings/registries/+page.svelte";
@@ -0,0 +1 @@
export { default as component } from "../../../../src/routes/+page.svelte";
@@ -0,0 +1,3 @@
import * as universal from "../../../../src/routes/containers/stale/+page.ts";
export { universal };
export { default as component } from "../../../../src/routes/containers/stale/+page.svelte";
@@ -0,0 +1 @@
export { default as component } from "../../../../src/routes/deploy/+page.svelte";
@@ -0,0 +1 @@
export { default as component } from "../../../../src/routes/dns/+page.svelte";
@@ -0,0 +1,3 @@
import * as universal from "../../../../src/routes/events/+page.ts";
export { universal };
export { default as component } from "../../../../src/routes/events/+page.svelte";
@@ -0,0 +1 @@
export { default as component } from "../../../../src/routes/login/+page.svelte";
@@ -0,0 +1 @@
export { default as component } from "../../../../src/routes/projects/+page.svelte";
+3
View File
@@ -0,0 +1,3 @@
import { asClassComponent } from 'svelte/legacy';
import Root from './root.svelte';
export default asClassComponent(Root);
+80
View File
@@ -0,0 +1,80 @@
<!-- This file is generated by @sveltejs/kit — do not edit it! -->
<svelte:options runes={true} />
<script>
import { setContext, onMount, tick } from 'svelte';
import { browser } from '$app/environment';
// stores
let { stores, page, constructors, components = [], form, data_0 = null, data_1 = null, data_2 = null } = $props();
if (!browser) {
// svelte-ignore state_referenced_locally
setContext('__svelte__', stores);
}
if (browser) {
$effect.pre(() => stores.page.set(page));
} else {
// svelte-ignore state_referenced_locally
stores.page.set(page);
}
$effect(() => {
stores;page;constructors;components;form;data_0;data_1;data_2;
stores.page.notify();
});
let mounted = $state(false);
let navigated = $state(false);
let title = $state(null);
onMount(() => {
const unsubscribe = stores.page.subscribe(() => {
if (mounted) {
navigated = true;
tick().then(() => {
title = document.title || 'untitled page';
});
}
});
mounted = true;
return unsubscribe;
});
const Pyramid_2=$derived(constructors[2])
</script>
{#if constructors[1]}
{@const Pyramid_0 = constructors[0]}
<!-- svelte-ignore binding_property_non_reactive -->
<Pyramid_0 bind:this={components[0]} data={data_0} {form} params={page.params}>
{#if constructors[2]}
{@const Pyramid_1 = constructors[1]}
<!-- svelte-ignore binding_property_non_reactive -->
<Pyramid_1 bind:this={components[1]} data={data_1} {form} params={page.params}>
<!-- svelte-ignore binding_property_non_reactive -->
<Pyramid_2 bind:this={components[2]} data={data_2} {form} params={page.params} />
</Pyramid_1>
{:else}
{@const Pyramid_1 = constructors[1]}
<!-- svelte-ignore binding_property_non_reactive -->
<Pyramid_1 bind:this={components[1]} data={data_1} {form} params={page.params} />
{/if}
</Pyramid_0>
{:else}
{@const Pyramid_0 = constructors[0]}
<!-- svelte-ignore binding_property_non_reactive -->
<Pyramid_0 bind:this={components[0]} data={data_0} {form} params={page.params} />
{/if}
{#if mounted}
<div id="svelte-announcer" aria-live="assertive" aria-atomic="true" style="position: absolute; left: 0; top: 0; clip: rect(0 0 0 0); clip-path: inset(50%); overflow: hidden; white-space: nowrap; width: 1px; height: 1px">
{#if navigated}
{title}
{/if}
</div>
{/if}
@@ -0,0 +1,54 @@
import root from '../root.js';
import { set_building, set_prerendering } from '__sveltekit/environment';
import { set_assets } from '$app/paths/internal/server';
import { set_manifest, set_read_implementation } from '__sveltekit/server';
import { set_private_env, set_public_env } from '../../../node_modules/@sveltejs/kit/src/runtime/shared-server.js';
export const options = {
app_template_contains_nonce: false,
async: false,
csp: {"mode":"auto","directives":{"upgrade-insecure-requests":false,"block-all-mixed-content":false},"reportOnly":{"upgrade-insecure-requests":false,"block-all-mixed-content":false}},
csrf_check_origin: true,
csrf_trusted_origins: [],
embedded: false,
env_public_prefix: 'PUBLIC_',
env_private_prefix: '',
hash_routing: false,
hooks: null, // added lazily, via `get_hooks`
preload_strategy: "modulepreload",
root,
service_worker: false,
service_worker_options: undefined,
server_error_boundaries: false,
templates: {
app: ({ head, body, assets, nonce, env }) => "<!doctype html>\r\n<html lang=\"en\">\r\n\t<head>\r\n\t\t<meta charset=\"utf-8\" />\r\n\t\t<link rel=\"icon\" href=\"" + assets + "/favicon.png\" />\r\n\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\r\n\t\t" + head + "\r\n\t</head>\r\n\t<body data-sveltekit-preload-data=\"hover\">\r\n\t\t<div style=\"display: contents\">" + body + "</div>\r\n\t</body>\r\n</html>\r\n",
error: ({ status, message }) => "<!doctype html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"utf-8\" />\n\t\t<title>" + message + "</title>\n\n\t\t<style>\n\t\t\tbody {\n\t\t\t\t--bg: white;\n\t\t\t\t--fg: #222;\n\t\t\t\t--divider: #ccc;\n\t\t\t\tbackground: var(--bg);\n\t\t\t\tcolor: var(--fg);\n\t\t\t\tfont-family:\n\t\t\t\t\tsystem-ui,\n\t\t\t\t\t-apple-system,\n\t\t\t\t\tBlinkMacSystemFont,\n\t\t\t\t\t'Segoe UI',\n\t\t\t\t\tRoboto,\n\t\t\t\t\tOxygen,\n\t\t\t\t\tUbuntu,\n\t\t\t\t\tCantarell,\n\t\t\t\t\t'Open Sans',\n\t\t\t\t\t'Helvetica Neue',\n\t\t\t\t\tsans-serif;\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tjustify-content: center;\n\t\t\t\theight: 100vh;\n\t\t\t\tmargin: 0;\n\t\t\t}\n\n\t\t\t.error {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tmax-width: 32rem;\n\t\t\t\tmargin: 0 1rem;\n\t\t\t}\n\n\t\t\t.status {\n\t\t\t\tfont-weight: 200;\n\t\t\t\tfont-size: 3rem;\n\t\t\t\tline-height: 1;\n\t\t\t\tposition: relative;\n\t\t\t\ttop: -0.05rem;\n\t\t\t}\n\n\t\t\t.message {\n\t\t\t\tborder-left: 1px solid var(--divider);\n\t\t\t\tpadding: 0 0 0 1rem;\n\t\t\t\tmargin: 0 0 0 1rem;\n\t\t\t\tmin-height: 2.5rem;\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t}\n\n\t\t\t.message h1 {\n\t\t\t\tfont-weight: 400;\n\t\t\t\tfont-size: 1em;\n\t\t\t\tmargin: 0;\n\t\t\t}\n\n\t\t\t@media (prefers-color-scheme: dark) {\n\t\t\t\tbody {\n\t\t\t\t\t--bg: #222;\n\t\t\t\t\t--fg: #ddd;\n\t\t\t\t\t--divider: #666;\n\t\t\t\t}\n\t\t\t}\n\t\t</style>\n\t</head>\n\t<body>\n\t\t<div class=\"error\">\n\t\t\t<span class=\"status\">" + status + "</span>\n\t\t\t<div class=\"message\">\n\t\t\t\t<h1>" + message + "</h1>\n\t\t\t</div>\n\t\t</div>\n\t</body>\n</html>\n"
},
version_hash: "1rlkqzj"
};
export async function get_hooks() {
let handle;
let handleFetch;
let handleError;
let handleValidationError;
let init;
let reroute;
let transport;
return {
handle,
handleFetch,
handleError,
handleValidationError,
init,
reroute,
transport
};
}
export { set_assets, set_building, set_manifest, set_prerendering, set_private_env, set_public_env, set_read_implementation };