flnxi-Wu/app/api/danmaku/route.ts
WuYongJie f646b71813
Some checks failed
CI Check / check (push) Has been cancelled
chore: 初始化本地仓库并同步最近改动
2025-11-18 17:50:16 +08:00

162 lines
4.7 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.

import { NextRequest, NextResponse } from 'next/server'
import { getServerSession } from 'next-auth'
import { authOptions } from '@/lib/auth'
import { prisma } from '@/lib/prisma'
// GET - 获取弹幕列表(支持SSE)
export async function GET(request: NextRequest) {
try {
const searchParams = request.nextUrl.searchParams
const isSSE = searchParams.get('sse') === 'true'
if (isSSE) {
// Server-Sent Events 实时推送
const encoder = new TextEncoder()
const stream = new ReadableStream({
async start(controller) {
// 发送初始弹幕(限制数量,减少数据传输)
const initialDanmakus = await prisma.danmaku.findMany({
orderBy: { createdAt: 'desc' },
take: 30, // 减少初始数据量
select: {
id: true,
content: true,
username: true,
createdAt: true,
}
})
controller.enqueue(
encoder.encode(`data: ${JSON.stringify(initialDanmakus)}\n\n`)
)
// 记录最后一条弹幕的ID,避免重复推送
let lastDanmakuId = initialDanmakus[0]?.id || null
// 轮询新弹幕(优化:只查询新弹幕)
const interval = setInterval(async () => {
try {
const latestDanmaku = await prisma.danmaku.findFirst({
where: lastDanmakuId ? {
id: { not: lastDanmakuId }
} : undefined,
orderBy: { createdAt: 'desc' },
select: {
id: true,
content: true,
username: true,
createdAt: true,
}
})
if (latestDanmaku) {
lastDanmakuId = latestDanmaku.id
controller.enqueue(
encoder.encode(`data: ${JSON.stringify(latestDanmaku)}\n\n`)
)
}
} catch (error) {
console.error('[Danmaku] SSE error:', error)
// 出错时关闭连接,避免资源泄漏
clearInterval(interval)
controller.close()
}
}, 2000) // 增加轮询间隔到2秒,减少数据库压力
// 清理
request.signal.addEventListener('abort', () => {
clearInterval(interval)
controller.close()
})
}
})
return new NextResponse(stream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
},
})
}
// 普通GET请求(支持分页)
const limit = Math.min(parseInt(searchParams.get('limit') || '50', 10), 100) // 最大100条
const danmakus = await prisma.danmaku.findMany({
orderBy: { createdAt: 'desc' },
take: limit,
select: {
id: true,
content: true,
username: true,
createdAt: true,
// 不返回userId,保护隐私
}
})
return NextResponse.json(danmakus)
} catch (error) {
console.error('Get danmaku error:', error)
return NextResponse.json(
{ error: '获取弹幕失败' },
{ status: 500 }
)
}
}
// POST - 发送弹幕
export async function POST(request: NextRequest) {
try {
const session = await getServerSession(authOptions)
if (!session) {
return NextResponse.json(
{ error: '请先登录' },
{ status: 401 }
)
}
const { content } = await request.json()
if (!content || content.trim().length === 0) {
return NextResponse.json(
{ error: '弹幕内容不能为空' },
{ status: 400 }
)
}
if (content.length > 100) {
return NextResponse.json(
{ error: '弹幕内容不能超过100字' },
{ status: 400 }
)
}
// 生成随机匿名用户名
const randomNames = [
'神秘访客', '路过的设计师', '热心网友', '匿名用户',
'悄悄路过', '神秘嘉宾', '隐身大佬', '低调看客',
'默默围观', '潜水员', '匿名网友', '神秘人士'
]
const anonymousName = randomNames[Math.floor(Math.random() * randomNames.length)]
const danmaku = await prisma.danmaku.create({
data: {
content: content.trim(),
username: anonymousName, // 使用匿名名称
userId: (session.user as any).id
}
})
return NextResponse.json(danmaku)
} catch (error) {
console.error('Send danmaku error:', error)
return NextResponse.json(
{ error: '发送弹幕失败' },
{ status: 500 }
)
}
}