Merge origin/master - keep reading time feature
This commit is contained in:
+56
-1
@@ -1,22 +1,43 @@
|
||||
/**
|
||||
* Blog Post Management Service
|
||||
* Zentrale Verwaltung aller Blog-Beiträge mit automatischer Kategorie-Erkennung
|
||||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { cache } from '@/utils/cache';
|
||||
|
||||
/**
|
||||
* Blog Post Interface
|
||||
* Definiert die Struktur eines Blog-Beitrags
|
||||
*/
|
||||
export interface BlogPost {
|
||||
/** URL-freundlicher Slug */
|
||||
slug: string;
|
||||
/** Titel des Beitrags */
|
||||
title: string;
|
||||
/** Veröffentlichungsdatum (ISO 8601) */
|
||||
date: string;
|
||||
/** Kurze Zusammenfassung */
|
||||
excerpt: string;
|
||||
/** Pfad zum Cover-Bild */
|
||||
coverImage: string;
|
||||
/** Kategorie des Beitrags */
|
||||
category?: string;
|
||||
/** Schlagwörter/Tags */
|
||||
tags?: string[];
|
||||
/** Vollständiger Markdown-Inhalt */
|
||||
content?: string;
|
||||
readingTime?: number;
|
||||
}
|
||||
|
||||
// Verzeichnis der Blog-Beiträge
|
||||
const BLOG_POSTS_DIR = path.join(process.cwd(), 'blog-posts');
|
||||
|
||||
// Category mapping based on filename patterns
|
||||
/**
|
||||
* Kategorie-Mapping basierend auf Dateinamen- und Inhaltsmustern
|
||||
* Ordnet Keywords automatisch passenden Kategorien zu
|
||||
*/
|
||||
const categoryMap: Record<string, string> = {
|
||||
'agentic': 'KI-Agenten',
|
||||
'ai-agent': 'KI-Agenten',
|
||||
@@ -60,6 +81,13 @@ const categoryMap: Record<string, string> = {
|
||||
'home-assistant': 'Smart Home',
|
||||
};
|
||||
|
||||
/**
|
||||
* Erkennt automatisch die Kategorie eines Blog-Posts
|
||||
* Analysiert Dateiname und Inhalt auf Keywords
|
||||
* @param filename - Dateiname des Blog-Posts
|
||||
* @param content - Inhalt des Blog-Posts
|
||||
* @returns Die erkannte Kategorie oder 'Technologie' als Fallback
|
||||
*/
|
||||
function detectCategory(filename: string, content: string): string {
|
||||
const lowerFilename = filename.toLowerCase();
|
||||
const lowerContent = content.toLowerCase().slice(0, 500);
|
||||
@@ -72,6 +100,11 @@ function detectCategory(filename: string, content: string): string {
|
||||
return 'Technologie';
|
||||
}
|
||||
|
||||
/**
|
||||
* Berechnet die geschätzte Lesezeit für einen Blog-Post
|
||||
* @param content - Der Markdown-Inhalt des Posts
|
||||
* @returns Lesezeit in Minuten (mindestens 1)
|
||||
*/
|
||||
export function calculateReadingTime(content: string): number {
|
||||
// Remove markdown syntax for more accurate word count
|
||||
const text = content
|
||||
@@ -92,6 +125,13 @@ export function calculateReadingTime(content: string): number {
|
||||
return Math.max(1, minutes); // Minimum 1 minute
|
||||
}
|
||||
|
||||
/**
|
||||
* Parst einen Markdown-Blog-Post und extrahiert alle Metadaten
|
||||
* Extrahiert Titel, Meta-Description, Keywords, Kategorie und generiert Slug
|
||||
* @param filename - Dateiname des Markdown-Posts
|
||||
* @param content - Vollständiger Markdown-Inhalt
|
||||
* @returns BlogPost-Objekt oder null bei Fehler
|
||||
*/
|
||||
function parseMarkdownPost(filename: string, content: string): BlogPost | null {
|
||||
try {
|
||||
// Extract title from first H1
|
||||
@@ -155,6 +195,11 @@ function parseMarkdownPost(filename: string, content: string): BlogPost | null {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lädt alle Blog-Posts aus dem Dateisystem
|
||||
* Posts werden nach Datum absteigend sortiert (neueste zuerst)
|
||||
* @returns Array aller Blog-Posts
|
||||
*/
|
||||
export function getAllBlogPosts(): BlogPost[] {
|
||||
const cacheKey = 'blog:all-posts';
|
||||
|
||||
@@ -199,11 +244,21 @@ export function getAllBlogPosts(): BlogPost[] {
|
||||
return sortedPosts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sucht einen Blog-Post anhand seines Slugs
|
||||
* @param slug - Der URL-Slug des gesuchten Posts
|
||||
* @returns Der gefundene Blog-Post oder null
|
||||
*/
|
||||
export function getBlogPostBySlug(slug: string): BlogPost | null {
|
||||
const posts = getAllBlogPosts();
|
||||
return posts.find(post => post.slug === slug) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt alle verfügbaren Blog-Post Slugs zurück
|
||||
* Nützlich für statische Seitengenerierung
|
||||
* @returns Array aller Post-Slugs
|
||||
*/
|
||||
export function getBlogPostSlugs(): string[] {
|
||||
return getAllBlogPosts().map(post => post.slug);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user