diff --git a/app/about/page.tsx b/app/about/page.tsx index ba10ad9..71511df 100644 --- a/app/about/page.tsx +++ b/app/about/page.tsx @@ -6,6 +6,7 @@ import type { Metadata } from "next" import type { ComponentType } from "react" export const metadata: Metadata = { + alternates: { canonical: "about/" }, title: "About RustFS | Next-Generation Distributed Object Storage for AI", description: "Learn about RustFS, an Apache 2.0 open-source distributed object storage system built in Rust. Powering AI data centers with native RDMA and DPU acceleration.", keywords: "about RustFS, RustFS project, open source object storage, distributed object storage, Rust infrastructure, NVIDIA Inception, RDMA storage, DPU hardware acceleration", diff --git a/app/blog/tag/[tag]/page.tsx b/app/blog/tag/[tag]/page.tsx index 51bea65..069ebc1 100644 --- a/app/blog/tag/[tag]/page.tsx +++ b/app/blog/tag/[tag]/page.tsx @@ -18,7 +18,10 @@ export async function generateStaticParams() { export async function generateMetadata({ params }: { params: Promise<{ tag: string }> }): Promise { const { tag } = await params; const label = await findTag(tag); - return { title: label ? `${label} | RustFS Blog` : "RustFS Blog" }; + return { + title: label ? `${label} | RustFS Blog` : "RustFS Blog", + alternates: { canonical: `/blog/tag/${tag}/` }, + }; } export default async function BlogTagPage({ params }: { params: Promise<{ tag: string }> }) { diff --git a/app/contact-us/page.tsx b/app/contact-us/page.tsx index 69e834b..1e511e4 100644 --- a/app/contact-us/page.tsx +++ b/app/contact-us/page.tsx @@ -2,6 +2,7 @@ import ContactForm from '@/components/business/contact-form' import type { Metadata } from 'next' export const metadata: Metadata = { + alternates: { canonical: "contact-us/" }, title: "Contact Us | RustFS | The High-Performance, Apache 2.0 Object Storage for AI", description: 'Get in touch with the RustFS team. Contact us for questions, support, or partnership opportunities.', openGraph: { diff --git a/app/contact/page.tsx b/app/contact/page.tsx index 516ba01..5560f25 100644 --- a/app/contact/page.tsx +++ b/app/contact/page.tsx @@ -2,6 +2,7 @@ import ContactForm from '@/components/business/contact-form' import type { Metadata } from 'next' export const metadata: Metadata = { + alternates: { canonical: "contact/" }, title: "Contact Us | RustFS | The High-Performance, Apache 2.0 Object Storage for AI", description: 'Get in touch with the RustFS team. Contact us for questions, support, or partnership opportunities.', openGraph: { diff --git a/app/cookie-policy/page.tsx b/app/cookie-policy/page.tsx index 0ed51a5..9219d9a 100644 --- a/app/cookie-policy/page.tsx +++ b/app/cookie-policy/page.tsx @@ -3,6 +3,7 @@ import type { Metadata } from 'next' import { CookieSettingsButton } from '@/components/business/cookie-consent' export const metadata: Metadata = { + alternates: { canonical: "cookie-policy/" }, title: 'Cookie Policy | RustFS', description: 'Learn how RustFS uses cookies and similar technologies on the official website.', } diff --git a/app/demo/page.tsx b/app/demo/page.tsx index f6340ef..c0dfc4d 100644 --- a/app/demo/page.tsx +++ b/app/demo/page.tsx @@ -36,6 +36,7 @@ const notices = [ ]; export const metadata: Metadata = { + alternates: { canonical: "demo/" }, title: "RustFS Demo | Public Demo Environment", description: "Review the RustFS public demo credentials, daily reset schedule, and data safety notice before entering the demo environment.", diff --git a/app/download/cli/page.tsx b/app/download/cli/page.tsx index 5309d01..7f67678 100644 --- a/app/download/cli/page.tsx +++ b/app/download/cli/page.tsx @@ -3,6 +3,7 @@ import { getLatestCliRelease } from '@/lib/github'; import type { Metadata } from 'next'; export const metadata: Metadata = { + alternates: { canonical: "download/cli/" }, title: 'Download RustFS CLI Client (rc) | Multi-Platform Install', description: 'Install the RustFS rc command-line client with a package manager, native binary, Docker, or source build.', }; diff --git a/app/download/page.tsx b/app/download/page.tsx index 6690db4..193509e 100644 --- a/app/download/page.tsx +++ b/app/download/page.tsx @@ -2,6 +2,7 @@ import DownloadPageClient from './components/download-page-client'; import type { Metadata } from 'next'; export const metadata: Metadata = { + alternates: { canonical: "download/" }, title: "Download & Install RustFS | Multi-OS & Cloud-Native Deployment", description: 'Download RustFS Server and native CLI (rc). Install seamlessly on Linux, macOS, Windows, and NixOS using pre-built binaries, Docker images, or Kubernetes.', keywords: 'download RustFS, install RustFS, RustFS binary, RustFS Docker, RustFS Kubernetes, RustFS CLI, RustFS command line, NixOS object storage, S3 server download, macOS, Linux, Windows, nixos', diff --git a/app/download/server/page.tsx b/app/download/server/page.tsx index 15d74d8..3c6fc5d 100644 --- a/app/download/server/page.tsx +++ b/app/download/server/page.tsx @@ -3,6 +3,7 @@ import { getLatestRelease } from '@/lib/github'; import type { Metadata } from 'next'; export const metadata: Metadata = { + alternates: { canonical: "download/server/" }, title: 'Download RustFS Server | Linux, Docker & Kubernetes', description: 'Install the RustFS server with a Linux binary, Docker, Compose, Kubernetes, macOS, or Windows.', }; diff --git a/app/en/layout.tsx b/app/en/layout.tsx new file mode 100644 index 0000000..c0d71ec --- /dev/null +++ b/app/en/layout.tsx @@ -0,0 +1,17 @@ +import type { Metadata } from "next"; + +// `/en/` client-redirects to the homepage; declare the homepage as canonical +// so search engines consolidate this duplicate URL into it. +export const metadata: Metadata = { + alternates: { + canonical: "/", + }, +}; + +export default function EnLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return children; +} diff --git a/app/erasure-code-calculator/page.tsx b/app/erasure-code-calculator/page.tsx index 1c957f0..452e574 100644 --- a/app/erasure-code-calculator/page.tsx +++ b/app/erasure-code-calculator/page.tsx @@ -2,6 +2,7 @@ import type { Metadata } from "next"; import ErasureCodeCalculator from "@/components/business/erasure-code-calculator"; export const metadata: Metadata = { + alternates: { canonical: "erasure-code-calculator/" }, title: "RustFS Erasure Code Calculator", description: "Calculate raw capacity, usable capacity, and failure tolerance for erasure coding.", }; diff --git a/app/page.tsx b/app/page.tsx index 5852b8c..54f42aa 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -12,6 +12,7 @@ import { getGitHubMetrics } from "@/lib/github"; import type { Metadata } from "next"; export const metadata: Metadata = { + alternates: { canonical: "/" }, title: "RustFS | High-Performance S3 Object Storage for AI & Cloud-Native", description: 'RustFS is an open-source, Apache 2.0-licensed distributed object storage system built in Rust. A high-performance, drop-in replacement for MinIO and Amazon S3 engineered for AI workloads.', keywords: 'RustFS, object storage, distributed storage, open source, Rust, Amazon S3, MinIO alternative, MinIO migration, Apache 2.0, cloud native storage, AI infrastructure', diff --git a/app/pricing/page.tsx b/app/pricing/page.tsx index 7beec3c..054c99d 100644 --- a/app/pricing/page.tsx +++ b/app/pricing/page.tsx @@ -8,6 +8,7 @@ import Link from "next/link"; import type { Metadata } from "next"; export const metadata: Metadata = { + alternates: { canonical: "pricing/" }, title: "Pricing | RustFS", description: "RustFS is Apache 2.0 open-source object storage. Contact the RustFS team for enterprise support, migration, and deployment planning.", }; diff --git a/app/privacy-policy/page.tsx b/app/privacy-policy/page.tsx index 894d7ac..49df90b 100644 --- a/app/privacy-policy/page.tsx +++ b/app/privacy-policy/page.tsx @@ -1,6 +1,7 @@ import type { Metadata } from "next"; export const metadata: Metadata = { + alternates: { canonical: "privacy-policy/" }, title: "Privacy Policy | RustFS", description: "Learn how RustFS handles privacy for self-hosted software deployments and the RustFS official website.", diff --git a/app/product/data-management/page.tsx b/app/product/data-management/page.tsx index dfe096d..582a6ff 100644 --- a/app/product/data-management/page.tsx +++ b/app/product/data-management/page.tsx @@ -2,6 +2,7 @@ import FeaturePage, { type FeaturePageSection } from "@/components/business/feat import type { Metadata } from "next"; export const metadata: Metadata = { + alternates: { canonical: "product/data-management/" }, title: "Data Management & S3 Tables | RustFS High-Performance Storage", description: "Optimize data management with RustFS. Benefit from full S3 compatibility, automated lifecycle management, and native Iceberg support powered by S3 Tables to lower TCO and accelerate AI and big data workloads.", diff --git a/app/product/high-availability-scale/page.tsx b/app/product/high-availability-scale/page.tsx index e35b45d..1934ea6 100644 --- a/app/product/high-availability-scale/page.tsx +++ b/app/product/high-availability-scale/page.tsx @@ -2,6 +2,7 @@ import FeaturePage, { type FeaturePageSection } from "@/components/business/feat import type { Metadata } from "next"; export const metadata: Metadata = { + alternates: { canonical: "product/high-availability-scale/" }, title: "RustFS High Availability & Scalability | Enterprise Distributed Storage", description: "Discover RustFS enterprise distributed architecture. Experience limitless scaling from PB to EB levels with distributed EC, automated self-healing, and absolute data reliability.", diff --git a/app/product/multiple-protocol-access/page.tsx b/app/product/multiple-protocol-access/page.tsx index 44132a9..3112db7 100644 --- a/app/product/multiple-protocol-access/page.tsx +++ b/app/product/multiple-protocol-access/page.tsx @@ -2,6 +2,7 @@ import FeaturePage, { type FeaturePageSection } from "@/components/business/feat import type { Metadata } from "next"; export const metadata: Metadata = { + alternates: { canonical: "product/multiple-protocol-access/" }, title: "Native Multi-Protocol Access | S3, Swift, WebDAV, FTP(s) & MCP | RustFS", description: "Connect RustFS natively to any workload via S3, Swift, FTP(s), WebDAV, and MCP. Zero code modifications, no legacy proxies, and absolute multi-protocol data coexistence.", keywords: [ diff --git a/app/product/operational-observability/page.tsx b/app/product/operational-observability/page.tsx index 2ff6e7b..84cbe02 100644 --- a/app/product/operational-observability/page.tsx +++ b/app/product/operational-observability/page.tsx @@ -3,6 +3,7 @@ import type { Metadata } from "next"; import Link from "next/link"; export const metadata: Metadata = { + alternates: { canonical: "product/operational-observability/" }, title: "RustFS Cluster Operations & OTEL Observability | Enterprise Management", description: "Simplify RustFS management with an intuitive console, rich rc CLI, and full-stack OTEL observability. Deploy Prometheus, Grafana, and Loki with one click for real-time cluster insights.", keywords: [ diff --git a/app/product/security-compliance/page.tsx b/app/product/security-compliance/page.tsx index aa51a6c..1f34517 100644 --- a/app/product/security-compliance/page.tsx +++ b/app/product/security-compliance/page.tsx @@ -2,6 +2,7 @@ import FeaturePage, { type FeaturePageSection } from "@/components/business/feat import type { Metadata } from "next"; export const metadata: Metadata = { + alternates: { canonical: "product/security-compliance/" }, title: "Security & Compliance | RustFS", description: "RustFS provides enterprise-grade authentication, authorization, log auditing, event handling, SSE/KMS encryption, OIDC SSO, and mTLS security controls.", keywords: [ diff --git a/app/rustfs-config-generator/page.tsx b/app/rustfs-config-generator/page.tsx index 4668e36..dc6684f 100644 --- a/app/rustfs-config-generator/page.tsx +++ b/app/rustfs-config-generator/page.tsx @@ -19,14 +19,14 @@ export const metadata: Metadata = { "RustFS configuration file", ], alternates: { - canonical: "/rustfs-config-generator", + canonical: "/rustfs-config-generator/", }, openGraph: { title: "RustFS Config Generator | Generate /etc/default/rustfs Online", description: "Generate a RustFS /etc/default/rustfs config file in seconds — set S3 API and console ports, volumes, access keys, log level, and OpenTelemetry.", type: "website", - url: "/rustfs-config-generator", + url: "/rustfs-config-generator/", locale: "en_US", siteName: "RustFS", images: [ diff --git a/app/zh/layout.tsx b/app/zh/layout.tsx new file mode 100644 index 0000000..ebd2e18 --- /dev/null +++ b/app/zh/layout.tsx @@ -0,0 +1,17 @@ +import type { Metadata } from "next"; + +// `/zh/` client-redirects to the Chinese site; declare that target as canonical +// so search engines consolidate this URL into rustfs.com.cn. +export const metadata: Metadata = { + alternates: { + canonical: "https://rustfs.com.cn/", + }, +}; + +export default function ZhLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return children; +}