// app/api/sync/route.ts // 实时同步API - 监听文件变化并同步到前端 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') const SYNC_LOG_FILE = path.join(process.cwd(), 'SYNC_LOG.md') // GET: 获取同步状态 export async function GET(req: NextRequest) { try { const { searchParams } = new URL(req.url) const action = searchParams.get('action') if (action === 'status') { // 返回同步状态 const lastSync = getLastSyncTime() const changedFiles = getChangedFiles(lastSync) return NextResponse.json({ success: true, lastSync, changedFiles: changedFiles.length, files: changedFiles, needSync: changedFiles.length > 0 }) } if (action === 'log') { // 返回同步日志 const log = fs.existsSync(SYNC_LOG_FILE) ? fs.readFileSync(SYNC_LOG_FILE, 'utf-8') : '暂无同步日志' return NextResponse.json({ success: true, log }) } return NextResponse.json( { error: '未知操作' }, { status: 400 } ) } catch (error) { console.error('获取同步状态失败:', error) return NextResponse.json( { error: '获取同步状态失败' }, { status: 500 } ) } } // POST: 执行同步 export async function POST(req: NextRequest) { try { const body = await req.json() const { force } = body const lastSync = force ? 0 : getLastSyncTime() const changedFiles = getChangedFiles(lastSync) if (changedFiles.length === 0 && !force) { return NextResponse.json({ success: true, message: '没有需要同步的文件', synced: 0 }) } // 执行同步(这里可以触发构建或缓存更新) const syncResults = await syncFiles(changedFiles) // 更新同步时间 updateSyncLog(changedFiles, syncResults) return NextResponse.json({ success: true, message: `成功同步 ${syncResults.success} 个文件`, synced: syncResults.success, failed: syncResults.failed, files: changedFiles }) } catch (error) { console.error('同步失败:', error) return NextResponse.json( { error: '同步失败' }, { status: 500 } ) } } // 获取最后同步时间 function getLastSyncTime(): number { try { if (!fs.existsSync(SYNC_LOG_FILE)) { return 0 } const log = fs.readFileSync(SYNC_LOG_FILE, 'utf-8') const match = log.match(/最后同步时间: (\d+)/) return match ? parseInt(match[1]) : 0 } catch (error) { return 0 } } // 获取变化的文件 function getChangedFiles(since: number): string[] { const changedFiles: string[] = [] function scanDirectory(dir: string) { const items = fs.readdirSync(dir) items.forEach(item => { const fullPath = path.join(dir, item) const stat = fs.statSync(fullPath) if (stat.isDirectory()) { scanDirectory(fullPath) } else if (item.endsWith('.md')) { // 检查文件修改时间 if (stat.mtimeMs > since) { changedFiles.push(fullPath) } } }) } scanDirectory(BOOK_DIR) return changedFiles } // 同步文件 async function syncFiles(files: string[]): Promise<{ success: number; failed: number }> { let success = 0 let failed = 0 for (const file of files) { try { // 读取文件内容 const content = fs.readFileSync(file, 'utf-8') const { data } = matter(content) // 这里可以执行实际的同步操作: // 1. 更新数据库 // 2. 清除缓存 // 3. 触发CDN刷新 // 4. 推送通知 console.log(`同步文件: ${file}`) success++ } catch (error) { console.error(`同步文件失败: ${file}`, error) failed++ } } return { success, failed } } // 更新同步日志 function updateSyncLog(files: string[], results: { success: number; failed: number }) { const timestamp = Date.now() const date = new Date().toISOString() let log = fs.existsSync(SYNC_LOG_FILE) ? fs.readFileSync(SYNC_LOG_FILE, 'utf-8') : '# 同步日志\n\n' const newEntry = ` ## ${date} - 同步文件数: ${files.length} - 成功: ${results.success} - 失败: ${results.failed} - 文件列表: ${files.map(f => ` - ${path.relative(BOOK_DIR, f)}`).join('\n')} 最后同步时间: ${timestamp} --- ` log = newEntry + log fs.writeFileSync(SYNC_LOG_FILE, log, 'utf-8') } // PUT: 手动标记文件为已同步 export async function PUT(req: NextRequest) { try { const body = await req.json() const { file } = body if (!file) { return NextResponse.json( { error: '文件路径不能为空' }, { status: 400 } ) } const filePath = path.join(BOOK_DIR, file) if (!fs.existsSync(filePath)) { return NextResponse.json( { error: '文件不存在' }, { status: 404 } ) } // 更新文件的访问时间和修改时间为当前时间 const now = new Date() fs.utimesSync(filePath, now, now) return NextResponse.json({ success: true, message: '文件已标记为同步' }) } catch (error) { return NextResponse.json( { error: '操作失败' }, { status: 500 } ) } }