fix(services): разрешить несколько доп. FQDN на IP включая wildcard
quality / commitlint (push) Skipped
CD / update-wiki (push) Successful in 6s
quality / changes (push) Successful in 10s
quality / docker-check (push) Skipped
quality / web (push) Successful in 1m16s
quality / api (push) Successful in 1m14s
CD / quality (push) Successful in 2m44s
CD / publish (push) Successful in 1m50s
quality / commitlint (push) Skipped
CD / update-wiki (push) Successful in 6s
quality / changes (push) Successful in 10s
quality / docker-check (push) Skipped
quality / web (push) Successful in 1m16s
quality / api (push) Successful in 1m14s
CD / quality (push) Successful in 2m44s
CD / publish (push) Successful in 1m50s
У IP был один extra FQDN; wildcard вида *.mdns.shnt.top не матчился с именем из Cloudflare. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -16,10 +16,13 @@ import { parseFqdn } from '@/lib/parse-fqdn'
|
||||
import {
|
||||
addAddressNode,
|
||||
addCommonFqdn,
|
||||
addExtraFqdn,
|
||||
addressHasFqdn,
|
||||
removeAddressNode,
|
||||
removeCommonFqdn,
|
||||
removeExtraFqdn,
|
||||
updateCommonFqdn,
|
||||
updateExtraFqdn,
|
||||
type AddressBlockState,
|
||||
} from '@/lib/service-address'
|
||||
import { Button } from '@cfdm/ui/components/button'
|
||||
@@ -67,7 +70,7 @@ function ZoneAddon({
|
||||
}
|
||||
|
||||
/**
|
||||
* Единый блок адресов сервиса: список общих FQDN на весь пул + IP с доп. доменом.
|
||||
* Единый блок адресов сервиса: список общих FQDN на весь пул + IP с доп. доменами.
|
||||
* Preview: https://reui.io/preview/base/settings-3
|
||||
* Preview: https://reui.io/preview/base/list-9
|
||||
* Preview: https://reui.io/preview/base/form-7
|
||||
@@ -88,6 +91,8 @@ export function ServiceAddressBlock({
|
||||
const [ipInvalid, setIpInvalid] = useState(false)
|
||||
const [pendingFqdn, setPendingFqdn] = useState('')
|
||||
const [fqdnInvalid, setFqdnInvalid] = useState(false)
|
||||
const [pendingExtraByIp, setPendingExtraByIp] = useState<Record<string, string>>({})
|
||||
const [extraInvalidByIp, setExtraInvalidByIp] = useState<Record<string, boolean>>({})
|
||||
|
||||
const pool = value.nodes.map((node) => node.ip)
|
||||
const pendingIpTrimmed = pendingIp.trim()
|
||||
@@ -141,13 +146,26 @@ export function ServiceAddressBlock({
|
||||
}
|
||||
}
|
||||
|
||||
function handleNodeFqdn(ip: string, extraFqdn: string) {
|
||||
onChange({
|
||||
...value,
|
||||
nodes: value.nodes.map((node) =>
|
||||
node.ip === ip ? { ...node, extraFqdn } : node,
|
||||
),
|
||||
})
|
||||
function tryAddExtra(ip: string, raw: string) {
|
||||
const trimmed = raw.trim()
|
||||
if (!trimmed) {
|
||||
setExtraInvalidByIp((current) => ({ ...current, [ip]: false }))
|
||||
return
|
||||
}
|
||||
if (addressHasFqdn(value, trimmed)) {
|
||||
setExtraInvalidByIp((current) => ({ ...current, [ip]: true }))
|
||||
return
|
||||
}
|
||||
onChange(addExtraFqdn(value, ip, trimmed))
|
||||
setPendingExtraByIp((current) => ({ ...current, [ip]: '' }))
|
||||
setExtraInvalidByIp((current) => ({ ...current, [ip]: false }))
|
||||
}
|
||||
|
||||
function handleExtraKeyDown(ip: string, event: KeyboardEvent<HTMLInputElement>) {
|
||||
if (event.key === 'Enter') {
|
||||
event.preventDefault()
|
||||
tryAddExtra(ip, pendingExtraByIp[ip] ?? '')
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -156,7 +174,7 @@ export function ServiceAddressBlock({
|
||||
<FrameHeader className="px-0 pt-0">
|
||||
<FrameTitle>Адреса</FrameTitle>
|
||||
<FrameDescription>
|
||||
Общие FQDN — на весь пул · у IP свой доп. домен
|
||||
Общие FQDN — на весь пул · у IP свои доп. домены
|
||||
</FrameDescription>
|
||||
</FrameHeader>
|
||||
<Field>
|
||||
@@ -224,7 +242,7 @@ export function ServiceAddressBlock({
|
||||
<EmptyState
|
||||
icon={ServerIcon}
|
||||
title="Добавьте IP пула"
|
||||
description="IPv4 сервиса. Для каждого адреса можно указать доп. FQDN."
|
||||
description="IPv4 сервиса. Для каждого адреса можно указать несколько доп. FQDN, в том числе wildcard."
|
||||
stackedIcon={false}
|
||||
centered={false}
|
||||
/>
|
||||
@@ -265,27 +283,98 @@ export function ServiceAddressBlock({
|
||||
</div>
|
||||
<Field className="gap-1.5">
|
||||
<FieldLabel
|
||||
htmlFor={`service-ip-extra-${node.ip}`}
|
||||
htmlFor={`service-ip-extra-add-${node.ip}`}
|
||||
className="text-muted-foreground text-xs"
|
||||
>
|
||||
Доп. FQDN
|
||||
</FieldLabel>
|
||||
<InputGroup>
|
||||
<InputGroupInput
|
||||
id={`service-ip-extra-${node.ip}`}
|
||||
className="font-mono"
|
||||
value={node.extraFqdn}
|
||||
placeholder={
|
||||
zoneHints[0]
|
||||
? `необязательно · spb.${zoneHints[0]}`
|
||||
: 'необязательно · spb.example.com'
|
||||
}
|
||||
onChange={(event) =>
|
||||
handleNodeFqdn(node.ip, event.target.value)
|
||||
}
|
||||
/>
|
||||
<ZoneAddon fqdn={node.extraFqdn} zoneHints={zoneHints} />
|
||||
</InputGroup>
|
||||
<div className="flex w-full flex-col gap-2">
|
||||
{node.extraFqdns.map((fqdn, index) => (
|
||||
<InputGroup key={`extra-fqdn-${node.ip}-${index}`}>
|
||||
<InputGroupInput
|
||||
id={`service-ip-extra-${node.ip}-${index}`}
|
||||
className="font-mono"
|
||||
value={fqdn}
|
||||
placeholder={
|
||||
zoneHints[0]
|
||||
? `*.mdns.${zoneHints[0]}`
|
||||
: '*.mdns.example.com'
|
||||
}
|
||||
onChange={(event) =>
|
||||
onChange(
|
||||
updateExtraFqdn(
|
||||
value,
|
||||
node.ip,
|
||||
index,
|
||||
event.target.value,
|
||||
),
|
||||
)
|
||||
}
|
||||
/>
|
||||
<ZoneAddon
|
||||
fqdn={fqdn}
|
||||
zoneHints={zoneHints}
|
||||
trailing={
|
||||
<InputGroupButton
|
||||
size="icon-xs"
|
||||
aria-label={`Удалить ${fqdn || 'FQDN'}`}
|
||||
onClick={() =>
|
||||
onChange(removeExtraFqdn(value, node.ip, index))
|
||||
}
|
||||
>
|
||||
<Trash2Icon />
|
||||
</InputGroupButton>
|
||||
}
|
||||
/>
|
||||
</InputGroup>
|
||||
))}
|
||||
<InputGroup>
|
||||
<InputGroupInput
|
||||
id={`service-ip-extra-add-${node.ip}`}
|
||||
className="font-mono"
|
||||
value={pendingExtraByIp[node.ip] ?? ''}
|
||||
placeholder={
|
||||
zoneHints[0]
|
||||
? `необязательно · *.mdns.${zoneHints[0]}`
|
||||
: 'необязательно · *.mdns.example.com'
|
||||
}
|
||||
aria-invalid={
|
||||
extraInvalidByIp[node.ip] &&
|
||||
(pendingExtraByIp[node.ip] ?? '').trim().length > 0
|
||||
? true
|
||||
: undefined
|
||||
}
|
||||
onChange={(event) => {
|
||||
setPendingExtraByIp((current) => ({
|
||||
...current,
|
||||
[node.ip]: event.target.value,
|
||||
}))
|
||||
setExtraInvalidByIp((current) => ({
|
||||
...current,
|
||||
[node.ip]: false,
|
||||
}))
|
||||
}}
|
||||
onKeyDown={(event) => handleExtraKeyDown(node.ip, event)}
|
||||
onBlur={() =>
|
||||
tryAddExtra(node.ip, pendingExtraByIp[node.ip] ?? '')
|
||||
}
|
||||
/>
|
||||
<ZoneAddon
|
||||
fqdn={pendingExtraByIp[node.ip] ?? ''}
|
||||
zoneHints={zoneHints}
|
||||
trailing={
|
||||
<InputGroupButton
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
tryAddExtra(node.ip, pendingExtraByIp[node.ip] ?? '')
|
||||
}
|
||||
>
|
||||
Добавить
|
||||
</InputGroupButton>
|
||||
}
|
||||
/>
|
||||
</InputGroup>
|
||||
</div>
|
||||
</Field>
|
||||
</ItemContent>
|
||||
</Item>
|
||||
|
||||
@@ -219,8 +219,8 @@ export function ServiceEditSheet({
|
||||
<SheetHeader className="shrink-0 border-b pb-4">
|
||||
<SheetTitle>{isCreate ? 'Новый сервис' : 'Редактирование сервиса'}</SheetTitle>
|
||||
<SheetDescription>
|
||||
Общие FQDN на весь пул IP. У каждого адреса можно указать свой доп.
|
||||
FQDN.
|
||||
Общие FQDN на весь пул IP. У каждого адреса можно указать несколько доп.
|
||||
FQDN, в том числе wildcard.
|
||||
</SheetDescription>
|
||||
</SheetHeader>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user