Add modification for post logic and add fields on user / contact

This commit is contained in:
2025-09-30 19:59:29 +02:00
parent a84fdbed9a
commit e7f91f1a7f
20 changed files with 20617 additions and 26 deletions

View File

@@ -41,7 +41,7 @@ export default {
});
if (!alreadyExists) {
await strapi.db.query("api::post.post").create({
const post = await strapi.db.query("api::post.post").create({
data: {
title: article.title,
content: article.description,
@@ -55,6 +55,15 @@ export default {
},
});
// Créer le postOwnership associé
await strapi.db.query("api::post-ownership.post-ownership").create({
data: {
post: post.id,
contextType: "system",
relation: "owner",
},
});
strapi.log.info(`🆕 Article créé : ${article.title}`);
} else {
strapi.log.info(`🔁 Article déjà existant : ${article.title}`);

View File

@@ -0,0 +1,33 @@
{
"kind": "collectionType",
"collectionName": "contact_metas",
"info": {
"singularName": "contact-meta",
"pluralName": "contact-metas",
"displayName": "ContactMeta"
},
"options": {
"draftAndPublish": false
},
"pluginOptions": {},
"attributes": {
"contact": {
"type": "relation",
"relation": "manyToOne",
"target": "api::contact.contact",
"inversedBy": "metas"
},
"owner": {
"type": "relation",
"relation": "manyToOne",
"target": "plugin::users-permissions.user",
"inversedBy": "contact_metas"
},
"favorite": {
"type": "boolean"
},
"note": {
"type": "string"
}
}
}

View File

@@ -0,0 +1,7 @@
/**
* contact-meta controller
*/
import { factories } from '@strapi/strapi'
export default factories.createCoreController('api::contact-meta.contact-meta');

View File

@@ -0,0 +1,7 @@
/**
* contact-meta router
*/
import { factories } from '@strapi/strapi';
export default factories.createCoreRouter('api::contact-meta.contact-meta');

View File

@@ -0,0 +1,7 @@
/**
* contact-meta service
*/
import { factories } from '@strapi/strapi';
export default factories.createCoreService('api::contact-meta.contact-meta');

View File

@@ -0,0 +1,53 @@
{
"kind": "collectionType",
"collectionName": "contacts",
"info": {
"singularName": "contact",
"pluralName": "contacts",
"displayName": "Contact",
"description": ""
},
"options": {
"draftAndPublish": false
},
"pluginOptions": {},
"attributes": {
"owner": {
"type": "relation",
"relation": "manyToOne",
"target": "plugin::users-permissions.user",
"inversedBy": "contacts"
},
"user": {
"type": "relation",
"relation": "manyToOne",
"target": "plugin::users-permissions.user",
"inversedBy": "related_contacts"
},
"state": {
"type": "enumeration",
"enum": [
"pending",
"accepted",
"rejected",
"blocked",
"follow"
]
},
"message": {
"type": "text"
},
"requestedAt": {
"type": "datetime"
},
"respondedAt": {
"type": "datetime"
},
"metas": {
"type": "relation",
"relation": "oneToMany",
"target": "api::contact-meta.contact-meta",
"mappedBy": "contact"
}
}
}

View File

@@ -0,0 +1,50 @@
/**
* contact controller
*/
import { factories } from "@strapi/strapi";
export default factories.createCoreController(
"api::contact.contact",
({ strapi }) => ({
async find(ctx) {
// Log de la requête entrante
console.log("GET /contacts - Requête reçue:", {
query: ctx.query,
params: ctx.params,
user: ctx.state.user?.id || "anonymous",
});
// Appel de la méthode parent
const result = await super.find(ctx);
// Log du résultat avant le return
console.log("GET /contacts - Résultat:", {
dataCount: result.data?.length || 0,
meta: result.meta,
});
return result;
},
async findOne(ctx) {
// Log de la requête entrante
console.log("GET /contacts/:id - Requête reçue:", {
id: ctx.params.id,
query: ctx.query,
user: ctx.state.user?.id || "anonymous",
});
// Appel de la méthode parent
const result = await super.findOne(ctx);
// Log du résultat avant le return
console.log("GET /contacts/:id - Résultat:", {
found: !!result.data,
id: result.data?.id,
});
return result;
},
})
);

View File

@@ -0,0 +1,7 @@
/**
* contact router
*/
import { factories } from '@strapi/strapi';
export default factories.createCoreRouter('api::contact.contact');

View File

@@ -0,0 +1,7 @@
/**
* contact service
*/
import { factories } from '@strapi/strapi';
export default factories.createCoreService('api::contact.contact');

View File

@@ -29,7 +29,8 @@
"enum": [
"member",
"admin",
"owner"
"owner",
"follow"
]
}
}

View File

@@ -0,0 +1,50 @@
{
"kind": "collectionType",
"collectionName": "post_ownerships",
"info": {
"singularName": "post-ownership",
"pluralName": "post-ownerships",
"displayName": "PostOwnership",
"description": ""
},
"options": {
"draftAndPublish": false
},
"pluginOptions": {},
"attributes": {
"post": {
"type": "relation",
"relation": "oneToOne",
"target": "api::post.post",
"inversedBy": "post_ownership"
},
"author": {
"type": "relation",
"relation": "manyToOne",
"target": "plugin::users-permissions.user",
"inversedBy": "post_ownerships"
},
"contextType": {
"type": "enumeration",
"enum": [
"user",
"group",
"system"
]
},
"contextId": {
"type": "integer"
},
"relation": {
"type": "enumeration",
"enum": [
"owner",
"saved",
"hidden"
]
},
"metas": {
"type": "json"
}
}
}

View File

@@ -0,0 +1,7 @@
/**
* post-ownership controller
*/
import { factories } from '@strapi/strapi'
export default factories.createCoreController('api::post-ownership.post-ownership');

View File

@@ -0,0 +1,7 @@
/**
* post-ownership router
*/
import { factories } from '@strapi/strapi';
export default factories.createCoreRouter('api::post-ownership.post-ownership');

View File

@@ -0,0 +1,7 @@
/**
* post-ownership service
*/
import { factories } from '@strapi/strapi';
export default factories.createCoreService('api::post-ownership.post-ownership');

View File

@@ -64,6 +64,12 @@
},
"source": {
"type": "string"
},
"post_ownership": {
"type": "relation",
"relation": "oneToOne",
"target": "api::post-ownership.post-ownership",
"mappedBy": "post"
}
}
}

