49 lines
1.6 KiB
JavaScript
49 lines
1.6 KiB
JavaScript
// 从 content/articles/*.md 读取文章列表和详情
|
|
import fs from 'node:fs'
|
|
import path from 'node:path'
|
|
import matter from 'gray-matter'
|
|
import { marked } from 'marked'
|
|
|
|
const DIR = path.join(process.cwd(), 'content', 'articles')
|
|
|
|
function readAll() {
|
|
if (!fs.existsSync(DIR)) return []
|
|
return fs.readdirSync(DIR)
|
|
.filter((f) => f.endsWith('.md'))
|
|
.map((fileName) => {
|
|
const fullPath = path.join(DIR, fileName)
|
|
const raw = fs.readFileSync(fullPath, 'utf8')
|
|
const { data, content } = matter(raw)
|
|
const slug = fileName.replace(/\.md$/, '')
|
|
const dateMatch = fileName.match(/^(\d{4}-\d{2}-\d{2})-/)
|
|
const date = dateMatch ? dateMatch[1] : ''
|
|
return {
|
|
slug,
|
|
date,
|
|
title: data.title || slug.replace(/^\d{4}-\d{2}-\d{2}-/, ''),
|
|
excerpt: content.replace(/\s+/g, ' ').trim().slice(0, 150) + (content.length > 150 ? '...' : ''),
|
|
}
|
|
})
|
|
.sort((a, b) => (a.date < b.date ? 1 : -1))
|
|
}
|
|
|
|
export function listArticles() {
|
|
return readAll().map(({ slug, title, date, excerpt }) => ({ slug, title, date, excerpt }))
|
|
}
|
|
|
|
export function getArticle(slug) {
|
|
// 防止路径穿越
|
|
if (slug.includes('/') || slug.includes('\\') || slug.includes('..')) return null
|
|
const fullPath = path.join(DIR, slug + '.md')
|
|
if (!fs.existsSync(fullPath)) return null
|
|
const raw = fs.readFileSync(fullPath, 'utf8')
|
|
const { data, content } = matter(raw)
|
|
const dateMatch = slug.match(/^(\d{4}-\d{2}-\d{2})-/)
|
|
const date = dateMatch ? dateMatch[1] : ''
|
|
return {
|
|
slug,
|
|
date,
|
|
title: data.title || slug.replace(/^\d{4}-\d{2}-\d{2}-/, ''),
|
|
contentHtml: marked.parse(content),
|
|
}
|
|
} |