Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions cms/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,13 @@ JWT_SECRET=tobemodified
PROJECTS_TOKEN=tobemodified
PROJECTS_ORG=base42
PROJECT_SYNC_SECRET=tobemodified

# Stripe
STRIPE_WEBHOOK_SECRET=tobemodified
STRIPE_MONTHLY_PRICE_ID=tobemodified
STRIPE_YEARLY_PRICE_ID=tobemodified
STRIPE_SUCCESS_URL=https://42.mk/membership/success
STRIPE_CANCEL_URL=https://42.mk/membership/cancel
STRIPE_RETURN_URL=https://42.mk/membership/manage
STRIPE_SECRET_KEY=tobemodified
STRIPE_PRICE_ID=tobemodified
1 change: 1 addition & 0 deletions cms/config/middlewares.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export default [
'strapi::poweredBy',
'strapi::logger',
'strapi::query',
{ name: 'global::stripe-webhook', config: {} },
'strapi::body',
'strapi::session',
'strapi::favicon',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
"eventType": {
"type": "string"
},
"room": {
"type": "enumeration",
"enum": ["events-hall", "workshop-area", "electronics-area", "studio", "full-space"]
},
"eventName": {
"type": "string"
},
Expand Down
63 changes: 1 addition & 62 deletions cms/src/api/event-request/controllers/event-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,65 +4,4 @@

import { factories } from '@strapi/strapi'

export default factories.createCoreController('api::event-request.event-request', ({ strapi }) => ({
async approve(ctx) {
const user = ctx.state?.user;
if (!user) {
return ctx.unauthorized();
}

const { id } = ctx.params;

const request = (await strapi.documents('api::event-request.event-request').findOne({
documentId: id,
populate: { event: true } as never,
})) as {
documentId: string;
status?: string;
initiatorEmail?: string;
eventName?: string;
event?: { documentId: string } | null;
} | null;

if (!request) {
return ctx.notFound('Event request not found');
}

if (!request.event) {
return ctx.badRequest('No linked draft event found');
}

if (request.status === 'approved') {
return ctx.badRequest('Event request already approved');
}

try {
await strapi.documents('api::event.event').publish({
documentId: request.event.documentId,
});

await strapi.documents('api::event-request.event-request').update({
documentId: id,
data: { status: 'approved' } as Record<string, unknown>,
});

await strapi.plugins['email'].services.email.send({
to: request.initiatorEmail,
from: '[email protected]',
replyTo: '[email protected]',
subject: 'Your event has been approved! - 42.mk',
html: `
<p>Great news! Your event request "<strong>${request.eventName}</strong>" has been approved and is now published.</p>
<p>You can view it on our platform.</p>
<p>Best regards,<br/>42.mk Team</p>
`,
});

ctx.body = { message: 'Event request approved' };
} catch (error) {
strapi.log.error('Error approving event request:', error);
ctx.status = 500;
ctx.body = { error: { message: 'Failed to approve event request' } };
}
},
}));
export default factories.createCoreController('api::event-request.event-request');
76 changes: 39 additions & 37 deletions cms/src/api/event-request/controllers/form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const formToJsonMap = {
"phone": "initiatorPhoneNumber",
"company-name": "organization",
"event-type": "eventType",
"room": "room",
"event-name": "eventName",
"event-theme": "eventTheme",
"event-purpose": "eventPurpose",
Expand All @@ -16,13 +17,24 @@ const formToJsonMap = {
"expected-guests": "expectedGuests",
};

const ROOM_KEYS = ['events-hall', 'workshop-area', 'electronics-area', 'studio', 'full-space'] as const;

type RoomKey = (typeof ROOM_KEYS)[number];

function parseRoom(value: unknown): RoomKey | undefined {
return typeof value === 'string' && (ROOM_KEYS as readonly string[]).includes(value)
? (value as RoomKey)
: undefined;
}

interface EventRequestInput {
organizingEntity: string;
initiatorName: string;
initiatorEmail: string;
initiatorPhoneNumber: string;
organization: string;
eventType: string;
room?: RoomKey;
eventName: string;
eventTheme: string;
eventPurpose: string;
Expand Down Expand Up @@ -76,13 +88,16 @@ export default {
return acc;
}, {} as Record<string, unknown>);

const room = parseRoom(raw.room);

const eventRequest: EventRequestInput = {
organizingEntity: assertString(raw.organizingEntity, 'organizingEntity'),
initiatorName: assertString(raw.initiatorName, 'initiatorName'),
initiatorEmail: assertString(raw.initiatorEmail, 'initiatorEmail'),
initiatorPhoneNumber: assertString(raw.initiatorPhoneNumber, 'initiatorPhoneNumber'),
organization: assertString(raw.organization, 'organization'),
eventType: assertString(raw.eventType, 'eventType'),
...(room !== undefined ? { room } : {}),
eventName: assertString(raw.eventName, 'eventName'),
eventTheme: assertString(raw.eventTheme, 'eventTheme'),
eventPurpose: assertString(raw.eventPurpose, 'eventPurpose'),
Expand Down Expand Up @@ -125,6 +140,7 @@ export default {
<p><strong>Initiator phone</strong> number: ${eventRequest.initiatorPhoneNumber}</p>
<p><strong>Organization:</strong> ${eventRequest.organization}</p>
<p><strong>Event type</strong>: ${eventRequest.eventType}</p>
<p><strong>Room</strong>: ${eventRequest.room}</p>
<p><strong>Event name</strong>: ${eventRequest.eventName}</p>
<p><strong>Event theme</strong>: ${eventRequest.eventTheme}</p>
<p><strong>Event purpose</strong>: ${eventRequest.eventPurpose}</p>
Expand All @@ -136,55 +152,41 @@ export default {
<p><strong>Expected guests</strong>: ${eventRequest.expectedGuests}</p>
`;

await strapi.plugins['email'].services.email.send({
to: eventRequest.initiatorEmail,
from: '[email protected]',
replyTo: '[email protected]',
subject: 'Your event request has been received! - 42.mk',
html: `Thank you for submitting your event request. We will get back to you as soon as possible.
Here's a copy of your request:
<br/><br/>
${requestCopy}
`
});

await strapi.plugins['email'].services.email.send({
to: '[email protected]',
from: '[email protected]',
replyTo: eventRequest.initiatorEmail,
subject: `New event request from ${eventRequest.initiatorName}`,
html: `A new event request has been submitted. Here's a copy of the request:
<br/><br/>
${requestCopy}
`
});

ctx.body = res;
} catch (error) {
console.error('Event request submission error:', error);

// Try to notify admin of the error
try {
await strapi.plugins['email'].services.email.send({
to: eventRequest.initiatorEmail,
from: '[email protected]',
replyTo: '[email protected]',
subject: 'Your event request has been received! - 42.mk',
html: `Thank you for submitting your event request. We will get back to you as soon as possible.
Here's a copy of your request:
<br/><br/>
${requestCopy}
`
});

await strapi.plugins['email'].services.email.send({
to: '[email protected]',
from: '[email protected]',
subject: '[ERROR] Event request submission failed',
html: `<p>An error occurred while processing an event request submission.</p>
<p><strong>Error:</strong> ${error.message || String(error)}</p>
<p><strong>Stack:</strong></p>
<pre>${error.stack || 'No stack trace available'}</pre>
<p><strong>Request body:</strong></p>
<pre>${JSON.stringify(body, null, 2)}</pre>
replyTo: eventRequest.initiatorEmail,
subject: `New event request from ${eventRequest.initiatorName}`,
html: `A new event request has been submitted. Here's a copy of the request:
<br/><br/>
${requestCopy}
`
});
} catch (emailError) {
console.error('Failed to send error notification email:', emailError);
console.error('Failed to send confirmation emails:', emailError);
}

ctx.body = res;
} catch (error) {
console.error('Event request submission error:', error);

ctx.status = 500;
ctx.body = {
error: {
message: 'Failed to submit event request. The administrator has been notified.',
message: 'Failed to submit event request.',
}
};
}
Expand Down
13 changes: 0 additions & 13 deletions cms/src/api/event-request/routes/custom-event-request.ts

This file was deleted.

4 changes: 3 additions & 1 deletion cms/src/api/event-request/routes/event-request.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { factories } from '@strapi/strapi';

export default factories.createCoreRouter('api::event-request.event-request');
export default {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR removes the custom approve controller method, but routes/custom-event-request.ts still registers POST /event-requests/:id/approve with handler event-request.approve. Strapi validates route handlers at startup, so the server throws "Handler not found" and won't boot. That route file needs to be deleted along with the controller method (the publish middleware in src/middlewares/event-request-approval.ts already covers the approval flow).

routes: [],
};
42 changes: 42 additions & 0 deletions cms/src/api/membership/content-types/membership/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"kind": "collectionType",
"collectionName": "memberships",
"info": {
"singularName": "membership",
"pluralName": "memberships",
"displayName": "Membership",
"description": "Tracks user membership subscriptions"
},
"options": {
"draftAndPublish": false
},
"pluginOptions": {},
"attributes": {
"user": {
"type": "relation",
"relation": "manyToOne",
"target": "plugin::users-permissions.user",
"inversedBy": "memberships"
},
"tier": {
"type": "enumeration",
"enum": ["monthly", "yearly"],
"required": true
},
"status": {
"type": "enumeration",
"enum": ["active", "inactive", "cancelled", "cancel_pending", "pending"],
"default": "pending"
},
"startDate": {
"type": "datetime"
},
"endDate": {
"type": "datetime"
},
"stripeSubscriptionId": {
"type": "string",
"private": true
}
}
}
Loading