View File

@@ -30,9 +30,177 @@ export default factories.createCoreController(
}
ctx.request.body = { data };
const result = await super.create(ctx);
// Créer le postOwnership associé
if (result.data) {
const userId = ctx.state.user?.id;
await strapi.db.query("api::post-ownership.post-ownership").create({
data: {
post: result.data.id,
author: userId,
contextType: data.contextType || "user",
contextId: data.contextId || null,
relation: "owner",
},
});
}
return result;
},
async feed(ctx) {
const userId = ctx.state.user?.id;
const { _limit = 20, _start = 0 } = ctx.query;
if (!userId) return ctx.badRequest("userId is required");
const queryParams = {
populate: {
post: {
populate: {
likes: true,
comments: {
populate: {
owner: {
populate: {
avatar: true,
},
},
},
},
author: {
populate: {
avatar: true,
},
},
media: true,
},
},
author: {
populate: {
avatar: true,
},
},
// populate context info if needed
},
sort: { createdAt: "desc" },
pagination: {
start: parseInt(String(_start)),
limit: parseInt(String(_limit)),
},
};
// 1⃣ Récupérer les groupes de l'utilisateur
const groups = await strapi.db
.query("api::group-membership.group-membership")
.findMany({
where: { user: { id: parseInt(userId) } },
populate: ["group"],
});
const groupIds = groups.map((g) => g.group.id);
// 2⃣ Récupérer les amis (contacts acceptés) et les follows
const friendsContacts = await strapi.db
.query("api::contact.contact")
.findMany({
where: {
$or: [
{ owner: { id: parseInt(userId) } },
{ user: { id: parseInt(userId) } },
],
state: "accepted",
},
populate: ["owner", "user"],
});
const friendIds = friendsContacts.map((c) =>
c.owner.id !== parseInt(userId) ? c.owner.id : c.user.id
);
// Récupérer les contacts suivis (follow)
const followContacts = await strapi.db
.query("api::contact.contact")
.findMany({
where: {
owner: { id: parseInt(userId) },
state: "follow",
},
populate: ["user"],
});
const followIds = followContacts.map((c) => c.user.id);
// 3⃣ Récupérer les groupes où mes amis sont membres (en excluant mes propres groupes)
const friendsGroupMemberships = await strapi.db
.query("api::group-membership.group-membership")
.findMany({
where: { user: { id: friendIds } },
populate: ["group"],
});
const friendsGroupIds = friendsGroupMemberships
.map((membership) => membership.group.id)
.filter((groupId) => !groupIds.includes(groupId)); // Exclure mes propres groupes
// 4⃣ Récupérer le feed
const feed = await strapi.db
.query("api::post-ownership.post-ownership")
.findMany({
where: {
$or: [
{ author: { id: friendIds } }, // Posts de mes amis
{ author: { id: followIds } }, // Posts des contacts que je suis
{ contextType: "group", contextId: groupIds }, // Posts de mes groupes
{ contextType: "group", contextId: friendsGroupIds }, // Posts des groupes de mes amis
{ contextType: "system" }, // Posts système
],
relation: { $ne: "hidden" }, // exclure les posts masqués
},
...queryParams,
});
// 5⃣ Récupérer tous les groupes mentionnés dans le feed pour les populer
const allGroupIds = [...new Set([...groupIds, ...friendsGroupIds])];
const allGroups = await strapi.db.query("api::group.group").findMany({
where: { id: allGroupIds },
});
// Créer un map pour un accès rapide aux groupes par ID
const groupsMap = new Map(allGroups.map((group) => [group.id, group]));
// 6⃣ Enrichir le feed avec les propriétés friend, member, contactFollow et group
const enrichedFeed = feed.map((postOwnership) => {
const authorId = postOwnership.author?.id;
const contextType = postOwnership.contextType;
const contextId = postOwnership.contextId;
// Vérifier si l'auteur est un ami
const isFriend = authorId ? friendIds.includes(authorId) : false;
// Vérifier si l'auteur est un contact suivi
const isContactFollow = authorId ? followIds.includes(authorId) : false;
// Vérifier si je suis membre du groupe (seulement pour les posts de groupe)
const isMember =
contextType === "group" && contextId
? groupIds.includes(contextId)
: false;
// Ajouter l'objet group si contextType est "group"
const group =
contextType === "group" && contextId
? groupsMap.get(contextId)
: null;
return {
...postOwnership,
friend: isFriend,
contactFollow: isContactFollow,
member: isMember,
...(group && { group }), // Ajouter group seulement s'il existe
};
});
ctx.send(enrichedFeed);
},
async savePost(ctx) {
console.log("🎯 savePost called with params:", ctx.params);
console.log("🎯 Request method:", ctx.request.method);

View File

@@ -9,5 +9,10 @@ export default {
path: "/posts/:id/save",
handler: "post.savePost",
},
{
method: "GET",
path: "/posts/feed",
handler: "post.feed",
},
],
};

View File

@@ -203,6 +203,33 @@
"type": "relation",
"relation": "oneToMany",
"target": "api::post.post"
},
"contacts": {
"type": "relation",
"relation": "oneToMany",
"target": "api::contact.contact",
"mappedBy": "owner"
},
"related_contacts": {
"type": "relation",
"relation": "oneToMany",
"target": "api::contact.contact",
"mappedBy": "user"
},
"contact_metas": {
"type": "relation",
"relation": "oneToMany",
"target": "api::contact-meta.contact-meta",
"mappedBy": "owner"
},
"phone": {
"type": "string"
},
"post_ownerships": {
"type": "relation",
"relation": "oneToMany",
"target": "api::post-ownership.post-ownership",
"mappedBy": "author"
}
}
}

View File

@@ -758,6 +758,86 @@ export interface ApiCommentComment extends Struct.CollectionTypeSchema {
};
}
export interface ApiContactMetaContactMeta extends Struct.CollectionTypeSchema {
collectionName: 'contact_metas';
info: {
displayName: 'ContactMeta';
pluralName: 'contact-metas';
singularName: 'contact-meta';
};
options: {
draftAndPublish: false;
};
attributes: {
contact: Schema.Attribute.Relation<'manyToOne', 'api::contact.contact'>;
createdAt: Schema.Attribute.DateTime;
createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
Schema.Attribute.Private;
favorite: Schema.Attribute.Boolean;
locale: Schema.Attribute.String & Schema.Attribute.Private;
localizations: Schema.Attribute.Relation<
'oneToMany',
'api::contact-meta.contact-meta'
> &
Schema.Attribute.Private;
note: Schema.Attribute.String;
owner: Schema.Attribute.Relation<
'manyToOne',
'plugin::users-permissions.user'
>;
publishedAt: Schema.Attribute.DateTime;
updatedAt: Schema.Attribute.DateTime;
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
Schema.Attribute.Private;
};
}
export interface ApiContactContact extends Struct.CollectionTypeSchema {
collectionName: 'contacts';
info: {
description: '';
displayName: 'Contact';
pluralName: 'contacts';
singularName: 'contact';
};
options: {
draftAndPublish: false;
};
attributes: {
createdAt: Schema.Attribute.DateTime;
createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
Schema.Attribute.Private;
locale: Schema.Attribute.String & Schema.Attribute.Private;
localizations: Schema.Attribute.Relation<
'oneToMany',
'api::contact.contact'
> &
Schema.Attribute.Private;
message: Schema.Attribute.Text;
metas: Schema.Attribute.Relation<
'oneToMany',
'api::contact-meta.contact-meta'
>;
owner: Schema.Attribute.Relation<
'manyToOne',
'plugin::users-permissions.user'
>;
publishedAt: Schema.Attribute.DateTime;
requestedAt: Schema.Attribute.DateTime;
respondedAt: Schema.Attribute.DateTime;
state: Schema.Attribute.Enumeration<
['pending', 'accepted', 'rejected', 'blocked', 'follow']
>;
updatedAt: Schema.Attribute.DateTime;
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
Schema.Attribute.Private;
user: Schema.Attribute.Relation<
'manyToOne',
'plugin::users-permissions.user'
>;
};
}
export interface ApiConversationConversation
extends Struct.CollectionTypeSchema {
collectionName: 'conversations';
@@ -939,7 +1019,7 @@ export interface ApiGroupMembershipGroupMembership
> &
Schema.Attribute.Private;
publishedAt: Schema.Attribute.DateTime;
role: Schema.Attribute.Enumeration<['member', 'admin', 'owner']>;
role: Schema.Attribute.Enumeration<['member', 'admin', 'owner', 'follow']>;
updatedAt: Schema.Attribute.DateTime;
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
Schema.Attribute.Private;
@@ -1100,6 +1180,44 @@ export interface ApiPermissionsTemplatePermissionsTemplate
};
}
export interface ApiPostOwnershipPostOwnership
extends Struct.CollectionTypeSchema {
collectionName: 'post_ownerships';
info: {
description: '';
displayName: 'PostOwnership';
pluralName: 'post-ownerships';
singularName: 'post-ownership';
};
options: {
draftAndPublish: false;
};
attributes: {
author: Schema.Attribute.Relation<
'manyToOne',
'plugin::users-permissions.user'
>;
contextId: Schema.Attribute.Integer;
contextType: Schema.Attribute.Enumeration<['user', 'group', 'system']>;
createdAt: Schema.Attribute.DateTime;
createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
Schema.Attribute.Private;
locale: Schema.Attribute.String & Schema.Attribute.Private;
localizations: Schema.Attribute.Relation<
'oneToMany',
'api::post-ownership.post-ownership'
> &
Schema.Attribute.Private;
metas: Schema.Attribute.JSON;
post: Schema.Attribute.Relation<'oneToOne', 'api::post.post'>;
publishedAt: Schema.Attribute.DateTime;
relation: Schema.Attribute.Enumeration<['owner', 'saved', 'hidden']>;
updatedAt: Schema.Attribute.DateTime;
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
Schema.Attribute.Private;
};
}
export interface ApiPostPost extends Struct.CollectionTypeSchema {
collectionName: 'posts';
info: {
@@ -1134,6 +1252,10 @@ export interface ApiPostPost extends Struct.CollectionTypeSchema {
'images' | 'files' | 'videos' | 'audios',
true
>;
post_ownership: Schema.Attribute.Relation<
'oneToOne',
'api::post-ownership.post-ownership'
>;
publishedAt: Schema.Attribute.DateTime;
shares: Schema.Attribute.Integer;
source: Schema.Attribute.String;
@@ -1662,6 +1784,11 @@ export interface PluginUsersPermissionsUser
chorals: Schema.Attribute.Relation<'manyToMany', 'api::choral.choral'>;
confirmationToken: Schema.Attribute.String & Schema.Attribute.Private;
confirmed: Schema.Attribute.Boolean & Schema.Attribute.DefaultTo<false>;
contact_metas: Schema.Attribute.Relation<
'oneToMany',
'api::contact-meta.contact-meta'
>;
contacts: Schema.Attribute.Relation<'oneToMany', 'api::contact.contact'>;
createdAt: Schema.Attribute.DateTime;
createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
Schema.Attribute.Private;
@@ -1699,9 +1826,18 @@ export interface PluginUsersPermissionsUser
Schema.Attribute.SetMinMaxLength<{
minLength: 6;
}>;
phone: Schema.Attribute.String;
post_ownerships: Schema.Attribute.Relation<
'oneToMany',
'api::post-ownership.post-ownership'
>;
posts: Schema.Attribute.Relation<'oneToMany', 'api::post.post'>;
provider: Schema.Attribute.String;
publishedAt: Schema.Attribute.DateTime;
related_contacts: Schema.Attribute.Relation<
'oneToMany',
'api::contact.contact'
>;
resetPasswordToken: Schema.Attribute.String & Schema.Attribute.Private;
role: Schema.Attribute.Relation<
'manyToOne',
@@ -1744,6 +1880,8 @@ declare module '@strapi/strapi' {
'api::choral-permission.choral-permission': ApiChoralPermissionChoralPermission;
'api::choral.choral': ApiChoralChoral;
'api::comment.comment': ApiCommentComment;
'api::contact-meta.contact-meta': ApiContactMetaContactMeta;
'api::contact.contact': ApiContactContact;
'api::conversation.conversation': ApiConversationConversation;
'api::direct-message.direct-message': ApiDirectMessageDirectMessage;
'api::event-other.event-other': ApiEventOtherEventOther;
@@ -1753,6 +1891,7 @@ declare module '@strapi/strapi' {
'api::invite.invite': ApiInviteInvite;
'api::message.message': ApiMessageMessage;
'api::permissions-template.permissions-template': ApiPermissionsTemplatePermissionsTemplate;
'api::post-ownership.post-ownership': ApiPostOwnershipPostOwnership;
'api::post.post': ApiPostPost;
'api::report.report': ApiReportReport;
'plugin::content-releases.release': PluginContentReleasesRelease;