feat: v8 - skill itinova-user-management (3 profils admin/standard/readonly, logo FEHAP, login/email)
This commit is contained in:
@@ -65,6 +65,14 @@ const gestionnaireProcedure = protectedProcedure.use(({ ctx, next }) => {
|
||||
return next({ ctx });
|
||||
});
|
||||
|
||||
/** Bloque les mutations pour les utilisateurs en lecture seule (role === 'readonly') */
|
||||
const writeProcedure = protectedProcedure.use(({ ctx, next }) => {
|
||||
if (ctx.user.role === "readonly") {
|
||||
throw new TRPCError({ code: "FORBIDDEN", message: "Votre compte est en lecture seule. Contactez un gestionnaire SONUM pour obtenir les droits de modification." });
|
||||
}
|
||||
return next({ ctx });
|
||||
});
|
||||
|
||||
// ─── Router principal ─────────────────────────────────────────────────────────
|
||||
|
||||
export const appRouter = router({
|
||||
@@ -86,13 +94,14 @@ export const appRouter = router({
|
||||
*/
|
||||
loginLocal: publicProcedure
|
||||
.input(z.object({
|
||||
email: z.string().email(),
|
||||
// Accepte email ou login court
|
||||
email: z.string().min(1),
|
||||
password: z.string().min(1),
|
||||
}))
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const user = await authenticateLocalUser(input.email, input.password);
|
||||
if (!user) {
|
||||
throw new TRPCError({ code: "UNAUTHORIZED", message: "Email ou mot de passe incorrect" });
|
||||
throw new TRPCError({ code: "UNAUTHORIZED", message: "Identifiant ou mot de passe incorrect" });
|
||||
}
|
||||
|
||||
// Créer un token de session avec l'openId de l'utilisateur local
|
||||
@@ -325,12 +334,14 @@ export const appRouter = router({
|
||||
if (etab.referentId !== ctx.user.id && ctx.user.sonumRole !== "gestionnaire" && ctx.user.role !== "admin") {
|
||||
throw new TRPCError({ code: "FORBIDDEN" });
|
||||
}
|
||||
if (ctx.user.role === "readonly") throw new TRPCError({ code: "FORBIDDEN", message: "Compte en lecture seule" });
|
||||
return upsertLogicielEtablissement({ ...input, saisiePar: ctx.user.id });
|
||||
}),
|
||||
|
||||
delete: protectedProcedure
|
||||
.input(z.object({ id: z.number().int(), etablissementId: z.number().int() }))
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
if (ctx.user.role === "readonly") throw new TRPCError({ code: "FORBIDDEN", message: "Compte en lecture seule" });
|
||||
const etab = await getEtablissementById(input.etablissementId);
|
||||
if (!etab) throw new TRPCError({ code: "NOT_FOUND" });
|
||||
if (etab.referentId !== ctx.user.id && ctx.user.sonumRole !== "gestionnaire" && ctx.user.role !== "admin") {
|
||||
@@ -379,7 +390,7 @@ export const appRouter = router({
|
||||
|
||||
// ─── Demandes de Contact ───────────────────────────────────────────────────
|
||||
contact: router({
|
||||
envoyer: protectedProcedure
|
||||
envoyer: writeProcedure
|
||||
.input(z.object({
|
||||
etablissementCibleId: z.number().int(),
|
||||
message: z.string().min(1),
|
||||
@@ -438,9 +449,13 @@ export const appRouter = router({
|
||||
/** Crée un utilisateur manuellement avec un mot de passe local */
|
||||
createUser: gestionnaireProcedure
|
||||
.input(z.object({
|
||||
name: z.string().min(1),
|
||||
firstName: z.string().min(1),
|
||||
lastName: z.string().min(1),
|
||||
login: z.string().min(2).optional(),
|
||||
email: z.string().email(),
|
||||
sonumRole: z.enum(["referent", "gestionnaire", "adherent"]),
|
||||
role: z.enum(["admin", "standard", "readonly"]).default("standard"),
|
||||
isActive: z.boolean().default(true),
|
||||
password: z.string().min(8, "Le mot de passe doit contenir au moins 8 caractères"),
|
||||
}))
|
||||
.mutation(async ({ input }) => {
|
||||
@@ -451,6 +466,9 @@ export const appRouter = router({
|
||||
if (err.message === "EMAIL_EXISTS") {
|
||||
throw new TRPCError({ code: "CONFLICT", message: "Un utilisateur avec cet email existe déjà" });
|
||||
}
|
||||
if (err.message === "LOGIN_EXISTS") {
|
||||
throw new TRPCError({ code: "CONFLICT", message: "Un utilisateur avec ce login existe déjà" });
|
||||
}
|
||||
throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: err.message });
|
||||
}
|
||||
}),
|
||||
@@ -459,9 +477,13 @@ export const appRouter = router({
|
||||
updateUser: gestionnaireProcedure
|
||||
.input(z.object({
|
||||
userId: z.number().int(),
|
||||
name: z.string().min(1).optional(),
|
||||
firstName: z.string().min(1).optional(),
|
||||
lastName: z.string().min(1).optional(),
|
||||
login: z.string().min(2).optional(),
|
||||
email: z.string().email().optional(),
|
||||
sonumRole: z.enum(["referent", "gestionnaire", "adherent"]).optional(),
|
||||
role: z.enum(["admin", "standard", "readonly"]).optional(),
|
||||
isActive: z.boolean().optional(),
|
||||
}))
|
||||
.mutation(async ({ input }) => {
|
||||
const { userId, ...data } = input;
|
||||
|
||||
Reference in New Issue
Block a user