feat(rental): Sprint C — espace prestataire (signup+dashboard+items+calendrier+résa)
All checks were successful
CI / test (pull_request) Successful in 2m33s
All checks were successful
CI / test (pull_request) Successful in 2m33s
This commit is contained in:
parent
8d7e9cfdc2
commit
59786e5365
16 changed files with 1509 additions and 9 deletions
|
|
@ -5,7 +5,7 @@ import { UserRole } from "@/generated/prisma/enums";
|
|||
import { hashPassword } from "@/lib/password";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { recordAudit } from "@/lib/admin/audit";
|
||||
import { sendSignupWelcome } from "@/lib/email";
|
||||
import { sendNewRentalProviderRequest, sendSignupWelcome } from "@/lib/email";
|
||||
import { rateLimitRequest } from "@/lib/rate-limit";
|
||||
|
||||
export const runtime = "nodejs";
|
||||
|
|
@ -16,11 +16,14 @@ const schema = z.object({
|
|||
firstName: z.string().trim().min(1).max(100),
|
||||
lastName: z.string().trim().min(1).max(100),
|
||||
phone: z.string().trim().max(40).optional().nullable(),
|
||||
role: z.enum([UserRole.TOURIST, UserRole.OWNER]).default(UserRole.TOURIST),
|
||||
role: z
|
||||
.enum([UserRole.TOURIST, UserRole.OWNER, UserRole.RENTAL_PROVIDER])
|
||||
.default(UserRole.TOURIST),
|
||||
providerName: z.string().trim().min(2).max(200).optional(),
|
||||
providerRivers: z.array(z.string().trim().min(1).max(80)).max(20).optional(),
|
||||
});
|
||||
|
||||
export async function POST(req: Request) {
|
||||
// 5 inscriptions max par IP par heure.
|
||||
const rl = rateLimitRequest(req, "signup", 60 * 60 * 1000, 5);
|
||||
if (!rl.ok) {
|
||||
return NextResponse.json(
|
||||
|
|
@ -43,6 +46,10 @@ export async function POST(req: Request) {
|
|||
}
|
||||
const data = parsed.data;
|
||||
|
||||
if (data.role === UserRole.RENTAL_PROVIDER && (!data.providerName || data.providerName.trim().length < 2)) {
|
||||
return NextResponse.json({ error: "Nom de votre activité requis." }, { status: 400 });
|
||||
}
|
||||
|
||||
const existing = await prisma.user.findUnique({ where: { email: data.email }, select: { id: true } });
|
||||
if (existing) {
|
||||
return NextResponse.json({ error: "Un compte existe déjà avec cet email." }, { status: 409 });
|
||||
|
|
@ -62,16 +69,36 @@ export async function POST(req: Request) {
|
|||
select: { id: true, email: true, role: true },
|
||||
});
|
||||
|
||||
// Pour un RENTAL_PROVIDER : crée le RentalProvider associé en attente d'approbation.
|
||||
let createdProviderId: string | null = null;
|
||||
if (user.role === UserRole.RENTAL_PROVIDER && data.providerName) {
|
||||
const provider = await prisma.rentalProvider.create({
|
||||
data: {
|
||||
name: data.providerName,
|
||||
isSystemD: false,
|
||||
managedByUserId: user.id,
|
||||
contactEmail: user.email,
|
||||
contactPhone: data.phone?.trim() || null,
|
||||
rivers: data.providerRivers ?? [],
|
||||
commissionPct: 10, // valeur par défaut, ajustable par admin
|
||||
active: true,
|
||||
approved: false,
|
||||
},
|
||||
select: { id: true, name: true },
|
||||
});
|
||||
createdProviderId = provider.id;
|
||||
sendNewRentalProviderRequest(provider.name, user.email).catch(() => {});
|
||||
}
|
||||
|
||||
await recordAudit({
|
||||
scope: "public.signup",
|
||||
event: "user.create",
|
||||
target: user.id,
|
||||
actorEmail: user.email,
|
||||
details: { role: user.role },
|
||||
details: { role: user.role, rentalProviderId: createdProviderId },
|
||||
});
|
||||
|
||||
// Best-effort welcome email.
|
||||
sendSignupWelcome(user.email, data.firstName).catch(() => {});
|
||||
|
||||
return NextResponse.json({ ok: true, userId: user.id });
|
||||
return NextResponse.json({ ok: true, userId: user.id, providerId: createdProviderId });
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue