512 lines
16 KiB
TypeScript
512 lines
16 KiB
TypeScript
/**
|
||
* post controller
|
||
*/
|
||
|
||
import { factories } from "@strapi/strapi";
|
||
|
||
export default factories.createCoreController(
|
||
"api::post.post",
|
||
({ strapi }) => ({
|
||
async create(ctx) {
|
||
const data = JSON.parse(ctx.request.body.data);
|
||
|
||
if (ctx.request.files.media) {
|
||
const files = Array.isArray(ctx.request.files.media)
|
||
? ctx.request.files.media[0]
|
||
: ctx.request.files.media;
|
||
const extension = files.originalFilename.match(/\.[0-9a-z]+$/i);
|
||
const payload = {
|
||
fileInfo: {
|
||
caption: "undefined",
|
||
alternativeText: data.name || "",
|
||
name: `${data.name}_media${extension}`,
|
||
},
|
||
};
|
||
const asset = await strapi.services["plugin::upload.upload"].upload({
|
||
data: payload,
|
||
files,
|
||
});
|
||
data.media = asset[0].id;
|
||
}
|
||
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;
|
||
},
|
||
|
||
/**
|
||
* Retrieves the feed for the authenticated user, including posts from friends, followed contacts,
|
||
* user's groups, friends' groups, and system posts. The feed is enriched with additional properties
|
||
* such as friend, contactFollow, member, and group.
|
||
*
|
||
* @param {Object} ctx - The Koa context object containing the request and response.
|
||
* @returns {Promise<void>} - Sends the enriched feed as the response.
|
||
*/
|
||
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);
|
||
|
||
// Récupérer les contacts suivis (follow)
|
||
const blockedContacts = await strapi.db
|
||
.query("api::contact.contact")
|
||
.findMany({
|
||
where: {
|
||
owner: { id: parseInt(userId) },
|
||
state: "blocked",
|
||
},
|
||
populate: ["user"],
|
||
});
|
||
const blockedIds = blockedContacts.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: [
|
||
// Posts réguliers (amis, follows, groupes, système) - exclure les cachés
|
||
{
|
||
$and: [
|
||
{
|
||
$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 pour les posts réguliers
|
||
],
|
||
},
|
||
// Posts sauvés par l'utilisateur
|
||
{
|
||
contextType: "user",
|
||
contextId: parseInt(userId),
|
||
relation: "saved",
|
||
},
|
||
// Posts cachés par l'utilisateur
|
||
{
|
||
contextType: "user",
|
||
contextId: parseInt(userId),
|
||
relation: "hidden",
|
||
},
|
||
],
|
||
},
|
||
...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 l'auteur est bloqué
|
||
const isBlocked = authorId ? blockedIds.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,
|
||
blocked: isBlocked,
|
||
...(group && { group }), // Ajouter group seulement s'il existe
|
||
};
|
||
});
|
||
|
||
const filteredFeedBlocked = enrichedFeed.filter((post) => !post.blocked);
|
||
|
||
ctx.send(filteredFeedBlocked);
|
||
},
|
||
|
||
async savePost(ctx) {
|
||
console.log("🎯 savePost called with params:", ctx.params);
|
||
console.log("🎯 Request method:", ctx.request.method);
|
||
console.log("🎯 Request URL:", ctx.request.url);
|
||
|
||
const user = ctx.state.user;
|
||
if (!user) {
|
||
console.log("❌ User not authenticated");
|
||
return ctx.unauthorized("You must be logged in to save posts");
|
||
}
|
||
|
||
console.log("✅ User authenticated:", user.id);
|
||
const postId = parseInt(String(ctx.params.id));
|
||
const authorId = parseInt(String(ctx.query.authorId));
|
||
|
||
if (isNaN(postId)) {
|
||
return ctx.badRequest("Invalid post ID");
|
||
}
|
||
|
||
// Verify the post exists
|
||
const post = await strapi.entityService.findOne("api::post.post", postId);
|
||
if (!post) {
|
||
return ctx.notFound("Post not found");
|
||
}
|
||
|
||
// Check if post is already saved by this user
|
||
const existingSavedPost = await strapi.db
|
||
.query("api::post-ownership.post-ownership")
|
||
.findOne({
|
||
where: {
|
||
post: postId,
|
||
author: authorId,
|
||
contextType: "user",
|
||
contextId: user.id,
|
||
relation: "saved",
|
||
},
|
||
});
|
||
|
||
if (existingSavedPost) {
|
||
console.log("⚠️ Post already saved");
|
||
return ctx.badRequest("Post is already saved");
|
||
}
|
||
|
||
// Create postOwnership entry to mark post as saved
|
||
const savedPostOwnership = await strapi.db
|
||
.query("api::post-ownership.post-ownership")
|
||
.create({
|
||
data: {
|
||
post: postId,
|
||
author: authorId,
|
||
contextType: "user",
|
||
contextId: user.id,
|
||
relation: "saved",
|
||
},
|
||
});
|
||
|
||
console.log("✅ Post marked as saved:", savedPostOwnership);
|
||
|
||
// Get count of saved posts for this user
|
||
const savedPostsCount = await strapi.db
|
||
.query("api::post-ownership.post-ownership")
|
||
.count({
|
||
where: {
|
||
author: authorId,
|
||
contextType: "user",
|
||
contextId: user.id,
|
||
relation: "saved",
|
||
},
|
||
});
|
||
|
||
return ctx.send({
|
||
message: "Post saved successfully",
|
||
savedPostsCount: savedPostsCount,
|
||
});
|
||
},
|
||
|
||
async removeSavedPost(ctx) {
|
||
console.log("🎯 removeSavedPost called with params:", ctx.params);
|
||
console.log("🎯 Request method:", ctx.request.method);
|
||
console.log("🎯 Request URL:", ctx.request.url);
|
||
|
||
const user = ctx.state.user;
|
||
if (!user) {
|
||
console.log("❌ User not authenticated");
|
||
return ctx.unauthorized("You must be logged in to remove saved posts");
|
||
}
|
||
|
||
console.log("✅ User authenticated:", user.id);
|
||
const postId = parseInt(String(ctx.params.id));
|
||
|
||
if (isNaN(postId)) {
|
||
return ctx.badRequest("Invalid post ID");
|
||
}
|
||
|
||
// Verify the post exists
|
||
const post = await strapi.entityService.findOne("api::post.post", postId);
|
||
if (!post) {
|
||
return ctx.notFound("Post not found");
|
||
}
|
||
|
||
// Find the saved post ownership entry
|
||
const savedPostOwnership = await strapi.db
|
||
.query("api::post-ownership.post-ownership")
|
||
.findOne({
|
||
where: {
|
||
post: postId,
|
||
contextType: "user",
|
||
contextId: user.id,
|
||
relation: "saved",
|
||
},
|
||
});
|
||
|
||
if (!savedPostOwnership) {
|
||
console.log("⚠️ Post is not saved");
|
||
return ctx.badRequest("Post is not in your saved posts");
|
||
}
|
||
|
||
// Delete the saved post ownership entry
|
||
await strapi.db.query("api::post-ownership.post-ownership").delete({
|
||
where: { id: savedPostOwnership.id },
|
||
});
|
||
|
||
console.log("✅ Post removed from saved posts successfully");
|
||
|
||
// Get updated count of saved posts for this user
|
||
const savedPostsCount = await strapi.db
|
||
.query("api::post-ownership.post-ownership")
|
||
.count({
|
||
where: {
|
||
author: user.id,
|
||
contextType: "user",
|
||
contextId: user.id,
|
||
relation: "saved",
|
||
},
|
||
});
|
||
|
||
return ctx.send({
|
||
message: "Post removed from saved posts successfully",
|
||
savedPostsCount: savedPostsCount,
|
||
});
|
||
},
|
||
|
||
async hidePost(ctx) {
|
||
console.log("🎯 hidePost called with params:", ctx.params);
|
||
console.log("🎯 Request method:", ctx.request.method);
|
||
console.log("🎯 Request URL:", ctx.request.url);
|
||
|
||
const user = ctx.state.user;
|
||
if (!user) {
|
||
console.log("❌ User not authenticated");
|
||
return ctx.unauthorized("You must be logged in to hide posts");
|
||
}
|
||
|
||
console.log("✅ User authenticated:", user.id);
|
||
const postId = parseInt(String(ctx.params.id));
|
||
|
||
if (isNaN(postId)) {
|
||
return ctx.badRequest("Invalid post ID");
|
||
}
|
||
|
||
// Verify the post exists
|
||
const post = await strapi.entityService.findOne("api::post.post", postId);
|
||
if (!post) {
|
||
return ctx.notFound("Post not found");
|
||
}
|
||
|
||
// Check if post is already hidden by this user
|
||
const existingHiddenPost = await strapi.db
|
||
.query("api::post-ownership.post-ownership")
|
||
.findOne({
|
||
where: {
|
||
post: postId,
|
||
author: user.id,
|
||
contextType: "user",
|
||
contextId: user.id,
|
||
relation: "hidden",
|
||
},
|
||
});
|
||
|
||
if (existingHiddenPost) {
|
||
console.log("⚠️ Post already hidden");
|
||
return ctx.badRequest("Post is already hidden");
|
||
}
|
||
|
||
// Create postOwnership entry to mark post as hidden
|
||
const hiddenPostOwnership = await strapi.db
|
||
.query("api::post-ownership.post-ownership")
|
||
.create({
|
||
data: {
|
||
post: postId,
|
||
author: user.id,
|
||
contextType: "user",
|
||
contextId: user.id,
|
||
relation: "hidden",
|
||
},
|
||
});
|
||
|
||
console.log("✅ Post marked as hidden:", hiddenPostOwnership);
|
||
|
||
return ctx.send({
|
||
message: "Post hidden successfully",
|
||
hiddenPostId: postId,
|
||
});
|
||
},
|
||
|
||
async removeHiddenPost(ctx) {
|
||
console.log("🎯 removeHiddenPost called with params:", ctx.params);
|
||
console.log("🎯 Request method:", ctx.request.method);
|
||
console.log("🎯 Request URL:", ctx.request.url);
|
||
|
||
const user = ctx.state.user;
|
||
if (!user) {
|
||
console.log("❌ User not authenticated");
|
||
return ctx.unauthorized("You must be logged in to unhide posts");
|
||
}
|
||
|
||
console.log("✅ User authenticated:", user.id);
|
||
const postId = parseInt(String(ctx.params.id));
|
||
|
||
if (isNaN(postId)) {
|
||
return ctx.badRequest("Invalid post ID");
|
||
}
|
||
|
||
// Verify the post exists
|
||
const post = await strapi.entityService.findOne("api::post.post", postId);
|
||
if (!post) {
|
||
return ctx.notFound("Post not found");
|
||
}
|
||
|
||
// Find the hidden post ownership entry
|
||
const hiddenPostOwnership = await strapi.db
|
||
.query("api::post-ownership.post-ownership")
|
||
.findOne({
|
||
where: {
|
||
post: postId,
|
||
author: user.id,
|
||
contextType: "user",
|
||
contextId: user.id,
|
||
relation: "hidden",
|
||
},
|
||
});
|
||
|
||
if (!hiddenPostOwnership) {
|
||
console.log("⚠️ Post is not hidden");
|
||
return ctx.badRequest("Post is not hidden");
|
||
}
|
||
|
||
// Delete the hidden post ownership entry
|
||
await strapi.db.query("api::post-ownership.post-ownership").delete({
|
||
where: { id: hiddenPostOwnership.id },
|
||
});
|
||
|
||
console.log("✅ Post unhidden successfully");
|
||
|
||
return ctx.send({
|
||
message: "Post unhidden successfully",
|
||
unhiddenPostId: postId,
|
||
});
|
||
},
|
||
})
|
||
);
|