feat(api): implement /api/event/[eventID] endpoint
This commit is contained in:
parent
50d915854f
commit
f5a5704be3
2 changed files with 412 additions and 0 deletions
318
src/app/api/event/[eventID]/route.ts
Normal file
318
src/app/api/event/[eventID]/route.ts
Normal file
|
@ -0,0 +1,318 @@
|
||||||
|
import { prisma } from '@/prisma';
|
||||||
|
import { auth } from '@/auth';
|
||||||
|
import {
|
||||||
|
returnZodTypeCheckedResponse,
|
||||||
|
userAuthenticated,
|
||||||
|
} from '@/lib/apiHelpers';
|
||||||
|
import {
|
||||||
|
ErrorResponseSchema,
|
||||||
|
SuccessResponseSchema,
|
||||||
|
ZodErrorResponseSchema,
|
||||||
|
} from '../../validation';
|
||||||
|
import { EventResponseSchema } from '../validation';
|
||||||
|
import { updateEventSchema } from '../validation';
|
||||||
|
|
||||||
|
export const GET = auth(async (req, { params }) => {
|
||||||
|
const authCheck = userAuthenticated(req);
|
||||||
|
if (!authCheck.continue)
|
||||||
|
return returnZodTypeCheckedResponse(
|
||||||
|
ErrorResponseSchema,
|
||||||
|
authCheck.response,
|
||||||
|
authCheck.metadata,
|
||||||
|
);
|
||||||
|
|
||||||
|
const dbUser = await prisma.user.findUnique({
|
||||||
|
where: {
|
||||||
|
id: authCheck.user.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!dbUser)
|
||||||
|
return returnZodTypeCheckedResponse(
|
||||||
|
ErrorResponseSchema,
|
||||||
|
{ success: false, message: 'User not found' },
|
||||||
|
{ status: 404 },
|
||||||
|
);
|
||||||
|
|
||||||
|
const eventID = (await params).eventID;
|
||||||
|
|
||||||
|
const event = await prisma.meeting.findUnique({
|
||||||
|
where: {
|
||||||
|
id: eventID,
|
||||||
|
OR: [
|
||||||
|
{ organizer_id: dbUser.id },
|
||||||
|
{ participants: { some: { user_id: dbUser.id } } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
title: true,
|
||||||
|
description: true,
|
||||||
|
start_time: true,
|
||||||
|
end_time: true,
|
||||||
|
status: true,
|
||||||
|
location: true,
|
||||||
|
organizer_id: true,
|
||||||
|
created_at: true,
|
||||||
|
updated_at: true,
|
||||||
|
organizer: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
first_name: true,
|
||||||
|
last_name: true,
|
||||||
|
image: true,
|
||||||
|
timezone: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
participants: {
|
||||||
|
select: {
|
||||||
|
user: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
first_name: true,
|
||||||
|
last_name: true,
|
||||||
|
image: true,
|
||||||
|
timezone: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
status: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!event)
|
||||||
|
return returnZodTypeCheckedResponse(
|
||||||
|
ErrorResponseSchema,
|
||||||
|
{ success: false, message: 'Event not found' },
|
||||||
|
{ status: 404 },
|
||||||
|
);
|
||||||
|
|
||||||
|
return returnZodTypeCheckedResponse(
|
||||||
|
EventResponseSchema,
|
||||||
|
{ success: true, event },
|
||||||
|
{ status: 200 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export const DELETE = auth(async (req, { params }) => {
|
||||||
|
const authCheck = userAuthenticated(req);
|
||||||
|
if (!authCheck.continue)
|
||||||
|
return returnZodTypeCheckedResponse(
|
||||||
|
ErrorResponseSchema,
|
||||||
|
authCheck.response,
|
||||||
|
authCheck.metadata,
|
||||||
|
);
|
||||||
|
|
||||||
|
const dbUser = await prisma.user.findUnique({
|
||||||
|
where: {
|
||||||
|
id: authCheck.user.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!dbUser)
|
||||||
|
return returnZodTypeCheckedResponse(
|
||||||
|
ErrorResponseSchema,
|
||||||
|
{ success: false, message: 'User not found' },
|
||||||
|
{ status: 404 },
|
||||||
|
);
|
||||||
|
|
||||||
|
const eventID = (await params).eventID;
|
||||||
|
|
||||||
|
const event = await prisma.meeting.findUnique({
|
||||||
|
where: {
|
||||||
|
id: eventID,
|
||||||
|
OR: [
|
||||||
|
{ organizer_id: dbUser.id },
|
||||||
|
{ participants: { some: { user_id: dbUser.id } } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!event)
|
||||||
|
return returnZodTypeCheckedResponse(
|
||||||
|
ErrorResponseSchema,
|
||||||
|
{ success: false, message: 'Event not found' },
|
||||||
|
{ status: 404 },
|
||||||
|
);
|
||||||
|
|
||||||
|
if (event.organizer_id !== dbUser.id)
|
||||||
|
return returnZodTypeCheckedResponse(
|
||||||
|
ErrorResponseSchema,
|
||||||
|
{ success: false, message: 'You are not the organizer of this event' },
|
||||||
|
{ status: 403 },
|
||||||
|
);
|
||||||
|
|
||||||
|
await prisma.meeting.delete({
|
||||||
|
where: {
|
||||||
|
id: eventID,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return returnZodTypeCheckedResponse(
|
||||||
|
SuccessResponseSchema,
|
||||||
|
{ success: true, message: 'Event deleted successfully' },
|
||||||
|
{ status: 200 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export const PATCH = auth(async (req, { params }) => {
|
||||||
|
const authCheck = userAuthenticated(req);
|
||||||
|
if (!authCheck.continue)
|
||||||
|
return returnZodTypeCheckedResponse(
|
||||||
|
ErrorResponseSchema,
|
||||||
|
authCheck.response,
|
||||||
|
authCheck.metadata,
|
||||||
|
);
|
||||||
|
|
||||||
|
const dbUser = await prisma.user.findUnique({
|
||||||
|
where: {
|
||||||
|
id: authCheck.user.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!dbUser)
|
||||||
|
return returnZodTypeCheckedResponse(
|
||||||
|
ErrorResponseSchema,
|
||||||
|
{ success: false, message: 'User not found' },
|
||||||
|
{ status: 404 },
|
||||||
|
);
|
||||||
|
|
||||||
|
const eventID = (await params).eventID;
|
||||||
|
|
||||||
|
const event = await prisma.meeting.findUnique({
|
||||||
|
where: {
|
||||||
|
id: eventID,
|
||||||
|
OR: [
|
||||||
|
{ organizer_id: dbUser.id },
|
||||||
|
{ participants: { some: { user_id: dbUser.id } } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!event)
|
||||||
|
return returnZodTypeCheckedResponse(
|
||||||
|
ErrorResponseSchema,
|
||||||
|
{ success: false, message: 'Event not found' },
|
||||||
|
{ status: 404 },
|
||||||
|
);
|
||||||
|
|
||||||
|
if (event.organizer_id !== dbUser.id)
|
||||||
|
return returnZodTypeCheckedResponse(
|
||||||
|
ErrorResponseSchema,
|
||||||
|
{ success: false, message: 'You are not the organizer of this event' },
|
||||||
|
{ status: 403 },
|
||||||
|
);
|
||||||
|
|
||||||
|
const dataRaw = await req.json();
|
||||||
|
const data = await updateEventSchema.safeParseAsync(dataRaw);
|
||||||
|
if (!data.success) {
|
||||||
|
return returnZodTypeCheckedResponse(
|
||||||
|
ZodErrorResponseSchema,
|
||||||
|
{
|
||||||
|
success: false,
|
||||||
|
message: 'Invalid input data',
|
||||||
|
errors: data.error.issues,
|
||||||
|
},
|
||||||
|
{ status: 400 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const {
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
start_time,
|
||||||
|
end_time,
|
||||||
|
location,
|
||||||
|
status,
|
||||||
|
participants,
|
||||||
|
} = data.data;
|
||||||
|
|
||||||
|
if (participants !== undefined)
|
||||||
|
for (const participant of participants) {
|
||||||
|
await prisma.meetingParticipant.upsert({
|
||||||
|
where: {
|
||||||
|
meeting_id_user_id: {
|
||||||
|
user_id: participant,
|
||||||
|
meeting_id: eventID,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
user_id: participant,
|
||||||
|
meeting_id: eventID,
|
||||||
|
},
|
||||||
|
update: {},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedEvent = await prisma.meeting.update({
|
||||||
|
where: {
|
||||||
|
id: eventID,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
start_time,
|
||||||
|
end_time,
|
||||||
|
location,
|
||||||
|
status,
|
||||||
|
participants:
|
||||||
|
participants !== undefined
|
||||||
|
? {
|
||||||
|
deleteMany: {
|
||||||
|
user_id: {
|
||||||
|
notIn: participants || [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: {},
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
title: true,
|
||||||
|
description: true,
|
||||||
|
start_time: true,
|
||||||
|
end_time: true,
|
||||||
|
status: true,
|
||||||
|
location: true,
|
||||||
|
organizer_id: true,
|
||||||
|
created_at: true,
|
||||||
|
updated_at: true,
|
||||||
|
organizer: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
first_name: true,
|
||||||
|
last_name: true,
|
||||||
|
image: true,
|
||||||
|
timezone: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
participants: {
|
||||||
|
select: {
|
||||||
|
user: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
first_name: true,
|
||||||
|
last_name: true,
|
||||||
|
image: true,
|
||||||
|
timezone: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
status: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return returnZodTypeCheckedResponse(
|
||||||
|
EventResponseSchema,
|
||||||
|
{
|
||||||
|
success: true,
|
||||||
|
event: updatedEvent,
|
||||||
|
},
|
||||||
|
{ status: 200 },
|
||||||
|
);
|
||||||
|
});
|
94
src/app/api/event/[eventID]/swagger.ts
Normal file
94
src/app/api/event/[eventID]/swagger.ts
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
import { OpenAPIRegistry } from '@asteasolutions/zod-to-openapi';
|
||||||
|
import { EventResponseSchema, updateEventSchema } from '../validation';
|
||||||
|
import {
|
||||||
|
invalidRequestDataResponse,
|
||||||
|
notAuthenticatedResponse,
|
||||||
|
serverReturnedDataValidationErrorResponse,
|
||||||
|
userNotFoundResponse,
|
||||||
|
} from '@/lib/defaultApiResponses';
|
||||||
|
import {
|
||||||
|
EventIdParamSchema,
|
||||||
|
SuccessResponseSchema,
|
||||||
|
} from '@/app/api/validation';
|
||||||
|
import zod from 'zod/v4';
|
||||||
|
|
||||||
|
export default function registerSwaggerPaths(registry: OpenAPIRegistry) {
|
||||||
|
registry.registerPath({
|
||||||
|
method: 'get',
|
||||||
|
path: '/api/event/{eventID}',
|
||||||
|
request: {
|
||||||
|
params: zod.object({
|
||||||
|
eventID: EventIdParamSchema,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: 'Event retrieved successfully',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: EventResponseSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...userNotFoundResponse,
|
||||||
|
...serverReturnedDataValidationErrorResponse,
|
||||||
|
},
|
||||||
|
tags: ['Event'],
|
||||||
|
});
|
||||||
|
|
||||||
|
registry.registerPath({
|
||||||
|
method: 'delete',
|
||||||
|
path: '/api/event/{eventID}',
|
||||||
|
request: {
|
||||||
|
params: zod.object({
|
||||||
|
eventID: EventIdParamSchema,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: 'Event deleted successfully',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: SuccessResponseSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...notAuthenticatedResponse,
|
||||||
|
...userNotFoundResponse,
|
||||||
|
...serverReturnedDataValidationErrorResponse,
|
||||||
|
},
|
||||||
|
tags: ['Event'],
|
||||||
|
});
|
||||||
|
|
||||||
|
registry.registerPath({
|
||||||
|
method: 'patch',
|
||||||
|
path: '/api/event/{eventID}',
|
||||||
|
request: {
|
||||||
|
params: zod.object({
|
||||||
|
eventID: EventIdParamSchema,
|
||||||
|
}),
|
||||||
|
body: {
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: updateEventSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: 'Event updated successfully',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: EventResponseSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...invalidRequestDataResponse,
|
||||||
|
...notAuthenticatedResponse,
|
||||||
|
...userNotFoundResponse,
|
||||||
|
...serverReturnedDataValidationErrorResponse,
|
||||||
|
},
|
||||||
|
tags: ['Event'],
|
||||||
|
});
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue