diff --git a/app/(dashboard)/proxy-hosts/ProxyHostsClient.tsx b/app/(dashboard)/proxy-hosts/ProxyHostsClient.tsx index ed669594..bfd3461d 100644 --- a/app/(dashboard)/proxy-hosts/ProxyHostsClient.tsx +++ b/app/(dashboard)/proxy-hosts/ProxyHostsClient.tsx @@ -4,6 +4,7 @@ import { useMemo, useState } from "react"; import { IconButton, Stack, Switch, Tooltip, Typography } from "@mui/material"; import EditIcon from "@mui/icons-material/Edit"; import DeleteIcon from "@mui/icons-material/Delete"; +import ContentCopyIcon from "@mui/icons-material/ContentCopy"; import type { AccessList } from "@/src/lib/models/access-lists"; import type { Certificate } from "@/src/lib/models/certificates"; import type { ProxyHost } from "@/src/lib/models/proxy-hosts"; @@ -24,6 +25,7 @@ type Props = { export default function ProxyHostsClient({ hosts, certificates, accessLists, authentikDefaults }: Props) { const [createOpen, setCreateOpen] = useState(false); + const [duplicateHost, setDuplicateHost] = useState(null); const [editHost, setEditHost] = useState(null); const [deleteHost, setDeleteHost] = useState(null); const [searchTerm, setSearchTerm] = useState(""); @@ -108,6 +110,18 @@ export default function ProxyHostsClient({ hosts, certificates, accessLists, aut size="small" color="success" /> + + { + setDuplicateHost(host); + setCreateOpen(true); + }} + color="info" + > + + + setEditHost(host)} color="primary"> @@ -149,7 +163,12 @@ export default function ProxyHostsClient({ hosts, certificates, accessLists, aut setCreateOpen(false)} + onClose={() => { + setCreateOpen(false); + // Clear duplicate host after dialog transition + setTimeout(() => setDuplicateHost(null), 200); + }} + initialData={duplicateHost} certificates={certificates} accessLists={accessLists} authentikDefaults={authentikDefaults} diff --git a/src/components/proxy-hosts/HostDialogs.tsx b/src/components/proxy-hosts/HostDialogs.tsx index 77f06d88..88c5ccec 100644 --- a/src/components/proxy-hosts/HostDialogs.tsx +++ b/src/components/proxy-hosts/HostDialogs.tsx @@ -22,13 +22,15 @@ export function CreateHostDialog({ onClose, certificates, accessLists, - authentikDefaults + authentikDefaults, + initialData }: { open: boolean; onClose: () => void; certificates: Certificate[]; accessLists: AccessList[]; authentikDefaults: AuthentikSettings | null; + initialData?: ProxyHost | null; }) { const [state, formAction] = useFormState(createProxyHostAction, INITIAL_ACTION_STATE); @@ -42,7 +44,7 @@ export function CreateHostDialog({ { @@ -56,20 +58,32 @@ export function CreateHostDialog({ {state.message} )} - - + + - - + + Managed by Caddy (Auto) {certificates.map((cert) => ( @@ -77,7 +91,7 @@ export function CreateHostDialog({ ))} - + None {accessLists.map((list) => ( @@ -89,6 +103,7 @@ export function CreateHostDialog({ name="custom_pre_handlers_json" label="Custom Pre-Handlers (JSON)" placeholder='[{"handler": "headers", ...}]' + defaultValue={initialData?.custom_pre_handlers_json ?? ""} helperText="Optional JSON array of Caddy handlers" multiline minRows={3} @@ -98,12 +113,13 @@ export function CreateHostDialog({ name="custom_reverse_proxy_json" label="Custom Reverse Proxy (JSON)" placeholder='{"headers": {"request": {...}}}' + defaultValue={initialData?.custom_reverse_proxy_json ?? ""} helperText="Deep-merge into reverse_proxy handler" multiline minRows={3} fullWidth /> - + ); diff --git a/src/components/proxy-hosts/UpstreamInput.tsx b/src/components/proxy-hosts/UpstreamInput.tsx index 7a77fd97..75cc42ce 100644 --- a/src/components/proxy-hosts/UpstreamInput.tsx +++ b/src/components/proxy-hosts/UpstreamInput.tsx @@ -42,7 +42,16 @@ export function UpstreamInput({ const handleAddressChange = (index: number, newAddress: string) => { const updated = [...entries]; - updated[index].address = newAddress; + // Strip protocol if user pasted a full URL + if (newAddress.startsWith("https://")) { + updated[index].protocol = "https://"; + updated[index].address = newAddress.slice(8); + } else if (newAddress.startsWith("http://")) { + updated[index].protocol = "http://"; + updated[index].address = newAddress.slice(7); + } else { + updated[index].address = newAddress; + } setEntries(updated); };