feat(seo): enhance seo and security configurations

- Remove static robots.txt and generate it dynamically during build
- Add security headers in vercel.json
- Improve structured data with BreadcrumbList and Course schema
- Make site URL dynamic based on deployment environment
- Enhance sitemap configuration with priority and changefreq
This commit is contained in:
sanbuphy
2026-03-02 21:28:16 +08:00
parent 6ed901a1e0
commit 7c1abbb407
3 changed files with 136 additions and 26 deletions
+90 -7
View File
@@ -12,6 +12,22 @@ const isEdgeOne = !!process.env.EDGEONE || process.env.EDGEONE === '1'
// 3. 否则(如 GitHub Pages),使用 '/easy-vibe/'
const base = process.env.BASE || (isVercel || isEdgeOne ? '/' : '/easy-vibe/')
// 站点 URL 配置 - 根据部署环境动态确定
const getSiteUrl = () => {
if (isVercel && process.env.VERCEL_URL) {
return `https://${process.env.VERCEL_URL}`
}
if (isEdgeOne && process.env.EDGEONE_URL) {
return `https://${process.env.EDGEONE_URL}`
}
if (process.env.SITE_URL) {
return process.env.SITE_URL
}
return 'https://datawhalechina.github.io/easy-vibe'
}
const siteUrl = getSiteUrl()
// 语言映射配置
const localeMap = {
'zh-cn': {
@@ -79,9 +95,6 @@ const localeMap = {
// SEO 相关配置
const getSeoHead = (locale, title, description, path = '') => {
const seoConfig = localeMap[locale] || localeMap['zh-cn']
const siteUrl = isVercel
? 'https://your-project.vercel.app'
: 'https://datawhalechina.github.io/easy-vibe'
const canonicalUrl = path ? `${siteUrl}${path}` : `${siteUrl}/${locale}/`
const ogImageUrl = `${siteUrl}${base}logo.png`
@@ -172,10 +185,41 @@ const getSeoHead = (locale, title, description, path = '') => {
'@type': 'ImageObject',
url: ogImageUrl
}
},
mainEntity: {
'@type': 'Course',
name: title,
description: description,
provider: {
'@type': 'Organization',
name: 'Datawhale',
sameAs: 'https://github.com/datawhalechina/easy-vibe'
}
}
}
head.push(['script', { type: 'application/ld+json' }, JSON.stringify(jsonLd)])
// 添加 BreadcrumbList 结构化数据
const breadcrumbJsonLd = {
'@context': 'https://schema.org',
'@type': 'BreadcrumbList',
itemListElement: [
{
'@type': 'ListItem',
position: 1,
name: '首页',
item: siteUrl
},
{
'@type': 'ListItem',
position: 2,
name: locale === 'zh-cn' ? '教程' : 'Tutorial',
item: `${siteUrl}/${locale}/`
}
]
}
head.push(['script', { type: 'application/ld+json', class: 'breadcrumb-jsonld' }, JSON.stringify(breadcrumbJsonLd)])
return head
}
@@ -1053,12 +1097,20 @@ export default defineConfig({
// Sitemap 配置
sitemap: {
hostname: 'https://datawhalechina.github.io/easy-vibe',
hostname: siteUrl,
changefreq: 'weekly',
priority: {
'/': 1.0,
'/zh-cn/': 0.9,
'/zh-cn/stage-0/': 0.9,
'/zh-cn/stage-1/': 0.8,
'/zh-cn/stage-2/': 0.8,
'/zh-cn/stage-3/': 0.8,
'/zh-cn/appendix/': 0.7
},
transformItems(items) {
// 过滤掉旧版内容和未完成的语言版本
return items.filter((item) => {
const url = item.url
// 排除旧版内容目录
if (
url.includes('/extra/') ||
url.includes('/examples/') ||
@@ -1066,12 +1118,43 @@ export default defineConfig({
) {
return false
}
// 包含所有语言版本
return true
})
}
},
// 构建结束时动态生成 robots.txt
async buildEnd(siteConfig) {
const fs = await import('fs')
const path = await import('path')
const robotsTxt = `# https://www.robotstxt.org/robotstxt.html
User-agent: *
Allow: /
# 禁止搜索引擎抓取旧版内容(已迁移到新目录结构)
Disallow: /zh-cn/extra/
Disallow: /zh-cn/examples/
Disallow: /zh-cn/project/
Disallow: /en/extra/
Disallow: /en/examples/
Disallow: /en/project/
# 禁止抓取 VitePress 缓存和构建文件
Disallow: /.vitepress/
Disallow: /@fs/
# Sitemap 位置
Sitemap: ${siteUrl}/sitemap.xml
`
const outDir = siteConfig.outDir || path.resolve(__dirname, '.vitepress/dist')
const robotsPath = path.join(outDir, 'robots.txt')
fs.writeFileSync(robotsPath, robotsTxt, 'utf-8')
console.log('✓ Generated robots.txt with sitemap URL:', `${siteUrl}/sitemap.xml`)
},
// 多语言配置 - 使用 cn/en-us/ja 结构
locales: {
// 根路径 — 仅用于 404 页面兜底,实际首页由 docs/index.md 自动重定向
-18
View File
@@ -1,18 +0,0 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Allow: /
# 禁止搜索引擎抓取旧版内容(已迁移到新目录结构)
Disallow: /zh-cn/extra/
Disallow: /zh-cn/examples/
Disallow: /zh-cn/project/
Disallow: /en/extra/
Disallow: /en/examples/
Disallow: /en/project/
# 禁止抓取 VitePress 缓存和构建文件
Disallow: /.vitepress/
Disallow: /@fs/
# Sitemap 位置
Sitemap: https://datawhalechina.github.io/easy-vibe/sitemap.xml.gz
+46 -1
View File
@@ -2,5 +2,50 @@
"buildCommand": "npm run build",
"installCommand": "npm install",
"framework": "vitepress",
"outputDirectory": "docs/.vitepress/dist"
"outputDirectory": "docs/.vitepress/dist",
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "X-Content-Type-Options",
"value": "nosniff"
},
{
"key": "X-Frame-Options",
"value": "DENY"
},
{
"key": "X-XSS-Protection",
"value": "1; mode=block"
},
{
"key": "Referrer-Policy",
"value": "strict-origin-when-cross-origin"
},
{
"key": "Permissions-Policy",
"value": "camera=(), microphone=(), geolocation=()"
}
]
},
{
"source": "/sitemap.xml",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=86400, s-maxage=86400"
}
]
},
{
"source": "/robots.txt",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=86400, s-maxage=86400"
}
]
}
]
}