feat: UX improvements, webhooks, invite management
- Photo lightbox: ArrowLeft/Right/Escape keyboard navigation - Growth chart: persistent WHO percentile legend (not hover-only) - Sleep stats: configurable window selector (18h/19h/20h/21h) - Quick-add FAB: now visible on desktop (bottom-right corner) - Invite code reset button in settings (non-admin, PARENT role only) - Pending email invites: tracked per-token with 7-day expiry, revocable from settings UI; new /auth/invite/t/[token] accept page - Webhooks: Prisma model, CRUD API, HMAC-SHA256 dispatcher wired into event.created / growth.created / doctor_note.created; settings UI - Docker cron service: medication reminders every 15min, milk-expiry and daily-digest daily; secured with CRON_SECRET - Photo uploads persist across redeploys via Docker named volume Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import bcrypt from "bcryptjs";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
|
||||
export async function POST(req: Request) {
|
||||
const { name, email, password, token } = await req.json();
|
||||
|
||||
if (!name || !email || !password || !token) {
|
||||
return NextResponse.json({ error: "Champs requis manquants" }, { status: 400 });
|
||||
}
|
||||
|
||||
const invite = await prisma.pendingInvite.findUnique({ where: { token } });
|
||||
if (!invite) return NextResponse.json({ error: "Lien d'invitation invalide" }, { status: 404 });
|
||||
if (invite.expiresAt < new Date()) return NextResponse.json({ error: "Lien d'invitation expiré" }, { status: 410 });
|
||||
|
||||
const existing = await prisma.user.findUnique({ where: { email } });
|
||||
if (existing) return NextResponse.json({ error: "Email déjà utilisé" }, { status: 409 });
|
||||
|
||||
const hashed = await bcrypt.hash(password, 12);
|
||||
const user = await prisma.user.create({
|
||||
data: { name, email, password: hashed, familyId: invite.familyId, role: invite.role },
|
||||
});
|
||||
|
||||
await prisma.pendingInvite.delete({ where: { token } });
|
||||
|
||||
return NextResponse.json({ id: user.id, email: user.email, name: user.name, role: user.role });
|
||||
}
|
||||
Reference in New Issue
Block a user