/** * 域名页(v1.1.0):域名由 Cloudflare OAuth 自动同步(无手工添加); * 点击域名配置 Email Workers(Worker + Email Routing + catch-all)与域名级 Webhook; * 支持搜索与按账号筛选。 */ import { useCallback, useEffect, useMemo, useState } from "react"; import { Button, Card, Drawer, Input, Select, Space, Table, Tag, Tooltip, Typography, App } from "antd"; import { ReloadOutlined, SearchOutlined } from "@ant-design/icons"; import { deleteDomain, listDomainAccounts, listDomains, setupDomain, type DomainRow, } from "../api.ts"; import { WebhookForm } from "../components/WebhookForm.tsx"; /** # 成员变量: 域名状态 -> Tag **/ function StatusTag({ row }: { row: DomainRow }) { if (row.status === "active") return 已配置 Email Workers; if (row.status === "error") return ( error ); return pending; } /** # 函数 * 域名页组件 */ export function DomainsPage() { const { message, modal } = App.useApp(); const [items, setItems] = useState([]); const [accountOptions, setAccountOptions] = useState<{ id: string; name: string }[]>([]); const [loading, setLoading] = useState(false); const [q, setQ] = useState(""); const [accountId, setAccountId] = useState(); const [editing, setEditing] = useState(null); const load = useCallback(async () => { setLoading(true); try { const [d, a] = await Promise.all([listDomains(), listDomainAccounts()]); setItems(d.items); setAccountOptions(a.items); } finally { setLoading(false); } }, []); useEffect(() => { void load(); }, [load]); const filtered = useMemo( () => items.filter( (row) => (!accountId || row.node_id === accountId) && row.name.toLowerCase().includes(q.trim().toLowerCase()), ), [items, q, accountId], ); /** 配置 Email Workers */ const runSetup = async (row: DomainRow) => { try { const res = await setupDomain(row.id); if (res.ok) message.success(`已为 ${row.name} 部署 Email Worker 并开启 Email Routing`); else message.error(res.error ?? "配置失败"); void load(); setEditing((cur) => (cur?.id === row.id ? { ...cur, status: res.ok ? "active" : "error" } : cur)); } catch (err) { message.error(err instanceof Error ? err.message : "配置失败"); } }; return (
} placeholder="搜索域名" style={{ width: 240 }} onChange={(e) => setQ(e.target.value)} />