mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-29 05:56:38 +08:00
优化仪表板页面,移除冗余组件,简化代码结构
This commit is contained in:
@@ -1,13 +1,8 @@
|
||||
import { DashboardOverview } from '@/features/dashboard/components/dashboard-overview';
|
||||
import { PageHeader } from '@/components/layout/page-header';
|
||||
|
||||
export default function DashboardPage() {
|
||||
return (
|
||||
<div className='space-y-6'>
|
||||
<PageHeader
|
||||
title='ATSFlare 管理端'
|
||||
description='集中展示系统核心数据概览,快速导航至各功能模块进行管理和配置。'
|
||||
/>
|
||||
<DashboardOverview />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -20,8 +20,8 @@ interface RootLayoutProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export default function RootLayout({ children }: RootLayoutProps) {
|
||||
return (
|
||||
export default function RootLayout({ children }: RootLayoutProps) {
|
||||
return (
|
||||
<html lang='zh-CN' suppressHydrationWarning>
|
||||
<body>
|
||||
<Script id='theme-init' strategy='beforeInteractive'>
|
||||
@@ -30,5 +30,5 @@ export default function RootLayout({ children }: RootLayoutProps) {
|
||||
<AppProviders>{children}</AppProviders>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -235,7 +235,6 @@ export function DashboardOverview() {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<WorldStage
|
||||
generatedAt={overview.generated_at}
|
||||
summary={overview.summary}
|
||||
traffic={overview.traffic}
|
||||
capacity={overview.capacity}
|
||||
|
||||
@@ -13,7 +13,6 @@ import type {
|
||||
DistributionItem,
|
||||
} from '@/features/dashboard/types';
|
||||
import { cn } from '@/lib/utils/cn';
|
||||
import { formatDateTime } from '@/lib/utils/date';
|
||||
|
||||
type Tone = 'healthy' | 'warning' | 'danger';
|
||||
|
||||
@@ -157,14 +156,12 @@ function CountrySignal({
|
||||
}
|
||||
|
||||
export function WorldStage({
|
||||
generatedAt,
|
||||
summary,
|
||||
traffic,
|
||||
capacity,
|
||||
nodes,
|
||||
sourceCountries,
|
||||
}: {
|
||||
generatedAt: string;
|
||||
summary: DashboardSummary;
|
||||
traffic: DashboardTraffic;
|
||||
capacity: DashboardCapacity;
|
||||
@@ -236,43 +233,14 @@ export function WorldStage({
|
||||
)}
|
||||
>
|
||||
<div className="flex flex-col gap-4 xl:flex-row xl:items-end xl:justify-between">
|
||||
<div className="space-y-2">
|
||||
<p
|
||||
<p
|
||||
className={cn(
|
||||
'text-[11px] tracking-[0.34em] uppercase',
|
||||
isDark ? 'text-sky-200/80' : 'text-sky-700/80',
|
||||
'text-[11px] tracking-[0.34em] uppercase',
|
||||
isDark ? 'text-sky-200/80' : 'text-sky-700/80',
|
||||
)}
|
||||
>
|
||||
Global Stage
|
||||
</p>
|
||||
<h2
|
||||
className={cn(
|
||||
'text-2xl font-semibold',
|
||||
isDark ? 'text-white' : 'text-slate-950',
|
||||
)}
|
||||
>
|
||||
全球态势板
|
||||
</h2>
|
||||
<p
|
||||
className={cn(
|
||||
'max-w-3xl text-sm leading-6',
|
||||
isDark ? 'text-slate-300' : 'text-slate-600',
|
||||
)}
|
||||
>
|
||||
在同一视图汇总节点在线率、运行状态、资源负载与全球流量来源,
|
||||
让首屏可以直接承担值守与研判入口。
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
'rounded-full border px-4 py-2 text-sm backdrop-blur',
|
||||
isDark
|
||||
? 'border-white/10 bg-white/6 text-slate-200'
|
||||
: 'border-slate-200/80 bg-white/80 text-slate-700',
|
||||
)}
|
||||
>
|
||||
数据生成于 {formatDateTime(generatedAt)}
|
||||
</div>
|
||||
Global Stage
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -320,7 +320,7 @@ export function NodeDetailPage({ nodeId }: { nodeId: string }) {
|
||||
const saveMutation = useMutation({
|
||||
mutationFn: async (payload: Parameters<typeof updateNode>[1]) =>
|
||||
updateNode(Number(nodeId), payload),
|
||||
onSuccess: async (updatedNode) => {
|
||||
onSuccess: async () => {
|
||||
setFeedback({ tone: 'success', message: '节点已更新。' });
|
||||
setIsEditorOpen(false);
|
||||
await queryClient.invalidateQueries({ queryKey: nodesQueryKey });
|
||||
|
||||
@@ -161,13 +161,6 @@ function isCacheLevelsValue(value: string) {
|
||||
return /^\d{1,2}(?::\d{1,2}){0,2}$/.test(value.trim());
|
||||
}
|
||||
|
||||
function extractMainConfigLines(content: string, matcher: RegExp) {
|
||||
return content
|
||||
.split('\n')
|
||||
.map((line) => line.trim())
|
||||
.filter((line) => line && matcher.test(line));
|
||||
}
|
||||
|
||||
export function PerformancePage() {
|
||||
const queryClient = useQueryClient();
|
||||
const {user} = useAuth();
|
||||
@@ -595,15 +588,6 @@ export function PerformancePage() {
|
||||
);
|
||||
}
|
||||
|
||||
const cachePreviewLines = extractMainConfigLines(
|
||||
preview.main_config,
|
||||
/proxy_cache_path|proxy_cache_key|proxy_cache_lock(?:_timeout)?|proxy_cache_use_stale/,
|
||||
);
|
||||
const gzipPreviewLines = extractMainConfigLines(
|
||||
preview.main_config,
|
||||
/gzip(?:_| )/,
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<PageHeader
|
||||
|
||||
@@ -196,48 +196,54 @@ export function TlsCertificatesPage() {
|
||||
</AppCard>
|
||||
</div>
|
||||
|
||||
<CertificateImportModal
|
||||
isOpen={isImportOpen}
|
||||
onClose={() => setIsImportOpen(false)}
|
||||
onImported={(certificate) => {
|
||||
setFeedback({
|
||||
tone: 'success',
|
||||
message: `证书 ${certificate.name} 已导入。`,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
{isImportOpen ? (
|
||||
<CertificateImportModal
|
||||
isOpen={isImportOpen}
|
||||
onClose={() => setIsImportOpen(false)}
|
||||
onImported={(certificate) => {
|
||||
setFeedback({
|
||||
tone: 'success',
|
||||
message: `证书 ${certificate.name} 已导入。`,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<CertificateDetailModal
|
||||
certificateId={selectedCertificateId}
|
||||
isOpen={isDetailOpen}
|
||||
onClose={() => setIsDetailOpen(false)}
|
||||
onEdit={() => {
|
||||
setIsDetailOpen(false);
|
||||
setIsEditorOpen(true);
|
||||
}}
|
||||
onDelete={() => {
|
||||
const certificate = certificates.find(
|
||||
(item) => item.id === selectedCertificateId,
|
||||
);
|
||||
if (certificate) {
|
||||
{isDetailOpen ? (
|
||||
<CertificateDetailModal
|
||||
certificateId={selectedCertificateId}
|
||||
isOpen={isDetailOpen}
|
||||
onClose={() => setIsDetailOpen(false)}
|
||||
onEdit={() => {
|
||||
setIsDetailOpen(false);
|
||||
handleDeleteCertificate(certificate);
|
||||
}
|
||||
}}
|
||||
deleting={deleteCertificateMutation.isPending}
|
||||
/>
|
||||
setIsEditorOpen(true);
|
||||
}}
|
||||
onDelete={() => {
|
||||
const certificate = certificates.find(
|
||||
(item) => item.id === selectedCertificateId,
|
||||
);
|
||||
if (certificate) {
|
||||
setIsDetailOpen(false);
|
||||
handleDeleteCertificate(certificate);
|
||||
}
|
||||
}}
|
||||
deleting={deleteCertificateMutation.isPending}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<CertificateEditorModal
|
||||
certificateId={selectedCertificateId}
|
||||
isOpen={isEditorOpen}
|
||||
onClose={() => setIsEditorOpen(false)}
|
||||
onSaved={(certificate) => {
|
||||
setFeedback({
|
||||
tone: 'success',
|
||||
message: `证书 ${certificate.name} 已更新。`,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
{isEditorOpen ? (
|
||||
<CertificateEditorModal
|
||||
certificateId={selectedCertificateId}
|
||||
isOpen={isEditorOpen}
|
||||
onClose={() => setIsEditorOpen(false)}
|
||||
onSaved={(certificate) => {
|
||||
setFeedback({
|
||||
tone: 'success',
|
||||
message: `证书 ${certificate.name} 已更新。`,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -434,62 +434,70 @@ export function WebsiteDetailPage({websiteId}: { websiteId: string }) {
|
||||
</AppCard>
|
||||
</div>
|
||||
|
||||
<WebsiteEditorModal
|
||||
isOpen={isEditorOpen}
|
||||
onClose={() => {
|
||||
setIsEditorOpen(false);
|
||||
setPreferredCertificateId(null);
|
||||
}}
|
||||
initialDomain={website}
|
||||
certificates={certificates}
|
||||
certificatesLoading={certificatesQuery.isLoading}
|
||||
preferredCertificateId={preferredCertificateId}
|
||||
onRequestImportCertificate={() => setIsCertificateImportOpen(true)}
|
||||
onSaved={() => {
|
||||
setPreferredCertificateId(null);
|
||||
setFeedback({tone: 'success', message: '网站已更新。'});
|
||||
}}
|
||||
/>
|
||||
{isEditorOpen ? (
|
||||
<WebsiteEditorModal
|
||||
isOpen={isEditorOpen}
|
||||
onClose={() => {
|
||||
setIsEditorOpen(false);
|
||||
setPreferredCertificateId(null);
|
||||
}}
|
||||
initialDomain={website}
|
||||
certificates={certificates}
|
||||
certificatesLoading={certificatesQuery.isLoading}
|
||||
preferredCertificateId={preferredCertificateId}
|
||||
onRequestImportCertificate={() => setIsCertificateImportOpen(true)}
|
||||
onSaved={() => {
|
||||
setPreferredCertificateId(null);
|
||||
setFeedback({tone: 'success', message: '网站已更新。'});
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<CertificateImportModal
|
||||
isOpen={isCertificateImportOpen}
|
||||
onClose={() => setIsCertificateImportOpen(false)}
|
||||
onImported={(importedCertificate) => {
|
||||
setPreferredCertificateId(importedCertificate.id);
|
||||
setFeedback({
|
||||
tone: 'success',
|
||||
message: `证书 ${importedCertificate.name} 已导入,可在编辑网站时直接应用。`,
|
||||
});
|
||||
setIsEditorOpen(true);
|
||||
}}
|
||||
/>
|
||||
{isCertificateImportOpen ? (
|
||||
<CertificateImportModal
|
||||
isOpen={isCertificateImportOpen}
|
||||
onClose={() => setIsCertificateImportOpen(false)}
|
||||
onImported={(importedCertificate) => {
|
||||
setPreferredCertificateId(importedCertificate.id);
|
||||
setFeedback({
|
||||
tone: 'success',
|
||||
message: `证书 ${importedCertificate.name} 已导入,可在编辑网站时直接应用。`,
|
||||
});
|
||||
setIsEditorOpen(true);
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<CertificateDetailModal
|
||||
certificateId={certificate?.id ?? null}
|
||||
isOpen={isCertificateDetailOpen}
|
||||
onClose={() => setIsCertificateDetailOpen(false)}
|
||||
onEdit={() => {
|
||||
setIsCertificateDetailOpen(false);
|
||||
setIsCertificateEditorOpen(true);
|
||||
}}
|
||||
onDelete={() => {
|
||||
setIsCertificateDetailOpen(false);
|
||||
handleDeleteCertificate();
|
||||
}}
|
||||
deleting={deleteCertificateMutation.isPending}
|
||||
/>
|
||||
{isCertificateDetailOpen ? (
|
||||
<CertificateDetailModal
|
||||
certificateId={certificate?.id ?? null}
|
||||
isOpen={isCertificateDetailOpen}
|
||||
onClose={() => setIsCertificateDetailOpen(false)}
|
||||
onEdit={() => {
|
||||
setIsCertificateDetailOpen(false);
|
||||
setIsCertificateEditorOpen(true);
|
||||
}}
|
||||
onDelete={() => {
|
||||
setIsCertificateDetailOpen(false);
|
||||
handleDeleteCertificate();
|
||||
}}
|
||||
deleting={deleteCertificateMutation.isPending}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<CertificateEditorModal
|
||||
certificateId={certificate?.id ?? null}
|
||||
isOpen={isCertificateEditorOpen}
|
||||
onClose={() => setIsCertificateEditorOpen(false)}
|
||||
onSaved={(updatedCertificate) => {
|
||||
setFeedback({
|
||||
tone: 'success',
|
||||
message: `证书 ${updatedCertificate.name} 已更新。`,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
{isCertificateEditorOpen ? (
|
||||
<CertificateEditorModal
|
||||
certificateId={certificate?.id ?? null}
|
||||
isOpen={isCertificateEditorOpen}
|
||||
onClose={() => setIsCertificateEditorOpen(false)}
|
||||
onSaved={(updatedCertificate) => {
|
||||
setFeedback({
|
||||
tone: 'success',
|
||||
message: `证书 ${updatedCertificate.name} 已更新。`,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ export function WebsiteEditorModal({
|
||||
? toManagedDomainFormValues(initialDomain)
|
||||
: defaultManagedDomainValues,
|
||||
);
|
||||
}, [form, initialDomain, isOpen]);
|
||||
}, [form, initialDomain, isOpen, saveMutation]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen || !preferredCertificateId) {
|
||||
|
||||
@@ -28,7 +28,6 @@ import {
|
||||
DangerButton,
|
||||
PrimaryButton,
|
||||
} from '@/features/shared/components/resource-primitives';
|
||||
import { formatDateTime } from '@/lib/utils/date';
|
||||
|
||||
type FeedbackState = {
|
||||
tone: 'info' | 'success' | 'danger';
|
||||
@@ -205,36 +204,40 @@ export function WebsitesPage() {
|
||||
</AppCard>
|
||||
</div>
|
||||
|
||||
<WebsiteEditorModal
|
||||
isOpen={isWebsiteModalOpen}
|
||||
onClose={() => {
|
||||
setIsWebsiteModalOpen(false);
|
||||
setPreferredCertificateId(null);
|
||||
}}
|
||||
certificates={certificates}
|
||||
certificatesLoading={certificatesQuery.isLoading}
|
||||
preferredCertificateId={preferredCertificateId}
|
||||
onRequestImportCertificate={() => setIsCertificateImportOpen(true)}
|
||||
onSaved={(_, mode) => {
|
||||
setPreferredCertificateId(null);
|
||||
setFeedback({
|
||||
tone: 'success',
|
||||
message: mode === 'create' ? '网站已创建。' : '网站已更新。',
|
||||
});
|
||||
}}
|
||||
/>
|
||||
{isWebsiteModalOpen ? (
|
||||
<WebsiteEditorModal
|
||||
isOpen={isWebsiteModalOpen}
|
||||
onClose={() => {
|
||||
setIsWebsiteModalOpen(false);
|
||||
setPreferredCertificateId(null);
|
||||
}}
|
||||
certificates={certificates}
|
||||
certificatesLoading={certificatesQuery.isLoading}
|
||||
preferredCertificateId={preferredCertificateId}
|
||||
onRequestImportCertificate={() => setIsCertificateImportOpen(true)}
|
||||
onSaved={(_, mode) => {
|
||||
setPreferredCertificateId(null);
|
||||
setFeedback({
|
||||
tone: 'success',
|
||||
message: mode === 'create' ? '网站已创建。' : '网站已更新。',
|
||||
});
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<CertificateImportModal
|
||||
isOpen={isCertificateImportOpen}
|
||||
onClose={() => setIsCertificateImportOpen(false)}
|
||||
onImported={(certificate) => {
|
||||
setPreferredCertificateId(certificate.id);
|
||||
setFeedback({
|
||||
tone: 'success',
|
||||
message: `证书 ${certificate.name} 已导入,可直接用于当前网站。`,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
{isCertificateImportOpen ? (
|
||||
<CertificateImportModal
|
||||
isOpen={isCertificateImportOpen}
|
||||
onClose={() => setIsCertificateImportOpen(false)}
|
||||
onImported={(certificate) => {
|
||||
setPreferredCertificateId(certificate.id);
|
||||
setFeedback({
|
||||
tone: 'success',
|
||||
message: `证书 ${certificate.name} 已导入,可直接用于当前网站。`,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import type {
|
||||
ManagedDomainItem,
|
||||
ManagedDomainMutationPayload,
|
||||
} from '@/features/managed-domains/types';
|
||||
import type { ProxyRouteItem } from '@/features/proxy-routes/types';
|
||||
import type {
|
||||
TlsCertificateFileImportPayload,
|
||||
TlsCertificateItem,
|
||||
|
||||
Reference in New Issue
Block a user