치지직 채팅창 인식 추가 및 주사위 횟수 증가 추가.
This commit is contained in:
83
app/api/rooms/[roomId]/events/route.ts
Normal file
83
app/api/rooms/[roomId]/events/route.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import {
|
||||
cancelScheduledDisconnectRoomChat,
|
||||
scheduleDisconnectRoomChat,
|
||||
} from '@/lib/chzzk/chat-session';
|
||||
import {
|
||||
addRoomViewer,
|
||||
getRoom,
|
||||
removeRoomViewer,
|
||||
subscribeRoom,
|
||||
toPublicRoom,
|
||||
type PublicRoomState,
|
||||
} from '@/lib/rooms/store';
|
||||
|
||||
export const runtime = 'nodejs';
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
type Params = { params: Promise<{ roomId: string }> };
|
||||
|
||||
export async function GET(request: Request, { params }: Params) {
|
||||
const { roomId } = await params;
|
||||
const room = getRoom(roomId);
|
||||
if (!room) {
|
||||
return new Response(JSON.stringify({ error: '방을 찾을 수 없습니다.' }), {
|
||||
status: 404,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
}
|
||||
|
||||
const clientId =
|
||||
new URL(request.url).searchParams.get('clientId')?.trim() ||
|
||||
crypto.randomUUID();
|
||||
|
||||
const encoder = new TextEncoder();
|
||||
let unsubscribe: (() => void) | null = null;
|
||||
let heartbeat: ReturnType<typeof setInterval> | null = null;
|
||||
let presenceHeld = false;
|
||||
|
||||
const release = () => {
|
||||
if (!presenceHeld) return;
|
||||
presenceHeld = false;
|
||||
const { emptied } = removeRoomViewer(roomId, clientId);
|
||||
if (emptied) {
|
||||
// 즉시 끊지 않음 — 빠른 재입장과 경쟁하지 않도록 유예
|
||||
scheduleDisconnectRoomChat(roomId);
|
||||
}
|
||||
};
|
||||
|
||||
const stream = new ReadableStream({
|
||||
start(controller) {
|
||||
// 재입장 시 예약된 disconnect 취소
|
||||
cancelScheduledDisconnectRoomChat(roomId);
|
||||
addRoomViewer(roomId, clientId);
|
||||
presenceHeld = true;
|
||||
|
||||
const send = (event: string, data: unknown) => {
|
||||
controller.enqueue(
|
||||
encoder.encode(`event: ${event}\ndata: ${JSON.stringify(data)}\n\n`),
|
||||
);
|
||||
};
|
||||
|
||||
send('room', toPublicRoom(room));
|
||||
unsubscribe = subscribeRoom(roomId, (state: PublicRoomState) => {
|
||||
send('room', state);
|
||||
});
|
||||
heartbeat = setInterval(() => {
|
||||
controller.enqueue(encoder.encode(`: ping\n\n`));
|
||||
}, 15000);
|
||||
},
|
||||
cancel() {
|
||||
if (unsubscribe) unsubscribe();
|
||||
if (heartbeat) clearInterval(heartbeat);
|
||||
release();
|
||||
},
|
||||
});
|
||||
|
||||
return new Response(stream, {
|
||||
headers: {
|
||||
'Content-Type': 'text/event-stream; charset=utf-8',
|
||||
'Cache-Control': 'no-cache, no-transform',
|
||||
Connection: 'keep-alive',
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user