예제 소스
오목으로 이해하는 자기대국과 모방 학습 · 파일 59개
route.ts
import { NextRequest, NextResponse } from "next/server";
import { getGameAuthorized, updateGameAuthorized, type GamePatch } from "@/lib/db";
import { PLAY_SIZES } from "@/lib/board";
export const runtime = "nodejs";
type Ctx = { params: Promise<{ code: string }> };
function authFrom(req: NextRequest, body?: { guestId?: string; accessKey?: string }) {
return {
guestId: body?.guestId || req.nextUrl.searchParams.get("guestId"),
accessKey: body?.accessKey || req.nextUrl.searchParams.get("key"),
};
}
export async function GET(req: NextRequest, ctx: Ctx) {
const { code } = await ctx.params;
const result = getGameAuthorized(code, authFrom(req));
if ("error" in result) {
const status = result.error === "not_found" ? 404 : 403;
return NextResponse.json(
{
error: result.error,
message:
result.error === "forbidden"
? "본인 대국만 열 수 있습니다. 다른 기기는 이어두기 링크가 필요합니다."
: "대국을 찾을 수 없습니다.",
},
{ status },
);
}
return NextResponse.json({ game: result.game });
}
export async function PUT(req: NextRequest, ctx: Ctx) {
const { code } = await ctx.params;
const body = (await req.json().catch(() => null)) as
| (GamePatch & { guestId?: string; accessKey?: string })
| null;
if (!body || !Array.isArray(body.board) || !body.size) {
return NextResponse.json({ error: "invalid body" }, { status: 400 });
}
if (!(PLAY_SIZES as readonly number[]).includes(body.size)) {
return NextResponse.json({ error: `unsupported size: ${body.size}` }, { status: 400 });
}
if (body.board.length !== body.size * body.size) {
return NextResponse.json(
{ error: `board length must be ${body.size * body.size}` },
{ status: 400 },
);
}
const { guestId, accessKey, ...patch } = body;
const result = updateGameAuthorized(code, authFrom(req, { guestId, accessKey }), patch);
if ("error" in result) {
const status = result.error === "not_found" ? 404 : 403;
return NextResponse.json({ error: result.error }, { status });
}
return NextResponse.json({ game: result.game });
}