fix(domain-proxy): improve endpoint and logic to retrieve domain proxy capabilities

This commit is contained in:
KodekaTeam committed 2026-08-07 14:52:54 +07:00
1 parent e71eaaa6ef
commit e488207bf8
6 files changed
+122 -37

No files matched your search

+25 -22
View File
@@ -21,11 +21,7 @@ import {
DomainConfigMode,
PortOption,
} from "./domain-types";
import {
detectAvailableProxies,
isIpv4WithPort,
parseInspectPortOptions,
} from "./domain-utils";
import { isIpv4WithPort, parseInspectPortOptions } from "./domain-utils";
interface AddDomainModalProps {
serverList: Server[];
@@ -84,6 +80,7 @@ export default function AddDomainModal({
const [proxyCapabilityError, setProxyCapabilityError] = useState("");
const [certbotInstalled, setCertbotInstalled] = useState(false);
const [serverContainers, setServerContainers] = useState<Container[]>([]);
const [serverContainersLoading, setServerContainersLoading] = useState(false);
const [portOptions, setPortOptions] = useState<PortOption[]>([]);
const [containerInspectLoading, setContainerInspectLoading] = useState(false);
const [availableProxies, setAvailableProxies] = useState<
@@ -175,6 +172,7 @@ export default function AddDomainModal({
setProxyCapabilityError("");
setAvailableProxies(null);
setServerContainers([]);
setServerContainersLoading(false);
setPortOptions([]);
setCertbotInstalled(false);
setForm((current) => {
@@ -203,32 +201,27 @@ export default function AddDomainModal({
}
setCapabilityLoading(true);
setServerContainersLoading(true);
setProxyCapabilityError("");
const containersRequest = containersApi.list({ serverId: form.serverId });
void Promise.all([
serversApi.getConfig(form.serverId),
containersApi.list({ serverId: form.serverId }),
serversApi.getDomainProxyCapability(form.serverId),
containersRequest,
])
.then(([configRes, containersRes]) => {
.then(([capabilityRes, containersRes]) => {
if (cancelled) {
return;
}
const proxies = detectAvailableProxies(
configRes.data,
containersRes.data,
);
const hasCertbot = configRes.data.webServer.components.some(
(component) => component.key === "certbot" && component.installed,
);
const proxies: DomainProxy[] = ["NONE"];
if (containersRes.data.some((container) => `${container.name} ${container.image}`.toLowerCase().includes("traefik"))) proxies.push("TRAEFIK");
if (capabilityRes.data.nginx) proxies.push("NGINX");
if (capabilityRes.data.caddy) proxies.push("CADDY");
const hasCertbot = capabilityRes.data.certbot;
setCertbotInstalled(hasCertbot);
setAvailableProxies(proxies);
setServerContainers(
containersRes.data.filter(
(container) => container.status === "RUNNING",
),
);
setForm((current) => ({
...current,
sslEnabled: hasCertbot ? current.sslEnabled : false,
@@ -244,8 +237,6 @@ export default function AddDomainModal({
setAvailableProxies(["NONE"]);
setCertbotInstalled(false);
setServerContainers([]);
setPortOptions([]);
setForm((current) => ({ ...current, proxy: "NONE" }));
setProxyCapabilityError(
err instanceof Error
@@ -259,6 +250,17 @@ export default function AddDomainModal({
}
});
void containersRequest
.then((containersRes) => {
if (!cancelled) setServerContainers(containersRes.data.filter((container) => container.status === "RUNNING"));
})
.catch(() => {
if (!cancelled) setServerContainers([]);
})
.finally(() => {
if (!cancelled) setServerContainersLoading(false);
});
return () => {
cancelled = true;
};
@@ -629,6 +631,7 @@ export default function AddDomainModal({
capabilityLoading={capabilityLoading}
proxyCapabilityError={proxyCapabilityError}
serverContainers={serverContainers}
serverContainersLoading={serverContainersLoading}
portOptions={portOptions}
containerInspectLoading={containerInspectLoading}
selectableProxies={selectableProxies}
@@ -1,4 +1,6 @@
import { Container, DomainProxy } from "@/lib/api";
import SearchableSelect, { type SearchableSelectOption } from "@/components/SearchableSelect";
import { Loader2 } from "lucide-react";
import { AddDomainFormState, PortOption } from "./domain-types";
interface AddDomainTargetingSectionProps {
@@ -6,6 +8,7 @@ interface AddDomainTargetingSectionProps {
capabilityLoading: boolean;
proxyCapabilityError: string;
serverContainers: Container[];
serverContainersLoading: boolean;
portOptions: PortOption[];
containerInspectLoading: boolean;
selectableProxies: DomainProxy[];
@@ -23,6 +26,7 @@ export default function AddDomainTargetingSection({
capabilityLoading,
proxyCapabilityError,
serverContainers,
serverContainersLoading,
portOptions,
containerInspectLoading,
selectableProxies,
@@ -32,16 +36,22 @@ export default function AddDomainTargetingSection({
linkedPortDomains,
onChange,
}: AddDomainTargetingSectionProps) {
const containerOptions: SearchableSelectOption[] = serverContainers.map((container) => {
const linkedDomains = linkedContainerDomains.get(container.id) ?? [];
const disabled = linkedDomains.length > 0 && !allowSharedTargetReuse;
return { value: container.id, label: `${container.name} (${container.image})`, keywords: `${container.name} ${container.image} ${container.dockerId ?? ""}`, description: disabled ? linkedDomains.length > 1 ? "Used by 1+ domains" : `Used by ${linkedDomains.join(", ")}` : undefined, disabled };
});
return (
<>
<div
style={{
display: "grid",
gridTemplateColumns: "1fr 1fr",
gridTemplateColumns: "minmax(0, 1fr)",
gap: 12,
}}
>
<div>
<div style={{ minWidth: 0 }}>
<label
style={{
fontSize: 12,
@@ -52,18 +62,22 @@ export default function AddDomainTargetingSection({
>
Target Container *
</label>
<select
className="input"
<SearchableSelect
value={form.targetContainerId}
onChange={(event) =>
options={containerOptions}
onChange={(targetContainerId) =>
onChange({
targetContainerId: event.target.value,
targetContainerId,
targetPort: "",
})
}
disabled={!form.serverId || capabilityLoading || !targetingEnabled}
placeholder="Select container..."
searchPlaceholder="Search containers..."
emptyText="No matching containers found"
disabled={!form.serverId || serverContainersLoading || !targetingEnabled}
style={{ width: "100%" }}
>
/>
{/*
<option value="">— Select container —</option>
{serverContainers.map((container) => {
const linkedDomains =
@@ -83,9 +97,9 @@ export default function AddDomainTargetingSection({
</option>
);
})}
</select>
*/}
</div>
<div>
<div style={{ minWidth: 0 }}>
<label
style={{
fontSize: 12,
@@ -136,7 +150,14 @@ export default function AddDomainTargetingSection({
</div>
</div>
{form.serverId && serverContainers.length === 0 && (
{form.serverId && serverContainersLoading && (
<div role="status" aria-live="polite" style={{ display: "flex", alignItems: "center", gap: 7, marginTop: 8, padding: "8px 10px", border: "1px solid rgba(59,130,246,0.28)", borderRadius: 7, background: "rgba(59,130,246,0.08)", color: "var(--text-muted)", fontSize: 12 }}>
<Loader2 size={14} className="animate-spin" aria-hidden="true" />
Loading running containers from the selected server…
</div>
)}
{form.serverId && !serverContainersLoading && serverContainers.length === 0 && (
<p style={{ fontSize: 11, color: "var(--text-muted)" }}>
No running containers were found on the selected server.
</p>
@@ -197,10 +218,11 @@ export default function AddDomainTargetingSection({
);
})}
</div>
<p style={{ fontSize: 11, color: "var(--text-muted)", marginTop: 6 }}>
<p style={{ display: "flex", alignItems: "center", gap: 6, fontSize: 11, color: "var(--text-muted)", marginTop: 6 }}>
{form.serverId && capabilityLoading && <Loader2 size={13} className="animate-spin" aria-hidden="true" />}
{form.serverId
? capabilityLoading
? "Inspecting selected server for available reverse proxies..."
? "Checking reverse proxy availability on the selected server…"
: "Choose container and port first, then pick one of the proxies detected on the selected server."
: "Select Server Target first to enable proxy-aware validation."}
</p>