feat: Initial Diabetix application commit
- Add authentication with NextAuth v5 (credentials + email verification) - Implement dashboard with glycemia tracking and AI analysis - Add PDF report generation for Premium users - Implement Stripe integration for Premium subscriptions - Add responsive UI with Tailwind CSS and shadcn components - Database schema with Prisma ORM and PostgreSQL support - Real-time glycemia visualization with Recharts - Mobile-optimized entry form - User profile management with medical information - Subscription lifecycle management (create, cancel, webhook) - Email notifications with Resend - Feature gates for Free vs Premium plans Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
72
src/app/api/readings/route.ts
Normal file
72
src/app/api/readings/route.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import type { Moment } from "@/lib/glycemia";
|
||||
|
||||
const VALID_MOMENTS: Moment[] = ["FASTING", "LUNCH", "DINNER"];
|
||||
|
||||
export async function GET(request: Request) {
|
||||
const session = await auth();
|
||||
if (!session?.user?.id) return NextResponse.json({ error: "Non authentifié" }, { status: 401 });
|
||||
const userId = session.user.id;
|
||||
|
||||
const url = new URL(request.url);
|
||||
const limit = Number(url.searchParams.get("limit") ?? 200);
|
||||
const from = url.searchParams.get("from");
|
||||
const to = url.searchParams.get("to");
|
||||
|
||||
const where: Record<string, unknown> = { userId };
|
||||
if (from || to) {
|
||||
where.measuredAt = {
|
||||
...(from ? { gte: new Date(from) } : {}),
|
||||
...(to ? { lte: new Date(to) } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
const readings = await prisma.reading.findMany({
|
||||
where,
|
||||
orderBy: { measuredAt: "desc" },
|
||||
take: Math.min(Math.max(limit, 1), 1000),
|
||||
});
|
||||
|
||||
return NextResponse.json(readings);
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const session = await auth();
|
||||
if (!session?.user?.id) return NextResponse.json({ error: "Non authentifié" }, { status: 401 });
|
||||
const userId = session.user.id;
|
||||
|
||||
const body = await request.json().catch(() => null);
|
||||
if (!body) return NextResponse.json({ error: "Invalid JSON" }, { status: 400 });
|
||||
|
||||
const { measuredAt, moment, value, notes } = body as {
|
||||
measuredAt?: string;
|
||||
moment?: string;
|
||||
value?: number | string;
|
||||
notes?: string | null;
|
||||
};
|
||||
|
||||
if (!moment || !VALID_MOMENTS.includes(moment as Moment)) {
|
||||
return NextResponse.json({ error: "Moment invalide" }, { status: 400 });
|
||||
}
|
||||
const numericValue = typeof value === "string" ? parseFloat(value.replace(",", ".")) : value;
|
||||
if (typeof numericValue !== "number" || Number.isNaN(numericValue) || numericValue <= 0) {
|
||||
return NextResponse.json({ error: "Valeur invalide" }, { status: 400 });
|
||||
}
|
||||
if (numericValue > 6) {
|
||||
return NextResponse.json({ error: "Valeur trop élevée (>6 g/L)" }, { status: 400 });
|
||||
}
|
||||
|
||||
const reading = await prisma.reading.create({
|
||||
data: {
|
||||
userId,
|
||||
measuredAt: measuredAt ? new Date(measuredAt) : new Date(),
|
||||
moment,
|
||||
value: numericValue,
|
||||
notes: notes?.trim() || null,
|
||||
},
|
||||
});
|
||||
|
||||
return NextResponse.json(reading, { status: 201 });
|
||||
}
|
||||
Reference in New Issue
Block a user