'use client'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { Cloud, Plus, RefreshCw, Settings, Trash2 } from 'lucide-react'; import Link from 'next/link'; import { useParams } from 'next/navigation'; import { useState } from 'react'; import { toast } from 'sonner'; import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '@/components/ui/card'; import { Switch } from '@/components/ui/switch'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; import { ErrorInline } from '@/components/layout/error'; import { LoadingStateWithBorder } from '@/components/layout/loading'; import { CloudflareService, cloudflareQueryKey, NodeService, type CloudflareGroupPayload, } from '@/lib/services/openflare'; import { getErrorMessage } from '../../../websites/components/website-utils'; import { GroupDialog } from '../../components/group-dialog'; import { MemberAddDialog } from '../../components/member-add-dialog'; export default function CloudflareGroupDetailPage() { const params = useParams<{ id: string }>(); const groupID = Number(params.id); const queryClient = useQueryClient(); const [editOpen, setEditOpen] = useState(false); const [addOpen, setAddOpen] = useState(false); const detailQuery = useQuery({ queryKey: [...cloudflareQueryKey, 'groups', groupID], queryFn: () => CloudflareService.getGroup(groupID), enabled: Number.isInteger(groupID) && groupID > 0, }); const domainsQuery = useQuery({ queryKey: [...cloudflareQueryKey, 'domains', 'available'], queryFn: () => CloudflareService.listAvailableDomains(), }); const nodesQuery = useQuery({ queryKey: ['openflare', 'nodes'], queryFn: () => NodeService.listNodes(), }); const invalidate = async () => queryClient.invalidateQueries({ queryKey: cloudflareQueryKey }); const updateGroupMutation = useMutation({ mutationFn: (payload: CloudflareGroupPayload) => CloudflareService.updateGroup(groupID, payload), onSuccess: async () => { toast.success('分组配置已更新'); setEditOpen(false); await invalidate(); }, onError: (error) => toast.error(getErrorMessage(error)), }); const addMutation = useMutation({ mutationFn: ({ domainID, proxied, }: { domainID: number; proxied: boolean; }) => CloudflareService.createMember(groupID, { zone_domain_id: domainID, proxied, }), onSuccess: async () => { toast.success('域名已加入并排队同步'); setAddOpen(false); await invalidate(); }, onError: (error) => toast.error(getErrorMessage(error)), }); const proxiedMutation = useMutation({ mutationFn: ({ memberID, proxied, }: { memberID: number; proxied: boolean; }) => CloudflareService.updateMember(groupID, memberID, proxied), onSuccess: async () => { toast.success('橙云设置已更新并排队同步'); await invalidate(); }, onError: (error) => toast.error(getErrorMessage(error)), }); const syncMutation = useMutation({ mutationFn: (memberID: number) => CloudflareService.syncMember(groupID, memberID), onSuccess: () => toast.success('成员同步任务已入队'), onError: (error) => toast.error(getErrorMessage(error)), }); const removeMutation = useMutation({ mutationFn: (memberID: number) => CloudflareService.removeMember(groupID, memberID), onSuccess: async () => { toast.success('成员及远端 A 记录已删除'); await invalidate(); }, onError: (error) => toast.error(getErrorMessage(error)), }); if (detailQuery.isLoading) return (
暂无域名成员。
) : (