0.11.6 : add event and export ICS
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "harmony-back",
|
"name": "harmony-back",
|
||||||
"version": "0.11.5",
|
"version": "0.11.6",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "A Strapi application",
|
"description": "A Strapi application",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -28,14 +28,14 @@
|
|||||||
"enum": [
|
"enum": [
|
||||||
"concert",
|
"concert",
|
||||||
"festival",
|
"festival",
|
||||||
"salon",
|
"trade show",
|
||||||
"symposium",
|
"symposium",
|
||||||
"concours",
|
"competition",
|
||||||
"masterclass",
|
"masterclass",
|
||||||
"atelier vocal",
|
"vocal_workshop",
|
||||||
"conférence",
|
"conference",
|
||||||
"répétition",
|
"rehearsal",
|
||||||
"sorties"
|
"outings"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"kind": "collectionType",
|
||||||
|
"collectionName": "event_relationships",
|
||||||
|
"info": {
|
||||||
|
"singularName": "event-relationship",
|
||||||
|
"pluralName": "event-relationships",
|
||||||
|
"displayName": "EventRelationship",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"draftAndPublish": false
|
||||||
|
},
|
||||||
|
"pluginOptions": {},
|
||||||
|
"attributes": {
|
||||||
|
"author": {
|
||||||
|
"type": "relation",
|
||||||
|
"relation": "oneToOne",
|
||||||
|
"target": "plugin::users-permissions.user"
|
||||||
|
},
|
||||||
|
"contextType": {
|
||||||
|
"type": "enumeration",
|
||||||
|
"enum": [
|
||||||
|
"user",
|
||||||
|
"group",
|
||||||
|
"choral",
|
||||||
|
"system"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"contextId": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"relation": {
|
||||||
|
"type": "enumeration",
|
||||||
|
"enum": [
|
||||||
|
"owner",
|
||||||
|
"interested",
|
||||||
|
"registered"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"metas": {
|
||||||
|
"type": "json"
|
||||||
|
},
|
||||||
|
"event": {
|
||||||
|
"type": "relation",
|
||||||
|
"relation": "oneToOne",
|
||||||
|
"target": "api::event.event"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* event-relationship controller
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { factories } from '@strapi/strapi'
|
||||||
|
|
||||||
|
export default factories.createCoreController('api::event-relationship.event-relationship');
|
||||||
7
src/api/event-relationship/routes/event-relationship.ts
Normal file
7
src/api/event-relationship/routes/event-relationship.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* event-relationship router
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { factories } from '@strapi/strapi';
|
||||||
|
|
||||||
|
export default factories.createCoreRouter('api::event-relationship.event-relationship');
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* event-relationship service
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { factories } from '@strapi/strapi';
|
||||||
|
|
||||||
|
export default factories.createCoreService('api::event-relationship.event-relationship');
|
||||||
@@ -35,6 +35,40 @@
|
|||||||
},
|
},
|
||||||
"location": {
|
"location": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "enumeration",
|
||||||
|
"enum": [
|
||||||
|
"concert",
|
||||||
|
"festival",
|
||||||
|
"trade show",
|
||||||
|
"symposium",
|
||||||
|
"competition",
|
||||||
|
"masterclass",
|
||||||
|
"vocal_workshop",
|
||||||
|
"conference",
|
||||||
|
"rehearsal",
|
||||||
|
"outings"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"voice": {
|
||||||
|
"type": "enumeration",
|
||||||
|
"enum": [
|
||||||
|
"soprano",
|
||||||
|
"alto",
|
||||||
|
"tenor",
|
||||||
|
"bass",
|
||||||
|
"mixed"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"isPublic": {
|
||||||
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
93
src/api/event/controllers/applyEvent.ts
Normal file
93
src/api/event/controllers/applyEvent.ts
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
import type { Core } from "@strapi/strapi";
|
||||||
|
import type { Context } from "koa";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller pour gérer l'inscription d'un utilisateur à un événement
|
||||||
|
* POST /events/:eventId/apply
|
||||||
|
*/
|
||||||
|
export default ({ strapi }: { strapi: Core.Strapi }) =>
|
||||||
|
async function applyEvent(ctx: Context) {
|
||||||
|
const userId = ctx.state.user?.id;
|
||||||
|
const eventId = ctx.params.eventId;
|
||||||
|
|
||||||
|
// Validation des paramètres
|
||||||
|
if (!userId) {
|
||||||
|
return ctx.badRequest("User ID is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!eventId || isNaN(parseInt(eventId))) {
|
||||||
|
return ctx.badRequest("Event ID is required and must be a valid number");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Vérifier que l'événement existe
|
||||||
|
const event = await strapi.db.query("api::event.event").findOne({
|
||||||
|
where: { id: parseInt(eventId) },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!event) {
|
||||||
|
return ctx.notFound("Event not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier que l'utilisateur n'est pas déjà inscrit
|
||||||
|
const existingRelationship = await strapi.db
|
||||||
|
.query("api::event-relationship.event-relationship")
|
||||||
|
.findOne({
|
||||||
|
where: {
|
||||||
|
event: { id: parseInt(eventId) },
|
||||||
|
contextType: "user",
|
||||||
|
contextId: parseInt(userId),
|
||||||
|
relation: { $in: ["registered", "interested"] },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (existingRelationship) {
|
||||||
|
return ctx.badRequest(
|
||||||
|
"User is already registered or interested in this event"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Créer la nouvelle relation d'inscription
|
||||||
|
const eventRelationship = await strapi.db
|
||||||
|
.query("api::event-relationship.event-relationship")
|
||||||
|
.create({
|
||||||
|
data: {
|
||||||
|
author: parseInt(userId),
|
||||||
|
event: parseInt(eventId),
|
||||||
|
contextType: "user",
|
||||||
|
contextId: parseInt(userId),
|
||||||
|
relation: "registered",
|
||||||
|
metas: {
|
||||||
|
appliedAt: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Populer les données pour la réponse
|
||||||
|
const populatedRelationship = await strapi.db
|
||||||
|
.query("api::event-relationship.event-relationship")
|
||||||
|
.findOne({
|
||||||
|
where: { id: eventRelationship.id },
|
||||||
|
populate: {
|
||||||
|
event: {
|
||||||
|
populate: {
|
||||||
|
choral: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
author: {
|
||||||
|
populate: {
|
||||||
|
avatar: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
ctx.send({
|
||||||
|
data: populatedRelationship,
|
||||||
|
message: "Successfully registered for the event",
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
strapi.log.error("Error in applyEvent controller:", error);
|
||||||
|
ctx.internalServerError(`Failed to register for event: ${error.message}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -2,6 +2,39 @@
|
|||||||
* event controller
|
* event controller
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { factories } from '@strapi/strapi'
|
import { factories } from "@strapi/strapi";
|
||||||
|
import fs from "fs";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
export default factories.createCoreController('api::event.event');
|
export default factories.createCoreController(
|
||||||
|
"api::event.event",
|
||||||
|
({ strapi }) => {
|
||||||
|
const controllersDir = __dirname;
|
||||||
|
const controllers: Record<string, any> = {};
|
||||||
|
|
||||||
|
fs.readdirSync(controllersDir).forEach((file) => {
|
||||||
|
// Ignorer le contrôleur principal + fichiers map
|
||||||
|
if (file.startsWith("event.") || file.endsWith(".map")) return;
|
||||||
|
|
||||||
|
// On accepte .ts et .js
|
||||||
|
const ext = path.extname(file);
|
||||||
|
if (![".ts", ".js"].includes(ext)) return;
|
||||||
|
|
||||||
|
const name = path.basename(file, ext);
|
||||||
|
const modulePath = path.join(controllersDir, file);
|
||||||
|
const module = require(modulePath);
|
||||||
|
|
||||||
|
const handler = module.default;
|
||||||
|
if (typeof handler === "function") {
|
||||||
|
controllers[name] = handler({ strapi });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(Object.keys(controllers));
|
||||||
|
|
||||||
|
return {
|
||||||
|
// ✅ injection automatique des customs
|
||||||
|
...controllers,
|
||||||
|
} as any;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|||||||
278
src/api/event/controllers/feed.ts
Normal file
278
src/api/event/controllers/feed.ts
Normal file
@@ -0,0 +1,278 @@
|
|||||||
|
import type { Core } from "@strapi/strapi";
|
||||||
|
import type { Context } from "koa";
|
||||||
|
|
||||||
|
export default ({ strapi }: { strapi: Core.Strapi }) =>
|
||||||
|
async function feed(ctx: Context) {
|
||||||
|
const userId = ctx.state.user?.id;
|
||||||
|
|
||||||
|
if (!userId) return ctx.badRequest("userId is required");
|
||||||
|
|
||||||
|
// Récupérer et valider la query avec les fonctions de Strapi
|
||||||
|
|
||||||
|
const contentType = strapi.contentType(
|
||||||
|
"api::event-relationship.event-relationship"
|
||||||
|
);
|
||||||
|
let limit: number;
|
||||||
|
let start: number;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Sanitize la query
|
||||||
|
await strapi.contentAPI.validate.query(ctx.query, contentType, {
|
||||||
|
auth: ctx.state.auth,
|
||||||
|
});
|
||||||
|
const sanitizedQueryParams = await strapi.contentAPI.sanitize.query(
|
||||||
|
ctx.query,
|
||||||
|
contentType,
|
||||||
|
{ auth: ctx.state.auth }
|
||||||
|
);
|
||||||
|
|
||||||
|
const { _limit = 20, _start = 0 } = sanitizedQueryParams;
|
||||||
|
limit = Math.max(1, Math.min(parseInt(String(_limit)) || 20, 100)); // Entre 1 et 100
|
||||||
|
start = Math.max(0, parseInt(String(_start)) || 0); // Minimum 0
|
||||||
|
} catch (error) {
|
||||||
|
return ctx.badRequest(`Invalid query parameters: ${error.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const queryParams = {
|
||||||
|
populate: {
|
||||||
|
event: {
|
||||||
|
populate: {
|
||||||
|
choral: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
author: {
|
||||||
|
populate: {
|
||||||
|
avatar: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
sort: { createdAt: "desc" },
|
||||||
|
pagination: {
|
||||||
|
start,
|
||||||
|
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 count total pour la pagination
|
||||||
|
const totalCount = await strapi.db
|
||||||
|
.query("api::event-relationship.event-relationship")
|
||||||
|
.count({
|
||||||
|
where: {
|
||||||
|
relation: "owner",
|
||||||
|
$or: [
|
||||||
|
// EventRelationships de l'utilisateur courant
|
||||||
|
{ author: { id: parseInt(userId) } },
|
||||||
|
// EventRelationships des amis
|
||||||
|
{ author: { id: friendIds } },
|
||||||
|
// EventRelationships des contacts suivis
|
||||||
|
{ author: { id: followIds } },
|
||||||
|
// EventRelationships des groupes de l'utilisateur
|
||||||
|
{ contextType: "group", contextId: groupIds },
|
||||||
|
// EventRelationships des groupes des amis
|
||||||
|
{ contextType: "group", contextId: friendsGroupIds },
|
||||||
|
// EventRelationships système
|
||||||
|
{ contextType: "system" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 4️⃣ Récupérer le feed avec pagination
|
||||||
|
const feed = await strapi.db
|
||||||
|
.query("api::event-relationship.event-relationship")
|
||||||
|
.findMany({
|
||||||
|
where: {
|
||||||
|
relation: "owner",
|
||||||
|
$or: [
|
||||||
|
// EventRelationships de l'utilisateur courant
|
||||||
|
{ author: { id: parseInt(userId) } },
|
||||||
|
// EventRelationships des amis
|
||||||
|
{ author: { id: friendIds } },
|
||||||
|
// EventRelationships des contacts suivis
|
||||||
|
{ author: { id: followIds } },
|
||||||
|
// EventRelationships des groupes de l'utilisateur
|
||||||
|
{ contextType: "group", contextId: groupIds },
|
||||||
|
// EventRelationships des groupes des amis
|
||||||
|
{ contextType: "group", contextId: friendsGroupIds },
|
||||||
|
// EventRelationships système
|
||||||
|
{ contextType: "system" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
...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️⃣ Récupérer les participants (registered) pour chaque événement du feed
|
||||||
|
const eventIds = feed.filter((er) => er.event?.id).map((er) => er.event.id);
|
||||||
|
|
||||||
|
const registeredRelationships = await strapi.db
|
||||||
|
.query("api::event-relationship.event-relationship")
|
||||||
|
.findMany({
|
||||||
|
where: {
|
||||||
|
event: { id: eventIds },
|
||||||
|
relation: { $in: ["registered", "interested", "pending"] },
|
||||||
|
contextType: "user",
|
||||||
|
},
|
||||||
|
populate: ["event"],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Extraire les userIds et récupérer les Users complètement populés
|
||||||
|
const userIds = [
|
||||||
|
...new Set(registeredRelationships.map((er) => er.contextId)),
|
||||||
|
];
|
||||||
|
const users = await strapi.db
|
||||||
|
.query("plugin::users-permissions.user")
|
||||||
|
.findMany({
|
||||||
|
where: { id: userIds },
|
||||||
|
populate: {
|
||||||
|
avatar: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Créer une map des users par ID pour accès rapide
|
||||||
|
const usersMap = new Map(users.map((user) => [user.id, user]));
|
||||||
|
|
||||||
|
// Grouper les participants par eventId
|
||||||
|
const participantsByEventId = new Map<number, any[]>();
|
||||||
|
registeredRelationships.forEach((er) => {
|
||||||
|
const eventId = er.event?.id;
|
||||||
|
if (eventId) {
|
||||||
|
if (!participantsByEventId.has(eventId)) {
|
||||||
|
participantsByEventId.set(eventId, []);
|
||||||
|
}
|
||||||
|
const user = usersMap.get(er.contextId);
|
||||||
|
if (user) {
|
||||||
|
participantsByEventId.get(eventId)!.push({
|
||||||
|
relation: er.relation,
|
||||||
|
createdAt: er.createdAt,
|
||||||
|
user: user,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 7️⃣ Enrichir le feed avec les propriétés friend, member, contactFollow, group et registered participants
|
||||||
|
const enrichedFeed = feed.map((eventRelationship) => {
|
||||||
|
const authorId = eventRelationship.author?.id;
|
||||||
|
const contextType = eventRelationship.contextType;
|
||||||
|
const contextId = eventRelationship.contextId;
|
||||||
|
const eventId = eventRelationship.event?.id;
|
||||||
|
|
||||||
|
// 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 events 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;
|
||||||
|
|
||||||
|
// Récupérer les participants enregistrés pour cet événement
|
||||||
|
const registered = eventId
|
||||||
|
? participantsByEventId.get(eventId) || []
|
||||||
|
: [];
|
||||||
|
|
||||||
|
return {
|
||||||
|
...eventRelationship,
|
||||||
|
friend: isFriend,
|
||||||
|
contactFollow: isContactFollow,
|
||||||
|
member: isMember,
|
||||||
|
...(group && { group }), // Ajouter group seulement s'il existe
|
||||||
|
registered, // Ajouter les participants enregistrés
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Trier par createdAt (le plus récent en premier)
|
||||||
|
const sortedFeed = enrichedFeed.sort((a, b) => {
|
||||||
|
const dateA = a.event?.createdAt
|
||||||
|
? new Date(a.event.createdAt).getTime()
|
||||||
|
: 0;
|
||||||
|
const dateB = b.event?.createdAt
|
||||||
|
? new Date(b.event.createdAt).getTime()
|
||||||
|
: 0;
|
||||||
|
return dateB - dateA; // Ordre décroissant (plus récent en premier)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Calculer les métadonnées de pagination comme Strapi
|
||||||
|
const pageSize = limit;
|
||||||
|
const page = Math.floor(start / limit) + 1;
|
||||||
|
const pageCount = Math.ceil(totalCount / limit);
|
||||||
|
|
||||||
|
// Retourner avec les métadonnées de pagination
|
||||||
|
ctx.send({
|
||||||
|
data: sortedFeed,
|
||||||
|
meta: {
|
||||||
|
pagination: {
|
||||||
|
start,
|
||||||
|
limit: pageSize,
|
||||||
|
total: totalCount,
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
pageCount,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
65
src/api/event/controllers/unapplyEvent.ts
Normal file
65
src/api/event/controllers/unapplyEvent.ts
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import type { Core } from "@strapi/strapi";
|
||||||
|
import type { Context } from "koa";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller pour gérer la désinscription d'un utilisateur d'un événement
|
||||||
|
* POST /events/:eventId/unapply
|
||||||
|
*/
|
||||||
|
export default ({ strapi }: { strapi: Core.Strapi }) =>
|
||||||
|
async function unapplyEvent(ctx: Context) {
|
||||||
|
const userId = ctx.state.user?.id;
|
||||||
|
const eventId = ctx.params.eventId;
|
||||||
|
|
||||||
|
// Validation des paramètres
|
||||||
|
if (!userId) {
|
||||||
|
return ctx.badRequest("User ID is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!eventId || isNaN(parseInt(eventId))) {
|
||||||
|
return ctx.badRequest("Event ID is required and must be a valid number");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Vérifier que l'événement existe
|
||||||
|
const event = await strapi.db.query("api::event.event").findOne({
|
||||||
|
where: { id: parseInt(eventId) },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!event) {
|
||||||
|
return ctx.notFound("Event not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vérifier que l'utilisateur est inscrit
|
||||||
|
const existingRelationship = await strapi.db
|
||||||
|
.query("api::event-relationship.event-relationship")
|
||||||
|
.findOne({
|
||||||
|
where: {
|
||||||
|
event: { id: parseInt(eventId) },
|
||||||
|
contextType: "user",
|
||||||
|
contextId: parseInt(userId),
|
||||||
|
relation: { $in: ["registered", "interested"] },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!existingRelationship) {
|
||||||
|
return ctx.badRequest("User is not registered for this event");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Supprimer la relation d'inscription
|
||||||
|
const deletedRelationship = await strapi.db
|
||||||
|
.query("api::event-relationship.event-relationship")
|
||||||
|
.delete({
|
||||||
|
where: { id: existingRelationship.id },
|
||||||
|
});
|
||||||
|
|
||||||
|
ctx.send({
|
||||||
|
data: deletedRelationship,
|
||||||
|
message: "Successfully unregistered from the event",
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
strapi.log.error("Error in unapplyEvent controller:", error);
|
||||||
|
ctx.internalServerError(
|
||||||
|
`Failed to unregister from event: ${error.message}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
23
src/api/event/routes/custom.ts
Normal file
23
src/api/event/routes/custom.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* Custom event routes
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
path: "/events/feed",
|
||||||
|
handler: "event.feed",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
path: "/events/:eventId/apply",
|
||||||
|
handler: "event.applyEvent",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
path: "/events/:eventId/unapply",
|
||||||
|
handler: "event.unapplyEvent",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
@@ -148,7 +148,7 @@ export default factories.createCoreController(
|
|||||||
});
|
});
|
||||||
const followIds = followContacts.map((c) => c.user.id);
|
const followIds = followContacts.map((c) => c.user.id);
|
||||||
|
|
||||||
// Récupérer les contacts suivis (follow)
|
// Récupérer les contacts bloqués (blocked)
|
||||||
const blockedContacts = await strapi.db
|
const blockedContacts = await strapi.db
|
||||||
.query("api::contact.contact")
|
.query("api::contact.contact")
|
||||||
.findMany({
|
.findMany({
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
75
types/generated/contentTypes.d.ts
vendored
75
types/generated/contentTypes.d.ts
vendored
@@ -947,14 +947,14 @@ export interface ApiEventOtherEventOther extends Struct.CollectionTypeSchema {
|
|||||||
[
|
[
|
||||||
'concert',
|
'concert',
|
||||||
'festival',
|
'festival',
|
||||||
'salon',
|
'trade show',
|
||||||
'symposium',
|
'symposium',
|
||||||
'concours',
|
'competition',
|
||||||
'masterclass',
|
'masterclass',
|
||||||
'atelier vocal',
|
'vocal_workshop',
|
||||||
'conf\u00E9rence',
|
'conference',
|
||||||
'r\u00E9p\u00E9tition',
|
'rehearsal',
|
||||||
'sorties',
|
'outings',
|
||||||
]
|
]
|
||||||
>;
|
>;
|
||||||
updatedAt: Schema.Attribute.DateTime;
|
updatedAt: Schema.Attribute.DateTime;
|
||||||
@@ -963,6 +963,48 @@ export interface ApiEventOtherEventOther extends Struct.CollectionTypeSchema {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ApiEventRelationshipEventRelationship
|
||||||
|
extends Struct.CollectionTypeSchema {
|
||||||
|
collectionName: 'event_relationships';
|
||||||
|
info: {
|
||||||
|
description: '';
|
||||||
|
displayName: 'EventRelationship';
|
||||||
|
pluralName: 'event-relationships';
|
||||||
|
singularName: 'event-relationship';
|
||||||
|
};
|
||||||
|
options: {
|
||||||
|
draftAndPublish: false;
|
||||||
|
};
|
||||||
|
attributes: {
|
||||||
|
author: Schema.Attribute.Relation<
|
||||||
|
'oneToOne',
|
||||||
|
'plugin::users-permissions.user'
|
||||||
|
>;
|
||||||
|
contextId: Schema.Attribute.Integer;
|
||||||
|
contextType: Schema.Attribute.Enumeration<
|
||||||
|
['user', 'group', 'choral', 'system']
|
||||||
|
>;
|
||||||
|
createdAt: Schema.Attribute.DateTime;
|
||||||
|
createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
||||||
|
Schema.Attribute.Private;
|
||||||
|
event: Schema.Attribute.Relation<'oneToOne', 'api::event.event'>;
|
||||||
|
locale: Schema.Attribute.String & Schema.Attribute.Private;
|
||||||
|
localizations: Schema.Attribute.Relation<
|
||||||
|
'oneToMany',
|
||||||
|
'api::event-relationship.event-relationship'
|
||||||
|
> &
|
||||||
|
Schema.Attribute.Private;
|
||||||
|
metas: Schema.Attribute.JSON;
|
||||||
|
publishedAt: Schema.Attribute.DateTime;
|
||||||
|
relation: Schema.Attribute.Enumeration<
|
||||||
|
['owner', 'interested', 'registered']
|
||||||
|
>;
|
||||||
|
updatedAt: Schema.Attribute.DateTime;
|
||||||
|
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
||||||
|
Schema.Attribute.Private;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export interface ApiEventEvent extends Struct.CollectionTypeSchema {
|
export interface ApiEventEvent extends Struct.CollectionTypeSchema {
|
||||||
collectionName: 'events';
|
collectionName: 'events';
|
||||||
info: {
|
info: {
|
||||||
@@ -981,17 +1023,37 @@ export interface ApiEventEvent extends Struct.CollectionTypeSchema {
|
|||||||
createdAt: Schema.Attribute.DateTime;
|
createdAt: Schema.Attribute.DateTime;
|
||||||
createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
createdBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
||||||
Schema.Attribute.Private;
|
Schema.Attribute.Private;
|
||||||
|
description: Schema.Attribute.Text;
|
||||||
end: Schema.Attribute.DateTime;
|
end: Schema.Attribute.DateTime;
|
||||||
|
isPublic: Schema.Attribute.Boolean;
|
||||||
locale: Schema.Attribute.String & Schema.Attribute.Private;
|
locale: Schema.Attribute.String & Schema.Attribute.Private;
|
||||||
localizations: Schema.Attribute.Relation<'oneToMany', 'api::event.event'> &
|
localizations: Schema.Attribute.Relation<'oneToMany', 'api::event.event'> &
|
||||||
Schema.Attribute.Private;
|
Schema.Attribute.Private;
|
||||||
location: Schema.Attribute.String;
|
location: Schema.Attribute.String;
|
||||||
publishedAt: Schema.Attribute.DateTime;
|
publishedAt: Schema.Attribute.DateTime;
|
||||||
|
size: Schema.Attribute.Integer;
|
||||||
start: Schema.Attribute.DateTime;
|
start: Schema.Attribute.DateTime;
|
||||||
title: Schema.Attribute.String;
|
title: Schema.Attribute.String;
|
||||||
|
type: Schema.Attribute.Enumeration<
|
||||||
|
[
|
||||||
|
'concert',
|
||||||
|
'festival',
|
||||||
|
'trade show',
|
||||||
|
'symposium',
|
||||||
|
'competition',
|
||||||
|
'masterclass',
|
||||||
|
'vocal_workshop',
|
||||||
|
'conference',
|
||||||
|
'rehearsal',
|
||||||
|
'outings',
|
||||||
|
]
|
||||||
|
>;
|
||||||
updatedAt: Schema.Attribute.DateTime;
|
updatedAt: Schema.Attribute.DateTime;
|
||||||
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
updatedBy: Schema.Attribute.Relation<'oneToOne', 'admin::user'> &
|
||||||
Schema.Attribute.Private;
|
Schema.Attribute.Private;
|
||||||
|
voice: Schema.Attribute.Enumeration<
|
||||||
|
['soprano', 'alto', 'tenor', 'bass', 'mixed']
|
||||||
|
>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1886,6 +1948,7 @@ declare module '@strapi/strapi' {
|
|||||||
'api::conversation.conversation': ApiConversationConversation;
|
'api::conversation.conversation': ApiConversationConversation;
|
||||||
'api::direct-message.direct-message': ApiDirectMessageDirectMessage;
|
'api::direct-message.direct-message': ApiDirectMessageDirectMessage;
|
||||||
'api::event-other.event-other': ApiEventOtherEventOther;
|
'api::event-other.event-other': ApiEventOtherEventOther;
|
||||||
|
'api::event-relationship.event-relationship': ApiEventRelationshipEventRelationship;
|
||||||
'api::event.event': ApiEventEvent;
|
'api::event.event': ApiEventEvent;
|
||||||
'api::group-membership.group-membership': ApiGroupMembershipGroupMembership;
|
'api::group-membership.group-membership': ApiGroupMembershipGroupMembership;
|
||||||
'api::group.group': ApiGroupGroup;
|
'api::group.group': ApiGroupGroup;
|
||||||
|
|||||||
Reference in New Issue
Block a user