From 30c4c4ed3c00d52472b783dc8078263967ac877d Mon Sep 17 00:00:00 2001 From: julien vdb Date: Tue, 5 May 2026 13:55:21 +0200 Subject: [PATCH] 0.13.11 : change register template an improve in user extension --- Dockerfile | 1 + package.json | 2 +- src/email-templates/register.html | 222 ++++++++++++++++ .../1.0.0/full_documentation.json | 2 +- .../users-permissions/strapi-server.ts | 251 ++++++------------ 5 files changed, 308 insertions(+), 170 deletions(-) create mode 100644 src/email-templates/register.html diff --git a/Dockerfile b/Dockerfile index 74a75c0..7c1ecc0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,6 +7,7 @@ ARG NODE_ENV=production ENV NODE_ENV=${NODE_ENV} # On les transforme en variables d'environnement pour le processus de build ENV NODE_ENV=${NODE_ENV} +ARG URL=https://back.harmonylab.ovh ENV URL=${URL} # Strapi v4/v5 utilise aussi souvent celle-ci pour l'admin ENV STRAPI_ADMIN_BACKEND_URL=${URL} diff --git a/package.json b/package.json index 8f79647..7546284 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "harmony-back", - "version": "0.13.10", + "version": "0.13.11", "private": true, "description": "A Strapi application", "scripts": { diff --git a/src/email-templates/register.html b/src/email-templates/register.html new file mode 100644 index 0000000..7cc7125 --- /dev/null +++ b/src/email-templates/register.html @@ -0,0 +1,222 @@ + + + + + + Confirmation d'e-mail - ChoralSync + + + + + + + +
+ + + + + + + + + + + + + + + + +
+
+ ChoralSync +
+
+ Bienvenue — plus qu’une étape +
+
+

+ Bonjour {{USER_NAME}}, +

+

+ Merci de t'être inscrit. Clique sur le bouton ci-dessous pour + confirmer ton adresse e-mail et activer ton compte. +

+ + + + + + +
+ + Confirmer mon e-mail → + +
+ + +
+

+ Si le bouton ne fonctionne pas, copie-colle ce lien dans ton + navigateur : +

+ +
+
+

+ Si tu n'es pas à l'origine de cette inscription, ignore cet + e-mail. +

+

+ © ChoralSync 2026 • + www.choralsync.com +

