'use client'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { ArrowLeft, Cloud, Save, ShieldCheck, Trash2 } from 'lucide-react'; import Link from 'next/link'; import { useEffect, useMemo, useState } from 'react'; import { toast } from 'sonner'; import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '@/components/ui/card'; import { Field, FieldDescription, FieldGroup, FieldLabel, } from '@/components/ui/field'; import { Input } from '@/components/ui/input'; import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { ErrorInline } from '@/components/layout/error'; import { CloudflareService, cloudflareQueryKey, DnsAccountService, type CloudflareConnectionSource, } from '@/lib/services/openflare'; import { getErrorMessage } from '../../websites/components/website-utils'; const dnsAccountsQueryKey = ['openflare', 'dns-accounts'] as const; export default function CloudflareSettingsPage() { const queryClient = useQueryClient(); const [source, setSource] = useState('dns_account'); const [dnsAccountID, setDNSAccountID] = useState(''); const [apiToken, setAPIToken] = useState(''); const connectionQuery = useQuery({ queryKey: [...cloudflareQueryKey, 'connection'], queryFn: () => CloudflareService.getConnection(), }); const accountsQuery = useQuery({ queryKey: dnsAccountsQueryKey, queryFn: () => DnsAccountService.list(), }); const cloudflareAccounts = useMemo( () => (accountsQuery.data ?? []).filter( (account) => account.type === 'cloudflare', ), [accountsQuery.data], ); useEffect(() => { const connection = connectionQuery.data; if (!connection?.configured) return; if (connection.source) setSource(connection.source); if (connection.dns_account_id) setDNSAccountID(String(connection.dns_account_id)); }, [connectionQuery.data]); const refresh = async () => { await queryClient.invalidateQueries({ queryKey: cloudflareQueryKey }); }; const saveMutation = useMutation({ mutationFn: () => CloudflareService.saveConnection({ source, dns_account_id: source === 'dns_account' ? Number(dnsAccountID) : 0, api_token: source === 'standalone' ? apiToken : '', }), onSuccess: async () => { toast.success('Cloudflare 连接配置已保存'); setAPIToken(''); await refresh(); }, onError: (error) => toast.error(getErrorMessage(error)), }); const verifyMutation = useMutation({ mutationFn: () => CloudflareService.verifyConnection(), onSuccess: async () => { toast.success('Cloudflare 连接验证成功'); await refresh(); }, onError: (error) => toast.error(getErrorMessage(error)), }); const clearMutation = useMutation({ mutationFn: () => CloudflareService.clearConnection(), onSuccess: async () => { toast.success('Cloudflare 连接已清除'); setDNSAccountID(''); setAPIToken(''); await refresh(); }, onError: (error) => toast.error(getErrorMessage(error)), }); return (

Cloudflare 连接设置

{connectionQuery.isError ? ( void connectionQuery.refetch()} /> ) : null} 凭据来源 Token 建议授予 Zone:Read 与 DNS:Edit 权限;Token 不会在 API 或页面中回显。 连接来源 {source === 'dns_account' ? ( Cloudflare DNS 账号 仅显示类型为 cloudflare 的 DNS 账号。可前往{' '} DNS 账号 新增。 ) : ( API Token setAPIToken(event.target.value)} placeholder={ connectionQuery.data?.configured ? '留空不会回显现有 Token;保存将替换' : '输入 Cloudflare API Token' } /> )}
当前状态 {connectionQuery.data?.ready ? 'Token 已验证,Cloudflare 指向同步可以执行。' : '保存配置后仍需测试连接;未验证状态下同步任务会被拒绝。'}
); }