diff --git a/apps/web/app/api/availability/calendar/route.ts b/apps/web/app/api/availability/calendar/route.ts index 91e37181a4..6cc6ea7d87 100644 --- a/apps/web/app/api/availability/calendar/route.ts +++ b/apps/web/app/api/availability/calendar/route.ts @@ -37,87 +37,68 @@ async function authMiddleware() { } // TODO: It doesn't seem to be used from within the app. It is possible that someone outside Cal.com is using this GET endpoint -export async function GET() { - try { - const user = await authMiddleware(); +async function getHandler() { + const user = await authMiddleware(); - const selectedCalendarIds = await SelectedCalendarRepository.findMany({ - where: { userId: user.id }, - select: { externalId: true }, - }); - // get user's credentials + their connected integrations - const calendarCredentials = getCalendarCredentials(user.credentials); - // get all the connected integrations' calendars (from third party) - const { connectedCalendars } = await getConnectedCalendars( - calendarCredentials, - user.userLevelSelectedCalendars - ); + const selectedCalendarIds = await SelectedCalendarRepository.findMany({ + where: { userId: user.id }, + select: { externalId: true }, + }); + // get user's credentials + their connected integrations + const calendarCredentials = getCalendarCredentials(user.credentials); + // get all the connected integrations' calendars (from third party) + const { connectedCalendars } = await getConnectedCalendars( + calendarCredentials, + user.userLevelSelectedCalendars + ); - const calendars = connectedCalendars.flatMap((c) => c.calendars).filter(notEmpty); - const selectableCalendars = calendars.map((cal) => { - return { selected: selectedCalendarIds.findIndex((s) => s.externalId === cal.externalId) > -1, ...cal }; - }); + const calendars = connectedCalendars.flatMap((c) => c.calendars).filter(notEmpty); + const selectableCalendars = calendars.map((cal) => { + return { selected: selectedCalendarIds.findIndex((s) => s.externalId === cal.externalId) > -1, ...cal }; + }); - return NextResponse.json(selectableCalendars); - } catch (error) { - if (error instanceof HttpError) { - throw error; - } - return NextResponse.json({ error: "Internal Server Error" }, { status: 500 }); - } + return NextResponse.json(selectableCalendars); } -export async function POST(req: NextRequest) { - try { - const user = await authMiddleware(); - const body = await req.json(); - const { integration, externalId, credentialId, eventTypeId } = selectedCalendarSelectSchema.parse(body); +async function postHandler(req: NextRequest) { + const user = await authMiddleware(); + const body = await req.json(); + const { integration, externalId, credentialId, eventTypeId } = selectedCalendarSelectSchema.parse(body); - await SelectedCalendarRepository.upsert({ + await SelectedCalendarRepository.upsert({ + userId: user.id, + integration, + externalId, + credentialId, + eventTypeId: eventTypeId ?? null, + }); + + return NextResponse.json({ message: "Calendar Selection Saved" }); +} + +async function deleteHandler(req: NextRequest) { + const user = await authMiddleware(); + const searchParams = Object.fromEntries(req.nextUrl.searchParams.entries()); + + const { integration, externalId, credentialId, eventTypeId } = + selectedCalendarSelectSchema.parse(searchParams); + + const calendarCacheRepository = await CalendarCache.initFromCredentialId(credentialId); + await calendarCacheRepository.unwatchCalendar({ + calendarId: externalId, + eventTypeIds: [eventTypeId ?? null], + }); + + await SelectedCalendarRepository.delete({ + where: { userId: user.id, - integration, externalId, - credentialId, + integration, eventTypeId: eventTypeId ?? null, - }); + }, + }); - return NextResponse.json({ message: "Calendar Selection Saved" }); - } catch (error) { - if (error instanceof HttpError) { - throw error; - } - return NextResponse.json({ error: "Internal Server Error" }, { status: 500 }); - } + return NextResponse.json({ message: "Calendar Selection Saved" }); } -export async function DELETE(req: NextRequest) { - try { - const user = await authMiddleware(); - const searchParams = Object.fromEntries(req.nextUrl.searchParams.entries()); - - const { integration, externalId, credentialId, eventTypeId } = - selectedCalendarSelectSchema.parse(searchParams); - - const calendarCacheRepository = await CalendarCache.initFromCredentialId(credentialId); - await calendarCacheRepository.unwatchCalendar({ - calendarId: externalId, - eventTypeIds: [eventTypeId ?? null], - }); - - await SelectedCalendarRepository.delete({ - where: { - userId: user.id, - externalId, - integration, - eventTypeId: eventTypeId ?? null, - }, - }); - - return NextResponse.json({ message: "Calendar Selection Saved" }); - } catch (error) { - if (error instanceof HttpError) { - throw error; - } - return NextResponse.json({ error: "Internal Server Error" }, { status: 500 }); - } -} +export { deleteHandler as DELETE, postHandler as POST, getHandler as GET }; diff --git a/apps/web/app/api/cancel/route.ts b/apps/web/app/api/cancel/route.ts index fbb4ba6ad6..ed7555c564 100644 --- a/apps/web/app/api/cancel/route.ts +++ b/apps/web/app/api/cancel/route.ts @@ -4,6 +4,7 @@ import type { NextRequest } from "next/server"; import { getServerSession } from "@calcom/features/auth/lib/getServerSession"; import handleCancelBooking from "@calcom/features/bookings/lib/handleCancelBooking"; +import { apiRouteMiddleware } from "@calcom/lib/server/apiRouteMiddleware"; import { buildLegacyRequest } from "@lib/buildLegacyCtx"; @@ -25,10 +26,7 @@ async function handler(req: NextRequest) { return NextResponse.json(result, { status: statusCode }); } -export async function DELETE(req: NextRequest) { - return handler(req); -} +const deleteHandler = apiRouteMiddleware((req: NextRequest) => handler(req)); +const postHandler = apiRouteMiddleware((req: NextRequest) => handler(req)); -export async function POST(req: NextRequest) { - return handler(req); -} +export { deleteHandler as DELETE, postHandler as POST }; diff --git a/apps/web/app/api/customer-card/route.ts b/apps/web/app/api/customer-card/route.ts index 117e03a45f..9da78df6d7 100644 --- a/apps/web/app/api/customer-card/route.ts +++ b/apps/web/app/api/customer-card/route.ts @@ -1,6 +1,7 @@ import { cardExamples } from "@pages/api/plain/example-cards"; import { createHmac } from "crypto"; import { headers } from "next/headers"; +import type { NextRequest } from "next/server"; import { NextResponse } from "next/server"; import { z } from "zod"; @@ -26,7 +27,7 @@ const inputSchema = z.object({ cardKeys: z.array(z.string()), }); -async function handler(request: Request) { +async function handler(request: NextRequest) { const headersList = headers(); const requestBody = await request.json(); @@ -38,7 +39,7 @@ async function handler(request: Request) { .digest("hex"); if (incomingSignature !== expectedSignature) { - return new Response("Forbidden", { status: 403 }); + return NextResponse.json({ error: "Forbidden" }, { status: 403 }); } // Validate request body diff --git a/apps/web/app/api/plain-hash/route.ts b/apps/web/app/api/plain-hash/route.ts index 1fba1238ac..b395de7b01 100644 --- a/apps/web/app/api/plain-hash/route.ts +++ b/apps/web/app/api/plain-hash/route.ts @@ -1,4 +1,5 @@ import { createHmac } from "crypto"; +import type { NextRequest } from "next/server"; import { NextResponse } from "next/server"; import { z } from "zod"; @@ -14,16 +15,16 @@ const responseSchema = z.object({ chatAvatarUrl: z.string(), }); -async function handler(request: Request) { +async function handler(request: NextRequest) { // eslint-disable-next-line @typescript-eslint/no-explicit-any const session = await getServerSession({ req: request as any }); if (!session?.user?.email) { - return new Response("Unauthorized - No session email found", { status: 401 }); + return NextResponse.json({ error: "Unauthorized - No session email found" }, { status: 401 }); } const secret = process.env.PLAIN_CHAT_HMAC_SECRET_KEY; if (!secret) { - return new Response("Missing Plain Chat secret", { status: 500 }); + return NextResponse.json({ error: "Missing Plain Chat secret" }, { status: 500 }); } const hmac = createHmac("sha256", secret); diff --git a/apps/web/app/api/teams/create/route.ts b/apps/web/app/api/teams/create/route.ts index 57b10a4655..a14e0e6f73 100644 --- a/apps/web/app/api/teams/create/route.ts +++ b/apps/web/app/api/teams/create/route.ts @@ -22,82 +22,72 @@ const generateRandomString = () => { return Math.random().toString(36).substring(2, 10); }; -export async function GET(req: NextRequest) { - try { - const searchParams = req.nextUrl.searchParams; - const { session_id } = querySchema.parse({ - session_id: searchParams.get("session_id"), +async function getHandler(req: NextRequest) { + const searchParams = req.nextUrl.searchParams; + const { session_id } = querySchema.parse({ + session_id: searchParams.get("session_id"), + }); + + const checkoutSession = await stripe.checkout.sessions.retrieve(session_id, { + expand: ["subscription"], + }); + if (!checkoutSession) throw new HttpError({ statusCode: 404, message: "Checkout session not found" }); + + const subscription = checkoutSession.subscription as Stripe.Subscription; + + if (checkoutSession.payment_status !== "paid") + throw new HttpError({ statusCode: 402, message: "Payment required" }); + + // Let's query to ensure that the team metadata carried over from the checkout session. + const parseCheckoutSessionMetadata = checkoutSessionMetadataSchema.safeParse(checkoutSession.metadata); + + if (!parseCheckoutSessionMetadata.success) { + console.error( + "Team metadata not found in checkout session", + parseCheckoutSessionMetadata.error, + checkoutSession.id + ); + } + + if (!checkoutSession.metadata?.userId) { + throw new HttpError({ + statusCode: 400, + message: "Can't publish team/org without userId", }); + } - const checkoutSession = await stripe.checkout.sessions.retrieve(session_id, { - expand: ["subscription"], - }); - if (!checkoutSession) throw new HttpError({ statusCode: 404, message: "Checkout session not found" }); + const checkoutSessionMetadata = parseCheckoutSessionMetadata.success + ? parseCheckoutSessionMetadata.data + : { + teamName: checkoutSession?.metadata?.teamName ?? generateRandomString(), + teamSlug: checkoutSession?.metadata?.teamSlug ?? generateRandomString(), + userId: checkoutSession.metadata.userId, + }; - const subscription = checkoutSession.subscription as Stripe.Subscription; - - if (checkoutSession.payment_status !== "paid") - throw new HttpError({ statusCode: 402, message: "Payment required" }); - - // Let's query to ensure that the team metadata carried over from the checkout session. - const parseCheckoutSessionMetadata = checkoutSessionMetadataSchema.safeParse(checkoutSession.metadata); - - if (!parseCheckoutSessionMetadata.success) { - console.error( - "Team metadata not found in checkout session", - parseCheckoutSessionMetadata.error, - checkoutSession.id - ); - } - - if (!checkoutSession.metadata?.userId) { - throw new HttpError({ - statusCode: 400, - message: "Can't publish team/org without userId", - }); - } - - const checkoutSessionMetadata = parseCheckoutSessionMetadata.success - ? parseCheckoutSessionMetadata.data - : { - teamName: checkoutSession?.metadata?.teamName ?? generateRandomString(), - teamSlug: checkoutSession?.metadata?.teamSlug ?? generateRandomString(), - userId: checkoutSession.metadata.userId, - }; - - const team = await prisma.team.create({ - data: { - name: checkoutSessionMetadata.teamName, - slug: checkoutSessionMetadata.teamSlug, - members: { - create: { - userId: checkoutSessionMetadata.userId as number, - role: MembershipRole.OWNER, - accepted: true, - }, - }, - metadata: { - paymentId: checkoutSession.id, - subscriptionId: subscription.id || null, - subscriptionItemId: subscription.items.data[0].id || null, + const team = await prisma.team.create({ + data: { + name: checkoutSessionMetadata.teamName, + slug: checkoutSessionMetadata.teamSlug, + members: { + create: { + userId: checkoutSessionMetadata.userId as number, + role: MembershipRole.OWNER, + accepted: true, }, }, - }); + metadata: { + paymentId: checkoutSession.id, + subscriptionId: subscription.id || null, + subscriptionItemId: subscription.items.data[0].id || null, + }, + }, + }); - // redirect to team screen - return NextResponse.redirect( - new URL(`/settings/teams/${team.id}/onboard-members?event=team_created`, req.nextUrl.origin), - { status: 302 } - ); - } catch (error) { - if (error instanceof HttpError) { - throw error; - } - - if (error instanceof z.ZodError) { - return NextResponse.json({ message: "Invalid request data" }, { status: 400 }); - } - - return NextResponse.json({ message: "Internal server error" }, { status: 500 }); - } + // redirect to team screen + return NextResponse.redirect( + new URL(`/settings/teams/${team.id}/onboard-members?event=team_created`, req.nextUrl.origin), + { status: 302 } + ); } + +export { getHandler as GET }; diff --git a/packages/lib/server/apiRouteMiddleware.ts b/packages/lib/server/apiRouteMiddleware.ts index 04427add88..295e4230c8 100644 --- a/packages/lib/server/apiRouteMiddleware.ts +++ b/packages/lib/server/apiRouteMiddleware.ts @@ -6,7 +6,7 @@ import { NextResponse } from "next/server"; import { getServerErrorFromUnknown } from "./getServerErrorFromUnknown"; export const apiRouteMiddleware = - (handler: (req: Request, res?: Response) => Promise) => + (handler: (req: NextRequest, res?: NextResponse) => Promise) => async (req: NextRequest, res: NextResponse) => { try { return await handler(req, res);