+
+ + + + + + +
+ Besoin d'aide ? Réponds directement à cet e-mail ou consulte + notre + centre d'aide. +
+
+ + diff --git a/src/extensions/documentation/documentation/1.0.0/full_documentation.json b/src/extensions/documentation/documentation/1.0.0/full_documentation.json index 8452950..0154a1f 100644 --- a/src/extensions/documentation/documentation/1.0.0/full_documentation.json +++ b/src/extensions/documentation/documentation/1.0.0/full_documentation.json @@ -14,7 +14,7 @@ "name": "Apache 2.0", "url": "https://www.apache.org/licenses/LICENSE-2.0.html" }, - "x-generation-date": "2026-05-04T16:37:25.949Z" + "x-generation-date": "2026-05-05T11:54:41.084Z" }, "x-strapi-config": { "plugins": [ diff --git a/src/extensions/users-permissions/strapi-server.ts b/src/extensions/users-permissions/strapi-server.ts index 304127d..1bbb3e9 100644 --- a/src/extensions/users-permissions/strapi-server.ts +++ b/src/extensions/users-permissions/strapi-server.ts @@ -1,7 +1,7 @@ "use strict"; const lod = require("lodash"); const utils = require("@strapi/utils"); -const { concat, compact, isArray, toNumber, getOr } = require("lodash/fp"); +const { toNumber, getOr } = require("lodash/fp"); const cryptoLib = require("crypto"); const bcrypt = require("bcryptjs"); const fs = require("fs").promises; @@ -9,16 +9,60 @@ const path = require("path"); module.exports = (plugin) => { const rawProviders = plugin.services.providers({ strapi }); - const { ApplicationError, ValidationError, ForbiddenError } = utils.errors; + const { ApplicationError, ValidationError } = utils.errors; const USER_MODEL_UID = "plugin::users-permissions.user"; - const sanitizeUser = (user, ctx) => { + const sanitizeUser = async (user, ctx) => { const { auth } = ctx.state; const userSchema = strapi.getModel("plugin::users-permissions.user"); return strapi.contentAPI.sanitize.output(user, userSchema, { auth }); }; + const getUserStats = async (userId) => { + const [ + contactsCount, + groupsCount, + postsCount, + eventsCount, + followersCount, + followingCount, + ] = await Promise.all([ + strapi.db.query("api::contact.contact").count({ + where: { + $or: [{ owner: userId }, { user: userId }], + }, + }), + strapi.db.query("api::group-membership.group-membership").count({ + where: { + user: userId, + role: { $in: ["owner", "member", "admin"] }, + }, + }), + strapi.db.query("api::post-ownership.post-ownership").count({ + where: { author: userId }, + }), + strapi.db.query("api::event-relationship.event-relationship").count({ + where: { author: userId }, + }), + strapi.db.query("api::contact.contact").count({ + where: { user: userId, state: "follow" }, + }), + strapi.db.query("api::contact.contact").count({ + where: { owner: userId, state: "follow" }, + }), + ]); + + return { + contacts: contactsCount, + groups: groupsCount, + posts: postsCount, + events: eventsCount, + followers: followersCount, + following: followingCount, + }; + }; + const ensureHashedPasswords = async (values) => { const attributes = strapi.getModel(USER_MODEL_UID).attributes; @@ -78,7 +122,7 @@ module.exports = (plugin) => { to: user.email, subject: onCreateUser?.subject ?? "Confirme ton adresse e-mail", html, - from: "ChoralSync ", + from: "ChoralSync ", }); return { ok: true }; @@ -111,7 +155,7 @@ module.exports = (plugin) => { query.access_token || query.code || query.oauth_token; if (!accessToken) { - throw new Error("No access_token."); + throw new ApplicationError("No access_token."); } const profile = await getProfile(provider, query); @@ -119,7 +163,7 @@ module.exports = (plugin) => { const email = lod.toLower(profile.email); if (!email) { - throw new Error("Email was not available."); + throw new ApplicationError("Email was not available."); } const users = await strapi.db @@ -142,7 +186,7 @@ module.exports = (plugin) => { } if (lod.isEmpty(user) && !advancedSettings.allow_register) { - throw new Error("Register action is actually not available."); + throw new ApplicationError("Register action is actually not available."); } if (!lod.isEmpty(user)) { @@ -150,7 +194,7 @@ module.exports = (plugin) => { } if (users.length && advancedSettings.unique_email) { - throw new Error("Email is already taken."); + throw new ApplicationError("Email is already taken."); } const defaultRole = await strapi.db @@ -174,8 +218,6 @@ module.exports = (plugin) => { }; }; - const originalMe = plugin.controllers.user.me; - plugin.controllers.user.me = async (ctx) => { const fullUser = await strapi.db .query("plugin::users-permissions.user") @@ -206,58 +248,11 @@ module.exports = (plugin) => { ); } - const user = ctx.state.user; + const stats = await getUserStats(ctx.state.user.id); - const userId = user.id; - - const [ - contactsCount, - groupsCount, - postsCount, - eventsCount, - followersCount, - followingCount, - ] = await Promise.all([ - strapi.db.query("api::contact.contact").count({ - where: { - $or: [{ owner: userId }, { user: userId }], - }, - }), - strapi.db.query("api::group-membership.group-membership").count({ - where: { - user: userId, - role: { $in: ["owner", "member", "admin"] }, - }, - }), - strapi.db - .query("api::post-ownership.post-ownership") - .count({ where: { author: userId } }), - strapi.db - .query("api::event-relationship.event-relationship") - .count({ where: { author: userId } }), - strapi.db.query("api::contact.contact").count({ - where: { - user: userId, - state: "follow", - }, - }), - strapi.db.query("api::contact.contact").count({ - where: { - owner: userId, - state: "follow", - }, - }), - ]); const result = { ...JSON.parse(JSON.stringify(fullUser)), - stats: { - contacts: contactsCount, - groups: groupsCount, - posts: postsCount, - events: eventsCount, - followers: followersCount, - following: followingCount, - }, + stats, }; return result; @@ -279,10 +274,6 @@ module.exports = (plugin) => { // 4️⃣ Ajoute un champ calculé user.profileCompleted = Boolean(user.username && user.surname); - // 3️⃣ Supprime les champs sensibles - const sensitive = ["password", "resetPasswordToken", "confirmationToken"]; - sensitive.forEach((key) => delete user[key]); //post_ownerships - // 5️⃣ Refetch avec relations peuplées try { const populatedUser = await strapi.entityService.findOne( @@ -291,6 +282,7 @@ module.exports = (plugin) => { { populate: { post_ownerships: { + filters: { contextType: "user", relation: "owner" }, populate: { post: { populate: { @@ -300,6 +292,7 @@ module.exports = (plugin) => { }, }, contacts: { + filters: { state: "accepted" }, populate: { owner: { populate: { @@ -314,6 +307,7 @@ module.exports = (plugin) => { }, }, group_memberships: { + filters: { role: { $in: ["member", "admin", "owner"] } }, populate: { group: { populate: { @@ -326,27 +320,6 @@ module.exports = (plugin) => { }, }, ); - // Fusionne les données originales (permissions/serialization) avec les relations - user = { ...user, ...populatedUser }; - - if (user.contacts && Array.isArray(user.contacts)) { - user.contacts = user.contacts.filter( - (contact) => contact.state === "accepted", - ); - } - - if (user.post_ownerships && Array.isArray(user.post_ownerships)) { - user.post_ownerships = user.post_ownerships.filter( - (ownership) => - ownership.contextType === "user" && ownership.relation === "owner", - ); - } - - if (user.group_memberships && Array.isArray(user.group_memberships)) { - user.group_memberships = user.group_memberships.filter((membership) => - ["member", "admin", "owner"].includes(membership.role), - ); - } const eventRelationships = await strapi.db .query("api::event-relationship.event-relationship") @@ -356,90 +329,28 @@ module.exports = (plugin) => { event: true, }, }); - user.event_relationships = eventRelationships || []; - const [ - contactsCount, - groupsCount, - postsCount, - eventsCount, - followersCount, - followingCount, - ] = await Promise.all([ - strapi.db.query("api::contact.contact").count({ - where: { - $or: [{ owner: user.id }, { user: user.id }], - }, - }), - strapi.db.query("api::group-membership.group-membership").count({ - where: { - user: user.id, - role: { $in: ["owner", "member", "admin"] }, - }, - }), - strapi.db - .query("api::post-ownership.post-ownership") - .count({ where: { author: user.id } }), - strapi.db - .query("api::event-relationship.event-relationship") - .count({ where: { author: user.id } }), - strapi.db.query("api::contact.contact").count({ - where: { - user: user.id, - state: "follow", - }, - }), - strapi.db.query("api::contact.contact").count({ - where: { - owner: user.id, - state: "follow", - }, - }), - ]); - user.stats = { - contacts: contactsCount, - groups: groupsCount, - posts: postsCount, - events: eventsCount, - followers: followersCount, - following: followingCount, + + const stats = await getUserStats(user.id); + + const sanitizedPopulatedUser = await sanitizeUser(populatedUser, ctx) as object; + + // Fusionne les données originales avec les relations sécurisées + user = { + ...user, + ...sanitizedPopulatedUser, + event_relationships: eventRelationships || [], + stats }; } catch (err) { console.error("Erreur populate relations user:", err); // fallback : retourne juste l'utilisateur original + user.stats = await getUserStats(user.id); } ctx.body = user; return ctx; }; - const uploadImage = async ( - ctx, - label: string, - username: string, - userId: number, - ) => { - const key = `${label}Image`; - if (ctx.request.files[key]) { - const files = Array.isArray(ctx.request.files[key]) - ? ctx.request.files[key][0] - : ctx.request.files[key]; - const extension = files.originalFilename.match(/\.[0-9a-z]+$/i); - const payload = { - fileInfo: { - caption: "undefined", - alternativeText: username || "", - name: `${username}_avatar${extension}`, - }, - path: `user/${userId}`, - }; - const asset = await strapi.services["plugin::upload.upload"].upload({ - data: payload, - files, - }); - return asset[0].id; - } - - return 0; - }; + // Removed unused uploadImage function plugin.services.providers = providers; @@ -450,13 +361,13 @@ module.exports = (plugin) => { return ctx.unauthorized(); } - let data; - if (ctx.request.body.data && typeof ctx.request.body.data === 'string') { - data = JSON.parse(ctx.request.body.data); - } else if (ctx.request.body.data && typeof ctx.request.body.data === 'object') { - data = ctx.request.body.data; - } else { - data = ctx.request.body; + let data = ctx.request.body.data || ctx.request.body; + if (typeof data === "string") { + try { + data = JSON.parse(data); + } catch (err) { + throw new ValidationError("Invalid JSON format in request body data"); + } } const newData = lod.pick(data, [ @@ -557,10 +468,14 @@ module.exports = (plugin) => { ?.createNotification({ title: "Bienvenue !", message: `Ton compte est maintenant activé.`, - type: "success", + type: "accountCreation", target_user: result.id, source: "system", - payload: { email: result.email }, + payload: { + email: result.email, + url: "/app/user", + description: "Penses à remplir ton profil", + }, }); strapi.log.info(