SEO blog post V2

This commit is contained in:
2026-08-05 22:28:34 +02:00
parent 8c50bf71c5
commit ca605f8852
7 changed files with 7456 additions and 8477 deletions

5
.gitignore vendored
View File

@@ -77,6 +77,11 @@ meta-fix.js
read-inbox.mjs read-inbox.mjs
quora_antwort_statisch_dynamisch.txt quora_antwort_statisch_dynamisch.txt
# Local blog audit reports and temporary snapshots
scratch_blog_analysis.json
scratch_scored_blog_posts.json
src/lib/blog-data.snapshot-*.ts
# Local developer-package workspaces and unreferenced generated media # Local developer-package workspaces and unreferenced generated media
/packages/ /packages/
/public/Events/ /public/Events/

5569
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -8,6 +8,7 @@ import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/Card';
import { Badge } from '@/components/ui/Badge'; import { Badge } from '@/components/ui/Badge';
import Breadcrumbs, { BreadcrumbItem } from '@/components/Breadcrumbs'; import Breadcrumbs, { BreadcrumbItem } from '@/components/Breadcrumbs';
import { blogPosts } from '@/lib/blog-data'; import { blogPosts } from '@/lib/blog-data';
import { REDIRECTED_BLOG_SLUGS } from "@/lib/content";
// Enable Incremental Static Regeneration (ISR) // Enable Incremental Static Regeneration (ISR)
// Revalidate every hour // Revalidate every hour
@@ -63,6 +64,7 @@ export default function BlogPage() {
const publishedPosts = blogPosts const publishedPosts = blogPosts
.filter(post => { .filter(post => {
if (REDIRECTED_BLOG_SLUGS.has(post.slug)) return false;
const publishDate = post.datePublished ? new Date(post.datePublished) : new Date(post.date); const publishDate = post.datePublished ? new Date(post.datePublished) : new Date(post.date);
return publishDate <= currentDate; return publishDate <= currentDate;
}) })

View File

@@ -1,5 +1,6 @@
import { MetadataRoute } from 'next'; import { MetadataRoute } from 'next';
import { blogPosts } from '../lib/blog-data'; import { blogPosts } from '../lib/blog-data';
import { REDIRECTED_BLOG_SLUGS } from '../lib/content';
import { pillarMeta } from '../lib/pillar-data'; import { pillarMeta } from '../lib/pillar-data';
import { authors } from '../lib/author-data'; import { authors } from '../lib/author-data';
import { industryPages } from '../lib/industry-pages'; import { industryPages } from '../lib/industry-pages';
@@ -42,6 +43,7 @@ export default function sitemap(): MetadataRoute.Sitemap {
// Filter out future posts so Google doesn't see them // Filter out future posts so Google doesn't see them
const blogPages = blogPosts const blogPages = blogPosts
.filter((post) => { .filter((post) => {
if (REDIRECTED_BLOG_SLUGS.has(post.slug)) return false;
const publishDate = post.datePublished const publishDate = post.datePublished
? new Date(post.datePublished) ? new Date(post.datePublished)
: new Date(post.date); : new Date(post.date);

File diff suppressed because it is too large Load Diff

View File

@@ -2,10 +2,27 @@ import { blogPosts } from "./blog-data";
import { authors } from "./author-data"; import { authors } from "./author-data";
import type { BlogPost, PillarKey, AuthorProfile } from "./types"; import type { BlogPost, PillarKey, AuthorProfile } from "./types";
/**
* Blog slugs that are 301-redirected in next.config.mjs.
* They must not appear in the blog index, the learn pillars, the sitemap
* or IndexNow submissions - linking to a redirect source wastes crawl budget
* and passes link equity through an unnecessary hop.
* Keep this in sync with the redirects() block in next.config.mjs.
*/
export const REDIRECTED_BLOG_SLUGS = new Set<string>([
"qr-code-analytics",
"qr-code-restaurant-menu",
]);
export function isRedirectedSlug(slug: string): boolean {
return REDIRECTED_BLOG_SLUGS.has(slug);
}
export function getPublishedPosts(): BlogPost[] { export function getPublishedPosts(): BlogPost[] {
const currentDate = new Date(); const currentDate = new Date();
return blogPosts.filter(p => { return blogPosts.filter(p => {
if (!p.published) return false; if (!p.published) return false;
if (REDIRECTED_BLOG_SLUGS.has(p.slug)) return false;
const publishDate = p.datePublished ? new Date(p.datePublished) : new Date(p.date); const publishDate = p.datePublished ? new Date(p.datePublished) : new Date(p.date);
return publishDate <= currentDate; return publishDate <= currentDate;
}); });

View File

@@ -1,6 +1,7 @@
import axios from 'axios'; import axios from 'axios';
import dotenv from 'dotenv'; import dotenv from 'dotenv';
import { blogPosts } from '../lib/blog-data'; import { blogPosts } from '../lib/blog-data';
import { REDIRECTED_BLOG_SLUGS } from './content';
import { pillarMeta } from '../lib/pillar-data'; import { pillarMeta } from '../lib/pillar-data';
import { authors } from '../lib/author-data'; import { authors } from '../lib/author-data';
import { publishedUseCases } from '../lib/growth-pages'; import { publishedUseCases } from '../lib/growth-pages';
@@ -73,7 +74,9 @@ export function getAllIndexableUrls(): string[] {
]; ];
// Blog posts // Blog posts
const blogPages = blogPosts.map(post => `${baseUrl}/blog/${post.slug}`); const blogPages = blogPosts
.filter(post => !REDIRECTED_BLOG_SLUGS.has(post.slug))
.map(post => `${baseUrl}/blog/${post.slug}`);
// Main pages (synced with sitemap.ts) // Main pages (synced with sitemap.ts)
const mainPages = [ const mainPages = [