'use client'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { useState } from 'react'; import { EmptyState } from '@/components/feedback/empty-state'; import { ErrorState } from '@/components/feedback/error-state'; import { InlineMessage } from '@/components/feedback/inline-message'; import { LoadingState } from '@/components/feedback/loading-state'; import { PageHeader } from '@/components/layout/page-header'; import { AppCard } from '@/components/ui/app-card'; import { StatusBadge } from '@/components/ui/status-badge'; import { activateConfigVersion, getConfigVersionDiff, getConfigVersionPreview, getConfigVersions, publishConfigVersion, } from '@/features/config-versions/api/config-versions'; import type { ConfigDiffResult, ConfigPreviewResult, ConfigVersionItem, SupportFile, } from '@/features/config-versions/types'; import { CodeBlock, PrimaryButton, SecondaryButton, } from '@/features/shared/components/resource-primitives'; import { formatDateTime } from '@/lib/utils/date'; const versionsQueryKey = ['config-versions']; type FeedbackState = { tone: 'info' | 'success' | 'danger'; message: string; }; function getErrorMessage(error: unknown) { return error instanceof Error ? error.message : '请求失败,请稍后重试。'; } function truncateChecksum(checksum: string) { if (!checksum) { return '—'; } return checksum.length > 16 ? `${checksum.slice(0, 16)}...` : checksum; } function DiffList({ title, items }: { title: string; items: string[] }) { return (

{title}

0 ? 'info' : 'warning'} />
{items.length > 0 ? (
{items.map((item) => ( {item} ))}
) : (

当前无相关变更。

)}
); } function SupportFilesList({ files }: { files: SupportFile[] }) { if (files.length === 0) { return

当前发布不需要额外支持文件。

; } return (
{files.map((file) => (
{file.path} {file.content}
))}
); } function VersionDetailCard({ version, }: { version: ConfigVersionItem; }) { return ( } >

Checksum

{version.checksum}

创建人

{version.created_by || '系统'}

创建时间

{formatDateTime(version.created_at)}

快照 JSON

{version.snapshot_json}

渲染结果

{version.rendered_config}
); } function PublishPreviewCard({ preview, diff, isPublishing, onConfirm, onCancel, }: { preview: ConfigPreviewResult; diff: ConfigDiffResult; isPublishing: boolean; onConfirm: () => void; onCancel: () => void; }) { return ( } >

当前激活版本

{diff.active_version || '无'}

新增域名

{diff.added_domains.length}

删除域名

{diff.removed_domains.length}

修改域名

{diff.modified_domains.length}

渲染结果

Checksum:{preview.checksum}

{preview.rendered_config}

支持文件

{isPublishing ? '发布中...' : '确认发布'} 取消预览
); } export function ConfigVersionsPage() { const queryClient = useQueryClient(); const [feedback, setFeedback] = useState(null); const [selectedVersion, setSelectedVersion] = useState(null); const [publishPreview, setPublishPreview] = useState<{ preview: ConfigPreviewResult; diff: ConfigDiffResult; } | null>(null); const [isPreviewLoading, setIsPreviewLoading] = useState(false); const versionsQuery = useQuery({ queryKey: versionsQueryKey, queryFn: getConfigVersions, }); const publishMutation = useMutation({ mutationFn: publishConfigVersion, onSuccess: async (version) => { setFeedback({ tone: 'success', message: `发布成功,版本 ${version.version}` }); setPublishPreview(null); setSelectedVersion(version); await queryClient.invalidateQueries({ queryKey: versionsQueryKey }); }, onError: (error) => { setFeedback({ tone: 'danger', message: getErrorMessage(error) }); }, }); const activateMutation = useMutation({ mutationFn: activateConfigVersion, onSuccess: async (version) => { setFeedback({ tone: 'success', message: `已激活版本 ${version.version}` }); setSelectedVersion(version); await queryClient.invalidateQueries({ queryKey: versionsQueryKey }); }, onError: (error) => { setFeedback({ tone: 'danger', message: getErrorMessage(error) }); }, }); const handleOpenPublishPreview = async () => { setFeedback(null); setIsPreviewLoading(true); try { const [preview, diff] = await Promise.all([getConfigVersionPreview(), getConfigVersionDiff()]); setPublishPreview({ preview, diff }); } catch (error) { setFeedback({ tone: 'danger', message: getErrorMessage(error) }); } finally { setIsPreviewLoading(false); } }; const handleActivate = (version: ConfigVersionItem) => { if (version.is_active) { return; } if (!window.confirm(`确认激活版本 ${version.version} 吗?`)) { return; } setFeedback(null); activateMutation.mutate(version.id); }; const versions = versionsQuery.data || []; return (
{isPreviewLoading ? '加载预览中...' : '预览并发布'} } /> {feedback ? : null}
{publishPreview ? ( publishMutation.mutate()} onCancel={() => setPublishPreview(null)} /> ) : (

1. 使用“预览并发布”查看新增、删除和修改的域名清单。

2. 确认渲染出的 Nginx 配置与支持文件内容符合预期。

3. 若需要回滚,可在下方历史版本列表中重新激活旧版本。

)}
{versionsQuery.isLoading ? ( ) : versionsQuery.isError ? ( ) : versions.length === 0 ? ( ) : (
{versions.map((version) => ( ))}
版本号 状态 创建人 Checksum 创建时间 操作
{version.version} {version.created_by || '系统'} {truncateChecksum(version.checksum)} {formatDateTime(version.created_at)}
setSelectedVersion(version)} className='px-3 py-2 text-xs' > 查看快照 {!version.is_active ? ( handleActivate(version)} disabled={activateMutation.isPending} className='px-3 py-2 text-xs' > 重新激活 ) : null}
)}
{selectedVersion ? : null}
); }