Files
chorus-ping-blog/lib/blog.ts
anthonyrawlins 6e13451dc4 Initial commit: CHORUS PING! blog
- Next.js 14 blog application with theme support
- Docker containerization with volume bindings
- Traefik integration with Let's Encrypt SSL
- MDX support for blog posts
- Theme toggle with localStorage persistence
- Scheduled posts directory structure
- Brand guidelines compliance with CHORUS colors

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-27 14:46:26 +10:00

106 lines
2.9 KiB
TypeScript

import fs from 'fs'
import path from 'path'
import matter from 'gray-matter'
import { BlogPost, BlogMeta } from '@/types/blog'
const postsDirectory = path.join(process.cwd(), 'content/posts')
export function getSortedPostsData(): BlogPost[] {
// Get file names under /content/posts
const fileNames = fs.readdirSync(postsDirectory)
const allPostsData = fileNames
.filter((fileName) => fileName.endsWith('.md'))
.map((fileName) => {
// Remove ".md" from file name to get id
const slug = fileName.replace(/\.md$/, '')
// Read markdown file as string
const fullPath = path.join(postsDirectory, fileName)
const fileContents = fs.readFileSync(fullPath, 'utf8')
// Use gray-matter to parse the post metadata section
const matterResult = matter(fileContents)
const meta = matterResult.data as BlogMeta
// Calculate reading time (average 200 words per minute)
const wordCount = matterResult.content.split(/\s+/).length
const readingTime = Math.ceil(wordCount / 200)
return {
slug,
content: matterResult.content,
readingTime,
...meta,
} as BlogPost
})
// Sort posts by date
return allPostsData.sort((a, b) => {
if (a.date < b.date) {
return 1
} else {
return -1
}
})
}
export function getAllPostSlugs() {
const fileNames = fs.readdirSync(postsDirectory)
return fileNames
.filter((fileName) => fileName.endsWith('.md'))
.map((fileName) => {
return {
params: {
slug: fileName.replace(/\.md$/, ''),
},
}
})
}
export function getPostData(slug: string): BlogPost | null {
try {
const fullPath = path.join(postsDirectory, `${slug}.md`)
const fileContents = fs.readFileSync(fullPath, 'utf8')
// Use gray-matter to parse the post metadata section
const matterResult = matter(fileContents)
const meta = matterResult.data as BlogMeta
// Calculate reading time (average 200 words per minute)
const wordCount = matterResult.content.split(/\s+/).length
const readingTime = Math.ceil(wordCount / 200)
return {
slug,
content: matterResult.content,
readingTime,
...meta,
} as BlogPost
} catch (error) {
console.error(`Error reading post ${slug}:`, error)
return null
}
}
export function getFeaturedPosts(): BlogPost[] {
const allPosts = getSortedPostsData()
return allPosts.filter(post => post.featured)
}
export function getPostsByTag(tag: string): BlogPost[] {
const allPosts = getSortedPostsData()
return allPosts.filter(post =>
post.tags.some(t => t.toLowerCase() === tag.toLowerCase())
)
}
export function getAllTags(): string[] {
const allPosts = getSortedPostsData()
const tags = new Set<string>()
allPosts.forEach(post => {
post.tags.forEach(tag => tags.add(tag))
})
return Array.from(tags).sort()
}