seo 检索开关

This commit is contained in:
ryan
2026-06-09 08:35:13 +08:00
parent b0023787c5
commit 2ae55da52e
16 changed files with 163 additions and 5 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 74 KiB

+2
View File
@@ -6,6 +6,7 @@ import {BellRingProvider} from "@/contexts/bell-ring-context";
import {NotificationSettingsProvider} from "@/contexts/notification-settings-context";
import {UserProvider} from "@/contexts/user-context";
import {AppQueryProvider} from "@/components/providers/query-provider";
import {RobotsMeta} from "@/components/layout/robots-meta";
import "./globals.css";
export const metadata: Metadata = {
@@ -35,6 +36,7 @@ export default function RootLayout({
>
<CustomThemeProvider>
<AppQueryProvider>
<RobotsMeta />
<UserProvider>
<NotificationSettingsProvider>
<BellRingProvider>
@@ -0,0 +1,84 @@
"use client"
import {useMutation, useQueryClient, type UseQueryResult} from "@tanstack/react-query"
import {Globe, Search} from "lucide-react"
import {Card, CardContent, CardDescription, CardHeader, CardTitle} from "@/components/ui/card"
import {Switch} from "@/components/ui/switch"
import {AdminService} from "@/lib/services"
import type {SystemConfig} from "@/lib/services/admin"
import {TemplatesManager} from "./templates"
import {toast} from "sonner"
interface OperationTabProps {
configs: Record<string, SystemConfig>
systemConfigsQuery: UseQueryResult<SystemConfig[], Error>
}
export function OperationTab({ configs, systemConfigsQuery }: OperationTabProps) {
const queryClient = useQueryClient()
const updateConfigMutation = useMutation({
mutationFn: async ({ key, value }: { key: string; value: boolean }) => {
const config = configs[key]
if (!config) {
throw new Error(`缺少配置项: ${key}`)
}
await AdminService.updateSystemConfig(key, {
value: value ? "true" : "false",
description: config.description,
})
},
onSuccess: async () => {
await queryClient.invalidateQueries({ queryKey: ["admin", "system-configs"] })
await queryClient.invalidateQueries({ queryKey: ["public-config"] })
toast.success("运营配置已更新")
},
onError: (error: Error) => {
toast.error(error.message || "更新配置失败")
},
})
const indexingEnabled = configs["search_engine_indexing_enabled"]?.value === "true"
return (
<div className="space-y-6">
{/* 搜索引擎检索设置 */}
<Card className="border border-dashed shadow-sm">
<CardHeader className="border-b border-dashed pb-4">
<div className="flex items-center gap-2">
<div className="p-1.5 rounded-lg bg-indigo-500/10 text-indigo-500">
<Globe className="size-4" />
</div>
<div>
<CardTitle className="text-base font-semibold">SEO 与搜索引擎检索</CardTitle>
<CardDescription className="text-xs">配置站点是否允许被搜索引擎抓取和检索</CardDescription>
</div>
</div>
</CardHeader>
<CardContent className="pt-6">
<div className="flex items-center justify-between gap-4 rounded-xl border border-dashed p-4 bg-card hover:bg-muted/10 hover:border-indigo-500/30 transition-all duration-300 shadow-sm">
<div className="space-y-1">
<div className="flex items-center gap-2">
<Search className="size-4 text-indigo-500" />
<span className="font-medium text-sm text-foreground">允许被搜索引擎检索</span>
</div>
<p className="text-xs text-muted-foreground leading-relaxed pr-2">
默认关闭。关闭后系统将自动下发 noindex 指令并限制 robots.txt,禁止搜索引擎爬虫抓取本站页面。
</p>
</div>
<Switch
checked={indexingEnabled}
disabled={updateConfigMutation.isPending || systemConfigsQuery.isPending}
onCheckedChange={(checked) =>
updateConfigMutation.mutate({ key: "search_engine_indexing_enabled", value: checked })
}
/>
</div>
</CardContent>
</Card>
{/* 通知模板管理 */}
<TemplatesManager />
</div>
)
}
@@ -8,7 +8,7 @@ import {motion} from "motion/react"
import {Tabs, TabsContent, TabsList, TabsTrigger} from "@/components/ui/tabs"
import {useAuth} from "@/components/providers/auth-provider"
import {TemplatesManager} from "@/components/common/settings/templates"
import {OperationTab} from "./operation-tab"
import {AdminService} from "@/lib/services"
import type {SystemConfig} from "@/lib/services/admin"
import {SystemStatusManager} from "@/components/common/admin/status"
@@ -93,7 +93,7 @@ export function SystemSettingsMain() {
<SecurityTab configs={configs} systemConfigsQuery={systemConfigsQuery} />
</TabsContent>
<TabsContent value="operation" className="pt-4">
<TemplatesManager />
<OperationTab configs={configs} systemConfigsQuery={systemConfigsQuery} />
</TabsContent>
<TabsContent value="system" className="pt-4">
<SystemTab configs={configs} systemConfigsQuery={systemConfigsQuery} />
@@ -0,0 +1,17 @@
"use client"
import {usePublicConfig} from "@/hooks/use-public-config"
export function RobotsMeta() {
const { config } = usePublicConfig()
// Default to noindex, nofollow if config is loading or if it's explicitly disabled
const enabled = config?.search_engine_indexing_enabled === true
return (
<meta
name="robots"
content={enabled ? "index, follow" : "noindex, nofollow"}
/>
)
}
+2
View File
@@ -26,4 +26,6 @@ export interface PublicConfigResponse {
email_register_verification_enabled: boolean;
/** 目录显示配置 */
menu_display_config?: Record<string, boolean>;
/** 是否允许搜索引擎检索 */
search_engine_indexing_enabled?: boolean;
}
+3
View File
@@ -7,6 +7,9 @@
* @returns API 基础 URL
*/
export function getApiBaseUrl(): string {
if (typeof window === 'undefined') {
return process.env.WAVELET_BACKEND_URL || process.env.NEXT_PUBLIC_WAVELET_BACKEND_URL || 'http://localhost:8000';
}
return process.env.NEXT_PUBLIC_WAVELET_BACKEND_URL || '';
}
+5
View File
@@ -30,6 +30,11 @@ const nextConfig: NextConfig = {
source: '/f/:id',
destination: `${ backendUrl }/f/:id`,
},
// robots.txt 路由代理到后端动态接口
{
source: '/robots.txt',
destination: `${ backendUrl }/robots.txt`,
},
// 标准 RESTful API 接口
{
source: '/api/:path*',