'use client'; import Link from 'next/link'; import { useEffect, useState } from 'react'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { ArrowLeft, FileCode2, Loader2, RotateCcw, Save, Sparkles, } from 'lucide-react'; import { toast } from 'sonner'; import { useAuth } from '@/components/providers/auth-provider'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '@/components/ui/alert-dialog'; import { Button } from '@/components/ui/button'; import { EmptyStateWithBorder } from '@/components/layout/empty'; import { ErrorInline } from '@/components/layout/error'; import { LoadingStateWithBorder } from '@/components/layout/loading'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { HtmlEditorWorkspace } from '@/components/common/html-editor-workspace'; import { ORIGIN_ERROR_PAGE_HTML_MAX_BYTES, previewOriginErrorPageHTML, } from '@/lib/openflare/default-origin-error-page-html'; import { DEFAULT_ORIGIN_ERROR_PAGE_TEMPLATE_ID, getOriginErrorPageTemplate, ORIGIN_ERROR_PAGE_TEMPLATES, } from '@/lib/openflare/origin-error-page-templates'; import { OptionService } from '@/lib/services/openflare'; import { invalidateResponseQueries, KEY_HTML, mapOptionsToErrorFields, OPTIONS_QUERY_KEY, optionsToMap, } from '../../components/shared'; export default function ErrorPageEditPage() { const { user, loading: authLoading } = useAuth(); const queryClient = useQueryClient(); const [html, setHtml] = useState(''); const [restoreOpen, setRestoreOpen] = useState(false); const [templateId, setTemplateId] = useState( DEFAULT_ORIGIN_ERROR_PAGE_TEMPLATE_ID, ); const optionsQuery = useQuery({ queryKey: OPTIONS_QUERY_KEY, queryFn: () => OptionService.list(), enabled: !!user?.is_admin, }); useEffect(() => { if (!optionsQuery.data) return; setHtml(mapOptionsToErrorFields(optionsToMap(optionsQuery.data)).html); }, [optionsQuery.data]); const saveMutation = useMutation({ mutationFn: async () => { const bytes = new TextEncoder().encode(html).length; if (bytes > ORIGIN_ERROR_PAGE_HTML_MAX_BYTES) { throw new Error('HTML 大小超出限制(最大 256KB)'); } await OptionService.updateBatch([{ key: KEY_HTML, value: html }]); }, onSuccess: async () => { toast.success('源站错误页 HTML 已保存,请前往版本发布使配置生效'); await invalidateResponseQueries(queryClient); }, onError: (error) => { toast.error(error instanceof Error ? error.message : '保存失败'); }, }); const loadSelectedTemplate = () => { const tmpl = getOriginErrorPageTemplate(templateId); if (!tmpl) { toast.error('未找到所选模板'); return; } setHtml(tmpl.html); toast.success(`已加载模板「${tmpl.name}」到编辑器`); }; const restoreDefault = () => { setHtml(''); setRestoreOpen(false); toast.success('已清空为使用服务端内置默认模板(需保存后生效)'); }; if (authLoading) { return (