import { NextRequest, NextResponse } from "next/server"; import { z } from "zod"; import { db, mealPlans, mealPlanMembers, users, eq, and } from "@epicure/db"; import { requireSession } from "@/lib/api-auth"; type Params = { params: Promise<{ weekStart: string }> }; async function getOrCreatePlan(userId: string, weekStart: string) { const existing = await db.query.mealPlans.findFirst({ where: and(eq(mealPlans.userId, userId), eq(mealPlans.weekStart, weekStart)), }); if (existing) return existing; const id = crypto.randomUUID(); await db.insert(mealPlans).values({ id, userId, weekStart }); return { id, userId, weekStart, createdAt: new Date() }; } // ─── GET /api/v1/meal-plans/[weekStart]/members ────────────────────────────── // Owner only — returns members joined with basic user info. export async function GET(_req: NextRequest, { params }: Params) { const { session, response } = await requireSession(); if (response) return response; const { weekStart } = await params; const plan = await db.query.mealPlans.findFirst({ where: and(eq(mealPlans.userId, session!.user.id), eq(mealPlans.weekStart, weekStart)), }); if (!plan) return NextResponse.json([]); const members = await db.query.mealPlanMembers.findMany({ where: eq(mealPlanMembers.mealPlanId, plan.id), with: { user: true }, }); return NextResponse.json( members.map((m) => ({ id: m.id, userId: m.userId, role: m.role, createdAt: m.createdAt, user: { name: m.user.name, username: m.user.username, avatarUrl: m.user.avatarUrl }, })) ); } // ─── POST /api/v1/meal-plans/[weekStart]/members ───────────────────────────── // Owner only — invite by email or userId. Auto-creates the plan for this week if missing. const InviteSchema = z .object({ email: z.string().email().optional(), userId: z.string().optional(), role: z.enum(["viewer", "editor"]), }) .refine((d) => d.email !== undefined || d.userId !== undefined, { message: "Provide either email or userId", }); export async function POST(req: NextRequest, { params }: Params) { const { session, response } = await requireSession(); if (response) return response; const { weekStart } = await params; const body = await req.json() as unknown; const parsed = InviteSchema.safeParse(body); if (!parsed.success) return NextResponse.json({ error: "Validation error" }, { status: 400 }); const { email, userId, role } = parsed.data; const targetUser = await db.query.users.findFirst({ where: email ? eq(users.email, email) : eq(users.id, userId!), }); if (!targetUser) return NextResponse.json({ error: "User not found" }, { status: 404 }); if (targetUser.id === session!.user.id) { return NextResponse.json({ error: "Cannot invite yourself" }, { status: 400 }); } const plan = await getOrCreatePlan(session!.user.id, weekStart); const existing = await db.query.mealPlanMembers.findFirst({ where: and(eq(mealPlanMembers.mealPlanId, plan.id), eq(mealPlanMembers.userId, targetUser.id)), }); if (existing) return NextResponse.json({ error: "Already a member" }, { status: 409 }); const memberId = crypto.randomUUID(); await db.insert(mealPlanMembers).values({ id: memberId, mealPlanId: plan.id, userId: targetUser.id, role, }); return NextResponse.json({ id: memberId, mealPlanId: plan.id }, { status: 201 }); } // ─── DELETE /api/v1/meal-plans/[weekStart]/members?memberId=… ──────────────── // Owner OR the member themselves can remove. export async function DELETE(req: NextRequest, { params }: Params) { const { session, response } = await requireSession(); if (response) return response; const { weekStart } = await params; const memberId = req.nextUrl.searchParams.get("memberId"); if (!memberId) return NextResponse.json({ error: "memberId required" }, { status: 400 }); const plan = await db.query.mealPlans.findFirst({ where: and(eq(mealPlans.userId, session!.user.id), eq(mealPlans.weekStart, weekStart)), }); if (!plan) return NextResponse.json({ error: "Not found" }, { status: 404 }); const member = await db.query.mealPlanMembers.findFirst({ where: and(eq(mealPlanMembers.id, memberId), eq(mealPlanMembers.mealPlanId, plan.id)), }); if (!member) return NextResponse.json({ error: "Not found" }, { status: 404 }); const isOwner = plan.userId === session!.user.id; const isSelf = member.userId === session!.user.id; if (!isOwner && !isSelf) return NextResponse.json({ error: "Forbidden" }, { status: 403 }); await db.delete(mealPlanMembers).where(eq(mealPlanMembers.id, memberId)); return new NextResponse(null, { status: 204 }); }