231 lines
5.3 KiB
TypeScript
231 lines
5.3 KiB
TypeScript
// 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 }
|
|
)
|
|
}
|
|
}
|