migrate frontend

This commit is contained in:
ryan
2026-06-18 18:34:20 +08:00
parent 75140fc3bb
commit 48421c12de
113 changed files with 116 additions and 152 deletions
@@ -194,7 +194,7 @@ export default function AboutPage() {
)}
<div className="flex flex-wrap gap-3 text-sm">
<Link href="/openflare" className="text-primary transition hover:opacity-80">
<Link href="/" className="text-primary transition hover:opacity-80">
返回总览
</Link>
</div>
@@ -28,7 +28,7 @@ import {ErrorInline} from "@/components/layout/error"
import {LoadingStateWithBorder} from "@/components/layout/loading"
import type {DatabaseCleanupTarget} from "@/lib/services/openflare"
import {NodeService, OptionService, StatusService, UpdateService, UptimeKumaService,} from "@/lib/services/openflare"
import {VersionUpgradeDialog} from "@/app/(main)/openflare/components/version-upgrade-dialog"
import {VersionUpgradeDialog} from "@/app/(main)/components/version-upgrade-dialog"
import {openflareLatestReleaseQueryKey, openflarePublicStatusQueryKey,} from "@/lib/hooks/use-openflare-server-upgrade"
import {
@@ -364,7 +364,7 @@ export function OpenFlareOpsSettings() {
</div>
<div className="flex flex-wrap gap-2">
<Button variant="outline" size="sm" asChild>
<Link href="/openflare/nodes">
<Link href="/nodes">
<ExternalLink className="size-3.5 mr-1" />
节点管理
</Link>
@@ -151,7 +151,7 @@ export function ApplyLogsPageClient() {
刷新
</Button>
<Button variant="outline" size="sm" asChild>
<Link href="/openflare/nodes">返回节点</Link>
<Link href="/nodes">返回节点</Link>
</Button>
</div>
</div>
@@ -47,7 +47,7 @@ export function NodeHealthTable({ nodes }: { nodes: DashboardNodeHealth[] }) {
</CardDescription>
</div>
<Button variant="outline" size="sm" className="h-8 text-xs" asChild>
<Link href="/openflare/nodes">进入节点页</Link>
<Link href="/nodes">进入节点页</Link>
</Button>
</CardHeader>
<CardContent>
@@ -117,7 +117,7 @@ export function NodeHealthTable({ nodes }: { nodes: DashboardNodeHealth[] }) {
</TableCell>
<TableCell className="py-3 text-right">
<Button variant="ghost" size="sm" className="h-8 text-xs" asChild>
<Link href={`/openflare/nodes/detail?id=${node.id}`}>详情</Link>
<Link href={`/nodes/detail?id=${node.id}`}>详情</Link>
</Button>
</TableCell>
</TableRow>
@@ -16,7 +16,7 @@ import {Tabs, TabsContent, TabsList, TabsTrigger} from '@/components/ui/tabs';
import {useOpenFlareServerUpgrade} from '@/lib/hooks/use-openflare-server-upgrade';
import type {LatestReleaseInfo, ReleaseChannel} from '@/lib/services/openflare';
import {formatDateTime} from '@/lib/utils';
import {formatRelativeTime} from '@/app/(main)/openflare/nodes/components/node-utils';
import {formatRelativeTime} from '@/app/(main)/nodes/components/node-utils';
import {UpgradeLogPanel} from './upgrade-log-panel';
+1 -1
View File
@@ -1,5 +1,5 @@
import {redirect} from "next/navigation"
export default function HomePage() {
redirect("/openflare")
redirect("/")
}
@@ -109,7 +109,7 @@ export function EdgeNodeDetail({ node }: { node: NodeItem }) {
onSuccess: async () => {
toast.success('节点已删除');
await queryClient.invalidateQueries({ queryKey: nodesQueryKey });
router.push('/openflare/nodes');
router.push('/nodes');
},
onError: (error) => toast.error(getErrorMessage(error)),
});
@@ -133,7 +133,7 @@ export function EdgeNodeDetail({ node }: { node: NodeItem }) {
<div className="flex flex-wrap items-center justify-between gap-3">
<div className="flex items-center gap-2">
<Button variant="ghost" size="sm" className="h-8 px-2" asChild>
<Link href="/openflare/nodes">
<Link href="/nodes">
<ArrowLeft className="size-4" />
</Link>
</Button>
@@ -295,7 +295,7 @@ export function EdgeNodeDetail({ node }: { node: NodeItem }) {
<CardDescription>来自 observability 接口的最近上报</CardDescription>
</div>
<Button variant="outline" size="sm" className="h-7 text-xs" asChild>
<Link href={`/openflare/apply-logs?node_id=${encodeURIComponent(node.node_id)}`}>
<Link href={`/apply-logs?node_id=${encodeURIComponent(node.node_id)}`}>
<FileText className="size-3.5 mr-1" />
应用记录
</Link>
@@ -8,10 +8,10 @@ import {cn} from '@/lib/utils';
export type NodeFilter = 'all' | 'edge' | 'relay' | 'tunnel';
const filters: Array<{ key: NodeFilter; label: string; href: string }> = [
{ key: 'all', label: '全部节点', href: '/openflare/nodes' },
{ key: 'edge', label: 'Edge', href: '/openflare/nodes?filter=edge' },
{ key: 'relay', label: 'Relay', href: '/openflare/nodes?filter=relay' },
{ key: 'tunnel', label: 'Tunnel', href: '/openflare/nodes?filter=tunnel' },
{ key: 'all', label: '全部节点', href: '/nodes' },
{ key: 'edge', label: 'Edge', href: '/nodes?filter=edge' },
{ key: 'relay', label: 'Relay', href: '/nodes?filter=relay' },
{ key: 'tunnel', label: 'Tunnel', href: '/nodes?filter=tunnel' },
];
export function getNodeFilter(searchParams: URLSearchParams): NodeFilter {
@@ -119,7 +119,7 @@ export function NodesTable({
<TableCell className="py-3">
<div className="flex flex-wrap justify-end gap-1">
<Button variant="outline" size="sm" className="h-7 text-xs" asChild>
<Link href={`/openflare/nodes/detail?id=${node.id}`}>详情</Link>
<Link href={`/nodes/detail?id=${node.id}`}>详情</Link>
</Button>
<Button
variant="outline"
@@ -122,7 +122,7 @@ export function RelayNodeDetail({ node }: { node: NodeItem }) {
onSuccess: async () => {
toast.success('中继节点已删除');
await queryClient.invalidateQueries({ queryKey: nodesQueryKey });
router.push('/openflare/nodes');
router.push('/nodes');
},
onError: (error) => toast.error(getErrorMessage(error)),
});
@@ -144,7 +144,7 @@ export function RelayNodeDetail({ node }: { node: NodeItem }) {
<div className="flex flex-wrap items-center justify-between gap-3">
<div className="flex items-center gap-2">
<Button variant="ghost" size="sm" className="h-8 px-2" asChild>
<Link href="/openflare/nodes">
<Link href="/nodes">
<ArrowLeft className="size-4" />
</Link>
</Button>
@@ -295,7 +295,7 @@ export function RelayNodeDetail({ node }: { node: NodeItem }) {
<CardDescription>控制 frps 内置 Web 管理界面是否启用</CardDescription>
</div>
<Button variant="outline" size="sm" className="h-7 text-xs" asChild>
<Link href={`/openflare/apply-logs?node_id=${encodeURIComponent(node.node_id)}`}>
<Link href={`/apply-logs?node_id=${encodeURIComponent(node.node_id)}`}>
<FileText className="size-3.5 mr-1" />
应用记录
</Link>
@@ -97,7 +97,7 @@ export function TunnelNodeDetail({ node }: { node: NodeItem }) {
onSuccess: async () => {
toast.success('隧道节点已删除');
await queryClient.invalidateQueries({ queryKey: nodesQueryKey });
router.push('/openflare/nodes');
router.push('/nodes');
},
onError: (error) => toast.error(getErrorMessage(error)),
});
@@ -114,7 +114,7 @@ export function TunnelNodeDetail({ node }: { node: NodeItem }) {
<div className="flex flex-wrap items-center justify-between gap-3">
<div className="flex items-center gap-2">
<Button variant="ghost" size="sm" className="h-8 px-2" asChild>
<Link href="/openflare/nodes">
<Link href="/nodes">
<ArrowLeft className="size-4" />
</Link>
</Button>
@@ -265,7 +265,7 @@ export function TunnelNodeDetail({ node }: { node: NodeItem }) {
<CardDescription>配置追平与应用结果</CardDescription>
</div>
<Button variant="outline" size="sm" className="h-7 text-xs" asChild>
<Link href={`/openflare/apply-logs?node_id=${encodeURIComponent(node.node_id)}`}>
<Link href={`/apply-logs?node_id=${encodeURIComponent(node.node_id)}`}>
<FileText className="size-3.5 mr-1" />
应用记录
</Link>
@@ -71,7 +71,7 @@ export function NodeDetailPageClient() {
return (
<div className="py-6 px-1 space-y-4">
<Button variant="ghost" size="sm" className="h-8 px-2" asChild>
<Link href="/openflare/nodes">
<Link href="/nodes">
<ArrowLeft className="size-4 mr-1" />
返回节点列表
</Link>
@@ -109,7 +109,7 @@ export function NodesPageClient() {
</div>
<div className="flex items-center gap-2">
<Button variant="outline" size="sm" className="h-7 text-xs" asChild>
<Link href="/openflare/apply-logs">应用记录</Link>
<Link href="/apply-logs">应用记录</Link>
</Button>
<Button variant="secondary" size="sm" className="h-7 text-xs" onClick={handleCreate}>
<Plus className="size-3.5 mr-1" />
@@ -1,7 +0,0 @@
export default function OpenFlareLayout({
children,
}: {
children: React.ReactNode;
}) {
return children;
}
@@ -49,7 +49,7 @@ export function OriginDetailPageClient() {
onSuccess: async () => {
toast.success("源站已删除")
await queryClient.invalidateQueries({ queryKey: ["openflare", "origins"] })
window.location.href = "/openflare/origins"
window.location.href = "/origins"
},
onError: (error) => {
toast.error(error instanceof Error ? error.message : "删除失败")
@@ -93,7 +93,7 @@ export function OriginDetailPageClient() {
return (
<div className="py-6 px-1 space-y-4">
<Button variant="ghost" size="sm" asChild>
<Link href="/openflare/origins">
<Link href="/origins">
<ArrowLeft className="size-4 mr-1" />
返回列表
</Link>
@@ -108,7 +108,7 @@ export function OriginDetailPageClient() {
<div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
<div className="space-y-2">
<Button variant="ghost" size="sm" className="h-8 px-2 -ml-2" asChild>
<Link href="/openflare/origins">
<Link href="/origins">
<ArrowLeft className="size-4 mr-1" />
返回列表
</Link>
@@ -141,7 +141,7 @@ export default function OriginsPage() {
</div>
<div className="flex flex-wrap gap-2">
<Button variant="outline" size="sm" asChild>
<Link href={`/openflare/origins/detail?id=${origin.id}`}>
<Link href={`/origins/detail?id=${origin.id}`}>
详情
</Link>
</Button>
@@ -14,7 +14,7 @@ interface ProjectListItemProps {
export function ProjectListItem({ project }: ProjectListItemProps) {
return (
<Link
href={`/openflare/pages/detail?id=${project.id}`}
href={`/pages/detail?id=${project.id}`}
className="group block rounded-lg border border-dashed bg-background p-4 transition hover:bg-muted/20"
>
<div className="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
@@ -157,7 +157,7 @@ export function PagesDetailPageClient() {
onSuccess: async () => {
toast.success("项目已删除")
await queryClient.invalidateQueries({ queryKey: projectsQueryKey })
window.location.href = "/openflare/pages"
window.location.href = "/pages"
},
onError: (error) => {
toast.error(error instanceof Error ? error.message : "删除失败")
@@ -202,7 +202,7 @@ export function PagesDetailPageClient() {
return (
<div className="py-6 px-1 space-y-4">
<Button variant="ghost" size="sm" asChild>
<Link href="/openflare/pages">
<Link href="/pages">
<ArrowLeft className="size-4 mr-1" />
返回列表
</Link>
@@ -217,7 +217,7 @@ export function PagesDetailPageClient() {
<div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
<div className="space-y-2">
<Button variant="ghost" size="sm" className="h-8 px-2 -ml-2" asChild>
<Link href="/openflare/pages">
<Link href="/pages">
<ArrowLeft className="size-4 mr-1" />
返回列表
</Link>
@@ -147,7 +147,7 @@ export default function PerformancePage() {
</div>
</div>
<Button variant="outline" size="sm" asChild>
<Link href="/openflare/config-versions">
<Link href="/config-versions">
<ExternalLink className="size-3.5 mr-1" />
查看配置预览
</Link>
@@ -24,8 +24,8 @@ import {
} from '@/components/ui/select';
import type {ProxyRouteItem, ProxyRoutePoWConfig} from '@/lib/services/openflare';
import {defaultPowConfig} from '@/app/(main)/openflare/waf/components/helpers';
import {PowConfigPanel} from '@/app/(main)/openflare/waf/components/pow-config-panel';
import {defaultPowConfig} from '@/app/(main)/waf/components/helpers';
import {PowConfigPanel} from '@/app/(main)/waf/components/pow-config-panel';
import {proxyRouteFormIds} from '../helpers';
import {useRouteSectionSave} from '../hooks/use-route-section-save';
@@ -16,7 +16,7 @@ import {
} from '@/components/ui/alert-dialog';
import {Badge} from '@/components/ui/badge';
import {Button} from '@/components/ui/button';
import {DiffDialog} from '@/app/(main)/openflare/config-versions/components/diff-dialog';
import {DiffDialog} from '@/app/(main)/config-versions/components/diff-dialog';
import type {ConfigDiffResult, ProxyRouteConfigSection, ProxyRouteItem} from '@/lib/services/openflare';
import {ConfigVersionService} from '@/lib/services/openflare';
@@ -104,7 +104,7 @@ export function RouteHeader({ route, activeSection, saving = false }: RouteHeade
<>
<div className="space-y-4">
<Button variant="ghost" size="sm" className="h-8 gap-1.5 px-0 text-xs" asChild>
<Link href="/openflare/proxy-routes">
<Link href="/proxy-routes">
<ArrowLeft className="size-3.5" />
返回规则列表
</Link>
@@ -35,7 +35,7 @@ export function ProxyRouteDetailPageClient() {
(section: ProxyRouteConfigSection) => {
const params = new URLSearchParams(searchParams.toString());
params.set('section', section);
router.replace(`/openflare/proxy-routes/detail?${params.toString()}`);
router.replace(`/proxy-routes/detail?${params.toString()}`);
},
[router, searchParams],
);
@@ -184,7 +184,7 @@ export function ProxyRoutesPageClient() {
<TableCell className="text-right">
<div className="flex justify-end gap-2">
<Button variant="outline" size="sm" className="h-7 text-xs" asChild>
<Link href={`/openflare/proxy-routes/detail?id=${route.id}`}>
<Link href={`/proxy-routes/detail?id=${route.id}`}>
配置
</Link>
</Button>
@@ -212,7 +212,7 @@ export function ProxyRoutesPageClient() {
onCreated={(route) => {
toast.success('网站已创建');
void fetchRoutes();
router.push(`/openflare/proxy-routes/detail?id=${route.id}&section=domains`);
router.push(`/proxy-routes/detail?id=${route.id}&section=domains`);
}}
/>
@@ -158,7 +158,7 @@ export default function WafIPGroupsPage() {
</div>
<div className="flex items-center gap-2">
<Button variant="outline" size="sm" className="h-7 text-xs" asChild>
<Link href="/openflare/waf">
<Link href="/waf">
<ArrowLeft className="size-3.5 mr-1" />
返回 WAF
</Link>
@@ -140,7 +140,7 @@ export default function WafPage() {
</div>
<div className="flex items-center gap-2">
<Button variant="outline" size="sm" className="h-7 text-xs" asChild>
<Link href="/openflare/waf/ip-groups">
<Link href="/waf/ip-groups">
<Network className="size-3.5 mr-1" />
IP 组
</Link>
@@ -96,10 +96,10 @@ export default function CertificatesPage() {
</div>
<div className="flex flex-wrap gap-2">
<Button variant="outline" size="sm" className="h-7 text-xs" asChild>
<Link href="/openflare/websites">返回网站</Link>
<Link href="/websites">返回网站</Link>
</Button>
<Button variant="outline" size="sm" className="h-7 text-xs" asChild>
<Link href="/openflare/websites/dns-accounts">DNS 账号</Link>
<Link href="/websites/dns-accounts">DNS 账号</Link>
</Button>
<Button
variant="outline"
@@ -26,7 +26,7 @@ import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow,} from '@/
import type {TlsCertificateItem} from '@/lib/services/openflare';
import {ProxyRouteService, TlsCertificateService, WebsiteService,} from '@/lib/services/openflare';
import {formatDateTime} from '@/lib/utils';
import {getUpstreamSummary} from '@/app/(main)/openflare/proxy-routes/components/helpers';
import {getUpstreamSummary} from '@/app/(main)/proxy-routes/components/helpers';
import {CertificateApplyDialog} from '../components/certificate-apply-dialog';
import {CertificateDetailDialog} from '../components/certificate-detail-dialog';
@@ -105,7 +105,7 @@ export function WebsiteDetailPageClient() {
mutationFn: (id: number) => WebsiteService.delete(id),
onSuccess: async () => {
await queryClient.invalidateQueries({queryKey: domainsQueryKey});
router.push('/openflare/websites');
router.push('/websites');
},
onError: (error) => toast.error(getErrorMessage(error)),
});
@@ -162,7 +162,7 @@ export function WebsiteDetailPageClient() {
return (
<div className="py-6 px-1 space-y-4">
<Button variant="ghost" size="sm" className="h-8 px-2" asChild>
<Link href="/openflare/websites">
<Link href="/websites">
<ArrowLeft className="size-4 mr-1" />
返回网站列表
</Link>
@@ -191,7 +191,7 @@ export function WebsiteDetailPageClient() {
</div>
<div className="flex flex-wrap gap-2">
<Button variant="outline" size="sm" className="h-7 text-xs" asChild>
<Link href="/openflare/websites">
<Link href="/websites">
<ArrowLeft className="size-3.5 mr-1" />
返回
</Link>
@@ -64,10 +64,10 @@ export default function DnsAccountsPage() {
</div>
<div className="flex items-center gap-2">
<Button variant="outline" size="sm" className="h-7 text-xs" asChild>
<Link href="/openflare/websites">返回网站</Link>
<Link href="/websites">返回网站</Link>
</Button>
<Button variant="outline" size="sm" className="h-7 text-xs" asChild>
<Link href="/openflare/websites/certificates">证书</Link>
<Link href="/websites/certificates">证书</Link>
</Button>
<Button size="sm" className="h-7 text-xs" onClick={() => setCreateOpen(true)}>
<Plus className="size-3.5 mr-1" />
@@ -78,10 +78,10 @@ export default function WebsitesPage() {
</div>
<div className="flex items-center gap-2">
<Button variant="outline" size="sm" className="h-7 text-xs" asChild>
<Link href="/openflare/websites/certificates">证书列表</Link>
<Link href="/websites/certificates">证书列表</Link>
</Button>
<Button variant="outline" size="sm" className="h-7 text-xs" asChild>
<Link href="/openflare/websites/dns-accounts">DNS 账号</Link>
<Link href="/websites/dns-accounts">DNS 账号</Link>
</Button>
<Button size="sm" className="h-7 text-xs" onClick={() => setEditorOpen(true)}>
<Plus className="size-3.5 mr-1" />
@@ -146,7 +146,7 @@ export default function WebsitesPage() {
</div>
<div className="flex shrink-0 gap-1">
<Button variant="outline" size="sm" className="h-7 text-xs" asChild>
<Link href={`/openflare/websites/detail?id=${domain.id}`}>
<Link href={`/websites/detail?id=${domain.id}`}>
详情
</Link>
</Button>
-15
View File
@@ -1,15 +0,0 @@
"use client";
import { HeroSection } from "@/components/home/hero-section";
import { DeveloperSection } from "@/components/home/developer-section";
import { FooterSection } from "@/components/home/footer-section";
export default function HomePage() {
return (
<div className="relative w-full min-h-screen bg-transparent text-foreground font-sans overflow-x-hidden selection:bg-primary selection:text-primary-foreground z-10">
<HeroSection />
<DeveloperSection />
<FooterSection />
</div>
)
}
@@ -42,7 +42,7 @@ export function LoginPage() {
const resolveRedirectTarget = useCallback(() => {
const callbackUrl = searchParams.get('callbackUrl')
const storedRedirect = sessionStorage.getItem('redirect_after_login')
const target = callbackUrl || storedRedirect || '/home'
const target = callbackUrl || storedRedirect || '/'
if (storedRedirect) {
sessionStorage.removeItem('redirect_after_login')
@@ -24,7 +24,7 @@ function getRedirectTarget(searchParams: ReturnType<typeof useSearchParams>) {
typeof window === "undefined"
? null
: sessionStorage.getItem("redirect_after_login")
return safeRedirectTarget(callbackUrl || storedRedirect || "/home")
return safeRedirectTarget(callbackUrl || storedRedirect || "/")
}

Some files were not shown because too many files have changed in this diff Show More