비행기(세계여행) 칸 선택 이동, 주사위 무게감, 설정 패널·주사위 초기화, 개인정보 고지·파비콘·탭 제목을 정리하고 런타임 로그는 추적에서 제외한다. Co-authored-by: Cursor <cursoragent@cursor.com>
129 lines
3.5 KiB
TypeScript
129 lines
3.5 KiB
TypeScript
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,
|
|
});
|
|
}
|