From b3d845a532d685639ee09b69ab6ad01e4639d231 Mon Sep 17 00:00:00 2001 From: gahusb Date: Thu, 2 Jul 2026 21:04:48 +0900 Subject: [PATCH] =?UTF-8?q?feat(phase2):=20=ED=83=80=EB=A1=9C=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=C2=B7=EC=A1=B0=ED=9A=8C=20API=20(user=5Fid=20+=20RLS?= =?UTF-8?q?=20=EB=B3=B8=EC=9D=B8=20=EC=A1=B0=ED=9A=8C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - POST /api/tarot/readings: 로그인 필수, interpretation_json 검증 후 insert - GET /api/tarot/readings: 세션 클라이언트로 본인 것만 조회 (RLS tarot_select_own), 최신순 - Task 6·9가 소비 Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01AAtcmKKtqDUe4NyVgy1aLQ --- app/api/tarot/readings/route.ts | 50 +++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 app/api/tarot/readings/route.ts diff --git a/app/api/tarot/readings/route.ts b/app/api/tarot/readings/route.ts new file mode 100644 index 0000000..76e856b --- /dev/null +++ b/app/api/tarot/readings/route.ts @@ -0,0 +1,50 @@ +import { NextResponse } from 'next/server'; +import { createClient } from '@/lib/supabase/server'; +import { createAdminClient } from '@/lib/supabase/admin'; + +export const runtime = 'nodejs'; + +export async function POST(request: Request) { + const supabase = await createClient(); + const { data: { user } } = await supabase.auth.getUser(); + if (!user) return NextResponse.json({ error: '로그인이 필요합니다.' }, { status: 401 }); + + let body: Record; + try { + body = await request.json(); + } catch { + return NextResponse.json({ error: '잘못된 요청 형식' }, { status: 400 }); + } + + const interp = body.interpretation_json as { summary?: string } | undefined; + if (!interp) return NextResponse.json({ error: 'interpretation_json 필요' }, { status: 400 }); + + const admin = createAdminClient(); + const { data, error } = await admin.from('tarot_readings').insert({ + user_id: user.id, + spread_type: (body.spread_type as string) ?? 'three_card', + category: (body.category as string) ?? null, + question: (body.question as string) ?? null, + cards: body.cards ?? [], + interpretation: interp, + summary: interp.summary ?? null, + }).select('id, created_at').single(); + + if (error) return NextResponse.json({ error: error.message }, { status: 500 }); + return NextResponse.json(data); +} + +export async function GET() { + const supabase = await createClient(); + const { data: { user } } = await supabase.auth.getUser(); + if (!user) return NextResponse.json({ error: '로그인이 필요합니다.' }, { status: 401 }); + + // 세션 클라이언트로 본인 것만(RLS tarot_select_own) + const { data, error } = await supabase + .from('tarot_readings') + .select('id, spread_type, category, question, cards, interpretation, summary, created_at') + .order('created_at', { ascending: false }); + + if (error) return NextResponse.json({ error: error.message }, { status: 500 }); + return NextResponse.json({ readings: data ?? [] }); +}