'use client'; import { useEffect, useMemo } from 'react'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { zodResolver } from '@hookform/resolvers/zod'; import { useForm } from 'react-hook-form'; import { Loader2 } from 'lucide-react'; import { toast } from 'sonner'; import { z } from 'zod'; import { Button } from '@/components/ui/button'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { TlsCertificateService, ZoneDomainService, ZoneService, zoneQueryKey, type ZoneDomainItem, type ZoneItem, } from '@/lib/services/openflare'; import { previewZoneDomainInput, resolveZoneDomainInput, } from './resolve-zone-domain-input'; const schema = z.object({ zone_id: z.string().min(1, '请选择 Zone'), domain_input: z.string().trim().min(1, '请输入域名'), cert_id: z.string(), }); type Values = z.infer; export function QuickCreateZoneDomainDialog({ open, onOpenChange, fixedZoneId, fixedZoneRoot, zones: zonesProp, onCreated, }: { open: boolean; onOpenChange(open: boolean): void; /** When set, Zone 选择器隐藏 */ fixedZoneId?: number; fixedZoneRoot?: string; zones?: ZoneItem[]; onCreated(domain: ZoneDomainItem): void | Promise; }) { const queryClient = useQueryClient(); const zonesQuery = useQuery({ queryKey: zoneQueryKey, queryFn: () => ZoneService.list(), enabled: open && !fixedZoneId && !zonesProp, }); const certificatesQuery = useQuery({ queryKey: ['openflare', 'tls-certificates'], queryFn: () => TlsCertificateService.list(), enabled: open, }); const zones = useMemo( () => zonesProp ?? zonesQuery.data ?? [], [zonesProp, zonesQuery.data], ); const fixedZone = useMemo(() => { if (!fixedZoneId) { return undefined; } return ( zones.find((zone) => zone.id === fixedZoneId) ?? (fixedZoneRoot ? ({ id: fixedZoneId, domain: fixedZoneRoot, created_at: '', updated_at: '', } as ZoneItem) : undefined) ); }, [fixedZoneId, fixedZoneRoot, zones]); const form = useForm({ resolver: zodResolver(schema), defaultValues: { zone_id: fixedZoneId ? String(fixedZoneId) : '', domain_input: '', cert_id: '', }, }); useEffect(() => { if (!open) { return; } form.reset({ zone_id: fixedZoneId ? String(fixedZoneId) : '', domain_input: '', cert_id: '', }); }, [fixedZoneId, form, open]); const watchedZoneId = form.watch('zone_id'); const watchedInput = form.watch('domain_input'); const selectedZone = useMemo(() => { if (fixedZone) { return fixedZone; } const id = Number(watchedZoneId); return zones.find((zone) => zone.id === id); }, [fixedZone, watchedZoneId, zones]); const preview = selectedZone ? previewZoneDomainInput(watchedInput, selectedZone.domain) : ''; const mutation = useMutation({ mutationFn: async (values: Values) => { const zoneId = Number(values.zone_id); const zone = fixedZone ?? zones.find((item) => item.id === zoneId); if (!zone) { throw new Error('请选择 Zone'); } const resolved = resolveZoneDomainInput(values.domain_input, zone.domain); if (resolved.error || !resolved.domain) { throw new Error(resolved.error || '域名格式不合法'); } return ZoneDomainService.create(zone.id, { domain: resolved.domain, cert_id: values.cert_id ? Number(values.cert_id) : null, }); }, onSuccess: async (domain) => { toast.success('域名已添加', { description: domain.domain }); await Promise.all([ onCreated(domain), queryClient.invalidateQueries({ queryKey: zoneQueryKey }), queryClient.invalidateQueries({ queryKey: [...zoneQueryKey, 'all-domains'], }), ]); onOpenChange(false); }, onError: (error) => toast.error(error instanceof Error ? error.message : '添加失败'), }); return ( 快捷新增域名 选择 Zone 后输入:子域名称(如 api)、@(根域)或完整 FQDN。
{ if (!selectedZone) { form.setError('zone_id', { message: '请选择 Zone' }); return; } const resolved = resolveZoneDomainInput( values.domain_input, selectedZone.domain, ); if (resolved.error) { form.setError('domain_input', { message: resolved.error }); return; } mutation.mutate(values); })} > {!fixedZoneId ? (
{form.formState.errors.zone_id ? (

{form.formState.errors.zone_id.message}

) : null}
) : (
Zone: {fixedZoneRoot || fixedZone?.domain || `#${fixedZoneId}`}
)}
{preview ? (

将创建: {preview}

) : (

示例:输入 api →{' '} api.zone.com; @ → 根域本身

)} {form.formState.errors.domain_input ? (

{form.formState.errors.domain_input.message}

) : null}
); }