diff --git a/app/pages/blog/[...slug].example.vue b/app/pages/blog/[...slug].example.vue
deleted file mode 100644
index 57c5398..0000000
--- a/app/pages/blog/[...slug].example.vue
+++ /dev/null
@@ -1,195 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
- {{ formatCategory(category) }}
-
-
-
- -
-
-
-
-
-
-
-
- -
- Author
-
- -
-
- -
-
-
-
- {{ author.name }}
-
-
-
-
-
- -
- Published at
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Related Articles
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/pages/blog/[...slug].vue b/app/pages/blog/[...slug].vue
index 896313b..e529ff6 100644
--- a/app/pages/blog/[...slug].vue
+++ b/app/pages/blog/[...slug].vue
@@ -56,6 +56,8 @@ const siteUrl = 'https://aliarghyani.vercel.app' // TODO: Move to runtime config
// Custom meta tags
if (post.value) {
const postData = post.value as any
+ const canonicalPath = postData.path ? postData.path.replace(/^\/(en|fa)/, '') : ''
+ const canonicalUrl = canonicalPath ? `${siteUrl}${canonicalPath}` : siteUrl
useSeoMeta({
title: `${postData.title} | ${t('blog.title')}`,
@@ -64,7 +66,7 @@ if (post.value) {
ogDescription: postData.description,
ogImage: postData.image || '/img/blog/default-cover.jpg',
ogType: 'article',
- ogUrl: `${siteUrl}${postData.path}`,
+ ogUrl: canonicalUrl,
twitterCard: 'summary_large_image',
twitterTitle: postData.title,
twitterDescription: postData.description,
@@ -77,6 +79,14 @@ if (post.value) {
// JSON-LD structured data
useHead({
+ link: canonicalPath
+ ? [
+ {
+ rel: 'canonical',
+ href: canonicalUrl
+ }
+ ]
+ : [],
script: [
{
type: 'application/ld+json',
@@ -140,7 +150,7 @@ if (post.value) {
-
+
diff --git a/nuxt.config.ts b/nuxt.config.ts
index 6249169..950bea7 100644
--- a/nuxt.config.ts
+++ b/nuxt.config.ts
@@ -175,9 +175,10 @@ export default defineNuxtConfig({
nitro: {
prerender: {
crawlLinks: true,
- routes: ['/', '/blog', '/fa/blog'],
+ routes: ['/', '/blog', '/fa/blog', '/blog/rss.xml', '/fa/blog/rss.xml'],
failOnError: false,
- ignore: ['/_vercel/image']
+ // Avoid crawling and prerendering internal/asset endpoints (speeds up builds significantly)
+ ignore: ['/_vercel/image', '/_ipx', '/_nuxt', '/_i18n', '/__nuxt_content']
},
devProxy: {
host: '0.0.0.0'
diff --git a/server/routes/blog/rss.xml.ts b/server/routes/blog/rss.xml.ts
index 1039275..c2e42ea 100644
--- a/server/routes/blog/rss.xml.ts
+++ b/server/routes/blog/rss.xml.ts
@@ -2,14 +2,14 @@ export default defineEventHandler(async (event) => {
const config = useRuntimeConfig()
const siteUrl = config.public.siteUrl || 'https://example.com'
- // Detect locale from path
const locale = 'en'
- // Fetch published blog posts
- const posts = await serverQueryContent(event, `${locale}/blog`)
- .where({ draft: { $ne: true } })
- .sort({ date: -1 })
- .find()
+ // In Nitro server routes, `queryCollection` expects the H3 event as the first argument.
+ const allPosts = await queryCollection(event, 'blog').all()
+
+ const posts = allPosts
+ .filter((post: any) => post.path?.startsWith('/en/blog/') && post.draft !== true)
+ .sort((a: any, b: any) => new Date(b.date).getTime() - new Date(a.date).getTime())
const escapeXml = (str: string) => {
return str
@@ -22,7 +22,8 @@ export default defineEventHandler(async (event) => {
const rssItems = posts
.map((post) => {
- const link = `${siteUrl}${post._path}`
+ const routePath = post.path?.replace(/^\/en/, '') || ''
+ const link = `${siteUrl}${routePath}`
const pubDate = new Date(post.date).toUTCString()
return `
diff --git a/server/routes/fa/blog/rss.xml.ts b/server/routes/fa/blog/rss.xml.ts
index 174d0dd..1c7677d 100644
--- a/server/routes/fa/blog/rss.xml.ts
+++ b/server/routes/fa/blog/rss.xml.ts
@@ -2,14 +2,14 @@ export default defineEventHandler(async (event) => {
const config = useRuntimeConfig()
const siteUrl = config.public.siteUrl || 'https://example.com'
- // Persian locale
const locale = 'fa'
- // Fetch published blog posts
- const posts = await serverQueryContent(event, `${locale}/blog`)
- .where({ draft: { $ne: true } })
- .sort({ date: -1 })
- .find()
+ // In Nitro server routes, `queryCollection` expects the H3 event as the first argument.
+ const allPosts = await queryCollection(event, 'blog').all()
+
+ const posts = allPosts
+ .filter((post: any) => post.path?.startsWith('/fa/blog/') && post.draft !== true)
+ .sort((a: any, b: any) => new Date(b.date).getTime() - new Date(a.date).getTime())
const escapeXml = (str: string) => {
return str
@@ -22,7 +22,7 @@ export default defineEventHandler(async (event) => {
const rssItems = posts
.map((post) => {
- const link = `${siteUrl}${post._path}`
+ const link = `${siteUrl}${post.path}`
const pubDate = new Date(post.date).toUTCString()
return `