import { NextResponse } from 'next/server'; import { parseChzzkChannelId } from '@/lib/chzzk/channel'; import { allowChannelIdJoin, isChzzkOAuthConfigured, requireChzzkLoginForChannelRooms, } from '@/lib/auth/mode'; import { getRequestAuthSession } from '@/lib/auth/room-access'; import { cancelScheduledDisconnectRoomChat, ensureRoomChat, } from '@/lib/chzzk/chat-session'; import { clearDiceQueue, createRoom, findRoomByChannelId, getRoom, notifyRoom, toPublicRoom, } from '@/lib/rooms/store'; export const runtime = 'nodejs'; /** * 치지직 채널 ID(또는 라이브 URL)로 룸 입장. * - 개발: 채널 ID 직접 입장 가능 (+ 로그인 세션 있으면 본인 채널도 가능) * - 릴리즈: 로그인 세션의 채널 ID와 일치할 때만 입장 */ export async function POST(request: Request) { const body = (await request.json().catch(() => ({}))) as { channelIdOrUrl?: string; cheesePerDice?: number; multiplyByCheese?: boolean; maxMultiplier?: number; resetDiceQueue?: boolean; }; const session = await getRequestAuthSession(request); const raw = body.channelIdOrUrl || ''; let channelId = parseChzzkChannelId(raw); if (requireChzzkLoginForChannelRooms()) { if (!session) { return NextResponse.json( { error: '릴리즈 환경에서는 치지직 로그인 후 본인 채널로만 입장할 수 있습니다.', code: 'CHZZK_AUTH_REQUIRED', allowChannelIdJoin: allowChannelIdJoin(), oauthConfigured: isChzzkOAuthConfigured(), }, { status: 401 }, ); } // 입력된 채널이 있으면 세션과 일치해야 함. 없으면 세션 채널 사용. if (channelId && channelId !== session.channelId) { return NextResponse.json( { error: '로그인한 채널과 다른 채널 ID로는 입장할 수 없습니다.', code: 'CHANNEL_MISMATCH', }, { status: 403 }, ); } channelId = session.channelId; } else if (!channelId) { // 개발: 세션만 있으면 세션 채널로 입장 가능 if (session?.channelId) channelId = session.channelId; } if (!channelId) { return NextResponse.json( { error: '유효한 치지직 채널 ID 또는 라이브 URL이 필요합니다. 예: https://chzzk.naver.com/live/{채널ID}', }, { status: 400 }, ); } let room = findRoomByChannelId(channelId); let created = false; if (!room) { created = true; room = createRoom({ chzzkEnabled: true, liveUrl: raw.trim() || `https://chzzk.naver.com/live/${channelId}`, channelId, cheesePerDice: body.cheesePerDice, multiplyByCheese: body.multiplyByCheese, maxMultiplier: body.maxMultiplier, }); } cancelScheduledDisconnectRoomChat(room.id); if (body.resetDiceQueue) { clearDiceQueue(room.id); notifyRoom(room.id); } if (room.settings.chzzkEnabled) { try { await ensureRoomChat(room.id); } catch (e) { if (created) { return NextResponse.json( { error: e instanceof Error ? e.message : '채팅 연결에 실패했습니다. 방송 중인지 확인해 주세요.', room: toPublicRoom(room), created, }, { status: 502 }, ); } } } const latest = getRoom(room.id) ?? room; return NextResponse.json({ room: toPublicRoom(latest), created, }); }