Files
Grow/src/app/api/v1/growth/route.ts
T
arnaudne 0a12cdfbcc fix: sanitize v1 API pagination inputs and validate growth date
- v1/events GET: offset now clamped ≥0, limit fallback prevents NaN
  being passed to Prisma take/skip
- v1/growth POST: validate date is a valid ISO date before new Date()
  to avoid Invalid Date silently propagating into DB

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 23:27:27 +02:00

55 lines
2.4 KiB
TypeScript

import { NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";
import { verifyApiKey, hasScope } from "@/lib/api-auth";
export async function GET(req: Request) {
const ctx = await verifyApiKey(req);
if (!ctx) return NextResponse.json({ error: "Invalid or missing API key" }, { status: 401 });
if (!hasScope(ctx, "growth:read")) return NextResponse.json({ error: "Insufficient scope — requires growth:read" }, { status: 403 });
const { searchParams } = new URL(req.url);
const babyId = searchParams.get("babyId");
if (!babyId) return NextResponse.json({ error: "babyId is required" }, { status: 400 });
const baby = await prisma.baby.findFirst({ where: { id: babyId, familyId: ctx.familyId } });
if (!baby) return NextResponse.json({ error: "Baby not found" }, { status: 404 });
const logs = await prisma.growthLog.findMany({
where: { babyId },
orderBy: { date: "asc" },
select: { id: true, date: true, weight: true, height: true, headCirc: true, notes: true, createdAt: true },
});
return NextResponse.json({ logs });
}
export async function POST(req: Request) {
const ctx = await verifyApiKey(req);
if (!ctx) return NextResponse.json({ error: "Invalid or missing API key" }, { status: 401 });
if (!hasScope(ctx, "growth:write")) return NextResponse.json({ error: "Insufficient scope — requires growth:write" }, { status: 403 });
const body = await req.json().catch(() => null);
if (!body) return NextResponse.json({ error: "Invalid JSON body" }, { status: 400 });
const { babyId, date, weight, height, headCirc, notes } = body;
if (!babyId || !date) return NextResponse.json({ error: "babyId and date are required" }, { status: 400 });
if (isNaN(new Date(date).getTime())) return NextResponse.json({ error: "Invalid date format" }, { status: 400 });
const baby = await prisma.baby.findFirst({ where: { id: babyId, familyId: ctx.familyId } });
if (!baby) return NextResponse.json({ error: "Baby not found" }, { status: 404 });
const log = await prisma.growthLog.create({
data: {
babyId,
date: new Date(date),
weight: weight != null ? Number(weight) : null,
height: height != null ? Number(height) : null,
headCirc: headCirc != null ? Number(headCirc) : null,
notes: notes ?? null,
},
select: { id: true, date: true, weight: true, height: true, headCirc: true, notes: true, createdAt: true },
});
return NextResponse.json({ log }, { status: 201 });
}