Files
soul/app/api/book/chapter/[id]/route.ts

90 lines
2.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// app/api/book/chapter/[id]/route.ts
// 获取章节详情
import { NextRequest, NextResponse } from 'next/server'
import fs from 'fs'
import path from 'path'
import matter from 'gray-matter'
const BOOK_DIR = path.join(process.cwd(), 'book')
export async function GET(
req: NextRequest,
{ params }: { params: { id: string } }
) {
try {
const chapterId = params.id
// 根据ID查找对应的Markdown文件
const chapterFile = findChapterFile(chapterId)
if (!chapterFile) {
return NextResponse.json(
{ error: '章节不存在' },
{ status: 404 }
)
}
const fileContent = fs.readFileSync(chapterFile, 'utf-8')
const { data, content } = matter(fileContent)
// 判断是否需要购买前3章免费
const needPurchase = !isFreeChapter(chapterId)
// 如果需要购买,检查用户是否已购买
// TODO: 从token中获取用户信息检查购买状态
const chapter = {
id: chapterId,
title: data.title || path.basename(chapterFile, '.md'),
content: content,
words: content.length,
updateTime: data.date || fs.statSync(chapterFile).mtime.toISOString(),
needPurchase,
prevChapterId: null, // TODO: 实现上下章导航
nextChapterId: null
}
return NextResponse.json(chapter)
} catch (error) {
console.error('获取章节失败:', error)
return NextResponse.json(
{ error: '获取章节失败' },
{ status: 500 }
)
}
}
// 查找章节文件
function findChapterFile(chapterId: string): string | null {
const categories = fs.readdirSync(BOOK_DIR).filter(item => {
const itemPath = path.join(BOOK_DIR, item)
return fs.statSync(itemPath).isDirectory()
})
for (const category of categories) {
const categoryPath = path.join(BOOK_DIR, category)
const files = fs.readdirSync(categoryPath).filter(file => file.endsWith('.md'))
for (const file of files) {
const slug = `${category}/${file.replace('.md', '')}`
if (slug === chapterId || file.replace('.md', '') === chapterId) {
return path.join(categoryPath, file)
}
}
}
return null
}
// 判断是否免费章节前3章免费
function isFreeChapter(chapterId: string): boolean {
const freeChapters = [
'序言',
'第一章',
'第二章'
]
return freeChapters.some(free => chapterId.includes(free))
}