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,45 @@
|
||||
import { j as getContext, e as escape_html } from "../../chunks/index.js";
|
||||
import "clsx";
|
||||
import "../../chunks/state.svelte.js";
|
||||
import "@sveltejs/kit/internal";
|
||||
import "../../chunks/exports.js";
|
||||
import "../../chunks/utils.js";
|
||||
import { w as writable } from "../../chunks/index3.js";
|
||||
import "@sveltejs/kit/internal/server";
|
||||
import "../../chunks/root.js";
|
||||
function create_updated_store() {
|
||||
const { set, subscribe } = writable(false);
|
||||
{
|
||||
return {
|
||||
subscribe,
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
check: async () => false
|
||||
};
|
||||
}
|
||||
}
|
||||
const stores = {
|
||||
updated: /* @__PURE__ */ create_updated_store()
|
||||
};
|
||||
({
|
||||
check: stores.updated.check
|
||||
});
|
||||
function context() {
|
||||
return getContext("__request__");
|
||||
}
|
||||
const page$1 = {
|
||||
get error() {
|
||||
return context().page.error;
|
||||
},
|
||||
get status() {
|
||||
return context().page.status;
|
||||
}
|
||||
};
|
||||
const page = page$1;
|
||||
function Error$1($$renderer, $$props) {
|
||||
$$renderer.component(($$renderer2) => {
|
||||
$$renderer2.push(`<h1>${escape_html(page.status)}</h1> <p>${escape_html(page.error?.message)}</p>`);
|
||||
});
|
||||
}
|
||||
export {
|
||||
Error$1 as default
|
||||
};
|
||||
@@ -0,0 +1,294 @@
|
||||
import { a as attr, b as attr_class, c as clsx, f as ensure_array_like, s as store_get, g as stringify, e as escape_html, u as unsubscribe_stores, i as derived } from "../../chunks/index.js";
|
||||
import { o as onDestroy } from "../../chunks/index-server.js";
|
||||
import { p as page } from "../../chunks/stores.js";
|
||||
import { t as toasts } from "../../chunks/toast.js";
|
||||
import { I as IconCheck } from "../../chunks/IconCheck.js";
|
||||
import { I as IconX } from "../../chunks/IconX.js";
|
||||
import { I as IconAlert } from "../../chunks/IconAlert.js";
|
||||
import { t as themeMode } from "../../chunks/theme.js";
|
||||
import { t, a as availableLocales, l as locale } from "../../chunks/index2.js";
|
||||
import { I as IconGlobe } from "../../chunks/IconGlobe.js";
|
||||
import { I as IconDeploy } from "../../chunks/IconDeploy.js";
|
||||
import { I as IconSettings } from "../../chunks/IconSettings.js";
|
||||
import { w as writable, g as get } from "../../chunks/index3.js";
|
||||
function IconDashboard($$renderer, $$props) {
|
||||
const { size = 20, class: c = "" } = $$props;
|
||||
$$renderer.push(`<svg xmlns="http://www.w3.org/2000/svg"${attr("width", size)}${attr("height", size)} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"${attr_class(clsx(c))} aria-hidden="true"><rect width="7" height="9" x="3" y="3" rx="1"></rect><rect width="7" height="5" x="14" y="3" rx="1"></rect><rect width="7" height="9" x="14" y="12" rx="1"></rect><rect width="7" height="5" x="3" y="16" rx="1"></rect></svg>`);
|
||||
}
|
||||
function IconProjects($$renderer, $$props) {
|
||||
const { size = 20, class: c = "" } = $$props;
|
||||
$$renderer.push(`<svg xmlns="http://www.w3.org/2000/svg"${attr("width", size)}${attr("height", size)} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"${attr_class(clsx(c))} aria-hidden="true"><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></svg>`);
|
||||
}
|
||||
function IconInfo($$renderer, $$props) {
|
||||
const { size = 20, class: c = "" } = $$props;
|
||||
$$renderer.push(`<svg xmlns="http://www.w3.org/2000/svg"${attr("width", size)}${attr("height", size)} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"${attr_class(clsx(c))} aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="M12 16v-4"></path><path d="M12 8h.01"></path></svg>`);
|
||||
}
|
||||
function IconSun($$renderer, $$props) {
|
||||
const { size = 20, class: c = "" } = $$props;
|
||||
$$renderer.push(`<svg xmlns="http://www.w3.org/2000/svg"${attr("width", size)}${attr("height", size)} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"${attr_class(clsx(c))} aria-hidden="true"><circle cx="12" cy="12" r="4"></circle><path d="M12 2v2"></path><path d="M12 20v2"></path><path d="m4.93 4.93 1.41 1.41"></path><path d="m17.66 17.66 1.41 1.41"></path><path d="M2 12h2"></path><path d="M20 12h2"></path><path d="m6.34 17.66-1.41 1.41"></path><path d="m19.07 4.93-1.41 1.41"></path></svg>`);
|
||||
}
|
||||
function IconMoon($$renderer, $$props) {
|
||||
const { size = 20, class: c = "" } = $$props;
|
||||
$$renderer.push(`<svg xmlns="http://www.w3.org/2000/svg"${attr("width", size)}${attr("height", size)} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"${attr_class(clsx(c))} aria-hidden="true"><path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"></path></svg>`);
|
||||
}
|
||||
function IconMonitor($$renderer, $$props) {
|
||||
const { size = 20, class: c = "" } = $$props;
|
||||
$$renderer.push(`<svg xmlns="http://www.w3.org/2000/svg"${attr("width", size)}${attr("height", size)} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"${attr_class(clsx(c))} aria-hidden="true"><rect width="20" height="14" x="2" y="3" rx="2"></rect><line x1="8" x2="16" y1="21" y2="21"></line><line x1="12" x2="12" y1="17" y2="21"></line></svg>`);
|
||||
}
|
||||
function IconMenu($$renderer, $$props) {
|
||||
const { size = 20, class: c = "" } = $$props;
|
||||
$$renderer.push(`<svg xmlns="http://www.w3.org/2000/svg"${attr("width", size)}${attr("height", size)} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"${attr_class(clsx(c))} aria-hidden="true"><line x1="4" x2="20" y1="12" y2="12"></line><line x1="4" x2="20" y1="6" y2="6"></line><line x1="4" x2="20" y1="18" y2="18"></line></svg>`);
|
||||
}
|
||||
function IconProxies($$renderer, $$props) {
|
||||
const { size = 20, class: c = "" } = $$props;
|
||||
$$renderer.push(`<svg xmlns="http://www.w3.org/2000/svg"${attr("width", size)}${attr("height", size)} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"${attr_class(clsx(c))} aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"></path><path d="M2 12h20"></path></svg>`);
|
||||
}
|
||||
function IconEvents($$renderer, $$props) {
|
||||
const { size = 20, class: c = "" } = $$props;
|
||||
$$renderer.push(`<svg xmlns="http://www.w3.org/2000/svg"${attr("width", size)}${attr("height", size)} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"${attr_class(clsx(c))} aria-hidden="true"><path d="M16 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V8Z"></path><path d="M15 3v4a2 2 0 0 0 2 2h4"></path><path d="M8 13h.01"></path><path d="M8 17h.01"></path><path d="M12 13h4"></path><path d="M12 17h4"></path></svg>`);
|
||||
}
|
||||
function IconLogout($$renderer, $$props) {
|
||||
const { size = 24, class: className = "" } = $$props;
|
||||
$$renderer.push(`<svg${attr("width", size)}${attr("height", size)} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"${attr_class(clsx(className))}><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path><polyline points="16 17 21 12 16 7"></polyline><line x1="21" y1="12" x2="9" y2="12"></line></svg>`);
|
||||
}
|
||||
function Toast($$renderer, $$props) {
|
||||
$$renderer.component(($$renderer2) => {
|
||||
var $$store_subs;
|
||||
const bgMap = {
|
||||
success: "bg-[var(--color-success)]",
|
||||
error: "bg-[var(--color-danger)]",
|
||||
warning: "bg-[var(--color-warning)]",
|
||||
info: "bg-[var(--color-info)]"
|
||||
};
|
||||
$$renderer2.push(`<div class="fixed top-4 right-4 z-[100] flex flex-col gap-2 pointer-events-none"><!--[-->`);
|
||||
const each_array = ensure_array_like(store_get($$store_subs ??= {}, "$toasts", toasts));
|
||||
for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) {
|
||||
let toast = each_array[$$index];
|
||||
$$renderer2.push(`<div${attr_class(`pointer-events-auto flex items-center gap-3 rounded-xl px-4 py-3 text-white shadow-lg animate-slide-in ${stringify(bgMap[toast.type])}`)} role="alert"><div class="flex h-5 w-5 flex-shrink-0 items-center justify-center">`);
|
||||
if (toast.type === "success") {
|
||||
$$renderer2.push("<!--[0-->");
|
||||
IconCheck($$renderer2, { size: 18 });
|
||||
} else if (toast.type === "error") {
|
||||
$$renderer2.push("<!--[1-->");
|
||||
IconX($$renderer2, { size: 18 });
|
||||
} else if (toast.type === "warning") {
|
||||
$$renderer2.push("<!--[2-->");
|
||||
IconAlert($$renderer2, { size: 18 });
|
||||
} else {
|
||||
$$renderer2.push("<!--[-1-->");
|
||||
IconInfo($$renderer2, { size: 18 });
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div> <span class="flex-1 text-sm font-medium">${escape_html(toast.message)}</span> <button class="ml-2 rounded-md p-0.5 text-white/70 hover:text-white transition-colors" aria-label="Dismiss notification">`);
|
||||
IconX($$renderer2, { size: 16 });
|
||||
$$renderer2.push(`<!----></button></div>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div>`);
|
||||
if ($$store_subs) unsubscribe_stores($$store_subs);
|
||||
});
|
||||
}
|
||||
function ThemeToggle($$renderer, $$props) {
|
||||
$$renderer.component(($$renderer2) => {
|
||||
var $$store_subs;
|
||||
const modes = ["light", "dark", "system"];
|
||||
$$renderer2.push(`<div class="flex items-center gap-0.5 rounded-lg border border-[var(--border-primary)] bg-[var(--surface-card)] p-0.5"><!--[-->`);
|
||||
const each_array = ensure_array_like(modes);
|
||||
for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) {
|
||||
let mode = each_array[$$index];
|
||||
$$renderer2.push(`<button type="button"${attr_class(`flex items-center justify-center rounded-md p-1.5 transition-all duration-150 ${stringify(store_get($$store_subs ??= {}, "$themeMode", themeMode) === mode ? "bg-[var(--color-brand-100)] text-[var(--color-brand-700)] shadow-sm" : "text-[var(--text-tertiary)] hover:text-[var(--text-secondary)]")}`)}${attr("title", store_get($$store_subs ??= {}, "$t", t)(`theme.${mode}`))}>`);
|
||||
if (mode === "light") {
|
||||
$$renderer2.push("<!--[0-->");
|
||||
IconSun($$renderer2, { size: 14 });
|
||||
} else if (mode === "dark") {
|
||||
$$renderer2.push("<!--[1-->");
|
||||
IconMoon($$renderer2, { size: 14 });
|
||||
} else {
|
||||
$$renderer2.push("<!--[-1-->");
|
||||
IconMonitor($$renderer2, { size: 14 });
|
||||
}
|
||||
$$renderer2.push(`<!--]--></button>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div>`);
|
||||
if ($$store_subs) unsubscribe_stores($$store_subs);
|
||||
});
|
||||
}
|
||||
function LocaleSwitcher($$renderer, $$props) {
|
||||
$$renderer.component(($$renderer2) => {
|
||||
var $$store_subs;
|
||||
$$renderer2.push(`<div class="flex items-center gap-1.5">`);
|
||||
IconGlobe($$renderer2, { size: 14, class: "text-[var(--text-tertiary)]" });
|
||||
$$renderer2.push(`<!----> <div class="flex items-center gap-0.5 rounded-lg border border-[var(--border-primary)] bg-[var(--surface-card)] p-0.5"><!--[-->`);
|
||||
const each_array = ensure_array_like(availableLocales);
|
||||
for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) {
|
||||
let loc = each_array[$$index];
|
||||
$$renderer2.push(`<button type="button"${attr_class(`rounded-md px-2 py-0.5 text-xs font-medium transition-all duration-150 ${stringify(store_get($$store_subs ??= {}, "$locale", locale) === loc ? "bg-[var(--color-brand-100)] text-[var(--color-brand-700)] shadow-sm" : "text-[var(--text-tertiary)] hover:text-[var(--text-secondary)]")}`)}>${escape_html(loc.toUpperCase())}</button>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div></div>`);
|
||||
if ($$store_subs) unsubscribe_stores($$store_subs);
|
||||
});
|
||||
}
|
||||
function createInstanceStatusStore() {
|
||||
const { subscribe, set, update: storeUpdate } = writable({
|
||||
statuses: {},
|
||||
lastUpdate: 0,
|
||||
deployStatuses: {}
|
||||
});
|
||||
return {
|
||||
subscribe,
|
||||
/** Update an instance's status from an SSE event. */
|
||||
update(payload) {
|
||||
storeUpdate((state) => ({
|
||||
...state,
|
||||
statuses: {
|
||||
...state.statuses,
|
||||
[payload.instance_id]: payload.status
|
||||
},
|
||||
lastUpdate: Date.now()
|
||||
}));
|
||||
},
|
||||
/** Record a deploy status change from an SSE event. */
|
||||
notifyDeploy(payload) {
|
||||
storeUpdate((state) => ({
|
||||
...state,
|
||||
deployStatuses: {
|
||||
...state.deployStatuses,
|
||||
[payload.deploy_id]: payload
|
||||
},
|
||||
lastUpdate: Date.now()
|
||||
}));
|
||||
},
|
||||
/** Get the current status of an instance, or undefined if not tracked. */
|
||||
getStatus(instanceId) {
|
||||
return get({ subscribe }).statuses[instanceId];
|
||||
},
|
||||
/** Reset the store (e.g., on disconnect). */
|
||||
reset() {
|
||||
set({
|
||||
statuses: {},
|
||||
lastUpdate: 0,
|
||||
deployStatuses: {}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
createInstanceStatusStore();
|
||||
function _layout($$renderer, $$props) {
|
||||
$$renderer.component(($$renderer2) => {
|
||||
var $$store_subs;
|
||||
const { children } = $$props;
|
||||
const navItems = [
|
||||
{ href: "/", labelKey: "nav.dashboard", icon: "dashboard" },
|
||||
{
|
||||
href: "/projects",
|
||||
labelKey: "nav.projects",
|
||||
icon: "projects"
|
||||
},
|
||||
{ href: "/deploy", labelKey: "nav.deploy", icon: "deploy" },
|
||||
{ href: "/proxies", labelKey: "nav.proxies", icon: "proxies" },
|
||||
{ href: "/events", labelKey: "nav.events", icon: "events" },
|
||||
{
|
||||
href: "/settings",
|
||||
labelKey: "nav.settings",
|
||||
icon: "settings"
|
||||
}
|
||||
];
|
||||
function isActive(href, pathname) {
|
||||
if (href === "/") return pathname === "/";
|
||||
return pathname.startsWith(href);
|
||||
}
|
||||
let sseConnection = null;
|
||||
const isLoginPage = derived(() => store_get($$store_subs ??= {}, "$page", page).url.pathname === "/login");
|
||||
onDestroy(() => {
|
||||
sseConnection?.close();
|
||||
sseConnection = null;
|
||||
});
|
||||
if (isLoginPage()) {
|
||||
$$renderer2.push("<!--[0-->");
|
||||
children($$renderer2);
|
||||
$$renderer2.push(`<!---->`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[-1-->");
|
||||
$$renderer2.push(`<div class="flex h-screen overflow-hidden bg-[var(--surface-page)]">`);
|
||||
{
|
||||
$$renderer2.push("<!--[-1-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> <aside${attr_class(`fixed inset-y-0 left-0 z-50 flex w-64 flex-col border-r border-[var(--border-primary)] bg-[var(--surface-sidebar)] transition-transform duration-300 lg:static lg:translate-x-0 ${stringify("-translate-x-full")}`)}><div class="flex h-16 items-center gap-2.5 border-b border-[var(--border-primary)] px-5"><div class="flex h-8 w-8 items-center justify-center rounded-lg bg-[var(--color-brand-600)]"><svg class="h-4.5 w-4.5 text-white" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M21 7.5l-2.25-1.313M21 7.5v2.25m0-2.25l-2.25 1.313M3 7.5l2.25-1.313M3 7.5l2.25 1.313M3 7.5v2.25m9 3l2.25-1.313M12 12.75l-2.25-1.313M12 12.75V15m0 6.75l2.25-1.313M12 21.75V19.5m0 2.25l-2.25-1.313m0-16.875L12 2.25l2.25 1.313M21 14.25v2.25l-2.25 1.313m-13.5 0L3 16.5v-2.25"></path></svg></div> <span class="text-base font-bold text-[var(--text-primary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("app.name"))}</span> <button class="ml-auto rounded-md p-1 text-[var(--text-tertiary)] hover:text-[var(--text-primary)] lg:hidden" aria-label="Close sidebar">`);
|
||||
IconX($$renderer2, { size: 20 });
|
||||
$$renderer2.push(`<!----></button></div> <nav class="flex-1 space-y-0.5 px-3 py-3"><!--[-->`);
|
||||
const each_array = ensure_array_like(navItems);
|
||||
for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) {
|
||||
let item = each_array[$$index];
|
||||
const active = isActive(item.href, store_get($$store_subs ??= {}, "$page", page).url.pathname);
|
||||
$$renderer2.push(`<a${attr("href", item.href)}${attr_class(`group flex items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium transition-all duration-150 ${stringify(active ? "bg-[var(--color-brand-50)] text-[var(--color-brand-700)] shadow-sm" : "text-[var(--text-secondary)] hover:bg-[var(--surface-card-hover)] hover:text-[var(--text-primary)]")}`)}>`);
|
||||
if (item.icon === "dashboard") {
|
||||
$$renderer2.push("<!--[0-->");
|
||||
IconDashboard($$renderer2, {
|
||||
size: 18,
|
||||
class: `${stringify(active ? "text-[var(--color-brand-600)]" : "text-[var(--text-tertiary)] group-hover:text-[var(--text-secondary)]")} transition-colors duration-150`
|
||||
});
|
||||
} else if (item.icon === "projects") {
|
||||
$$renderer2.push("<!--[1-->");
|
||||
IconProjects($$renderer2, {
|
||||
size: 18,
|
||||
class: `${stringify(active ? "text-[var(--color-brand-600)]" : "text-[var(--text-tertiary)] group-hover:text-[var(--text-secondary)]")} transition-colors duration-150`
|
||||
});
|
||||
} else if (item.icon === "deploy") {
|
||||
$$renderer2.push("<!--[2-->");
|
||||
IconDeploy($$renderer2, {
|
||||
size: 18,
|
||||
class: `${stringify(active ? "text-[var(--color-brand-600)]" : "text-[var(--text-tertiary)] group-hover:text-[var(--text-secondary)]")} transition-colors duration-150`
|
||||
});
|
||||
} else if (item.icon === "proxies") {
|
||||
$$renderer2.push("<!--[3-->");
|
||||
IconProxies($$renderer2, {
|
||||
size: 18,
|
||||
class: `${stringify(active ? "text-[var(--color-brand-600)]" : "text-[var(--text-tertiary)] group-hover:text-[var(--text-secondary)]")} transition-colors duration-150`
|
||||
});
|
||||
} else if (item.icon === "events") {
|
||||
$$renderer2.push("<!--[4-->");
|
||||
IconEvents($$renderer2, {
|
||||
size: 18,
|
||||
class: `${stringify(active ? "text-[var(--color-brand-600)]" : "text-[var(--text-tertiary)] group-hover:text-[var(--text-secondary)]")} transition-colors duration-150`
|
||||
});
|
||||
} else if (item.icon === "settings") {
|
||||
$$renderer2.push("<!--[5-->");
|
||||
IconSettings($$renderer2, {
|
||||
size: 18,
|
||||
class: `${stringify(active ? "text-[var(--color-brand-600)]" : "text-[var(--text-tertiary)] group-hover:text-[var(--text-secondary)]")} transition-colors duration-150`
|
||||
});
|
||||
} else {
|
||||
$$renderer2.push("<!--[-1-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> ${escape_html(store_get($$store_subs ??= {}, "$t", t)(item.labelKey))} `);
|
||||
if (active) {
|
||||
$$renderer2.push("<!--[0-->");
|
||||
$$renderer2.push(`<div class="ml-auto h-1.5 w-1.5 rounded-full bg-[var(--color-brand-600)]"></div>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[-1-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--></a>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--></nav> <div class="space-y-3 border-t border-[var(--border-primary)] px-4 py-3">`);
|
||||
{
|
||||
$$renderer2.push("<!--[-1-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> <div class="flex items-center justify-between">`);
|
||||
ThemeToggle($$renderer2);
|
||||
$$renderer2.push(`<!----> `);
|
||||
LocaleSwitcher($$renderer2);
|
||||
$$renderer2.push(`<!----></div> <div class="flex items-center justify-between"><p class="text-xs text-[var(--text-tertiary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("app.name"))} ${escape_html(store_get($$store_subs ??= {}, "$t", t)("app.version"))}</p> <button class="inline-flex items-center gap-1.5 rounded-md px-2 py-1 text-xs font-medium text-[var(--text-secondary)] transition-colors hover:bg-[var(--surface-card-hover)] hover:text-[var(--color-danger)]"${attr("title", store_get($$store_subs ??= {}, "$t", t)("nav.logout"))}>`);
|
||||
IconLogout($$renderer2, { size: 14 });
|
||||
$$renderer2.push(`<!----> ${escape_html(store_get($$store_subs ??= {}, "$t", t)("nav.logout"))}</button></div></div></aside> <div class="flex flex-1 flex-col overflow-hidden"><header class="flex h-14 items-center gap-3 border-b border-[var(--border-primary)] bg-[var(--surface-sidebar)] px-4 lg:hidden"><button class="rounded-md p-1.5 text-[var(--text-secondary)] hover:bg-[var(--surface-card-hover)] hover:text-[var(--text-primary)] transition-colors" aria-label="Open sidebar">`);
|
||||
IconMenu($$renderer2, { size: 22 });
|
||||
$$renderer2.push(`<!----></button> <div class="flex h-7 w-7 items-center justify-center rounded-lg bg-[var(--color-brand-600)]"><svg class="h-3.5 w-3.5 text-white" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M21 7.5l-2.25-1.313M21 7.5v2.25m0-2.25l-2.25 1.313M3 7.5l2.25-1.313M3 7.5l2.25 1.313M3 7.5v2.25m9 3l2.25-1.313M12 12.75l-2.25-1.313M12 12.75V15m0 6.75l2.25-1.313M12 21.75V19.5m0 2.25l-2.25-1.313m0-16.875L12 2.25l2.25 1.313M21 14.25v2.25l-2.25 1.313m-13.5 0L3 16.5v-2.25"></path></svg></div> <span class="text-sm font-bold text-[var(--text-primary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("app.name"))}</span></header> <main class="flex-1 overflow-y-auto"><div class="mx-auto max-w-7xl px-4 py-6 sm:px-6 sm:py-8">`);
|
||||
children($$renderer2);
|
||||
$$renderer2.push(`<!----></div></main></div></div>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--> `);
|
||||
Toast($$renderer2);
|
||||
$$renderer2.push(`<!---->`);
|
||||
if ($$store_subs) unsubscribe_stores($$store_subs);
|
||||
});
|
||||
}
|
||||
export {
|
||||
_layout as default
|
||||
};
|
||||
@@ -0,0 +1,6 @@
|
||||
const ssr = false;
|
||||
const prerender = false;
|
||||
export {
|
||||
prerender,
|
||||
ssr
|
||||
};
|
||||
@@ -0,0 +1,71 @@
|
||||
import { a as attr, b as attr_class, c as clsx, h as head, e as escape_html, s as store_get, g as stringify, f as ensure_array_like, u as unsubscribe_stores, i as derived } from "../../chunks/index.js";
|
||||
import { t } from "../../chunks/index2.js";
|
||||
import { I as IconDeploy } from "../../chunks/IconDeploy.js";
|
||||
import { I as IconAlert } from "../../chunks/IconAlert.js";
|
||||
import { S as SkeletonCard } from "../../chunks/SkeletonCard.js";
|
||||
import "clsx";
|
||||
function IconServer($$renderer, $$props) {
|
||||
const { size = 20, class: c = "" } = $$props;
|
||||
$$renderer.push(`<svg xmlns="http://www.w3.org/2000/svg"${attr("width", size)}${attr("height", size)} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"${attr_class(clsx(c))} aria-hidden="true"><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>`);
|
||||
}
|
||||
function IconBox($$renderer, $$props) {
|
||||
const { size = 20, class: c = "" } = $$props;
|
||||
$$renderer.push(`<svg xmlns="http://www.w3.org/2000/svg"${attr("width", size)}${attr("height", size)} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"${attr_class(clsx(c))} aria-hidden="true"><path d="M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z"></path><path d="m3.3 7 8.7 5 8.7-5"></path><path d="M12 22V12"></path></svg>`);
|
||||
}
|
||||
function IconClock($$renderer, $$props) {
|
||||
const { size = 20, class: c = "" } = $$props;
|
||||
$$renderer.push(`<svg xmlns="http://www.w3.org/2000/svg"${attr("width", size)}${attr("height", size)} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"${attr_class(clsx(c))} aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg>`);
|
||||
}
|
||||
function SystemHealthCard($$renderer, $$props) {
|
||||
$$renderer.component(($$renderer2) => {
|
||||
{
|
||||
$$renderer2.push("<!--[-1-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]-->`);
|
||||
});
|
||||
}
|
||||
function _page($$renderer, $$props) {
|
||||
$$renderer.component(($$renderer2) => {
|
||||
var $$store_subs;
|
||||
let projects = [];
|
||||
let instancesByProject = {};
|
||||
let staleContainers = [];
|
||||
const totalProjects = derived(() => projects.length);
|
||||
const totalRunning = derived(() => Object.values(instancesByProject).flat().filter((i) => i.status === "running").length);
|
||||
const totalFailed = derived(() => Object.values(instancesByProject).flat().filter((i) => i.status === "failed").length);
|
||||
const totalStale = derived(() => staleContainers.length);
|
||||
head("1uha8ag", $$renderer2, ($$renderer3) => {
|
||||
$$renderer3.title(($$renderer4) => {
|
||||
$$renderer4.push(`<title>${escape_html(store_get($$store_subs ??= {}, "$t", t)("dashboard.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)("dashboard.title"))}</h1> <a href="/deploy" 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 transition-all duration-150 hover:bg-[var(--color-brand-700)] active:animate-press">`);
|
||||
IconDeploy($$renderer2, { size: 16 });
|
||||
$$renderer2.push(`<!----> ${escape_html(store_get($$store_subs ??= {}, "$t", t)("dashboard.quickDeploy"))}</a></div> <div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4"><div class="flex items-center gap-4 rounded-xl border border-[var(--border-primary)] bg-[var(--surface-card)] p-5 shadow-[var(--shadow-sm)]"><div class="flex h-12 w-12 items-center justify-center rounded-xl bg-[var(--color-brand-50)] text-[var(--color-brand-600)]">`);
|
||||
IconBox($$renderer2, { size: 24 });
|
||||
$$renderer2.push(`<!----></div> <div><p class="text-sm text-[var(--text-secondary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("dashboard.totalProjects"))}</p> <p class="mt-0.5 text-2xl font-bold text-[var(--text-primary)]">${escape_html(totalProjects())}</p></div></div> <div class="flex items-center gap-4 rounded-xl border border-[var(--border-primary)] bg-[var(--surface-card)] p-5 shadow-[var(--shadow-sm)]"><div class="flex h-12 w-12 items-center justify-center rounded-xl bg-emerald-50 text-emerald-600">`);
|
||||
IconServer($$renderer2, { size: 24 });
|
||||
$$renderer2.push(`<!----></div> <div><p class="text-sm text-[var(--text-secondary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("dashboard.runningInstances"))}</p> <p class="mt-0.5 text-2xl font-bold text-emerald-600">${escape_html(totalRunning())}</p></div></div> <div class="flex items-center gap-4 rounded-xl border border-[var(--border-primary)] bg-[var(--surface-card)] p-5 shadow-[var(--shadow-sm)]"><div${attr_class(`flex h-12 w-12 items-center justify-center rounded-xl ${stringify(totalFailed() > 0 ? "bg-red-50 text-red-600" : "bg-gray-50 text-gray-400")}`)}>`);
|
||||
IconAlert($$renderer2, { size: 24 });
|
||||
$$renderer2.push(`<!----></div> <div><p class="text-sm text-[var(--text-secondary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("dashboard.failedInstances"))}</p> <p${attr_class(`mt-0.5 text-2xl font-bold ${stringify(totalFailed() > 0 ? "text-red-600" : "text-[var(--text-primary)]")}`)}>${escape_html(totalFailed())}</p></div></div> <a href="/containers/stale" class="flex items-center gap-4 rounded-xl border border-[var(--border-primary)] bg-[var(--surface-card)] p-5 shadow-[var(--shadow-sm)] transition-colors hover:bg-[var(--surface-card-hover)]"><div${attr_class(`flex h-12 w-12 items-center justify-center rounded-xl ${stringify(totalStale() > 0 ? "bg-amber-50 text-amber-600" : "bg-gray-50 text-gray-400")}`)}>`);
|
||||
IconClock($$renderer2, { size: 24 });
|
||||
$$renderer2.push(`<!----></div> <div><p class="text-sm text-[var(--text-secondary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("dashboard.staleContainers"))}</p> <p${attr_class(`mt-0.5 text-2xl font-bold ${stringify(totalStale() > 0 ? "text-amber-600" : "text-[var(--text-primary)]")}`)}>${escape_html(totalStale())}</p></div></a></div> `);
|
||||
SystemHealthCard($$renderer2);
|
||||
$$renderer2.push(`<!----> <div><h2 class="text-lg font-semibold text-[var(--text-primary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("dashboard.projects"))}</h2> `);
|
||||
{
|
||||
$$renderer2.push("<!--[0-->");
|
||||
$$renderer2.push(`<div class="mt-4 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></div>`);
|
||||
if ($$store_subs) unsubscribe_stores($$store_subs);
|
||||
});
|
||||
}
|
||||
export {
|
||||
_page as default
|
||||
};
|
||||
@@ -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
|
||||
};
|
||||
@@ -0,0 +1,89 @@
|
||||
import { h as head, e as escape_html, s as store_get, a as attr, u as unsubscribe_stores } from "../../../chunks/index.js";
|
||||
import { F as FormField } from "../../../chunks/FormField.js";
|
||||
import { I as IconSearch, E as EntityPicker } from "../../../chunks/EntityPicker.js";
|
||||
import "../../../chunks/toast.js";
|
||||
import { t } from "../../../chunks/index2.js";
|
||||
function _page($$renderer, $$props) {
|
||||
$$renderer.component(($$renderer2) => {
|
||||
var $$store_subs;
|
||||
let imageUrl = "";
|
||||
let inspecting = false;
|
||||
let errors = {};
|
||||
let showImagePicker = false;
|
||||
let imagePickerItems = [];
|
||||
function selectPickedImage(value) {
|
||||
imageUrl = value;
|
||||
showImagePicker = false;
|
||||
}
|
||||
let $$settled = true;
|
||||
let $$inner_renderer;
|
||||
function $$render_inner($$renderer3) {
|
||||
head("2t7dow", $$renderer3, ($$renderer4) => {
|
||||
$$renderer4.title(($$renderer5) => {
|
||||
$$renderer5.push(`<title>${escape_html(store_get($$store_subs ??= {}, "$t", t)("quickDeploy.title"))} - ${escape_html(store_get($$store_subs ??= {}, "$t", t)("app.name"))}</title>`);
|
||||
});
|
||||
});
|
||||
$$renderer3.push(`<div class="mx-auto max-w-2xl space-y-6"><div><h1 class="text-2xl font-bold text-[var(--text-primary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("quickDeploy.title"))}</h1> <p class="mt-1 text-sm text-[var(--text-secondary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("quickDeploy.description"))}</p></div> <div class="rounded-xl border border-[var(--border-primary)] bg-[var(--surface-card)] p-6 shadow-[var(--shadow-sm)]"><h2 class="mb-4 text-base font-semibold text-[var(--text-primary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("quickDeploy.step1"))}</h2> <div class="flex gap-3"><div class="flex-1">`);
|
||||
FormField($$renderer3, {
|
||||
label: store_get($$store_subs ??= {}, "$t", t)("quickDeploy.imageUrl"),
|
||||
name: "imageUrl",
|
||||
placeholder: "registry.example.com/org/app:tag",
|
||||
required: true,
|
||||
error: errors.imageUrl ?? "",
|
||||
helpText: store_get($$store_subs ??= {}, "$t", t)("quickDeploy.imageUrlHelp"),
|
||||
disabled: inspecting,
|
||||
get value() {
|
||||
return imageUrl;
|
||||
},
|
||||
set value($$value) {
|
||||
imageUrl = $$value;
|
||||
$$settled = false;
|
||||
}
|
||||
});
|
||||
$$renderer3.push(`<!----></div> <div class="flex items-start gap-2 pt-[26px]"><button type="button"${attr("title", store_get($$store_subs ??= {}, "$t", t)("quickDeploy.browseImages"))}${attr("aria-label", store_get($$store_subs ??= {}, "$t", t)("quickDeploy.browseImages"))} class="rounded-lg border border-[var(--border-primary)] p-2 text-[var(--text-secondary)] hover:bg-[var(--surface-card-hover)] hover:text-[var(--text-primary)] transition-colors">`);
|
||||
{
|
||||
$$renderer3.push("<!--[-1-->");
|
||||
IconSearch($$renderer3, { size: 16 });
|
||||
}
|
||||
$$renderer3.push(`<!--]--></button> <button${attr("disabled", !imageUrl.trim(), true)} class="inline-flex items-center gap-2 rounded-lg bg-[var(--color-info)] px-4 py-2 text-sm font-medium text-white transition-all duration-150 hover:bg-[var(--color-info-dark)] disabled:cursor-not-allowed disabled:opacity-50 active:animate-press">`);
|
||||
{
|
||||
$$renderer3.push("<!--[-1-->");
|
||||
IconSearch($$renderer3, { size: 16 });
|
||||
$$renderer3.push(`<!----> ${escape_html(store_get($$store_subs ??= {}, "$t", t)("quickDeploy.inspect"))}`);
|
||||
}
|
||||
$$renderer3.push(`<!--]--></button></div></div> `);
|
||||
EntityPicker($$renderer3, {
|
||||
items: imagePickerItems,
|
||||
current: imageUrl,
|
||||
title: store_get($$store_subs ??= {}, "$t", t)("quickDeploy.selectImage"),
|
||||
placeholder: store_get($$store_subs ??= {}, "$t", t)("entityPicker.search"),
|
||||
onselect: selectPickedImage,
|
||||
onclose: () => {
|
||||
showImagePicker = false;
|
||||
},
|
||||
get open() {
|
||||
return showImagePicker;
|
||||
},
|
||||
set open($$value) {
|
||||
showImagePicker = $$value;
|
||||
$$settled = false;
|
||||
}
|
||||
});
|
||||
$$renderer3.push(`<!----></div> `);
|
||||
{
|
||||
$$renderer3.push("<!--[-1-->");
|
||||
}
|
||||
$$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,126 @@
|
||||
import { f as ensure_array_like, b as attr_class, g as stringify, e as escape_html, s as store_get, a as attr, u as unsubscribe_stores, i as derived } from "../../../chunks/index.js";
|
||||
import { o as onDestroy } from "../../../chunks/index-server.js";
|
||||
import { t } from "../../../chunks/index2.js";
|
||||
function EventLogFilter($$renderer, $$props) {
|
||||
$$renderer.component(($$renderer2) => {
|
||||
var $$store_subs;
|
||||
const {
|
||||
severities,
|
||||
sources,
|
||||
dateRange,
|
||||
searchText,
|
||||
stats
|
||||
} = $$props;
|
||||
const allSeverities = ["info", "warn", "error"];
|
||||
const allSources = ["deploy", "container", "proxy", "system"];
|
||||
const dateRangeOptions = [
|
||||
{ value: "1h", labelKey: "events.filter.lastHour" },
|
||||
{ value: "24h", labelKey: "events.filter.last24h" },
|
||||
{ value: "7d", labelKey: "events.filter.last7d" },
|
||||
{ value: "all", labelKey: "events.filter.allTime" }
|
||||
];
|
||||
const severityStyles = {
|
||||
info: {
|
||||
active: "bg-blue-100 text-blue-700 ring-1 ring-blue-300 dark:bg-blue-900/40 dark:text-blue-300 dark:ring-blue-700",
|
||||
inactive: "bg-[var(--surface-card-hover)] text-[var(--text-tertiary)] line-through decoration-1",
|
||||
dot: "bg-blue-500"
|
||||
},
|
||||
warn: {
|
||||
active: "bg-amber-100 text-amber-700 ring-1 ring-amber-300 dark:bg-amber-900/40 dark:text-amber-300 dark:ring-amber-700",
|
||||
inactive: "bg-[var(--surface-card-hover)] text-[var(--text-tertiary)] line-through decoration-1",
|
||||
dot: "bg-amber-500"
|
||||
},
|
||||
error: {
|
||||
active: "bg-red-100 text-red-700 ring-1 ring-red-300 dark:bg-red-900/40 dark:text-red-300 dark:ring-red-700",
|
||||
inactive: "bg-[var(--surface-card-hover)] text-[var(--text-tertiary)] line-through decoration-1",
|
||||
dot: "bg-red-500"
|
||||
}
|
||||
};
|
||||
const activeFilterCount = derived(() => (severities.length < allSeverities.length ? 1 : 0) + (sources.length < allSources.length ? 1 : 0) + 0 + (searchText.trim() !== "" ? 1 : 0));
|
||||
$$renderer2.push(`<div class="space-y-3"><div class="flex items-center gap-2 flex-wrap"><!--[-->`);
|
||||
const each_array = ensure_array_like(allSeverities);
|
||||
for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) {
|
||||
let sev = each_array[$$index];
|
||||
const active = severities.includes(sev);
|
||||
const style = severityStyles[sev];
|
||||
$$renderer2.push(`<button type="button"${attr_class(`inline-flex items-center gap-1.5 rounded-full px-2.5 py-1 text-xs font-medium transition-all duration-150 select-none cursor-pointer ${stringify(active ? style.active : style.inactive)}`)}><span${attr_class(`h-1.5 w-1.5 rounded-full ${stringify(active ? style.dot : "bg-[var(--text-tertiary)] opacity-40")}`)}></span> ${escape_html(store_get($$store_subs ??= {}, "$t", t)(`events.severity.${sev}`))} `);
|
||||
if (stats) {
|
||||
$$renderer2.push("<!--[0-->");
|
||||
$$renderer2.push(`<span${attr_class(`tabular-nums font-semibold ${stringify(active ? "" : "opacity-50")}`)}>${escape_html(stats[sev] ?? 0)}</span>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[-1-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--></button>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--> <div class="h-4 w-px bg-[var(--border-primary)] mx-0.5 hidden sm:block"></div> <!--[-->`);
|
||||
const each_array_1 = ensure_array_like(allSources);
|
||||
for (let $$index_1 = 0, $$length = each_array_1.length; $$index_1 < $$length; $$index_1++) {
|
||||
let src = each_array_1[$$index_1];
|
||||
const active = sources.includes(src);
|
||||
$$renderer2.push(`<button type="button"${attr_class(`inline-flex items-center gap-1 rounded-full px-2.5 py-1 text-xs font-medium transition-all duration-150 select-none cursor-pointer ${stringify(active ? "bg-[var(--surface-card)] text-[var(--text-primary)] ring-1 ring-[var(--border-primary)] shadow-[var(--shadow-sm)]" : "bg-[var(--surface-card-hover)] text-[var(--text-tertiary)] line-through decoration-1")}`)}>${escape_html(store_get($$store_subs ??= {}, "$t", t)(`events.source.${src}`))}</button>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--> <div class="flex-1"></div> `);
|
||||
if (activeFilterCount() > 0) {
|
||||
$$renderer2.push("<!--[0-->");
|
||||
$$renderer2.push(`<button type="button" class="inline-flex items-center gap-1 rounded-full px-2 py-1 text-xs font-medium text-[var(--text-tertiary)] hover:text-[var(--text-primary)] hover:bg-[var(--surface-card-hover)] transition-colors"><svg class="h-3 w-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"></path><path d="m6 6 12 12"></path></svg> ${escape_html(store_get($$store_subs ??= {}, "$t", t)("events.filter.clear"))}</button>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[-1-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div> <div class="flex items-center gap-2 flex-wrap"><div class="inline-flex items-center rounded-lg bg-[var(--surface-card-hover)] p-0.5"><!--[-->`);
|
||||
const each_array_2 = ensure_array_like(dateRangeOptions);
|
||||
for (let $$index_2 = 0, $$length = each_array_2.length; $$index_2 < $$length; $$index_2++) {
|
||||
let opt = each_array_2[$$index_2];
|
||||
$$renderer2.push(`<button type="button"${attr_class(`rounded-md px-2.5 py-1 text-xs font-medium transition-all duration-150 ${stringify(dateRange === opt.value ? "bg-[var(--surface-card)] text-[var(--text-primary)] shadow-[var(--shadow-sm)]" : "text-[var(--text-tertiary)] hover:text-[var(--text-secondary)]")}`)}>${escape_html(store_get($$store_subs ??= {}, "$t", t)(opt.labelKey))}</button>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div> <div class="relative flex-1 min-w-[180px] max-w-sm"><svg class="absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-[var(--text-tertiary)]" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><path d="m21 21-4.3-4.3"></path></svg> <input type="text"${attr("placeholder", store_get($$store_subs ??= {}, "$t", t)("events.filter.search"))}${attr("value", searchText)} class="w-full rounded-lg border border-[var(--border-primary)] bg-[var(--surface-card)] py-1.5 pl-8 pr-3 text-xs text-[var(--text-primary)] placeholder:text-[var(--text-tertiary)] focus:border-[var(--color-brand-400)] focus:outline-none focus:ring-1 focus:ring-[var(--color-brand-400)] transition-colors"/></div></div></div>`);
|
||||
if ($$store_subs) unsubscribe_stores($$store_subs);
|
||||
});
|
||||
}
|
||||
function _page($$renderer, $$props) {
|
||||
$$renderer.component(($$renderer2) => {
|
||||
var $$store_subs;
|
||||
let stats = { info: 0, warn: 0, error: 0, total: 0 };
|
||||
let pendingNewEvents = [];
|
||||
let severities = ["info", "warn", "error"];
|
||||
let sources = ["deploy", "container", "proxy", "system"];
|
||||
let dateRange = "all";
|
||||
let searchText = "";
|
||||
let sseConnection = null;
|
||||
onDestroy(() => {
|
||||
sseConnection?.close();
|
||||
sseConnection = null;
|
||||
});
|
||||
$$renderer2.push(`<div class="space-y-4"><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)("events.title"))}</h1> `);
|
||||
if (stats.total > 0) {
|
||||
$$renderer2.push("<!--[0-->");
|
||||
$$renderer2.push(`<span class="text-xs text-[var(--text-tertiary)] tabular-nums">${escape_html(stats.total)} total</span>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[-1-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div> `);
|
||||
EventLogFilter($$renderer2, {
|
||||
severities,
|
||||
sources,
|
||||
dateRange,
|
||||
searchText,
|
||||
stats
|
||||
});
|
||||
$$renderer2.push(`<!----> `);
|
||||
if (pendingNewEvents.length > 0) {
|
||||
$$renderer2.push("<!--[0-->");
|
||||
$$renderer2.push(`<button type="button" class="w-full rounded-lg bg-[var(--color-brand-600)] px-4 py-2 text-sm font-medium text-white shadow-sm transition-all hover:bg-[var(--color-brand-700)] animate-fade-in">${escape_html(pendingNewEvents.length)} ${escape_html(store_get($$store_subs ??= {}, "$t", t)("events.newEvents"))}</button>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[-1-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> `);
|
||||
{
|
||||
$$renderer2.push("<!--[0-->");
|
||||
$$renderer2.push(`<div class="flex items-center justify-center py-16"><svg class="h-5 w-5 animate-spin text-[var(--color-brand-500)]" viewBox="0 0 24 24" fill="none"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path></svg></div>`);
|
||||
}
|
||||
$$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,31 @@
|
||||
import { e as escape_html, s as store_get, a as attr, 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 "../../../chunks/theme.js";
|
||||
function _page($$renderer, $$props) {
|
||||
$$renderer.component(($$renderer2) => {
|
||||
var $$store_subs;
|
||||
let username = "";
|
||||
let password = "";
|
||||
let loading = false;
|
||||
$$renderer2.push(`<div class="flex min-h-screen items-center justify-center bg-[var(--surface-page)] px-4"><div class="w-full max-w-sm"><div class="rounded-2xl border border-[var(--border-primary)] bg-[var(--surface-card)] p-8 shadow-[var(--shadow-lg)]"><div class="mb-6 text-center"><div class="mx-auto flex h-12 w-12 items-center justify-center rounded-xl bg-[var(--color-brand-600)] shadow-md"><svg class="h-6 w-6 text-white" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M21 7.5l-2.25-1.313M21 7.5v2.25m0-2.25l-2.25 1.313M3 7.5l2.25-1.313M3 7.5l2.25 1.313M3 7.5v2.25m9 3l2.25-1.313M12 12.75l-2.25-1.313M12 12.75V15m0 6.75l2.25-1.313M12 21.75V19.5m0 2.25l-2.25-1.313m0-16.875L12 2.25l2.25 1.313M21 14.25v2.25l-2.25 1.313m-13.5 0L3 16.5v-2.25"></path></svg></div> <h1 class="mt-4 text-xl font-bold text-[var(--text-primary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("login.title"))}</h1> <p class="mt-1 text-sm text-[var(--text-secondary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("login.subtitle"))}</p></div> `);
|
||||
{
|
||||
$$renderer2.push("<!--[-1-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> <form class="space-y-4"><div class="flex flex-col gap-1.5"><label for="username" class="text-sm font-medium text-[var(--text-primary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("login.username"))}</label> <input id="username" type="text"${attr("value", username)} required="" autocomplete="username" class="w-full rounded-lg border border-[var(--border-input)] bg-[var(--surface-input)] px-3 py-2.5 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)]"/></div> <div class="flex flex-col gap-1.5"><label for="password" class="text-sm font-medium text-[var(--text-primary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("login.password"))}</label> <input id="password" type="password"${attr("value", password)} required="" autocomplete="current-password" class="w-full rounded-lg border border-[var(--border-input)] bg-[var(--surface-input)] px-3 py-2.5 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)]"/></div> <button type="submit"${attr("disabled", loading, true)} class="w-full inline-flex items-center justify-center gap-2 rounded-lg bg-[var(--color-brand-600)] px-4 py-2.5 text-sm font-medium text-white shadow-sm transition-all duration-150 hover:bg-[var(--color-brand-700)] focus:outline-none focus:ring-2 focus:ring-[var(--color-brand-500)] focus:ring-offset-2 disabled:opacity-50 active:animate-press">`);
|
||||
{
|
||||
$$renderer2.push("<!--[-1-->");
|
||||
$$renderer2.push(`${escape_html(store_get($$store_subs ??= {}, "$t", t)("login.signIn"))}`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--></button></form> <div class="mt-5"><div class="relative"><div class="absolute inset-0 flex items-center"><div class="w-full border-t border-[var(--border-primary)]"></div></div> <div class="relative flex justify-center text-xs"><span class="bg-[var(--surface-card)] px-3 text-[var(--text-tertiary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("login.or"))}</span></div></div> <button class="mt-4 w-full rounded-lg border border-[var(--border-primary)] bg-[var(--surface-card)] px-4 py-2.5 text-sm font-medium text-[var(--text-secondary)] shadow-sm transition-all duration-150 hover:bg-[var(--surface-card-hover)] focus:outline-none focus:ring-2 focus:ring-[var(--color-brand-500)] focus:ring-offset-2">${escape_html(store_get($$store_subs ??= {}, "$t", t)("login.ssoButton"))}</button></div></div></div></div>`);
|
||||
if ($$store_subs) unsubscribe_stores($$store_subs);
|
||||
});
|
||||
}
|
||||
export {
|
||||
_page as default
|
||||
};
|
||||
@@ -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
|
||||
};
|
||||
+43
@@ -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
|
||||
};
|
||||
+45
@@ -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
|
||||
};
|
||||
+4
@@ -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
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
import { h as head, e as escape_html, s as store_get, 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 { I as IconGlobe } from "../../../../../chunks/IconGlobe.js";
|
||||
import { I as IconLoader } from "../../../../../chunks/IconLoader.js";
|
||||
function _page($$renderer, $$props) {
|
||||
$$renderer.component(($$renderer2) => {
|
||||
var $$store_subs;
|
||||
head("57j31g", $$renderer2, ($$renderer3) => {
|
||||
$$renderer3.title(($$renderer4) => {
|
||||
$$renderer4.push(`<title>${escape_html(store_get($$store_subs ??= {}, "$t", t)("proxies.form.editTitle"))} - ${escape_html(store_get($$store_subs ??= {}, "$t", t)("app.name"))}</title>`);
|
||||
});
|
||||
});
|
||||
$$renderer2.push(`<div class="mb-6"><a href="/proxies" class="inline-flex items-center gap-1 text-sm text-[var(--text-secondary)] hover:text-[var(--color-brand-600)] transition-colors"><svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 12H5"></path><path d="m12 19-7-7 7-7"></path></svg> ${escape_html(store_get($$store_subs ??= {}, "$t", t)("common.back"))}</a></div> <div class="mb-6 flex items-center gap-3"><div class="flex h-10 w-10 items-center justify-center rounded-xl bg-[var(--color-brand-50)] text-[var(--color-brand-600)]">`);
|
||||
IconGlobe($$renderer2, { size: 22 });
|
||||
$$renderer2.push(`<!----></div> <h1 class="text-xl font-bold text-[var(--text-primary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("proxies.form.editTitle"))}</h1></div> `);
|
||||
{
|
||||
$$renderer2.push("<!--[0-->");
|
||||
$$renderer2.push(`<div class="flex items-center justify-center py-20">`);
|
||||
IconLoader($$renderer2, { size: 24, class: "text-[var(--color-brand-500)]" });
|
||||
$$renderer2.push(`<!----> <span class="ml-2 text-sm text-[var(--text-secondary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("common.loading"))}</span></div>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]-->`);
|
||||
if ($$store_subs) unsubscribe_stores($$store_subs);
|
||||
});
|
||||
}
|
||||
export {
|
||||
_page as default
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { h as head, e as escape_html, s as store_get, u as unsubscribe_stores } from "../../../chunks/index.js";
|
||||
import { t } from "../../../chunks/index2.js";
|
||||
import { I as IconGlobe } from "../../../chunks/IconGlobe.js";
|
||||
import { I as IconLoader } from "../../../chunks/IconLoader.js";
|
||||
function _page($$renderer, $$props) {
|
||||
$$renderer.component(($$renderer2) => {
|
||||
var $$store_subs;
|
||||
head("88k8ar", $$renderer2, ($$renderer3) => {
|
||||
$$renderer3.title(($$renderer4) => {
|
||||
$$renderer4.push(`<title>${escape_html(store_get($$store_subs ??= {}, "$t", t)("proxies.title"))} - ${escape_html(store_get($$store_subs ??= {}, "$t", t)("app.name"))}</title>`);
|
||||
});
|
||||
});
|
||||
$$renderer2.push(`<div class="mb-6 flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between"><div class="flex items-center gap-3"><div class="flex h-10 w-10 items-center justify-center rounded-xl bg-[var(--color-brand-50)] text-[var(--color-brand-600)]">`);
|
||||
IconGlobe($$renderer2, { size: 22 });
|
||||
$$renderer2.push(`<!----></div> <div><h1 class="text-xl font-bold text-[var(--text-primary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("proxies.title"))}</h1> `);
|
||||
{
|
||||
$$renderer2.push("<!--[-1-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div></div> <a href="/proxies/create" 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 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="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"></path><path d="M12 5v14"></path></svg> ${escape_html(store_get($$store_subs ??= {}, "$t", t)("proxies.create"))}</a></div> `);
|
||||
{
|
||||
$$renderer2.push("<!--[0-->");
|
||||
$$renderer2.push(`<div class="flex items-center justify-center py-20">`);
|
||||
IconLoader($$renderer2, {
|
||||
size: 24,
|
||||
class: "animate-spin text-[var(--color-brand-500)]"
|
||||
});
|
||||
$$renderer2.push(`<!----> <span class="ml-2 text-sm text-[var(--text-secondary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("common.loading"))}</span></div>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]-->`);
|
||||
if ($$store_subs) unsubscribe_stores($$store_subs);
|
||||
});
|
||||
}
|
||||
export {
|
||||
_page as default
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,272 @@
|
||||
import { e as escape_html, s as store_get, u as unsubscribe_stores, f as ensure_array_like, a as attr, i as derived, h as head } 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 { v as validateProxy } from "../../../../chunks/api.js";
|
||||
import { F as FormField } from "../../../../chunks/FormField.js";
|
||||
import { I as IconCheck } from "../../../../chunks/IconCheck.js";
|
||||
import { I as IconX } from "../../../../chunks/IconX.js";
|
||||
import { I as IconLoader } from "../../../../chunks/IconLoader.js";
|
||||
import { I as IconGlobe } from "../../../../chunks/IconGlobe.js";
|
||||
function ValidationChecklist($$renderer, $$props) {
|
||||
$$renderer.component(($$renderer2) => {
|
||||
var $$store_subs;
|
||||
const { result, loading = false } = $$props;
|
||||
const stepLabelKeys = {
|
||||
syntax: "proxies.validation.syntax",
|
||||
dns: "proxies.validation.dns",
|
||||
tcp: "proxies.validation.tcp",
|
||||
http: "proxies.validation.http"
|
||||
};
|
||||
function getStepLabel(name) {
|
||||
const key = stepLabelKeys[name];
|
||||
return key ? store_get($$store_subs ??= {}, "$t", t)(key) : name;
|
||||
}
|
||||
if (loading || result) {
|
||||
$$renderer2.push("<!--[0-->");
|
||||
$$renderer2.push(`<div class="rounded-lg border border-[var(--border-primary)] bg-[var(--surface-card)] p-4"><h4 class="text-sm font-medium text-[var(--text-primary)] mb-3">${escape_html(store_get($$store_subs ??= {}, "$t", t)("proxies.validation.title"))}</h4> `);
|
||||
if (loading && !result) {
|
||||
$$renderer2.push("<!--[0-->");
|
||||
$$renderer2.push(`<div class="flex items-center gap-2 text-sm text-[var(--text-secondary)]">`);
|
||||
IconLoader($$renderer2, { size: 16 });
|
||||
$$renderer2.push(`<!----> <span>${escape_html(store_get($$store_subs ??= {}, "$t", t)("proxies.validation.checking"))}</span></div>`);
|
||||
} else if (result) {
|
||||
$$renderer2.push("<!--[1-->");
|
||||
$$renderer2.push(`<ul class="space-y-2"><!--[-->`);
|
||||
const each_array = ensure_array_like(result.steps);
|
||||
for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) {
|
||||
let step = each_array[$$index];
|
||||
$$renderer2.push(`<li><div class="flex items-center gap-2">`);
|
||||
if (step.passed) {
|
||||
$$renderer2.push("<!--[0-->");
|
||||
$$renderer2.push(`<span class="flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-full bg-emerald-100 dark:bg-emerald-950">`);
|
||||
IconCheck($$renderer2, { size: 14, class: "text-emerald-600 dark:text-emerald-400" });
|
||||
$$renderer2.push(`<!----></span> <span class="text-sm text-[var(--text-primary)]">${escape_html(getStepLabel(step.name))}</span> `);
|
||||
if (step.message) {
|
||||
$$renderer2.push("<!--[0-->");
|
||||
$$renderer2.push(`<span class="text-xs text-[var(--text-tertiary)]">— ${escape_html(step.message)}</span>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[-1-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]-->`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[-1-->");
|
||||
$$renderer2.push(`<span class="flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-full bg-red-100 dark:bg-red-950">`);
|
||||
IconX($$renderer2, { size: 14, class: "text-red-600 dark:text-red-400" });
|
||||
$$renderer2.push(`<!----></span> <span class="text-sm text-[var(--text-primary)]">${escape_html(getStepLabel(step.name))}</span> `);
|
||||
if (step.message) {
|
||||
$$renderer2.push("<!--[0-->");
|
||||
$$renderer2.push(`<span class="text-xs text-[var(--text-tertiary)]">— ${escape_html(step.message)}</span>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[-1-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]-->`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div> `);
|
||||
if (!step.passed && step.hint) {
|
||||
$$renderer2.push("<!--[0-->");
|
||||
$$renderer2.push(`<p class="ml-7 mt-1 text-xs text-amber-600 dark:text-amber-400">${escape_html(step.hint)}</p>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[-1-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--></li>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--></ul>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[-1-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--></div>`);
|
||||
} else {
|
||||
$$renderer2.push("<!--[-1-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]-->`);
|
||||
if ($$store_subs) unsubscribe_stores($$store_subs);
|
||||
});
|
||||
}
|
||||
function ProxyForm($$renderer, $$props) {
|
||||
$$renderer.component(($$renderer2) => {
|
||||
var $$store_subs;
|
||||
const { proxy } = $$props;
|
||||
let destinationUrl = proxy?.destination_url ?? "";
|
||||
let port = proxy?.destination_port?.toString() ?? "";
|
||||
let domain = proxy?.domain ?? "";
|
||||
let validationResult = null;
|
||||
let validating = false;
|
||||
let validationTimer = null;
|
||||
const portNum = derived(() => parseInt(port, 10));
|
||||
const portValid = derived(() => !isNaN(portNum()) && portNum() >= 1 && portNum() <= 65535);
|
||||
const canSubmit = derived(() => destinationUrl.trim().length > 0 && port.trim().length > 0 && portValid() && domain.trim().length > 0 && true);
|
||||
const title = derived(
|
||||
() => store_get($$store_subs ??= {}, "$t", t)("proxies.form.title")
|
||||
);
|
||||
const submitLabel = derived(
|
||||
() => store_get($$store_subs ??= {}, "$t", t)("proxies.form.create")
|
||||
);
|
||||
function suggestDomain(dest) {
|
||||
if (!dest) return "";
|
||||
try {
|
||||
const withScheme = dest.includes("://") ? dest : `http://${dest}`;
|
||||
const url = new URL(withScheme);
|
||||
const host = url.hostname;
|
||||
const cleaned = host.replace(/^(www|api|app)\./, "").replace(/\.\w+$/, "").replace(/[^a-z0-9.-]/gi, "-").toLowerCase();
|
||||
return cleaned || "";
|
||||
} catch {
|
||||
return dest.replace(/[^a-z0-9.-]/gi, "-").toLowerCase();
|
||||
}
|
||||
}
|
||||
function scheduleValidation() {
|
||||
if (validationTimer !== null) {
|
||||
clearTimeout(validationTimer);
|
||||
}
|
||||
validationResult = null;
|
||||
if (!destinationUrl.trim() || !port.trim() || !portValid()) {
|
||||
return;
|
||||
}
|
||||
validationTimer = setTimeout(
|
||||
() => {
|
||||
runValidation();
|
||||
},
|
||||
300
|
||||
);
|
||||
}
|
||||
async function runValidation() {
|
||||
if (!destinationUrl.trim() || !portValid()) return;
|
||||
validating = true;
|
||||
try {
|
||||
validationResult = await validateProxy(destinationUrl.trim(), portNum());
|
||||
} catch {
|
||||
validationResult = null;
|
||||
} finally {
|
||||
validating = false;
|
||||
}
|
||||
}
|
||||
function handleDestinationInput() {
|
||||
{
|
||||
const suggested = suggestDomain(destinationUrl);
|
||||
if (!domain || domain === suggestDomain(destinationUrl.slice(0, -1))) {
|
||||
domain = suggested;
|
||||
}
|
||||
}
|
||||
scheduleValidation();
|
||||
}
|
||||
function handlePortInput() {
|
||||
scheduleValidation();
|
||||
}
|
||||
let $$settled = true;
|
||||
let $$inner_renderer;
|
||||
function $$render_inner($$renderer3) {
|
||||
$$renderer3.push(`<div class="space-y-6"><h3 class="text-lg font-semibold text-[var(--text-primary)]">${escape_html(title())}</h3> <form class="space-y-4">`);
|
||||
FormField($$renderer3, {
|
||||
label: store_get($$store_subs ??= {}, "$t", t)("proxies.form.destination"),
|
||||
name: "destination_url",
|
||||
placeholder: "192.168.1.100 or http://my-service",
|
||||
required: true,
|
||||
oninput: handleDestinationInput,
|
||||
get value() {
|
||||
return destinationUrl;
|
||||
},
|
||||
set value($$value) {
|
||||
destinationUrl = $$value;
|
||||
$$settled = false;
|
||||
}
|
||||
});
|
||||
$$renderer3.push(`<!----> `);
|
||||
FormField($$renderer3, {
|
||||
label: store_get($$store_subs ??= {}, "$t", t)("proxies.form.port"),
|
||||
name: "destination_port",
|
||||
type: "number",
|
||||
placeholder: "8080",
|
||||
required: true,
|
||||
error: port && !portValid() ? store_get($$store_subs ??= {}, "$t", t)("validation.invalidPort") : "",
|
||||
oninput: handlePortInput,
|
||||
get value() {
|
||||
return port;
|
||||
},
|
||||
set value($$value) {
|
||||
port = $$value;
|
||||
$$settled = false;
|
||||
}
|
||||
});
|
||||
$$renderer3.push(`<!----> `);
|
||||
FormField($$renderer3, {
|
||||
label: store_get($$store_subs ??= {}, "$t", t)("proxies.form.domain"),
|
||||
name: "domain",
|
||||
placeholder: "my-service.example.com",
|
||||
required: true,
|
||||
helpText: store_get($$store_subs ??= {}, "$t", t)("proxies.form.domainHelp"),
|
||||
get value() {
|
||||
return domain;
|
||||
},
|
||||
set value($$value) {
|
||||
domain = $$value;
|
||||
$$settled = false;
|
||||
}
|
||||
});
|
||||
$$renderer3.push(`<!----> <div class="space-y-2">`);
|
||||
ValidationChecklist($$renderer3, { result: validationResult, loading: validating });
|
||||
$$renderer3.push(`<!----> <button type="button" class="inline-flex items-center gap-1.5 rounded-lg border border-[var(--border-primary)] px-3 py-1.5 text-sm font-medium text-[var(--text-secondary)] hover:bg-[var(--surface-card-hover)] transition-colors disabled:opacity-50 disabled:cursor-not-allowed"${attr("disabled", !destinationUrl.trim() || !portValid() || validating, true)}>`);
|
||||
if (validating) {
|
||||
$$renderer3.push("<!--[0-->");
|
||||
IconLoader($$renderer3, { size: 14 });
|
||||
$$renderer3.push(`<!----> ${escape_html(store_get($$store_subs ??= {}, "$t", t)("proxies.form.validating"))}`);
|
||||
} else {
|
||||
$$renderer3.push("<!--[-1-->");
|
||||
$$renderer3.push(`${escape_html(store_get($$store_subs ??= {}, "$t", t)("proxies.form.validate"))}`);
|
||||
}
|
||||
$$renderer3.push(`<!--]--></button></div> `);
|
||||
if (validationResult && !validationResult.valid) {
|
||||
$$renderer3.push("<!--[0-->");
|
||||
$$renderer3.push(`<p class="text-xs text-amber-600 dark:text-amber-400">Validation reported issues but you can still create the proxy.</p>`);
|
||||
} else {
|
||||
$$renderer3.push("<!--[-1-->");
|
||||
}
|
||||
$$renderer3.push(`<!--]--> `);
|
||||
{
|
||||
$$renderer3.push("<!--[-1-->");
|
||||
}
|
||||
$$renderer3.push(`<!--]--> <div class="flex items-center justify-between pt-2"><div>`);
|
||||
{
|
||||
$$renderer3.push("<!--[-1-->");
|
||||
}
|
||||
$$renderer3.push(`<!--]--></div> <div class="flex items-center 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">${escape_html(store_get($$store_subs ??= {}, "$t", t)("proxies.form.cancel"))}</button> <button type="submit" class="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 hover:bg-[var(--color-brand-700)] transition-colors focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--color-brand-600)] disabled:opacity-50 disabled:cursor-not-allowed"${attr("disabled", !canSubmit(), true)}>`);
|
||||
{
|
||||
$$renderer3.push("<!--[-1-->");
|
||||
}
|
||||
$$renderer3.push(`<!--]--> ${escape_html(submitLabel())}</button></div></div></form></div> `);
|
||||
{
|
||||
$$renderer3.push("<!--[-1-->");
|
||||
}
|
||||
$$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);
|
||||
});
|
||||
}
|
||||
function _page($$renderer, $$props) {
|
||||
$$renderer.component(($$renderer2) => {
|
||||
var $$store_subs;
|
||||
head("1eyu80y", $$renderer2, ($$renderer3) => {
|
||||
$$renderer3.title(($$renderer4) => {
|
||||
$$renderer4.push(`<title>${escape_html(store_get($$store_subs ??= {}, "$t", t)("proxies.form.title"))} - ${escape_html(store_get($$store_subs ??= {}, "$t", t)("app.name"))}</title>`);
|
||||
});
|
||||
});
|
||||
$$renderer2.push(`<div class="mb-6"><a href="/proxies" class="inline-flex items-center gap-1 text-sm text-[var(--text-secondary)] hover:text-[var(--color-brand-600)] transition-colors"><svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 12H5"></path><path d="m12 19-7-7 7-7"></path></svg> ${escape_html(store_get($$store_subs ??= {}, "$t", t)("common.back"))}</a></div> <div class="mb-6 flex items-center gap-3"><div class="flex h-10 w-10 items-center justify-center rounded-xl bg-[var(--color-brand-50)] text-[var(--color-brand-600)]">`);
|
||||
IconGlobe($$renderer2, { size: 22 });
|
||||
$$renderer2.push(`<!----></div> <h1 class="text-xl font-bold text-[var(--text-primary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("proxies.form.title"))}</h1></div> <div class="mx-auto max-w-2xl rounded-2xl border border-[var(--border-primary)] bg-[var(--surface-card)] p-6 shadow-[var(--shadow-sm)]">`);
|
||||
ProxyForm($$renderer2, {});
|
||||
$$renderer2.push(`<!----></div>`);
|
||||
if ($$store_subs) unsubscribe_stores($$store_subs);
|
||||
});
|
||||
}
|
||||
export {
|
||||
_page as default
|
||||
};
|
||||
@@ -0,0 +1,90 @@
|
||||
import { a as attr, b as attr_class, c as clsx, e as escape_html, s as store_get, f as ensure_array_like, g as stringify, u as unsubscribe_stores, i as derived } from "../../../chunks/index.js";
|
||||
import { p as page } from "../../../chunks/stores.js";
|
||||
import { t } from "../../../chunks/index2.js";
|
||||
import { I as IconSettings } from "../../../chunks/IconSettings.js";
|
||||
function IconKey($$renderer, $$props) {
|
||||
const { size = 20, class: c = "" } = $$props;
|
||||
$$renderer.push(`<svg xmlns="http://www.w3.org/2000/svg"${attr("width", size)}${attr("height", size)} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"${attr_class(clsx(c))} aria-hidden="true"><circle cx="7.5" cy="15.5" r="5.5"></circle><path d="m21 2-9.3 9.3"></path><path d="M18.5 5.5 21 3"></path><path d="m15 8 2.5 2.5"></path></svg>`);
|
||||
}
|
||||
function IconShield($$renderer, $$props) {
|
||||
const { size = 20, class: c = "" } = $$props;
|
||||
$$renderer.push(`<svg xmlns="http://www.w3.org/2000/svg"${attr("width", size)}${attr("height", size)} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"${attr_class(clsx(c))} aria-hidden="true"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"></path></svg>`);
|
||||
}
|
||||
function IconDatabase($$renderer, $$props) {
|
||||
const { size = 20, class: c = "" } = $$props;
|
||||
$$renderer.push(`<svg xmlns="http://www.w3.org/2000/svg"${attr("width", size)}${attr("height", size)} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"${attr_class(clsx(c))} aria-hidden="true"><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>`);
|
||||
}
|
||||
function _layout($$renderer, $$props) {
|
||||
$$renderer.component(($$renderer2) => {
|
||||
var $$store_subs;
|
||||
let { children } = $$props;
|
||||
const navItems = [
|
||||
{
|
||||
href: "/settings",
|
||||
labelKey: "settings.general",
|
||||
icon: "general"
|
||||
},
|
||||
{
|
||||
href: "/settings/registries",
|
||||
labelKey: "settings.registries",
|
||||
icon: "registries"
|
||||
},
|
||||
{
|
||||
href: "/settings/credentials",
|
||||
labelKey: "settings.credentials",
|
||||
icon: "credentials"
|
||||
},
|
||||
{
|
||||
href: "/settings/auth",
|
||||
labelKey: "settings.authentication",
|
||||
icon: "auth"
|
||||
}
|
||||
];
|
||||
let currentPath = derived(() => store_get($$store_subs ??= {}, "$page", page).url.pathname);
|
||||
function isActive(href) {
|
||||
if (href === "/settings") return currentPath() === "/settings";
|
||||
return currentPath().startsWith(href);
|
||||
}
|
||||
$$renderer2.push(`<div class="mx-auto max-w-4xl"><h1 class="mb-6 text-2xl font-bold text-[var(--text-primary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("settings.title"))}</h1> <div class="flex flex-col gap-6 sm:flex-row"><nav class="w-full flex-shrink-0 sm:w-48"><ul class="flex gap-1 overflow-x-auto sm:flex-col sm:space-y-0.5"><!--[-->`);
|
||||
const each_array = ensure_array_like(navItems);
|
||||
for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) {
|
||||
let item = each_array[$$index];
|
||||
$$renderer2.push(`<li><a${attr("href", item.href)}${attr_class(`flex items-center gap-2.5 whitespace-nowrap rounded-lg px-3 py-2 text-sm font-medium transition-all duration-150 ${stringify(isActive(item.href) ? "bg-[var(--color-brand-50)] text-[var(--color-brand-700)] shadow-sm" : "text-[var(--text-secondary)] hover:bg-[var(--surface-card-hover)] hover:text-[var(--text-primary)]")}`)}>`);
|
||||
if (item.icon === "general") {
|
||||
$$renderer2.push("<!--[0-->");
|
||||
IconSettings($$renderer2, {
|
||||
size: 16,
|
||||
class: isActive(item.href) ? "text-[var(--color-brand-600)]" : "text-[var(--text-tertiary)]"
|
||||
});
|
||||
} else if (item.icon === "registries") {
|
||||
$$renderer2.push("<!--[1-->");
|
||||
IconDatabase($$renderer2, {
|
||||
size: 16,
|
||||
class: isActive(item.href) ? "text-[var(--color-brand-600)]" : "text-[var(--text-tertiary)]"
|
||||
});
|
||||
} else if (item.icon === "credentials") {
|
||||
$$renderer2.push("<!--[2-->");
|
||||
IconKey($$renderer2, {
|
||||
size: 16,
|
||||
class: isActive(item.href) ? "text-[var(--color-brand-600)]" : "text-[var(--text-tertiary)]"
|
||||
});
|
||||
} else if (item.icon === "auth") {
|
||||
$$renderer2.push("<!--[3-->");
|
||||
IconShield($$renderer2, {
|
||||
size: 16,
|
||||
class: isActive(item.href) ? "text-[var(--color-brand-600)]" : "text-[var(--text-tertiary)]"
|
||||
});
|
||||
} else {
|
||||
$$renderer2.push("<!--[-1-->");
|
||||
}
|
||||
$$renderer2.push(`<!--]--> ${escape_html(store_get($$store_subs ??= {}, "$t", t)(item.labelKey))}</a></li>`);
|
||||
}
|
||||
$$renderer2.push(`<!--]--></ul></nav> <div class="flex-1 min-w-0">`);
|
||||
children($$renderer2);
|
||||
$$renderer2.push(`<!----></div></div></div>`);
|
||||
if ($$store_subs) unsubscribe_stores($$store_subs);
|
||||
});
|
||||
}
|
||||
export {
|
||||
_layout as default
|
||||
};
|
||||
@@ -0,0 +1,70 @@
|
||||
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 { E as EntityPicker } from "../../../chunks/EntityPicker.js";
|
||||
import "../../../chunks/toast.js";
|
||||
import { t } from "../../../chunks/index2.js";
|
||||
import { S as Skeleton } from "../../../chunks/Skeleton.js";
|
||||
function _page($$renderer, $$props) {
|
||||
$$renderer.component(($$renderer2) => {
|
||||
var $$store_subs;
|
||||
let sslCertificateId = 0;
|
||||
let certPickerOpen = false;
|
||||
let certPickerItems = [];
|
||||
function handleCertSelect(value) {
|
||||
const id = parseInt(value, 10);
|
||||
sslCertificateId = id;
|
||||
const item = certPickerItems.find((i) => i.value === value);
|
||||
item?.label ?? "";
|
||||
certPickerOpen = false;
|
||||
}
|
||||
let $$settled = true;
|
||||
let $$inner_renderer;
|
||||
function $$render_inner($$renderer3) {
|
||||
head("1i19ct2", $$renderer3, ($$renderer4) => {
|
||||
$$renderer4.title(($$renderer5) => {
|
||||
$$renderer5.push(`<title>${escape_html(store_get($$store_subs ??= {}, "$t", t)("settingsGeneral.title"))} - ${escape_html(store_get($$store_subs ??= {}, "$t", t)("app.name"))}</title>`);
|
||||
});
|
||||
});
|
||||
$$renderer3.push(`<div class="space-y-6">`);
|
||||
{
|
||||
$$renderer3.push("<!--[0-->");
|
||||
$$renderer3.push(`<div class="space-y-4">`);
|
||||
Skeleton($$renderer3, { height: "2rem", width: "12rem" });
|
||||
$$renderer3.push(`<!----> <div class="grid grid-cols-2 gap-4"><!--[-->`);
|
||||
const each_array = ensure_array_like(Array(6));
|
||||
for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) {
|
||||
each_array[$$index];
|
||||
Skeleton($$renderer3, { height: "4rem" });
|
||||
}
|
||||
$$renderer3.push(`<!--]--></div></div>`);
|
||||
}
|
||||
$$renderer3.push(`<!--]--></div> `);
|
||||
EntityPicker($$renderer3, {
|
||||
items: certPickerItems,
|
||||
current: String(sslCertificateId),
|
||||
title: store_get($$store_subs ??= {}, "$t", t)("settingsGeneral.selectCertificate"),
|
||||
onselect: handleCertSelect,
|
||||
onclose: () => {
|
||||
certPickerOpen = false;
|
||||
},
|
||||
get open() {
|
||||
return certPickerOpen;
|
||||
},
|
||||
set open($$value) {
|
||||
certPickerOpen = $$value;
|
||||
$$settled = false;
|
||||
}
|
||||
});
|
||||
$$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,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
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
import { h as head, e as escape_html, s as store_get, u as unsubscribe_stores } from "../../../../chunks/index.js";
|
||||
import { S as Skeleton } from "../../../../chunks/Skeleton.js";
|
||||
import "../../../../chunks/toast.js";
|
||||
import { t } from "../../../../chunks/index2.js";
|
||||
function _page($$renderer, $$props) {
|
||||
$$renderer.component(($$renderer2) => {
|
||||
var $$store_subs;
|
||||
let $$settled = true;
|
||||
let $$inner_renderer;
|
||||
function $$render_inner($$renderer3) {
|
||||
head("5z23cl", $$renderer3, ($$renderer4) => {
|
||||
$$renderer4.title(($$renderer5) => {
|
||||
$$renderer5.push(`<title>${escape_html(store_get($$store_subs ??= {}, "$t", t)("settingsCredentials.title"))} - ${escape_html(store_get($$store_subs ??= {}, "$t", t)("app.name"))}</title>`);
|
||||
});
|
||||
});
|
||||
$$renderer3.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)("settingsCredentials.title"))}</h2> <p class="text-sm text-[var(--text-secondary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("settingsCredentials.description"))}</p></div> `);
|
||||
{
|
||||
$$renderer3.push("<!--[0-->");
|
||||
$$renderer3.push(`<div class="space-y-4">`);
|
||||
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,52 @@
|
||||
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 { S as Skeleton } from "../../../../chunks/Skeleton.js";
|
||||
import "../../../../chunks/toast.js";
|
||||
import { t } from "../../../../chunks/index2.js";
|
||||
import { I as IconPlus } from "../../../../chunks/IconPlus.js";
|
||||
function _page($$renderer, $$props) {
|
||||
$$renderer.component(($$renderer2) => {
|
||||
var $$store_subs;
|
||||
let $$settled = true;
|
||||
let $$inner_renderer;
|
||||
function $$render_inner($$renderer3) {
|
||||
head("p19oey", $$renderer3, ($$renderer4) => {
|
||||
$$renderer4.title(($$renderer5) => {
|
||||
$$renderer5.push(`<title>${escape_html(store_get($$store_subs ??= {}, "$t", t)("settingsRegistries.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"><div><h2 class="text-lg font-semibold text-[var(--text-primary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("settingsRegistries.title"))}</h2> <p class="text-sm text-[var(--text-secondary)]">${escape_html(store_get($$store_subs ??= {}, "$t", t)("settingsRegistries.description"))}</p></div> `);
|
||||
{
|
||||
$$renderer3.push("<!--[0-->");
|
||||
$$renderer3.push(`<button 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)] transition-colors active:animate-press">`);
|
||||
IconPlus($$renderer3, { size: 16 });
|
||||
$$renderer3.push(`<!----> ${escape_html(store_get($$store_subs ??= {}, "$t", t)("settingsRegistries.addRegistry"))}</button>`);
|
||||
}
|
||||
$$renderer3.push(`<!--]--></div> `);
|
||||
{
|
||||
$$renderer3.push("<!--[-1-->");
|
||||
}
|
||||
$$renderer3.push(`<!--]--> `);
|
||||
{
|
||||
$$renderer3.push("<!--[0-->");
|
||||
$$renderer3.push(`<div class="space-y-3"><!--[-->`);
|
||||
const each_array = ensure_array_like(Array(2));
|
||||
for (let $$index = 0, $$length = each_array.length; $$index < $$length; $$index++) {
|
||||
each_array[$$index];
|
||||
Skeleton($$renderer3, { height: "5rem" });
|
||||
}
|
||||
$$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
|
||||
};
|
||||
Reference in New Issue
Block a user