feat: reset password + page mon-compte (RGPD) + facettes recherche (prix max, équipements)
All checks were successful
CI / test (pull_request) Successful in 2m19s
All checks were successful
CI / test (pull_request) Successful in 2m19s
This commit is contained in:
parent
0b5e5408e8
commit
a6df96db7e
19 changed files with 922 additions and 0 deletions
50
src/app/api/password/reset-request/route.ts
Normal file
50
src/app/api/password/reset-request/route.ts
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
import { NextResponse } from "next/server";
|
||||
import { z } from "zod";
|
||||
|
||||
import { createPasswordResetToken } from "@/lib/password-reset";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { sendPasswordReset } from "@/lib/email";
|
||||
import { recordAudit } from "@/lib/admin/audit";
|
||||
|
||||
export const runtime = "nodejs";
|
||||
|
||||
const schema = z.object({
|
||||
email: z.string().trim().toLowerCase().email(),
|
||||
});
|
||||
|
||||
const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL ?? "https://karbe.cosmolan.fr";
|
||||
|
||||
export async function POST(req: Request) {
|
||||
let body: unknown;
|
||||
try {
|
||||
body = await req.json();
|
||||
} catch {
|
||||
return NextResponse.json({ error: "Corps JSON invalide." }, { status: 400 });
|
||||
}
|
||||
const parsed = schema.safeParse(body);
|
||||
if (!parsed.success) {
|
||||
// Réponse générique pour ne pas leak la validité du format à un attaquant.
|
||||
return NextResponse.json({ ok: true });
|
||||
}
|
||||
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { email: parsed.data.email },
|
||||
select: { id: true, email: true, firstName: true, isActive: true },
|
||||
});
|
||||
|
||||
if (user && user.isActive) {
|
||||
const token = await createPasswordResetToken(user.id);
|
||||
const resetUrl = `${SITE_URL}/mot-de-passe-oublie/${token}`;
|
||||
sendPasswordReset(user.email, resetUrl).catch(() => {});
|
||||
await recordAudit({
|
||||
scope: "public.password",
|
||||
event: "reset.request",
|
||||
target: user.id,
|
||||
actorEmail: user.email,
|
||||
details: {},
|
||||
});
|
||||
}
|
||||
|
||||
// Réponse identique que l'email existe ou non (énumération-safe).
|
||||
return NextResponse.json({ ok: true });
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue