diff --git a/src/app/(auth)/privacyPolicy/layout.tsx b/src/app/(auth)/privacyPolicy/layout.tsx new file mode 100644 index 0000000..d6179d3 --- /dev/null +++ b/src/app/(auth)/privacyPolicy/layout.tsx @@ -0,0 +1,17 @@ +import {ReactNode} from 'react' +import {Metadata} from 'next' + +export async function generateMetadata(): Promise { + return { + title: '隐私政策', + description: '蓝狐代理隐私政策 - 了解我们如何收集、使用和保护您的个人信息', + openGraph: { + title: '隐私政策', + description: '蓝狐代理隐私政策 - 了解我们如何收集、使用和保护您的个人信息', + }, + } +} + +export default function PrivacyPolicyLayout({children}: {children: ReactNode}) { + return children +} diff --git a/src/app/(auth)/userAgreement/layout.tsx b/src/app/(auth)/userAgreement/layout.tsx new file mode 100644 index 0000000..96d5c65 --- /dev/null +++ b/src/app/(auth)/userAgreement/layout.tsx @@ -0,0 +1,17 @@ +import {ReactNode} from 'react' +import {Metadata} from 'next' + +export async function generateMetadata(): Promise { + return { + title: '用户协议', + description: '蓝狐代理用户服务协议 - 使用服务前请仔细阅读用户协议条款', + openGraph: { + title: '用户协议', + description: '蓝狐代理用户服务协议 - 使用服务前请仔细阅读用户协议条款', + }, + } +} + +export default function UserAgreementLayout({children}: {children: ReactNode}) { + return children +} diff --git a/src/app/(home)/(index)/page.tsx b/src/app/(home)/(index)/page.tsx index bd51ab5..934165a 100644 --- a/src/app/(home)/(index)/page.tsx +++ b/src/app/(home)/(index)/page.tsx @@ -1,9 +1,34 @@ +import {Metadata} from 'next' +import {siteConfig} from '@/config/site' import {HeroSection} from './hero-section' import {StatsSection} from './stats-section' import {ProductTypesSection} from './product-types-section' import {AdvantagesSection} from './advantages-section' import {ArticlesSection} from './articles-section' +export async function generateMetadata(): Promise { + return { + title: siteConfig.name, + description: siteConfig.description, + openGraph: { + title: siteConfig.name, + description: siteConfig.description, + url: siteConfig.url, + images: [ + { + url: siteConfig.ogImage.url, + width: siteConfig.ogImage.width, + height: siteConfig.ogImage.height, + alt: siteConfig.name, + }, + ], + }, + alternates: { + canonical: siteConfig.url, + }, + } +} + export default function Home() { return (
diff --git a/src/app/(home)/account-management/page.tsx b/src/app/(home)/account-management/page.tsx index c6318ec..4374ffb 100644 --- a/src/app/(home)/account-management/page.tsx +++ b/src/app/(home)/account-management/page.tsx @@ -1,4 +1,6 @@ +import {Metadata} from 'next' import ScenePage, {ScenePageConfig} from '@/components/scene-page' +import {siteConfig} from '@/config/site' import bannerImg from './_assets/banner.webp' import solutionImg from './_assets/solution-main.webp' import value1Img from './_assets/value-1.webp' @@ -46,6 +48,28 @@ const config: ScenePageConfig = { }, } +export async function generateMetadata(): Promise { + return { + title: config.banner.title, + description: config.banner.description, + openGraph: { + title: config.banner.title, + description: config.banner.description, + images: [ + { + url: siteConfig.ogImage.url, + width: siteConfig.ogImage.width, + height: siteConfig.ogImage.height, + alt: config.banner.title, + }, + ], + }, + alternates: { + canonical: `${siteConfig.url}/account-management`, + }, + } +} + export default function AccountManagementPage() { return } diff --git a/src/app/(home)/advertising/page.tsx b/src/app/(home)/advertising/page.tsx index 9baf006..f6f7b19 100644 --- a/src/app/(home)/advertising/page.tsx +++ b/src/app/(home)/advertising/page.tsx @@ -1,4 +1,6 @@ +import {Metadata} from 'next' import ScenePage, {ScenePageConfig} from '@/components/scene-page' +import {siteConfig} from '@/config/site' import bannerImg from './_assets/banner.webp' import solutionImg from './_assets/solution-main.webp' import value1Img from './_assets/value-1.webp' @@ -46,6 +48,28 @@ const config: ScenePageConfig = { }, } +export async function generateMetadata(): Promise { + return { + title: config.banner.title, + description: config.banner.description, + openGraph: { + title: config.banner.title, + description: config.banner.description, + images: [ + { + url: siteConfig.ogImage.url, + width: siteConfig.ogImage.width, + height: siteConfig.ogImage.height, + alt: config.banner.title, + }, + ], + }, + alternates: { + canonical: `${siteConfig.url}/advertising`, + }, + } +} + export default function AdvertisingPage() { return } diff --git a/src/app/(home)/collect/page.tsx b/src/app/(home)/collect/page.tsx index 2238240..c3cebae 100644 --- a/src/app/(home)/collect/page.tsx +++ b/src/app/(home)/collect/page.tsx @@ -1,3 +1,5 @@ +import {Metadata} from 'next' +import {siteConfig} from '@/config/site' import BreadCrumb from '@/components/bread-crumb' import Wrap from '@/components/wrap' import Extract from '@/components/composites/extract' @@ -5,6 +7,28 @@ import HomePage from '@/components/home/page' export type CollectPageProps = {} +export async function generateMetadata(): Promise { + return { + title: 'IP提取', + description: '短效/长效IP提取,高可用性代理IP,支持API调用,即时获取全国各地代理IP,适用于数据采集、网络测试等场景', + openGraph: { + title: 'IP提取', + description: '短效/长效IP提取,高可用性代理IP,支持API调用,即时获取全国各地代理IP', + images: [ + { + url: siteConfig.ogImage.url, + width: siteConfig.ogImage.width, + height: siteConfig.ogImage.height, + alt: 'IP提取', + }, + ], + }, + alternates: { + canonical: `${siteConfig.url}/collect`, + }, + } +} + export default function CollectPage(props: CollectPageProps) { return ( //
diff --git a/src/app/(home)/custom/_client.tsx b/src/app/(home)/custom/_client.tsx new file mode 100644 index 0000000..89e5d56 --- /dev/null +++ b/src/app/(home)/custom/_client.tsx @@ -0,0 +1,264 @@ +'use client' +import {useState} from 'react' +import Image from 'next/image' +import {useRouter} from 'next/navigation' +import {useForm} from 'react-hook-form' +import {zodResolver} from '@hookform/resolvers/zod' +import {z} from 'zod' +import {toast} from 'sonner' +import HomePage from '@/components/home/page' +import Wrap from '@/components/wrap' +import {Form, FormField} from '@/components/ui/form' +import {Input} from '@/components/ui/input' +import {Button} from '@/components/ui/button' +import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from '@/components/ui/select' +import {merge} from '@/lib/utils' +import {submitInquiry} from '@/actions/inquiry' +import group from './_assets/Group.webp' +import SelfDesc from '@/components/features/self-desc' + +const formSchema = z.object({ + company: z.string().min(2, '企业名称至少2个字符'), + name: z.string().min(2, '联系人姓名至少2个字符'), + phone: z.string().regex(/^1[3-9]\d{9}$/, '请输入正确的11位手机号码'), + usage: z.string().min(1, '请选择您需要的用量'), + purpose: z.string().min(2, '请输入用途说明').max(200, '用途说明不超过200字符'), +}) + +type FormValues = z.infer + +export default function CustomPage() { + const router = useRouter() + const [isSubmitting, setIsSubmitting] = useState(false) + + const form = useForm({ + resolver: zodResolver(formSchema), + defaultValues: { + company: '', + name: '', + phone: '', + usage: '', + purpose: '', + }, + }) + + const onSubmit = async (data: FormValues) => { + setIsSubmitting(true) + try { + const result = await submitInquiry(data) + if (result.success) { + toast.success('提交成功!我们的专属顾问会在24小时内联系您') + form.reset() + } + else { + toast.error(result.message || '提交失败,请稍后重试') + } + } + catch (error) { + toast.error('网络错误,请稍后重试') + } + finally { + setIsSubmitting(false) + } + } + + const scrollToForm = () => { + const formElement = document.getElementById('inquiry-form') + if (formElement) { + formElement.scrollIntoView({behavior: 'smooth', block: 'start'}) + } + } + + return ( + + + {/* 1. 顶部介绍区 */} + { + document.getElementById('inquiry-form')?.scrollIntoView({behavior: 'smooth', block: 'start'}) + }}/> + + {/* 2. 表单区 */} +
+
+

业务定制

+

+ 请填写您的企业信息,我们的专属顾问将在24小时内与您联系 +

+
+
+
+ {/* 企业名称 */} + + {({id, field}) => ( +
+ +
+ +
+
+ )} +
+ + {/* 联系人姓名 */} + + {({id, field}) => ( +
+ +
+ +
+
+ )} +
+ + {/* 联系人手机号码 */} + + {({id, field}) => ( +
+ +
+ +
+
+ )} +
+ + {/* 每月需求用量 */} + + {({id, field}) => ( +
+ +
+ +
+
+ )} +
+ + {/* 用途 */} + + {({id, field}) => ( +
+ +
+ +
+
+ )} +
+ +
+ +
+
+
+
+ + {/* 3. 底部引导区 */} +
+ 立即试用背景 +
+
+
+ 现在注册,免费领取5000IP +
+ +
+
+
+
+
+ ) +} diff --git a/src/app/(home)/custom/page.tsx b/src/app/(home)/custom/page.tsx index 89e5d56..34a92fb 100644 --- a/src/app/(home)/custom/page.tsx +++ b/src/app/(home)/custom/page.tsx @@ -1,264 +1,27 @@ -'use client' -import {useState} from 'react' -import Image from 'next/image' -import {useRouter} from 'next/navigation' -import {useForm} from 'react-hook-form' -import {zodResolver} from '@hookform/resolvers/zod' -import {z} from 'zod' -import {toast} from 'sonner' -import HomePage from '@/components/home/page' -import Wrap from '@/components/wrap' -import {Form, FormField} from '@/components/ui/form' -import {Input} from '@/components/ui/input' -import {Button} from '@/components/ui/button' -import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from '@/components/ui/select' -import {merge} from '@/lib/utils' -import {submitInquiry} from '@/actions/inquiry' -import group from './_assets/Group.webp' -import SelfDesc from '@/components/features/self-desc' +import {Metadata} from 'next' +import {siteConfig} from '@/config/site' +import CustomPage from './_client' -const formSchema = z.object({ - company: z.string().min(2, '企业名称至少2个字符'), - name: z.string().min(2, '联系人姓名至少2个字符'), - phone: z.string().regex(/^1[3-9]\d{9}$/, '请输入正确的11位手机号码'), - usage: z.string().min(1, '请选择您需要的用量'), - purpose: z.string().min(2, '请输入用途说明').max(200, '用途说明不超过200字符'), -}) - -type FormValues = z.infer - -export default function CustomPage() { - const router = useRouter() - const [isSubmitting, setIsSubmitting] = useState(false) - - const form = useForm({ - resolver: zodResolver(formSchema), - defaultValues: { - company: '', - name: '', - phone: '', - usage: '', - purpose: '', +export async function generateMetadata(): Promise { + return { + title: '业务定制', + description: '蓝狐代理为您提供企业级代理IP定制服务,专属顾问1对1服务,量身打造代理解决方案,满足企业个性化需求', + openGraph: { + title: '业务定制', + description: '蓝狐代理为您提供企业级代理IP定制服务,专属顾问1对1服务,量身打造代理解决方案', + images: [ + { + url: siteConfig.ogImage.url, + width: siteConfig.ogImage.width, + height: siteConfig.ogImage.height, + alt: '业务定制', + }, + ], + }, + alternates: { + canonical: `${siteConfig.url}/custom`, }, - }) - - const onSubmit = async (data: FormValues) => { - setIsSubmitting(true) - try { - const result = await submitInquiry(data) - if (result.success) { - toast.success('提交成功!我们的专属顾问会在24小时内联系您') - form.reset() - } - else { - toast.error(result.message || '提交失败,请稍后重试') - } - } - catch (error) { - toast.error('网络错误,请稍后重试') - } - finally { - setIsSubmitting(false) - } } - - const scrollToForm = () => { - const formElement = document.getElementById('inquiry-form') - if (formElement) { - formElement.scrollIntoView({behavior: 'smooth', block: 'start'}) - } - } - - return ( - - - {/* 1. 顶部介绍区 */} - { - document.getElementById('inquiry-form')?.scrollIntoView({behavior: 'smooth', block: 'start'}) - }}/> - - {/* 2. 表单区 */} -
-
-

业务定制

-

- 请填写您的企业信息,我们的专属顾问将在24小时内与您联系 -

-
-
-
- {/* 企业名称 */} - - {({id, field}) => ( -
- -
- -
-
- )} -
- - {/* 联系人姓名 */} - - {({id, field}) => ( -
- -
- -
-
- )} -
- - {/* 联系人手机号码 */} - - {({id, field}) => ( -
- -
- -
-
- )} -
- - {/* 每月需求用量 */} - - {({id, field}) => ( -
- -
- -
-
- )} -
- - {/* 用途 */} - - {({id, field}) => ( -
- -
- -
-
- )} -
- -
- -
-
-
-
- - {/* 3. 底部引导区 */} -
- 立即试用背景 -
-
-
- 现在注册,免费领取5000IP -
- -
-
-
-
-
- ) } + +export default CustomPage diff --git a/src/app/(home)/data-capture/page.tsx b/src/app/(home)/data-capture/page.tsx index 7fb24b5..f6da435 100644 --- a/src/app/(home)/data-capture/page.tsx +++ b/src/app/(home)/data-capture/page.tsx @@ -1,4 +1,6 @@ +import {Metadata} from 'next' import ScenePage, {ScenePageConfig} from '@/components/scene-page' +import {siteConfig} from '@/config/site' import bannerImg from './_assets/banner.webp' import solutionImg from './_assets/solution-main.webp' import value1Img from './_assets/value-1.webp' @@ -46,6 +48,28 @@ const config: ScenePageConfig = { }, } +export async function generateMetadata(): Promise { + return { + title: config.banner.title, + description: config.banner.description, + openGraph: { + title: config.banner.title, + description: config.banner.description, + images: [ + { + url: siteConfig.ogImage.url, + width: siteConfig.ogImage.width, + height: siteConfig.ogImage.height, + alt: config.banner.title, + }, + ], + }, + alternates: { + canonical: `${siteConfig.url}/data-capture`, + }, + } +} + export default function DataCapturePage() { return } diff --git a/src/app/(home)/docs/page.tsx b/src/app/(home)/docs/page.tsx index 74b299a..0112d2e 100644 --- a/src/app/(home)/docs/page.tsx +++ b/src/app/(home)/docs/page.tsx @@ -1,3 +1,28 @@ +import {Metadata} from 'next' +import {siteConfig} from '@/config/site' + +export async function generateMetadata(): Promise { + return { + title: '帮助中心', + description: '蓝狐代理帮助中心 - 产品使用教程、常见问题解答、行业资讯、代理IP设置指南', + openGraph: { + title: '帮助中心', + description: '蓝狐代理帮助中心 - 产品使用教程、常见问题解答、行业资讯', + images: [ + { + url: siteConfig.ogImage.url, + width: siteConfig.ogImage.width, + height: siteConfig.ogImage.height, + alt: '帮助中心', + }, + ], + }, + alternates: { + canonical: `${siteConfig.url}/docs`, + }, + } +} + export default function DocsIndexPage() { return (
diff --git a/src/app/(home)/e-commerce/page.tsx b/src/app/(home)/e-commerce/page.tsx index 72b92ff..0c434d6 100644 --- a/src/app/(home)/e-commerce/page.tsx +++ b/src/app/(home)/e-commerce/page.tsx @@ -1,4 +1,6 @@ +import {Metadata} from 'next' import ScenePage, {ScenePageConfig} from '@/components/scene-page' +import {siteConfig} from '@/config/site' import bannerImg from './_assets/banner.webp' import solutionImg from './_assets/solution-main.webp' import value1Img from './_assets/value-1.webp' @@ -46,6 +48,28 @@ const config: ScenePageConfig = { }, } +export async function generateMetadata(): Promise { + return { + title: config.banner.title, + description: config.banner.description, + openGraph: { + title: config.banner.title, + description: config.banner.description, + images: [ + { + url: siteConfig.ogImage.url, + width: siteConfig.ogImage.width, + height: siteConfig.ogImage.height, + alt: config.banner.title, + }, + ], + }, + alternates: { + canonical: `${siteConfig.url}/e-commerce`, + }, + } +} + export default function ECommercePage() { return } diff --git a/src/app/(home)/market-research/page.tsx b/src/app/(home)/market-research/page.tsx index 7360623..1445684 100644 --- a/src/app/(home)/market-research/page.tsx +++ b/src/app/(home)/market-research/page.tsx @@ -1,4 +1,6 @@ +import {Metadata} from 'next' import ScenePage, {ScenePageConfig} from '@/components/scene-page' +import {siteConfig} from '@/config/site' import bannerImg from './_assets/banner.webp' import solutionImg from './_assets/solution-main.webp' import value1Img from './_assets/value-1.webp' @@ -46,6 +48,28 @@ const config: ScenePageConfig = { }, } +export async function generateMetadata(): Promise { + return { + title: config.banner.title, + description: config.banner.description, + openGraph: { + title: config.banner.title, + description: config.banner.description, + images: [ + { + url: siteConfig.ogImage.url, + width: siteConfig.ogImage.width, + height: siteConfig.ogImage.height, + alt: config.banner.title, + }, + ], + }, + alternates: { + canonical: `${siteConfig.url}/market-research`, + }, + } +} + export default function MarketResearchPage() { return } diff --git a/src/app/(home)/network-testing/page.tsx b/src/app/(home)/network-testing/page.tsx index e962561..d2bec8b 100644 --- a/src/app/(home)/network-testing/page.tsx +++ b/src/app/(home)/network-testing/page.tsx @@ -1,4 +1,6 @@ +import {Metadata} from 'next' import ScenePage, {ScenePageConfig} from '@/components/scene-page' +import {siteConfig} from '@/config/site' import bannerImg from './_assets/banner.webp' import solutionImg from './_assets/solution-main.webp' import value1Img from './_assets/value-1.webp' @@ -46,6 +48,28 @@ const config: ScenePageConfig = { }, } +export async function generateMetadata(): Promise { + return { + title: config.banner.title, + description: config.banner.description, + openGraph: { + title: config.banner.title, + description: config.banner.description, + images: [ + { + url: siteConfig.ogImage.url, + width: siteConfig.ogImage.width, + height: siteConfig.ogImage.height, + alt: config.banner.title, + }, + ], + }, + alternates: { + canonical: `${siteConfig.url}/network-testing`, + }, + } +} + export default function NetworkTestingPage() { return } diff --git a/src/app/(home)/product/page.tsx b/src/app/(home)/product/page.tsx index 32d1087..3b5386a 100644 --- a/src/app/(home)/product/page.tsx +++ b/src/app/(home)/product/page.tsx @@ -1,7 +1,9 @@ +import {Suspense} from 'react' +import {Metadata} from 'next' +import {siteConfig} from '@/config/site' import BreadCrumb from '@/components/bread-crumb' import Wrap from '@/components/wrap' import Purchase, {TabType} from '@/components/composites/purchase' -import {Suspense} from 'react' import HomePage from '@/components/home/page' export type ProductPageProps = { @@ -10,6 +12,28 @@ export type ProductPageProps = { }> } +export async function generateMetadata(): Promise { + return { + title: '产品中心', + description: '为您的业务提供多样化代理产品 - 短效代理、长效代理、固定IP代理、SOCKS5代理,高可用性、低延迟', + openGraph: { + title: '产品中心', + description: '为您的业务提供多样化代理产品 - 短效代理、长效代理、固定IP代理、SOCKS5代理,高可用性、低延迟', + images: [ + { + url: siteConfig.ogImage.url, + width: siteConfig.ogImage.width, + height: siteConfig.ogImage.height, + alt: '产品中心', + }, + ], + }, + alternates: { + canonical: `${siteConfig.url}/product`, + }, + } +} + export default function ProductPage(props: ProductPageProps) { return ( { + return { + title: config.banner.title, + description: config.banner.description, + openGraph: { + title: config.banner.title, + description: config.banner.description, + images: [ + { + url: siteConfig.ogImage.url, + width: siteConfig.ogImage.width, + height: siteConfig.ogImage.height, + alt: config.banner.title, + }, + ], + }, + alternates: { + canonical: `${siteConfig.url}/seo-optimization`, + }, + } +} + export default function SeoOptimizationPage() { return } diff --git a/src/app/(home)/social-media/page.tsx b/src/app/(home)/social-media/page.tsx index b4c036e..c2d7374 100644 --- a/src/app/(home)/social-media/page.tsx +++ b/src/app/(home)/social-media/page.tsx @@ -1,4 +1,6 @@ +import {Metadata} from 'next' import ScenePage, {ScenePageConfig} from '@/components/scene-page' +import {siteConfig} from '@/config/site' import bannerImg from './_assets/banner.webp' import solutionImg from './_assets/solution-main.webp' import value1Img from './_assets/value-1.webp' @@ -46,6 +48,28 @@ const config: ScenePageConfig = { }, } +export async function generateMetadata(): Promise { + return { + title: config.banner.title, + description: config.banner.description, + openGraph: { + title: config.banner.title, + description: config.banner.description, + images: [ + { + url: siteConfig.ogImage.url, + width: siteConfig.ogImage.width, + height: siteConfig.ogImage.height, + alt: config.banner.title, + }, + ], + }, + alternates: { + canonical: `${siteConfig.url}/social-media`, + }, + } +} + export default function SocialMediaPage() { return } diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 59faa57..977174a 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,19 +1,71 @@ import './globals.css' import {ReactNode} from 'react' -import {Metadata} from 'next' +import {Metadata, Viewport} from 'next' import {Toaster} from '@/components/ui/sonner' import Effects from '@/app/effects' import {ProfileStoreProvider} from '@/components/stores/profile' import {LayoutStoreProvider} from '@/components/stores/layout' import {ClientStoreProvider} from '@/components/stores/client' import {getProfile} from '@/actions/auth' -import Script from 'next/script' import {AppStoreProvider} from '@/components/stores/app' import {getApiUrl} from '@/actions/base' +import {siteConfig} from '@/config/site' +import {JsonLd} from '@/components/seo/json-ld' + +export const viewport: Viewport = { + width: 'device-width', + initialScale: 1, + themeColor: '#3b82f6', +} export async function generateMetadata(): Promise { return { - title: '蓝狐代理', + metadataBase: new URL(siteConfig.url), + title: { + default: siteConfig.name, + template: `%s`, + }, + description: siteConfig.description, + keywords: siteConfig.keywords, + robots: { + index: true, + follow: true, + googleBot: { + 'index': true, + 'follow': true, + 'max-video-preview': -1, + 'max-image-preview': 'large', + 'max-snippet': -1, + }, + }, + openGraph: { + type: 'website', + locale: siteConfig.locale, + url: siteConfig.url, + siteName: siteConfig.name, + title: siteConfig.name, + description: siteConfig.description, + images: [ + { + url: siteConfig.ogImage.url, + width: siteConfig.ogImage.width, + height: siteConfig.ogImage.height, + alt: siteConfig.name, + }, + ], + }, + twitter: { + card: 'summary_large_image', + title: siteConfig.name, + description: siteConfig.description, + images: [siteConfig.ogImage.url], + }, + alternates: { + canonical: siteConfig.url, + }, + icons: { + icon: '/favicon.ico', + }, } } @@ -27,6 +79,16 @@ export default async function RootLayout(props: Readonly<{ {props.children} + ) diff --git a/src/app/manifest.ts b/src/app/manifest.ts new file mode 100644 index 0000000..1418630 --- /dev/null +++ b/src/app/manifest.ts @@ -0,0 +1,21 @@ +import {MetadataRoute} from 'next' +import {siteConfig} from '@/config/site' + +export default function manifest(): MetadataRoute.Manifest { + return { + name: siteConfig.name, + short_name: siteConfig.shortName, + description: siteConfig.description, + start_url: '/', + display: 'standalone', + background_color: '#ffffff', + theme_color: '#3b82f6', + icons: [ + { + src: '/favicon.ico', + sizes: '48x48', + type: 'image/x-icon', + }, + ], + } +} diff --git a/src/app/robots.ts b/src/app/robots.ts new file mode 100644 index 0000000..71472fc --- /dev/null +++ b/src/app/robots.ts @@ -0,0 +1,18 @@ +import {MetadataRoute} from 'next' +import {siteConfig} from '@/config/site' + +export default function robots(): MetadataRoute.Robots { + return { + rules: { + userAgent: '*', + allow: '/', + disallow: [ + '/api/', + '/admin/', + '/profile/', + '/settings/', + ], + }, + sitemap: `${siteConfig.url}/sitemap.xml`, + } +} diff --git a/src/app/sitemap.ts b/src/app/sitemap.ts new file mode 100644 index 0000000..13b5bfc --- /dev/null +++ b/src/app/sitemap.ts @@ -0,0 +1,178 @@ +import {MetadataRoute} from 'next' +import {siteConfig} from '@/config/site' + +export default async function sitemap(): Promise { + const baseUrl = siteConfig.url + const now = new Date() + + return [ + { + url: baseUrl, + lastModified: now, + changeFrequency: 'daily', + priority: 1.0, + }, + { + url: `${baseUrl}/product`, + lastModified: now, + changeFrequency: 'weekly', + priority: 0.9, + }, + { + url: `${baseUrl}/collect`, + lastModified: now, + changeFrequency: 'weekly', + priority: 0.8, + }, + { + url: `${baseUrl}/custom`, + lastModified: now, + changeFrequency: 'monthly', + priority: 0.7, + }, + { + url: `${baseUrl}/data-capture`, + lastModified: now, + changeFrequency: 'weekly', + priority: 0.7, + }, + { + url: `${baseUrl}/e-commerce`, + lastModified: now, + changeFrequency: 'weekly', + priority: 0.7, + }, + { + url: `${baseUrl}/market-research`, + lastModified: now, + changeFrequency: 'weekly', + priority: 0.7, + }, + { + url: `${baseUrl}/seo-optimization`, + lastModified: now, + changeFrequency: 'weekly', + priority: 0.7, + }, + { + url: `${baseUrl}/social-media`, + lastModified: now, + changeFrequency: 'weekly', + priority: 0.7, + }, + { + url: `${baseUrl}/advertising`, + lastModified: now, + changeFrequency: 'weekly', + priority: 0.7, + }, + { + url: `${baseUrl}/account-management`, + lastModified: now, + changeFrequency: 'weekly', + priority: 0.7, + }, + { + url: `${baseUrl}/network-testing`, + lastModified: now, + changeFrequency: 'weekly', + priority: 0.7, + }, + { + url: `${baseUrl}/docs`, + lastModified: now, + changeFrequency: 'weekly', + priority: 0.6, + }, + { + url: `${baseUrl}/docs/product/city-lines`, + lastModified: now, + changeFrequency: 'monthly', + priority: 0.5, + }, + { + url: `${baseUrl}/docs/faqs/faq-general`, + lastModified: now, + changeFrequency: 'monthly', + priority: 0.5, + }, + { + url: `${baseUrl}/docs/faqs/faq-billing`, + lastModified: now, + changeFrequency: 'monthly', + priority: 0.5, + }, + { + url: `${baseUrl}/docs/client/android-proxy`, + lastModified: now, + changeFrequency: 'monthly', + priority: 0.5, + }, + { + url: `${baseUrl}/docs/client/browser-proxy`, + lastModified: now, + changeFrequency: 'monthly', + priority: 0.5, + }, + { + url: `${baseUrl}/docs/client/ios-proxy`, + lastModified: now, + changeFrequency: 'monthly', + priority: 0.5, + }, + { + url: `${baseUrl}/docs/client/windows10-proxy`, + lastModified: now, + changeFrequency: 'monthly', + priority: 0.5, + }, + { + url: `${baseUrl}/docs/news/news-announce`, + lastModified: now, + changeFrequency: 'monthly', + priority: 0.5, + }, + { + url: `${baseUrl}/docs/news/news-latest`, + lastModified: now, + changeFrequency: 'monthly', + priority: 0.5, + }, + { + url: `${baseUrl}/docs/operation/extract-link`, + lastModified: now, + changeFrequency: 'monthly', + priority: 0.5, + }, + { + url: `${baseUrl}/docs/operation/payment-records`, + lastModified: now, + changeFrequency: 'monthly', + priority: 0.5, + }, + { + url: `${baseUrl}/docs/operation/profile-settings`, + lastModified: now, + changeFrequency: 'monthly', + priority: 0.5, + }, + { + url: `${baseUrl}/docs/operation/verify-guide`, + lastModified: now, + changeFrequency: 'monthly', + priority: 0.5, + }, + { + url: `${baseUrl}/docs/operation/whitelist-guide`, + lastModified: now, + changeFrequency: 'monthly', + priority: 0.5, + }, + { + url: `${baseUrl}/login`, + lastModified: now, + changeFrequency: 'monthly', + priority: 0.3, + }, + ] +} diff --git a/src/components/seo/json-ld.tsx b/src/components/seo/json-ld.tsx new file mode 100644 index 0000000..9963edb --- /dev/null +++ b/src/components/seo/json-ld.tsx @@ -0,0 +1,8 @@ +export function JsonLd({schema}: {schema: Record}) { + return ( +