From 62064f053a08c20f3733f45615bcbd765a342664 Mon Sep 17 00:00:00 2001 From: julien vdb Date: Mon, 27 Jul 2026 14:02:01 +0200 Subject: [PATCH] 0.13.22 : tell a join request apart from an invitation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le controller forçait `state = "pending_user_approval"` quel que soit le sens — commentaire à l'appui : « On force le statut en attente peu importe le type d'invité ». Une candidature déposée par un utilisateur ressortait donc en invitation, et il recevait un email « vous avez été invité » pour une chorale qu'il venait lui-même de demander à rejoindre. - une adhésion créée par un utilisateur pour lui-même est une candidature : `pending_admin_approval`, à valider par un administrateur - rôle imposé à `member` dans ce cas : sans cela, une candidature pouvait se déclarer `owner` de la chorale visée - aucun email d'invitation n'est envoyé pour une candidature - l'invitation, elle, garde son comportement : `pending_user_approval`, token pour un email externe, notification pour un compte existant Co-Authored-By: Claude Opus 5 --- package.json | 2 +- .../controllers/choral-membership.ts | 32 ++++++++++++++----- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 7f641b9..0cb00d6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "harmony-back", - "version": "0.13.21", + "version": "0.13.22", "private": true, "description": "A Strapi application", "scripts": { diff --git a/src/api/choral-membership/controllers/choral-membership.ts b/src/api/choral-membership/controllers/choral-membership.ts index 632b9f3..6ddddcd 100644 --- a/src/api/choral-membership/controllers/choral-membership.ts +++ b/src/api/choral-membership/controllers/choral-membership.ts @@ -8,18 +8,32 @@ export default factories.createCoreController( const { data } = ctx.request.body; const inviter = ctx.state.user; - // 1. Déterminer le type d'invitation + // 1. Déterminer le sens de la demande. + // Une adhésion peut naître dans deux sens opposés, que ce controller + // confondait : il forçait `pending_user_approval` et envoyait un + // email d'invitation dans tous les cas — y compris à quelqu'un qui + // venait lui-même de demander à rejoindre la chorale. + const isSelfRequest = + !!data.user && !!inviter?.id && String(data.user) === String(inviter.id); const isExternalInvite = !!data.invite_email && !data.user; - const isInternalInvite = !!data.user; + const isInternalInvite = !!data.user && !isSelfRequest; let token = null; let targetEmail = null; - // 2. Configuration commune - // On force le statut "en attente" peut importe le type d'invité - ctx.request.body.data.state = "pending_user_approval"; - if (!data.role) { + // 2. Statut selon le sens : c'est à l'autre partie de se prononcer. + if (isSelfRequest) { + ctx.request.body.data.state = "pending_admin_approval"; + // Rôle imposé : sans cela, une candidature pourrait se déclarer + // `owner` de la chorale qu'elle demande à rejoindre. ctx.request.body.data.role = "member"; + ctx.request.body.data.invite_token = null; + ctx.request.body.data.invite_email = null; + } else { + ctx.request.body.data.state = "pending_user_approval"; + if (!data.role) { + ctx.request.body.data.role = "member"; + } } // 3. Traitement spécifique selon le cas @@ -51,8 +65,10 @@ export default factories.createCoreController( // 4. Exécution de la création native par Strapi const response = await super.create(ctx); - // 5. Post-traitement : Envoi de l'email - if (response && targetEmail) { + // 5. Post-traitement : Envoi de l'email d'invitation. + // Jamais pour une candidature : `targetEmail` reste nul dans ce cas, + // on n'écrit pas « vous avez été invité » à qui vient de postuler. + if (response && targetEmail && !isSelfRequest) { try { const chorals = await strapi .documents("api::choral.choral")