mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-09 00:56:37 +08:00
前端优化
This commit is contained in:
@@ -1,203 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import Link from "next/link"
|
|
||||||
import {useQuery} from "@tanstack/react-query"
|
|
||||||
import {Info} from "lucide-react"
|
|
||||||
import ReactMarkdown from "react-markdown"
|
|
||||||
import remarkGfm from "remark-gfm"
|
|
||||||
|
|
||||||
import {EmptyStateWithBorder} from "@/components/layout/empty"
|
|
||||||
import {ErrorInline} from "@/components/layout/error"
|
|
||||||
import {Badge} from "@/components/ui/badge"
|
|
||||||
import {Card, CardContent, CardDescription, CardHeader, CardTitle} from "@/components/ui/card"
|
|
||||||
import {Skeleton} from "@/components/ui/skeleton"
|
|
||||||
import {AboutService, StatusService} from "@/lib/services/openflare"
|
|
||||||
import {formatDateTime} from "@/lib/utils"
|
|
||||||
|
|
||||||
const aboutQueryKey = ["openflare", "about"] as const
|
|
||||||
const publicStatusQueryKey = ["openflare", "public-status"] as const
|
|
||||||
|
|
||||||
function AboutPageSkeleton() {
|
|
||||||
return (
|
|
||||||
<div className="py-6 px-1 space-y-6">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Skeleton className="size-5 rounded-md" />
|
|
||||||
<Skeleton className="h-8 w-48" />
|
|
||||||
</div>
|
|
||||||
<Card className="border-dashed shadow-none">
|
|
||||||
<CardHeader>
|
|
||||||
<Skeleton className="h-5 w-40" />
|
|
||||||
<Skeleton className="h-4 w-72" />
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="grid gap-3 md:grid-cols-3">
|
|
||||||
<Skeleton className="h-20 w-full" />
|
|
||||||
<Skeleton className="h-20 w-full" />
|
|
||||||
<Skeleton className="h-20 w-full" />
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
<Card className="border-dashed shadow-none">
|
|
||||||
<CardHeader>
|
|
||||||
<Skeleton className="h-5 w-32" />
|
|
||||||
<Skeleton className="h-4 w-64" />
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="space-y-3">
|
|
||||||
<Skeleton className="h-4 w-full" />
|
|
||||||
<Skeleton className="h-4 w-full" />
|
|
||||||
<Skeleton className="h-4 w-3/4" />
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function AboutPage() {
|
|
||||||
const aboutQuery = useQuery({
|
|
||||||
queryKey: aboutQueryKey,
|
|
||||||
queryFn: () => AboutService.getAboutContent(),
|
|
||||||
})
|
|
||||||
|
|
||||||
const statusQuery = useQuery({
|
|
||||||
queryKey: publicStatusQueryKey,
|
|
||||||
queryFn: () => StatusService.getPublicStatus(),
|
|
||||||
})
|
|
||||||
|
|
||||||
const isLoading = aboutQuery.isLoading || statusQuery.isLoading
|
|
||||||
|
|
||||||
if (isLoading) {
|
|
||||||
return <AboutPageSkeleton />
|
|
||||||
}
|
|
||||||
|
|
||||||
if (aboutQuery.isError) {
|
|
||||||
return (
|
|
||||||
<div className="py-6 px-1">
|
|
||||||
<ErrorInline
|
|
||||||
message={
|
|
||||||
aboutQuery.error instanceof Error
|
|
||||||
? aboutQuery.error.message
|
|
||||||
: "关于内容加载失败"
|
|
||||||
}
|
|
||||||
onRetry={() => void aboutQuery.refetch()}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (statusQuery.isError) {
|
|
||||||
return (
|
|
||||||
<div className="py-6 px-1">
|
|
||||||
<ErrorInline
|
|
||||||
message={
|
|
||||||
statusQuery.error instanceof Error
|
|
||||||
? statusQuery.error.message
|
|
||||||
: "系统状态加载失败"
|
|
||||||
}
|
|
||||||
onRetry={() => void statusQuery.refetch()}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const aboutContent = aboutQuery.data?.trim() ?? ""
|
|
||||||
const status = statusQuery.data
|
|
||||||
|
|
||||||
if (!status) {
|
|
||||||
return (
|
|
||||||
<div className="py-6 px-1">
|
|
||||||
<EmptyStateWithBorder
|
|
||||||
title="暂无系统信息"
|
|
||||||
description="未能获取 OpenFlare 的公开状态信息。"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="py-6 px-1 space-y-6">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Info className="size-5 text-primary" />
|
|
||||||
<div>
|
|
||||||
<h1 className="text-2xl font-semibold tracking-tight">关于 OpenFlare</h1>
|
|
||||||
<p className="text-sm text-muted-foreground">
|
|
||||||
公开展示当前系统简介、版本信息与项目入口。
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Card className="border-dashed shadow-none">
|
|
||||||
<CardHeader className="flex flex-row items-start justify-between gap-4">
|
|
||||||
<div>
|
|
||||||
<CardTitle className="text-base">系统信息</CardTitle>
|
|
||||||
<CardDescription>当前运行实例的公开状态与项目链接。</CardDescription>
|
|
||||||
</div>
|
|
||||||
<Badge variant="secondary">{status.version || "dev"}</Badge>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="grid gap-3 md:grid-cols-3">
|
|
||||||
<div className="rounded-lg border border-dashed px-4 py-3">
|
|
||||||
<p className="text-[10px] uppercase tracking-wider text-muted-foreground">
|
|
||||||
系统名称
|
|
||||||
</p>
|
|
||||||
<p className="mt-2 text-sm font-medium">
|
|
||||||
{status.system_name || "OpenFlare"}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="rounded-lg border border-dashed px-4 py-3">
|
|
||||||
<p className="text-[10px] uppercase tracking-wider text-muted-foreground">
|
|
||||||
Server 启动时间
|
|
||||||
</p>
|
|
||||||
<p className="mt-2 text-sm font-medium">
|
|
||||||
{formatDateTime(new Date(status.start_time * 1000))}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="rounded-lg border border-dashed px-4 py-3">
|
|
||||||
<p className="text-[10px] uppercase tracking-wider text-muted-foreground">
|
|
||||||
项目仓库
|
|
||||||
</p>
|
|
||||||
<a
|
|
||||||
href="https://github.com/Rain-kl/OpenFlare"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
className="mt-2 block text-sm font-medium text-primary transition hover:opacity-80"
|
|
||||||
>
|
|
||||||
github.com/Rain-kl/OpenFlare
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
{aboutContent ? (
|
|
||||||
<Card className="border-dashed shadow-none">
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle className="text-base">项目介绍</CardTitle>
|
|
||||||
<CardDescription>
|
|
||||||
以下内容由系统设置中的「关于内容」维护。
|
|
||||||
</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<div
|
|
||||||
className={[
|
|
||||||
"prose prose-sm max-w-none dark:prose-invert",
|
|
||||||
"prose-p:text-foreground prose-p:leading-7",
|
|
||||||
"prose-li:text-foreground",
|
|
||||||
"prose-strong:text-foreground",
|
|
||||||
"prose-a:text-primary",
|
|
||||||
].join(" ")}
|
|
||||||
>
|
|
||||||
<ReactMarkdown remarkPlugins={[remarkGfm]}>{aboutContent}</ReactMarkdown>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
) : (
|
|
||||||
<EmptyStateWithBorder
|
|
||||||
title="尚未配置关于内容"
|
|
||||||
description="可在设置页的「其他设置」标签中编写 Markdown / HTML 内容,这里会自动同步展示。"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="flex flex-wrap gap-3 text-sm">
|
|
||||||
<Link href="/" className="text-primary transition hover:opacity-80">
|
|
||||||
返回总览
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -105,6 +105,9 @@ export function AdminSettingsPageClient() {
|
|||||||
</div>
|
</div>
|
||||||
<Tabs defaultValue="security" className="w-full">
|
<Tabs defaultValue="security" className="w-full">
|
||||||
<TabsList variant="line" className="w-fit inline-flex gap-8 mb-6">
|
<TabsList variant="line" className="w-fit inline-flex gap-8 mb-6">
|
||||||
|
<TabsTrigger value="openflare-ops" className="px-0 pb-2 text-xs font-semibold">
|
||||||
|
OpenFlare
|
||||||
|
</TabsTrigger>
|
||||||
<TabsTrigger value="security" className="px-0 pb-2 text-xs font-semibold">
|
<TabsTrigger value="security" className="px-0 pb-2 text-xs font-semibold">
|
||||||
安全设置
|
安全设置
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
@@ -123,9 +126,6 @@ export function AdminSettingsPageClient() {
|
|||||||
<TabsTrigger value="info" className="px-0 pb-2 text-xs font-semibold">
|
<TabsTrigger value="info" className="px-0 pb-2 text-xs font-semibold">
|
||||||
系统信息
|
系统信息
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
<TabsTrigger value="openflare-ops" className="px-0 pb-2 text-xs font-semibold">
|
|
||||||
OpenFlare 运维
|
|
||||||
</TabsTrigger>
|
|
||||||
</TabsList>
|
</TabsList>
|
||||||
|
|
||||||
<TabsContent value="security" className="focus-visible:outline-none">
|
<TabsContent value="security" className="focus-visible:outline-none">
|
||||||
@@ -152,4 +152,4 @@ export function AdminSettingsPageClient() {
|
|||||||
</Tabs>
|
</Tabs>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
import type {LucideIcon} from 'lucide-react';
|
import type {LucideIcon} from 'lucide-react';
|
||||||
import {
|
import {
|
||||||
ClipboardList,
|
|
||||||
FileText,
|
FileText,
|
||||||
Gauge,
|
Gauge,
|
||||||
GitBranch,
|
GitBranch,
|
||||||
Globe,
|
Globe,
|
||||||
Info,
|
|
||||||
LayoutDashboard,
|
LayoutDashboard,
|
||||||
MapPin,
|
MapPin,
|
||||||
Route,
|
Route,
|
||||||
@@ -24,9 +22,9 @@ export interface OpenFlareNavItem {
|
|||||||
|
|
||||||
/** OpenFlare 业务控制台侧栏导航(子页面通过父级入口或页内链接访问) */
|
/** OpenFlare 业务控制台侧栏导航(子页面通过父级入口或页内链接访问) */
|
||||||
export const openflareNavItems: OpenFlareNavItem[] = [
|
export const openflareNavItems: OpenFlareNavItem[] = [
|
||||||
{title: '总览', url: '/', icon: LayoutDashboard},
|
{title: '数据看板', url: '/', icon: LayoutDashboard},
|
||||||
{title: '节点', url: '/nodes', icon: Server, childUrls: ['/nodes/detail']},
|
{title: '节点管理', url: '/nodes', icon: Server, childUrls: ['/nodes/detail']},
|
||||||
{title: '规则', url: '/proxy-routes', icon: Route, childUrls: ['/proxy-routes/detail']},
|
{title: '规则管理', url: '/proxy-routes', icon: Route, childUrls: ['/proxy-routes/detail']},
|
||||||
{title: 'Pages', url: '/pages', icon: FileText, childUrls: ['/pages/detail']},
|
{title: 'Pages', url: '/pages', icon: FileText, childUrls: ['/pages/detail']},
|
||||||
{
|
{
|
||||||
title: '网站',
|
title: '网站',
|
||||||
@@ -40,11 +38,9 @@ export const openflareNavItems: OpenFlareNavItem[] = [
|
|||||||
},
|
},
|
||||||
{title: 'WAF', url: '/waf', icon: Shield, childUrls: ['/waf/ip-groups']},
|
{title: 'WAF', url: '/waf', icon: Shield, childUrls: ['/waf/ip-groups']},
|
||||||
{title: '源站', url: '/origins', icon: MapPin, childUrls: ['/origins/detail']},
|
{title: '源站', url: '/origins', icon: MapPin, childUrls: ['/origins/detail']},
|
||||||
{title: '发布', url: '/config-versions', icon: GitBranch},
|
{title: '版本发布', url: '/config-versions', icon: GitBranch},
|
||||||
{title: '访问日志', url: '/access-logs', icon: ScrollText},
|
{title: '访问日志', url: '/access-logs', icon: ScrollText},
|
||||||
{title: '应用日志', url: '/apply-logs', icon: ClipboardList},
|
{title: '性能调优', url: '/performance', icon: Gauge},
|
||||||
{title: '性能', url: '/performance', icon: Gauge},
|
|
||||||
{title: '关于', url: '/about', icon: Info},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
/** 网站模块页内二级导航 */
|
/** 网站模块页内二级导航 */
|
||||||
@@ -68,4 +64,4 @@ export function isOpenFlareConsoleRoute(pathname: string): boolean {
|
|||||||
}
|
}
|
||||||
return pathname === item.url || pathname.startsWith(`${item.url}/`);
|
return pathname === item.url || pathname.startsWith(`${item.url}/`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user