From 1f2b59c18d21d9dec9b8ef81edecc6cfda6f6dec Mon Sep 17 00:00:00 2001 From: Agusti Fernandez Pardo Date: Thu, 5 May 2022 05:02:59 +0200 Subject: [PATCH 1/3] feat: adds users post endpoint for admin only --- pages/api/users/[id].ts | 176 ++++++++++++++++++++++++--------------- pages/api/users/index.ts | 48 +++++++---- 2 files changed, 140 insertions(+), 84 deletions(-) diff --git a/pages/api/users/[id].ts b/pages/api/users/[id].ts index 3093b43cfc..ffaabcbf4f 100644 --- a/pages/api/users/[id].ts +++ b/pages/api/users/[id].ts @@ -10,68 +10,10 @@ import { } from "@lib/validations/shared/queryIdTransformParseInt"; import { schemaUserEditBodyParams, schemaUserReadPublic } from "@lib/validations/user"; -/** - * @swagger - * /users/{id}: - * get: - * summary: Find a user, returns your user if regular user. - * operationId: getUserById - * parameters: - * - in: path - * name: id - * schema: - * type: integer - * required: true - * description: Numeric ID of the user to get - * tags: - * - users - * responses: - * 200: - * description: OK - * 401: - * description: Authorization information is missing or invalid. - * 404: - * description: User was not found - * patch: - * summary: Edit an existing user - * operationId: editUserById - * parameters: - * - in: path - * name: id - * schema: - * type: integer - * required: true - * description: Numeric ID of the user to edit - * tags: - * - users - * responses: - * 201: - * description: OK, user edited successfuly - * 400: - * description: Bad request. User body is invalid. - * 401: - * description: Authorization information is missing or invalid. - * delete: - * summary: Remove an existing user - * operationId: deleteUserById - * parameters: - * - in: path - * name: id - * schema: - * type: integer - * required: true - * description: Numeric ID of the user to delete - * tags: - * - users - * responses: - * 201: - * description: OK, user removed successfuly - * 400: - * description: Bad request. User id is invalid. - * 401: - * description: Authorization information is missing or invalid. - */ -export async function userById({ method, query, body, userId }: NextApiRequest, res: NextApiResponse) { +export async function userById( + { method, query, body, userId }: NextApiRequest, + res: NextApiResponse +) { const safeQuery = schemaQueryIdParseInt.safeParse(query); console.log(body); if (!safeQuery.success) throw new Error("Invalid request query", safeQuery.error); @@ -79,6 +21,31 @@ export async function userById({ method, query, body, userId }: NextApiRequest, else { switch (method) { case "GET": + /** + * @swagger + * /users/{id}: + * get: + * summary: Find a user, returns your user if regular user. + * operationId: getUserById + * parameters: + * - in: path + * name: id + * example: 4 + * schema: + * type: integer + * required: true + * description: ID of the user to get + * tags: + * - users + * responses: + * 200: + * description: OK + * 401: + * description: Authorization information is missing or invalid. + * 404: + * description: User was not found + */ + await prisma.user .findUnique({ where: { id: safeQuery.data.id } }) .then((data) => schemaUserReadPublic.parse(data)) @@ -87,24 +54,72 @@ export async function userById({ method, query, body, userId }: NextApiRequest, res.status(404).json({ message: `User with id: ${safeQuery.data.id} not found`, error }) ); break; - case "PATCH": + /** + * @swagger + * /users/{id}: + * patch: + * summary: Edit an existing user + * operationId: editUserById + * requestBody: + * description: Edit an existing attendee related to one of your bookings + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * weekStart: + * type: string + * enum: [Monday, Sunday, Saturday] + * example: Monday + * brandColor: + * type: string + * example: "#FF000F" + * darkBrandColor: + * type: string + * example: "#000000" + * timeZone: + * type: string + * example: Europe/London + * parameters: + * - in: path + * name: id + * example: 4 + * schema: + * type: integer + * required: true + * description: ID of the user to edit + * tags: + * - users + * responses: + * 201: + * description: OK, user edited successfuly + * 400: + * description: Bad request. User body is invalid. + * 401: + * description: Authorization information is missing or invalid. + */ + const safeBody = schemaUserEditBodyParams.safeParse(body); if (!safeBody.success) { res.status(400).json({ message: "Bad request", error: safeBody.error }); - throw new Error("Invalid request body"); + // throw new Error("Invalid request body"); + return; } const userSchedules = await prisma.schedule.findMany({ where: { userId }, }); const userSchedulesIds = userSchedules.map((schedule) => schedule.id); // @note: here we make sure user can only make as default his own scheudles - if (!userSchedulesIds.includes(Number(safeBody?.data?.defaultScheduleId))) { + if ( + safeBody?.data?.defaultScheduleId && + !userSchedulesIds.includes(Number(safeBody?.data?.defaultScheduleId)) + ) { res.status(400).json({ - message: "Bad request", - error: "Invalid default schedule id", + message: "Bad request: Invalid default schedule id", }); - throw new Error("Invalid request body value: defaultScheduleId"); + return; } await prisma.user .update({ @@ -118,6 +133,31 @@ export async function userById({ method, query, body, userId }: NextApiRequest, ); break; + /** + * @swagger + * /users/{id}: + * delete: + * summary: Remove an existing user + * operationId: removeUserById + * parameters: + * - in: path + * name: id + * example: 1 + * schema: + * type: integer + * required: true + * description: ID of the user to delete + * tags: + * - users + * responses: + * 201: + * description: OK, user removed successfuly + * 400: + * description: Bad request. User id is invalid. + * 401: + * description: Authorization information is missing or invalid. + */ + case "DELETE": await prisma.user .delete({ where: { id: safeQuery.data.id } }) diff --git a/pages/api/users/index.ts b/pages/api/users/index.ts index 046d456de9..6eea24cb3b 100644 --- a/pages/api/users/index.ts +++ b/pages/api/users/index.ts @@ -3,17 +3,17 @@ import type { NextApiRequest, NextApiResponse } from "next"; import prisma from "@calcom/prisma"; import { withMiddleware } from "@lib/helpers/withMiddleware"; -import { UsersResponse } from "@lib/types"; +import { UserResponse, UsersResponse } from "@lib/types"; import { schemaUserReadPublic } from "@lib/validations/user"; /** * @swagger * /users: * get: + * operationId: listUsers * summary: Find all users. * tags: * - users - * operationId: listUsers * responses: * 200: * description: OK @@ -22,21 +22,37 @@ import { schemaUserReadPublic } from "@lib/validations/user"; * 404: * description: No users were found */ -async function allUsers({ userId }: NextApiRequest, res: NextApiResponse) { - const data = await prisma.user.findMany({ - where: { - id: userId, - }, - }); - const users = data.map((user) => schemaUserReadPublic.parse(user)); - if (users) res.status(200).json({ users }); - else - (error: Error) => - res.status(404).json({ - message: "No Users were found", - error, +async function getAllorCreateUser( + { userId, method, body }: NextApiRequest, + res: NextApiResponse +) { + if (method === "GET") { + const data = await prisma.user.findMany({ + where: { + id: userId, + }, + }); + const users = data.map((user) => schemaUserReadPublic.parse(user)); + if (users) res.status(200).json({ users }); + else + (error: Error) => + res.status(404).json({ + message: "No Users were found", + error, + }); + } else if (method === "POST") { + const isAdmin = await prisma.user + .findUnique({ where: { id: userId } }) + .then((user) => user?.role === "ADMIN"); + if (!isAdmin) res.status(401).json({ message: "You are not authorized" }); + else { + const user = await prisma.user.create({ + data: schemaUserReadPublic.parse(body), }); + res.status(201).json({ user }); + } + } } // No POST endpoint for users for now as a regular user you're expected to signup. -export default withMiddleware("HTTP_GET")(allUsers); +export default withMiddleware("HTTP_GET_OR_POST")(getAllorCreateUser); From 08eeb36d47e51b73c250a13fe534f1e9e239a9c7 Mon Sep 17 00:00:00 2001 From: Agusti Fernandez Pardo Date: Thu, 5 May 2022 18:18:00 +0200 Subject: [PATCH 2/3] feat: add operationId for autogenerated sdk --- lib/validations/booking-reference.ts | 4 +- lib/validations/shared/timeZone.ts | 4 +- lib/validations/user.ts | 3 +- package.json | 18 +- pages/api/attendees/[id].ts | 18 +- pages/api/attendees/index.ts | 20 +- pages/api/availabilities/[id].ts | 42 ++++- pages/api/availabilities/index.ts | 28 ++- pages/api/booking-references/[id].ts | 233 +++++++++++++----------- pages/api/booking-references/index.ts | 35 +++- pages/api/bookings/[id].ts | 6 +- pages/api/custom-inputs/[id].ts | 6 +- pages/api/destination-calendars/[id].ts | 10 +- pages/api/docs.ts | 23 ++- pages/api/event-references/[id].ts | 6 +- pages/api/event-types/[id].ts | 10 +- pages/api/payments/[id].ts | 2 +- pages/api/schedules/[id].ts | 9 +- pages/api/schedules/index.ts | 2 + pages/api/selected-calendars/[id].ts | 5 +- pages/api/selected-calendars/index.ts | 2 + pages/api/teams/[id].ts | 9 +- pages/api/teams/index.ts | 2 + templates/endpoints/[id]/delete.ts | 2 +- templates/endpoints/[id]/edit.ts | 2 +- templates/endpoints/[id]/index.ts | 2 +- 26 files changed, 323 insertions(+), 180 deletions(-) diff --git a/lib/validations/booking-reference.ts b/lib/validations/booking-reference.ts index 66aabc3d88..e6f90a0450 100644 --- a/lib/validations/booking-reference.ts +++ b/lib/validations/booking-reference.ts @@ -28,8 +28,8 @@ const schemaBookingReferenceCreateParams = z type: z.string(), uid: z.string(), meetingId: z.string(), - meetingPassword: z.string(), - meetingUrl: z.string(), + meetingPassword: z.string().optional(), + meetingUrl: z.string().optional(), deleted: z.boolean(), }) .strict(); diff --git a/lib/validations/shared/timeZone.ts b/lib/validations/shared/timeZone.ts index 9dfa98003d..13019690d0 100644 --- a/lib/validations/shared/timeZone.ts +++ b/lib/validations/shared/timeZone.ts @@ -1,5 +1,5 @@ -import * as tzdb from "tzdata"; +import tzdata from "tzdata"; import * as z from "zod"; // @note: This is a custom validation that checks if the timezone is valid and exists in the tzdb library -export const timeZone = z.string().refine((tz: string) => Object.keys(tzdb.zones).includes(tz)); +export const timeZone = z.string().refine((tz: string) => Object.keys(tzdata.zones).includes(tz)); diff --git a/lib/validations/user.ts b/lib/validations/user.ts index 9eb7b05094..b70d52f898 100644 --- a/lib/validations/user.ts +++ b/lib/validations/user.ts @@ -77,6 +77,7 @@ export const schemaUserBaseBodyParams = User.pick({ const schemaUserEditParams = z.object({ weekStart: z.nativeEnum(weekdays).optional(), brandColor: z.string().min(4).max(9).regex(/^#/).optional(), + darkBrandColor: z.string().min(4).max(9).regex(/^#/).optional(), timeZone: timeZone.optional(), bufferTime: z.number().min(0).max(86400).optional(), startTime: z.number().min(0).max(86400).optional(), @@ -87,7 +88,7 @@ const schemaUserEditParams = z.object({ .number() .refine((id: number) => id > 0) .optional(), - locale: z.nativeEnum(locales), + locale: z.nativeEnum(locales).optional(), }); // @note: These are the values that are editable via PATCH method on the user Model, diff --git a/package.json b/package.json index 91eca0e3cc..5677dc043f 100644 --- a/package.json +++ b/package.json @@ -1,4 +1,4 @@ -{ + { "name": "@calcom/api", "version": "1.0.0", "description": "Public API for Cal.com", @@ -19,21 +19,21 @@ }, "devDependencies": { "@calcom/tsconfig": "*", - "@typescript-eslint/eslint-plugin": "^5.16.0", - "babel-jest": "^27.5.1", - "jest": "^27.5.1", + "@typescript-eslint/eslint-plugin": "^5.22.0", + "babel-jest": "^28.0.3", + "jest": "^28.0.3", "node-mocks-http": "^1.11.0" }, "dependencies": { "@calcom/prisma": "*", - "@sentry/nextjs": "^6.19.3", + "@sentry/nextjs": "^6.19.7", "modify-response-middleware": "^1.1.0", - "next": "^12.1.4", + "next": "^12.1.6", "next-api-middleware": "^1.0.1", - "next-swagger-doc": "^0.2.1", + "next-swagger-doc": "^0.3.4", "next-transpile-modules": "^9.0.0", - "next-validations": "^0.1.11", - "typescript": "^4.6.3", + "next-validations": "^0.2.0", + "typescript": "^4.6.4", "tzdata": "^1.0.30" } } diff --git a/pages/api/attendees/[id].ts b/pages/api/attendees/[id].ts index 9a2e40f106..a1674d87a4 100644 --- a/pages/api/attendees/[id].ts +++ b/pages/api/attendees/[id].ts @@ -17,7 +17,7 @@ export async function attendeeById( const safeQuery = schemaQueryIdParseInt.safeParse(query); if (!safeQuery.success) { res.status(400).json({ error: safeQuery.error }); - throw new Error("Invalid request query", safeQuery.error); + return; } const userBookingsAttendeeIds = await prisma.booking // Find all user bookings, including attendees @@ -41,6 +41,7 @@ export async function attendeeById( * @swagger * /attendees/{id}: * get: + * operationId: getAttendeeById * summary: Find an attendee * parameters: * - in: path @@ -48,7 +49,7 @@ export async function attendeeById( * schema: * type: integer * required: true - * description: Numeric ID of the attendee to get + * description: ID of the attendee to get * example: 3 * tags: * - attendees @@ -77,6 +78,7 @@ export async function attendeeById( * /attendees/{id}: * patch: * summary: Edit an existing attendee + * operationId: editAttendeeById * requestBody: * description: Edit an existing attendee related to one of your bookings * required: true @@ -84,11 +86,6 @@ export async function attendeeById( * application/json: * schema: * type: object - * required: - * - bookingId - * - name - * - email - * - timeZone * properties: * email: * type: string @@ -106,7 +103,7 @@ export async function attendeeById( * type: integer * example: 3 * required: true - * description: Numeric ID of the attendee to edit + * description: ID of the attendee to edit * tags: * - attendees * responses: @@ -121,7 +118,7 @@ export async function attendeeById( const safeBody = schemaAttendeeEditBodyParams.safeParse(body); if (!safeBody.success) { res.status(400).json({ message: "Bad request", error: safeBody.error }); - throw new Error("Invalid request body"); + return; } await prisma.attendee .update({ where: { id: safeQuery.data.id }, data: safeBody.data }) @@ -138,6 +135,7 @@ export async function attendeeById( * @swagger * /attendees/{id}: * delete: + * operationId: removeAttendeeById * summary: Remove an existing attendee * parameters: * - in: path @@ -145,7 +143,7 @@ export async function attendeeById( * schema: * type: integer * required: true - * description: Numeric ID of the attendee to delete + * description: ID of the attendee to delete * tags: * - attendees * responses: diff --git a/pages/api/attendees/index.ts b/pages/api/attendees/index.ts index 18bbce51d7..eaae0b28ff 100644 --- a/pages/api/attendees/index.ts +++ b/pages/api/attendees/index.ts @@ -1,6 +1,6 @@ import type { NextApiRequest, NextApiResponse } from "next"; -import prisma from "@calcom/prisma"; +import db from "@calcom/prisma"; import { withMiddleware } from "@lib/helpers/withMiddleware"; import { AttendeeResponse, AttendeesResponse } from "@lib/types"; @@ -10,7 +10,7 @@ async function createOrlistAllAttendees( { method, userId, body }: NextApiRequest, res: NextApiResponse ) { - const userBookings = await prisma.booking.findMany({ + const userBookings = await db.booking.findMany({ where: { userId, }, @@ -24,6 +24,7 @@ async function createOrlistAllAttendees( * @swagger * /attendees: * get: + * operationId: listAttendees * summary: Find all attendees * tags: * - attendees @@ -47,6 +48,7 @@ async function createOrlistAllAttendees( * @swagger * /attendees: * post: + * operationId: addAttendee * summary: Creates a new attendee * requestBody: * description: Create a new attendee related to one of your bookings @@ -85,21 +87,19 @@ async function createOrlistAllAttendees( */ const safePost = schemaAttendeeCreateBodyParams.safeParse(body); if (!safePost.success) { - res.status(400).json({ error: safePost.error }); - throw new Error("Invalid request body", safePost.error); + res.status(400).json({ message: "Invalid request body", error: safePost.error }); + return; } - const userWithBookings = await prisma.user.findUnique({ - where: { id: userId }, - include: { bookings: true }, - }); + const userWithBookings = await db.user.findUnique({ where: { id: userId }, include: { bookings: true } }); if (!userWithBookings) { - throw new Error("User not found"); + res.status(404).json({ message: "User not found" }); + return; } const userBookingIds = userWithBookings.bookings.map((booking: { id: number }) => booking.id).flat(); // Here we make sure to only return attendee's of the user's own bookings. if (!userBookingIds.includes(safePost.data.bookingId)) res.status(401).json({ message: "Unauthorized" }); else { - const data = await prisma.attendee.create({ + const data = await db.attendee.create({ data: { email: safePost.data.email, name: safePost.data.name, diff --git a/pages/api/availabilities/[id].ts b/pages/api/availabilities/[id].ts index 139e36e22f..0b6a58c338 100644 --- a/pages/api/availabilities/[id].ts +++ b/pages/api/availabilities/[id].ts @@ -18,7 +18,10 @@ export async function availabilityById( res: NextApiResponse ) { const safeQuery = schemaQueryIdParseInt.safeParse(query); - if (!safeQuery.success) throw new Error("Invalid request query", safeQuery.error); + if (!safeQuery.success) { + res.status(400).json({ message: "Your query is invalid", error: safeQuery.error }); + return; + } const data = await prisma.availability.findMany({ where: { userId } }); const availabiltiesIds = data.map((availability) => availability.id); if (!availabiltiesIds.includes(safeQuery.data.id)) res.status(401).json({ message: "Unauthorized" }); @@ -28,6 +31,7 @@ export async function availabilityById( * @swagger * /availabilities/{id}: * get: + * operationId: getAvailabilityById * summary: Find an availability * parameters: * - in: path @@ -35,7 +39,7 @@ export async function availabilityById( * schema: * type: integer * required: true - * description: Numeric ID of the availability to get + * description: ID of the availability to get * tags: * - availabilities * externalDocs: @@ -44,9 +48,7 @@ export async function availabilityById( * 200: * description: OK * 401: - * description: Authorization information is missing or invalid. - * 404: - * description: Availability was not found + * description: Unathorized */ case "GET": await prisma.availability @@ -61,14 +63,32 @@ export async function availabilityById( * @swagger * /availabilities/{id}: * patch: + * operationId: editAvailabilityById * summary: Edit an existing availability + * requestBody: + * description: Edit an existing availability related to one of your bookings + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * days: + * type: array + * example: email@example.com + * startTime: + * type: string + * example: 1970-01-01T17:00:00.000Z + * endTime: + * type: string + * example: 1970-01-01T17:00:00.000Z * parameters: * - in: path * name: id * schema: * type: integer * required: true - * description: Numeric ID of the availability to edit + * description: ID of the availability to edit * tags: * - availabilities * externalDocs: @@ -82,8 +102,13 @@ export async function availabilityById( * description: Authorization information is missing or invalid. */ case "PATCH": + console.log(body); const safeBody = schemaAvailabilityEditBodyParams.safeParse(body); - if (!safeBody.success) throw new Error("Invalid request body"); + if (!safeBody.success) { + console.log(safeBody.error); + res.status(400).json({ message: "Bad request" + safeBody.error, error: safeBody.error }); + return; + } const userEventTypes = await prisma.eventType.findMany({ where: { userId } }); const userEventTypesIds = userEventTypes.map((event) => event.id); if (safeBody.data.eventTypeId && !userEventTypesIds.includes(safeBody.data.eventTypeId)) { @@ -109,6 +134,7 @@ export async function availabilityById( * @swagger * /availabilities/{id}: * delete: + * operationId: removeAvailabilityById * summary: Remove an existing availability * parameters: * - in: path @@ -116,7 +142,7 @@ export async function availabilityById( * schema: * type: integer * required: true - * description: Numeric ID of the availability to delete + * description: ID of the availability to delete * tags: * - availabilities * externalDocs: diff --git a/pages/api/availabilities/index.ts b/pages/api/availabilities/index.ts index c7b53c211d..bf17bd1ede 100644 --- a/pages/api/availabilities/index.ts +++ b/pages/api/availabilities/index.ts @@ -18,6 +18,7 @@ async function createOrlistAllAvailabilities( * @swagger * /availabilities: * get: + * operationId: listAvailabilities * summary: Find all availabilities * tags: * - availabilities @@ -45,7 +46,28 @@ async function createOrlistAllAvailabilities( * @swagger * /availabilities: * post: + * operationId: addAvailability * summary: Creates a new availability + * requestBody: + * description: Edit an existing availability related to one of your bookings + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - startTime + * - endTime + * properties: + * days: + * type: array + * example: email@example.com + * startTime: + * type: string + * example: 1970-01-01T17:00:00.000Z + * endTime: + * type: string + * example: 1970-01-01T17:00:00.000Z * tags: * - availabilities * externalDocs: @@ -59,7 +81,11 @@ async function createOrlistAllAvailabilities( * description: Authorization information is missing or invalid. */ const safe = schemaAvailabilityCreateBodyParams.safeParse(body); - if (!safe.success) throw new Error("Invalid request body"); + if (!safe.success) { + res.status(400).json({ message: "Your request is invalid", error: safe.error }); + return; + } + // FIXME: check for eventTypeId ad scheduleId ownership if passed const data = await prisma.availability.create({ data: { ...safe.data, userId } }); const availability = schemaAvailabilityReadPublic.parse(data); diff --git a/pages/api/booking-references/[id].ts b/pages/api/booking-references/[id].ts index 03bc87fafb..d86d0154dc 100644 --- a/pages/api/booking-references/[id].ts +++ b/pages/api/booking-references/[id].ts @@ -1,4 +1,3 @@ -import { BookingModel } from "@/../../packages/prisma/zod"; import type { NextApiRequest, NextApiResponse } from "next"; import prisma from "@calcom/prisma"; @@ -20,95 +19,120 @@ export async function bookingReferenceById( ) { const safeQuery = schemaQueryIdParseInt.safeParse(query); if (!safeQuery.success) throw new Error("Invalid request query", safeQuery.error); - const userWithBookings = await prisma.user.findUnique({ - where: { id: userId }, - include: { bookings: true }, - }); - if (!userWithBookings) throw new Error("User not found"); - const userBookingIds = userWithBookings.bookings.map((booking: { id: number }) => booking.id).flat(); - const bookingReference = await prisma.bookingReference.findUnique({ where: { id: safeQuery.data.id } }); - if (!bookingReference?.bookingId) throw new Error("BookingReference: bookingId not found"); - if (userBookingIds.includes(bookingReference.bookingId)) { - switch (method) { - case "GET": - /** - * @swagger - * /booking-references/{id}: - * get: - * summary: Find a booking reference - * parameters: - * - in: path - * name: id - * schema: - * type: integer - * required: true - * description: Numeric ID of the booking reference to get - * tags: - * - booking-references - * responses: - * 200: - * description: OK - * 401: - * description: Authorization information is missing or invalid. - * 404: - * description: BookingReference was not found - */ + // const userWithBookings = await prisma.user.findUnique({ + // where: { id: userId }, + // include: { bookings: true }, + // }); + // if (!userWithBookings) throw new Error("User not found"); + // const userBookingIds = userWithBookings.bookings.map((booking: { id: number }) => booking.id).flat(); + // console.log(userBookingIds); + // const bookingReferences = await prisma.bookingReference + // .findMany({ where: { id: { in: userBookingIds } } }) + // .then((bookingReferences) => { + // console.log(bookingReferences); + // return bookingReferences.map((bookingReference) => bookingReference.id); + // }); - await prisma.bookingReference - .findUnique({ where: { id: safeQuery.data.id } }) - .then((data) => schemaBookingReferenceReadPublic.parse(data)) - .then((booking_reference) => res.status(200).json({ booking_reference })) - .catch((error: Error) => - res.status(404).json({ - message: `BookingReference with id: ${safeQuery.data.id} not found`, - error, - }) - ); + // res.status(400).json({ message: "Booking reference not found" }); + // return; + // } + // if (userBookingIds.includes(safeQuery.data.id)) { + // if (!bookingReferences?.includes(safeQuery.data.id)) { + // throw new Error("BookingReference: bookingId not found"); + switch (method) { + case "GET": + /** + * @swagger + * /booking-references/{id}: + * get: + * operationId: getBookingReferenceById + * summary: Find a booking reference + * parameters: + * - in: path + * name: id + * schema: + * type: integer + * required: true + * description: ID of the booking reference to get + * tags: + * - booking-references + * responses: + * 200: + * description: OK + * 401: + * description: Authorization information is missing or invalid. + * 404: + * description: BookingReference was not found + */ + console.log(safeQuery.data.id); + const bookingReference = await prisma.bookingReference.findFirst().catch((error: Error) => { + console.log("hoerr:", error); + }); + console.log(bookingReference); + if (!bookingReference) res.status(404).json({ message: "Booking reference not found" }); + else res.status(200).json({ booking_reference: bookingReference }); - break; - case "PATCH": - /** - * @swagger - * /booking-references/{id}: - * patch: - * summary: Edit an existing booking reference - * parameters: - * - in: path - * name: id - * schema: - * type: integer - * required: true - * description: Numeric ID of the booking reference to edit - * tags: - * - booking-references - * responses: - * 201: - * description: OK, bookingReference edited successfuly - * 400: - * description: Bad request. BookingReference body is invalid. - * 401: - * description: Authorization information is missing or invalid. - */ + // .then((data) => { + // console.log(data); + // return schemaBookingReferenceReadPublic.parse(data); + // }) + // .then((booking_reference) => res.status(200).json({ booking_reference })) + // .catch((error: Error) => { + // console.log(error); + // res.status(404).json({ + // message: `BookingReference with id: ${safeQuery.data.id} not found`, + // error, + // }); + // }); + break; + case "PATCH": + /** + * @swagger + * /booking-references/{id}: + * patch: + * operationId: editBookingReferenceById + * summary: Edit an existing booking reference + * parameters: + * - in: path + * name: id + * schema: + * type: integer + * required: true + * description: ID of the booking reference to edit + * tags: + * - booking-references + * responses: + * 201: + * description: OK, safeBody.data edited successfuly + * 400: + * description: Bad request. BookingReference body is invalid. + * 401: + * description: Authorization information is missing or invalid. + */ - const safeBody = schemaBookingEditBodyParams.safeParse(body); - if (!safeBody.success) { - throw new Error("Invalid request body"); - } - await prisma.bookingReference - .update({ where: { id: safeQuery.data.id }, data: safeBody.data }) - .then((data) => schemaBookingReferenceReadPublic.parse(data)) - .then((booking_reference) => res.status(200).json({ booking_reference })) - .catch((error: Error) => - res.status(404).json({ - message: `BookingReference with id: ${safeQuery.data.id} not found`, - error, - }) - ); - break; + const safeBody = schemaBookingEditBodyParams.safeParse(body); + if (!safeBody.success) { + res.status(401).json({ message: "Invalid request body", error: safeBody.error }); + return; + // throw new Error("Invalid request body"); + } + await prisma.bookingReference + .update({ where: { id: safeQuery.data.id }, data: safeBody.data }) + .then((data) => schemaBookingReferenceReadPublic.parse(data)) + .then((booking_reference) => res.status(200).json({ booking_reference })) + .catch((error: Error) => + res.status(404).json({ + message: `BookingReference with id: ${safeQuery.data.id} not found`, + error, + }) + ); + break; + case "DELETE": /** * @swagger * /booking-references/{id}: * delete: + * operationId: removeBookingReferenceById * summary: Remove an existing booking reference * parameters: * - in: path @@ -116,7 +140,7 @@ export async function bookingReferenceById( * schema: * type: integer * required: true - * description: Numeric ID of the booking reference to delete + * description: ID of the booking reference to delete * tags: * - booking-references * responses: @@ -127,29 +151,28 @@ export async function bookingReferenceById( * 401: * description: Authorization information is missing or invalid. */ - case "DELETE": - await prisma.bookingReference - .delete({ - where: { id: safeQuery.data.id }, + await prisma.bookingReference + .delete({ + where: { id: safeQuery.data.id }, + }) + .then(() => + res.status(200).json({ + message: `BookingReference with id: ${safeQuery.data.id} deleted`, }) - .then(() => - res.status(200).json({ - message: `BookingReference with id: ${safeQuery.data.id} deleted`, - }) - ) - .catch((error: Error) => - res.status(404).json({ - message: `BookingReference with id: ${safeQuery.data.id} not found`, - error, - }) - ); - break; + ) + .catch((error: Error) => + res.status(404).json({ + message: `BookingReference with id: ${safeQuery.data.id} not found`, + error, + }) + ); + break; - default: - res.status(405).json({ message: "Method not allowed" }); - break; - } - } else res.status(401).json({ message: "Unauthorized" }); + default: + res.status(405).json({ message: "Method not allowed" }); + break; + } + // } else res.status(401).json({ message: "Unauthorized" }); } export default withMiddleware("HTTP_GET_DELETE_PATCH")( diff --git a/pages/api/booking-references/index.ts b/pages/api/booking-references/index.ts index c228673240..3386d5e42d 100644 --- a/pages/api/booking-references/index.ts +++ b/pages/api/booking-references/index.ts @@ -24,6 +24,7 @@ async function createOrlistAllBookingReferences( * @swagger * /booking-references: * get: + * operationId: listBookingReferences * summary: Find all booking references * tags: * - booking-references @@ -51,7 +52,37 @@ async function createOrlistAllBookingReferences( * @swagger * /booking-references: * post: + * operationId: addBookingReference * summary: Creates a new booking reference + * requestBody: + * description: Create a new booking reference related to one of your bookings + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - type + * - uid + * - meetindId + * - bookingId + * - deleted + * properties: + * deleted: + * type: boolean + * example: false + * uid: + * type: string + * example: '123456789' + * type: + * type: string + * example: email@example.com + * bookingId: + * type: number + * example: 1 + * meetingId: + * type: string + * example: 'meeting-id' * tags: * - booking-references * responses: @@ -64,7 +95,9 @@ async function createOrlistAllBookingReferences( */ const safe = schemaBookingCreateBodyParams.safeParse(body); if (!safe.success) { - throw new Error("Invalid request body"); + res.status(400).json({ message: "Bad request. BookingReference body is invalid", error: safe.error }); + return; + // throw new Error("Invalid request body"); } const userWithBookings = await prisma.user.findUnique({ diff --git a/pages/api/bookings/[id].ts b/pages/api/bookings/[id].ts index 21fdd9a937..ff2f40671c 100644 --- a/pages/api/bookings/[id].ts +++ b/pages/api/bookings/[id].ts @@ -36,7 +36,7 @@ export async function bookingById( * schema: * type: integer * required: true - * description: Numeric ID of the booking to get + * description: ID of the booking to get * tags: * - bookings * responses: @@ -70,7 +70,7 @@ export async function bookingById( * schema: * type: integer * required: true - * description: Numeric ID of the booking to edit + * description: ID of the booking to edit * tags: * - bookings * responses: @@ -111,7 +111,7 @@ export async function bookingById( * schema: * type: integer * required: true - * description: Numeric ID of the booking to delete + * description: ID of the booking to delete * tags: * - bookings * responses: diff --git a/pages/api/custom-inputs/[id].ts b/pages/api/custom-inputs/[id].ts index 3339f31027..eb5493235f 100644 --- a/pages/api/custom-inputs/[id].ts +++ b/pages/api/custom-inputs/[id].ts @@ -24,7 +24,7 @@ import { * schema: * type: integer * required: true - * description: Numeric ID of the eventTypeCustomInput to get + * description: ID of the eventTypeCustomInput to get * tags: * - custom-inputs * responses: @@ -42,7 +42,7 @@ import { * schema: * type: integer * required: true - * description: Numeric ID of the eventTypeCustomInput to edit + * description: ID of the eventTypeCustomInput to edit * tags: * - custom-inputs * responses: @@ -60,7 +60,7 @@ import { * schema: * type: integer * required: true - * description: Numeric ID of the eventTypeCustomInput to delete + * description: ID of the eventTypeCustomInput to delete * tags: * - custom-inputs * responses: diff --git a/pages/api/destination-calendars/[id].ts b/pages/api/destination-calendars/[id].ts index f5d187eb25..60e8aae6b3 100644 --- a/pages/api/destination-calendars/[id].ts +++ b/pages/api/destination-calendars/[id].ts @@ -38,7 +38,7 @@ export async function destionationCalendarById( * schema: * type: integer * required: true - * description: Numeric ID of the destination calendar to get + * description: ID of the destination calendar to get * tags: * - destination-calendars * responses: @@ -56,7 +56,7 @@ export async function destionationCalendarById( * schema: * type: integer * required: true - * description: Numeric ID of the destination calendar to edit + * description: ID of the destination calendar to edit * tags: * - destination-calendars * responses: @@ -74,7 +74,7 @@ export async function destionationCalendarById( * schema: * type: integer * required: true - * description: Numeric ID of the destination calendar to delete + * description: ID of the destination calendar to delete * tags: * - destination-calendars * responses: @@ -108,7 +108,7 @@ export async function destionationCalendarById( * schema: * type: integer * required: true - * description: Numeric ID of the destination calendar to edit + * description: ID of the destination calendar to edit * tags: * - destination-calendars * responses: @@ -145,7 +145,7 @@ export async function destionationCalendarById( * schema: * type: integer * required: true - * description: Numeric ID of the destination calendar to delete + * description: ID of the destination calendar to delete * tags: * - destination-calendars * responses: diff --git a/pages/api/docs.ts b/pages/api/docs.ts index 943c6b9f25..d339043741 100644 --- a/pages/api/docs.ts +++ b/pages/api/docs.ts @@ -6,11 +6,11 @@ import { NextApiRequest, NextApiResponse } from "next/types"; const swaggerHandler = withSwagger({ definition: { - openapi: "3.0.0", + openapi: "3.0.3", servers: [ - { url: "https://api.cal.com/v1" }, - { url: "https://api.cal.dev/v1" }, { url: "http://localhost:3002/v1" }, + { url: "https://api.cal.dev/v1" }, + { url: "https://api.cal.com/v1" }, ], externalDocs: { url: "https://docs.cal.com", @@ -24,6 +24,22 @@ const swaggerHandler = withSwagger({ securitySchemes: { ApiKeyAuth: { type: "apiKey", in: "query", name: "apiKey" } }, }, security: [{ ApiKeyAuth: [] }], + tags: [ + { name: "users" }, + { name: "event-types" }, + { name: "bookings" }, + { name: "attendees" }, + { name: "payments" }, + { name: "schedules" }, + { name: "teams" }, + { name: "memberships" }, + { name: "availabilities" }, + { name: "custom-inputs" }, + { name: "event-references" }, + { name: "booking-references" }, + { name: "destination-calendars" }, + { name: "selected-calendars" }, + ], }, apiFolder: "pages/api", }); @@ -41,6 +57,7 @@ export default use( ); if (content) { const parsed = JSON.parse(content); + // HACK: This is a hack to fix the swagger-ui issue with the extra channels property. delete parsed.channels; return Buffer.from(JSON.stringify(parsed)); } diff --git a/pages/api/event-references/[id].ts b/pages/api/event-references/[id].ts index a98d5f74e9..a6a15b9e19 100644 --- a/pages/api/event-references/[id].ts +++ b/pages/api/event-references/[id].ts @@ -43,7 +43,7 @@ export async function dailyEventReferenceById( * schema: * type: integer * required: true - * description: Numeric ID of the event reference to get + * description: ID of the event reference to get * tags: * - event-references * responses: @@ -78,7 +78,7 @@ export async function dailyEventReferenceById( * schema: * type: integer * required: true - * description: Numeric ID of the event reference to edit + * description: ID of the event reference to edit * tags: * - event-references * responses: @@ -116,7 +116,7 @@ export async function dailyEventReferenceById( * schema: * type: integer * required: true - * description: Numeric ID of the event reference to delete + * description: ID of the event reference to delete * tags: * - event-references * responses: diff --git a/pages/api/event-types/[id].ts b/pages/api/event-types/[id].ts index 0ece116d17..a6900b1d27 100644 --- a/pages/api/event-types/[id].ts +++ b/pages/api/event-types/[id].ts @@ -25,14 +25,16 @@ export async function eventTypeById( * @swagger * /event-types/{id}: * get: + * operationId: getEventTypeById * summary: Find a eventType * parameters: * - in: path * name: id + * example: 4 * schema: * type: integer * required: true - * description: Numeric ID of the eventType to get + * description: ID of the eventType to get * security: * - ApiKeyAuth: [] * tags: @@ -63,6 +65,7 @@ export async function eventTypeById( * @swagger * /event-types/{id}: * patch: + * operationId: editEventTypeById * summary: Edit an existing eventType * parameters: * - in: path @@ -70,7 +73,7 @@ export async function eventTypeById( * schema: * type: integer * required: true - * description: Numeric ID of the eventType to edit + * description: ID of the eventType to edit * security: * - ApiKeyAuth: [] * tags: @@ -105,6 +108,7 @@ export async function eventTypeById( * @swagger * /event-types/{id}: * delete: + * operationId: removeEventTypeById * summary: Remove an existing eventType * parameters: * - in: path @@ -112,7 +116,7 @@ export async function eventTypeById( * schema: * type: integer * required: true - * description: Numeric ID of the eventType to delete + * description: ID of the eventType to delete * security: * - ApiKeyAuth: [] * tags: diff --git a/pages/api/payments/[id].ts b/pages/api/payments/[id].ts index ebcaa0c504..99d515fe45 100644 --- a/pages/api/payments/[id].ts +++ b/pages/api/payments/[id].ts @@ -21,7 +21,7 @@ import { * schema: * type: integer * required: true - * description: Numeric ID of the payment to get + * description: ID of the payment to get * tags: * - payments * responses: diff --git a/pages/api/schedules/[id].ts b/pages/api/schedules/[id].ts index 38066c4ab8..5b90027ac3 100644 --- a/pages/api/schedules/[id].ts +++ b/pages/api/schedules/[id].ts @@ -26,6 +26,7 @@ export async function scheduleById( * @swagger * /schedules/{id}: * get: + * operationId: getScheduleById * summary: Find a schedule * parameters: * - in: path @@ -33,7 +34,7 @@ export async function scheduleById( * schema: * type: integer * required: true - * description: Numeric ID of the schedule to get + * description: ID of the schedule to get * tags: * - schedules * responses: @@ -61,6 +62,7 @@ export async function scheduleById( * @swagger * /schedules/{id}: * patch: + * operationId: editScheduleById * summary: Edit an existing schedule * parameters: * - in: path @@ -68,7 +70,7 @@ export async function scheduleById( * schema: * type: integer * required: true - * description: Numeric ID of the schedule to edit + * description: ID of the schedule to edit * tags: * - schedules * responses: @@ -99,6 +101,7 @@ export async function scheduleById( * @swagger * /schedules/{id}: * delete: + * operationId: removeScheduleById * summary: Remove an existing schedule * parameters: * - in: path @@ -106,7 +109,7 @@ export async function scheduleById( * schema: * type: integer * required: true - * description: Numeric ID of the schedule to delete + * description: ID of the schedule to delete * tags: * - schedules * responses: diff --git a/pages/api/schedules/index.ts b/pages/api/schedules/index.ts index 04dbc6b1a1..ac002345a3 100644 --- a/pages/api/schedules/index.ts +++ b/pages/api/schedules/index.ts @@ -15,6 +15,7 @@ async function createOrlistAllSchedules( * @swagger * /schedules: * get: + * operationId: listSchedules * summary: Find all schedules * tags: * - schedules @@ -40,6 +41,7 @@ async function createOrlistAllSchedules( * @swagger * /schedules: * post: + * operationId: addSchedule * summary: Creates a new schedule * tags: * - schedules diff --git a/pages/api/selected-calendars/[id].ts b/pages/api/selected-calendars/[id].ts index de4d7a5570..3915332412 100644 --- a/pages/api/selected-calendars/[id].ts +++ b/pages/api/selected-calendars/[id].ts @@ -26,7 +26,8 @@ export async function selectedCalendarById( * @swagger * /selected-calendars/{userId}_{integration}_{externalId}: * get: - * summary: Find a selected calendar + * operationId: getSelectedCalendarById + * summary: Find a selected calendar by providing the compoundId(userId_integration_externalId) separated by `_` * parameters: * - in: path * name: userId @@ -81,6 +82,7 @@ export async function selectedCalendarById( * @swagger * /selected-calendars/{userId}_{integration}_{externalId}: * patch: + * operationId: editSelectedCalendarById * summary: Edit a selected calendar * parameters: * - in: path @@ -139,6 +141,7 @@ export async function selectedCalendarById( * @swagger * /selected-calendars/{userId}_{integration}_{externalId}: * delete: + * operationId: removeSelectedCalendarById * summary: Remove a selected calendar * parameters: * - in: path diff --git a/pages/api/selected-calendars/index.ts b/pages/api/selected-calendars/index.ts index cbf2e94b81..e7ccd9d101 100644 --- a/pages/api/selected-calendars/index.ts +++ b/pages/api/selected-calendars/index.ts @@ -18,6 +18,7 @@ async function createOrlistAllSelectedCalendars( * @swagger * /selected-calendars: * get: + * operationId: listSelectedCalendars * summary: Find all selected calendars * tags: * - selected-calendars @@ -45,6 +46,7 @@ async function createOrlistAllSelectedCalendars( * @swagger * /selected-calendars: * get: + * operationId: addSelectedCalendars * summary: Find all selected calendars * tags: * - selected-calendars diff --git a/pages/api/teams/[id].ts b/pages/api/teams/[id].ts index e49277a143..adbc5e0c12 100644 --- a/pages/api/teams/[id].ts +++ b/pages/api/teams/[id].ts @@ -14,6 +14,7 @@ import { schemaTeamBodyParams, schemaTeamPublic } from "@lib/validations/team"; * @swagger * /teams/{id}: * get: + * operationId: getTeamById * summary: Find a team * parameters: * - in: path @@ -21,7 +22,7 @@ import { schemaTeamBodyParams, schemaTeamPublic } from "@lib/validations/team"; * schema: * type: integer * required: true - * description: Numeric ID of the team to get + * description: ID of the team to get * tags: * - teams * responses: @@ -32,6 +33,7 @@ import { schemaTeamBodyParams, schemaTeamPublic } from "@lib/validations/team"; * 404: * description: Team was not found * patch: + * operationId: editTeamById * summary: Edit an existing team * parameters: * - in: path @@ -39,7 +41,7 @@ import { schemaTeamBodyParams, schemaTeamPublic } from "@lib/validations/team"; * schema: * type: integer * required: true - * description: Numeric ID of the team to edit + * description: ID of the team to edit * tags: * - teams * responses: @@ -50,6 +52,7 @@ import { schemaTeamBodyParams, schemaTeamPublic } from "@lib/validations/team"; * 401: * description: Authorization information is missing or invalid. * delete: + * operationId: removeTeamById * summary: Remove an existing team * parameters: * - in: path @@ -57,7 +60,7 @@ import { schemaTeamBodyParams, schemaTeamPublic } from "@lib/validations/team"; * schema: * type: integer * required: true - * description: Numeric ID of the team to delete + * description: ID of the team to delete * tags: * - teams * responses: diff --git a/pages/api/teams/index.ts b/pages/api/teams/index.ts index 75aabd47b9..5ab45bc21d 100644 --- a/pages/api/teams/index.ts +++ b/pages/api/teams/index.ts @@ -16,6 +16,7 @@ async function createOrlistAllTeams( * @swagger * /teams: * get: + * operationId: listTeams * summary: Find all teams * tags: * - teams @@ -44,6 +45,7 @@ async function createOrlistAllTeams( * @swagger * /teams: * post: + * operationId: addTeam * summary: Creates a new team * tags: * - teams diff --git a/templates/endpoints/[id]/delete.ts b/templates/endpoints/[id]/delete.ts index 133554ac25..79645cbdfd 100644 --- a/templates/endpoints/[id]/delete.ts +++ b/templates/endpoints/[id]/delete.ts @@ -20,7 +20,7 @@ import { * schema: * type: integer * required: true - * description: Numeric ID of the resource to delete + * description: ID of the resource to delete * tags: * - resources diff --git a/templates/endpoints/[id]/edit.ts b/templates/endpoints/[id]/edit.ts index e72198d82e..8346840ec3 100644 --- a/templates/endpoints/[id]/edit.ts +++ b/templates/endpoints/[id]/edit.ts @@ -21,7 +21,7 @@ import { * schema: * type: integer * required: true - * description: Numeric ID of the resource to edit + * description: ID of the resource to edit * tags: * - resources diff --git a/templates/endpoints/[id]/index.ts b/templates/endpoints/[id]/index.ts index 4a0656d3b5..7933c5cab1 100644 --- a/templates/endpoints/[id]/index.ts +++ b/templates/endpoints/[id]/index.ts @@ -21,7 +21,7 @@ import { * schema: * type: integer * required: true - * description: Numeric ID of the resource to get + * description: ID of the resource to get * tags: * - resources From 4c131fbcdda83950b5e8561e66780262f6767cb0 Mon Sep 17 00:00:00 2001 From: Agusti Fernandez Pardo Date: Tue, 10 May 2022 19:52:59 +0200 Subject: [PATCH 3/3] feat: adds auth/signup for ee customers --- .gitignore | 8 +- auth/README.md | 15 +++ auth/signup.tsx | 182 +++++++++++++++++++++++++++ package.json | 2 +- pages/api/booking-references/[id].ts | 24 +++- pages/api/bookings/[id].ts | 27 +++- pages/api/bookings/index.ts | 30 ++++- tsconfig.json | 2 +- 8 files changed, 280 insertions(+), 10 deletions(-) create mode 100644 auth/README.md create mode 100644 auth/signup.tsx diff --git a/.gitignore b/.gitignore index b93b0f6248..a2c49f1abb 100644 --- a/.gitignore +++ b/.gitignore @@ -72,4 +72,10 @@ dist lint-results # Yarn -yarn-error.log \ No newline at end of file +yarn-error.log + +.turbo +.next +.husky +.vscode +.env \ No newline at end of file diff --git a/auth/README.md b/auth/README.md new file mode 100644 index 0000000000..5b10e7d32a --- /dev/null +++ b/auth/README.md @@ -0,0 +1,15 @@ +# Signup for self-hosted EE + +In order to open the signup, please replace the contents of the file in + +`calcom/apps/web/pages/auth/signup.tsx` + +with the contents of the new file provided here in the @calcom/api repo at the folder: +`/auth/signup.tsx` + +Once you do, if you run your cal.com self-hosted instance and go to: +`http://localhost:3000/auth/signup` + +You should see the signup page, and your users should be able to create new accounts. + +Please, keep in mind this will create git conflict resolutions if you try to git pull from main, where the signup.tsx file will still be different. We will find a better workaround for this soon in order to make it pain free. diff --git a/auth/signup.tsx b/auth/signup.tsx new file mode 100644 index 0000000000..8dc735ddab --- /dev/null +++ b/auth/signup.tsx @@ -0,0 +1,182 @@ +import { GetServerSidePropsContext } from "next"; +import { signIn } from "next-auth/react"; +import { useRouter } from "next/router"; +import { FormProvider, SubmitHandler, useForm } from "react-hook-form"; + +import { useLocale } from "@calcom/lib/hooks/useLocale"; +import { Alert } from "@calcom/ui/Alert"; +import Button from "@calcom/ui/Button"; +import { EmailField, PasswordField, TextField } from "@calcom/ui/form/fields"; +import { HeadSeo } from "@calcom/web/components/seo/head-seo"; +import { asStringOrNull } from "@calcom/web/lib/asStringOrNull"; +import { WEBSITE_URL, WEBAPP_URL } from "@calcom/web/lib/config/constants"; +import prisma from "@calcom/web/lib/prisma"; +import { isSAMLLoginEnabled } from "@calcom/web/lib/saml"; +import { IS_GOOGLE_LOGIN_ENABLED } from "@calcom/web/server/lib/constants"; +import { ssrInit } from "@calcom/web/server/lib/ssr"; + +type FormValues = { + username: string; + email: string; + password: string; + passwordcheck: string; + apiError: string; +}; + +export default function Signup() { + const { t } = useLocale(); + const router = useRouter(); + const methods = useForm(); + const { + register, + formState: { errors, isSubmitting }, + } = methods; + + const handleErrors = async (resp: Response) => { + if (!resp.ok) { + const err = await resp.json(); + throw new Error(err.message); + } + }; + + const signUp: SubmitHandler = async (data) => { + await fetch("/api/auth/signup", { + body: JSON.stringify({ + ...data, + }), + headers: { + "Content-Type": "application/json", + }, + method: "POST", + }) + .then(handleErrors) + .then( + async () => + await signIn("Cal.com", { + callbackUrl: (`${WEBSITE_URL}/${router.query.callbackUrl}` || "") as string, + }) + ) + .catch((err) => { + methods.setError("apiError", { message: err.message }); + }); + }; + + return ( +
+ +
+

+ {t("create_your_account")} +

+
+
+
+ +
+ {errors.apiError && } +
+ + {process.env.NEXT_PUBLIC_WEBSITE_URL}/ + + } + labelProps={{ className: "block text-sm font-medium text-gray-700" }} + className="block w-full min-w-0 flex-grow rounded-none rounded-r-sm border-gray-300 lowercase focus:border-black focus:ring-black sm:text-sm" + {...register("username")} + required + /> + + + + value === methods.watch("password") || (t("error_password_mismatch") as string), + })} + className="mt-1 block w-full rounded-md border border-gray-300 px-3 py-2 shadow-sm focus:border-black focus:outline-none focus:ring-black sm:text-sm" + /> +
+
+ + +
+ +
+
+
+
+ ); +} + +export const getServerSideProps = async (ctx: GetServerSidePropsContext) => { + const ssr = await ssrInit(ctx); + const token = asStringOrNull(ctx.query.token); + if (token) { + const verificationToken = await prisma.verificationToken.findUnique({ + where: { + token, + }, + }); + if (verificationToken) { + const existingUser = await prisma.user.findFirst({ + where: { + AND: [ + { + email: verificationToken?.identifier, + }, + { + emailVerified: { + not: null, + }, + }, + ], + }, + }); + + if (existingUser) { + return { + redirect: { + permanent: false, + destination: "/auth/login?callbackUrl=" + `${WEBAPP_URL}/${ctx.query.callbackUrl}`, + }, + }; + } + } + } + + return { + props: { + isGoogleLoginEnabled: IS_GOOGLE_LOGIN_ENABLED, + isSAMLLoginEnabled, + trpcState: ssr.dehydrate(), + }, + }; +}; diff --git a/package.json b/package.json index 5677dc043f..14c34d18af 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "build": "next build", "lint": "next lint", "lint-fix": "next lint --fix && prettier --write .", - "test": "jest --detectOpenHandles", + "test": "jest --detectOpenHandles --passWithNoTests", "type-check": "tsc --pretty --noEmit", "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist" }, diff --git a/pages/api/booking-references/[id].ts b/pages/api/booking-references/[id].ts index d86d0154dc..d1c28b8d04 100644 --- a/pages/api/booking-references/[id].ts +++ b/pages/api/booking-references/[id].ts @@ -64,11 +64,11 @@ export async function bookingReferenceById( * 404: * description: BookingReference was not found */ - console.log(safeQuery.data.id); + // console.log(safeQuery.data.id); const bookingReference = await prisma.bookingReference.findFirst().catch((error: Error) => { console.log("hoerr:", error); }); - console.log(bookingReference); + // console.log(bookingReference); if (!bookingReference) res.status(404).json({ message: "Booking reference not found" }); else res.status(200).json({ booking_reference: bookingReference }); @@ -92,6 +92,23 @@ export async function bookingReferenceById( * patch: * operationId: editBookingReferenceById * summary: Edit an existing booking reference + * requestBody: + * description: Edit an existing booking reference related to one of your bookings + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * days: + * type: array + * example: email@example.com + * startTime: + * type: string + * example: 1970-01-01T17:00:00.000Z + * endTime: + * type: string + * example: 1970-01-01T17:00:00.000Z * parameters: * - in: path * name: id @@ -112,7 +129,8 @@ export async function bookingReferenceById( const safeBody = schemaBookingEditBodyParams.safeParse(body); if (!safeBody.success) { - res.status(401).json({ message: "Invalid request body", error: safeBody.error }); + console.log(safeBody.error); + res.status(400).json({ message: "Invalid request body", error: safeBody.error }); return; // throw new Error("Invalid request body"); } diff --git a/pages/api/bookings/[id].ts b/pages/api/bookings/[id].ts index ff2f40671c..b094df73e9 100644 --- a/pages/api/bookings/[id].ts +++ b/pages/api/bookings/[id].ts @@ -30,6 +30,7 @@ export async function bookingById( * /bookings/{id}: * get: * summary: Find a booking + * operationId: getBookingById * parameters: * - in: path * name: id @@ -64,6 +65,24 @@ export async function bookingById( * /bookings/{id}: * patch: * summary: Edit an existing booking + * operationId: editBookingById + * requestBody: + * description: Edit an existing booking related to one of your event-types + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * title: + * type: string + * example: 15min + * startTime: + * type: string + * example: 1970-01-01T17:00:00.000Z + * endTime: + * type: string + * example: 1970-01-01T17:00:00.000Z * parameters: * - in: path * name: id @@ -84,14 +103,17 @@ export async function bookingById( case "PATCH": const safeBody = schemaBookingEditBodyParams.safeParse(body); if (!safeBody.success) { - throw new Error("Invalid request body"); + console.log(safeBody.error); + res.status(400).json({ message: "Bad request", error: safeBody.error }); + return; + // throw new Error("Invalid request body"); } await prisma.booking .update({ where: { id: safeQuery.data.id }, data: safeBody.data, }) - .then((data) => schemaBookingReadPublic.parse(data)) + // .then((data) => schemaBookingReadPublic.parse(data)) .then((booking) => res.status(200).json({ booking })) .catch((error: Error) => res.status(404).json({ @@ -105,6 +127,7 @@ export async function bookingById( * /bookings/{id}: * delete: * summary: Remove an existing booking + * operationId: removeBookingById * parameters: * - in: path * name: id diff --git a/pages/api/bookings/index.ts b/pages/api/bookings/index.ts index bd1595ff6c..1ee689b195 100644 --- a/pages/api/bookings/index.ts +++ b/pages/api/bookings/index.ts @@ -16,6 +16,7 @@ async function createOrlistAllBookings( * /bookings: * get: * summary: Find all bookings + * operationId: listBookings * tags: * - bookings * responses: @@ -41,6 +42,24 @@ async function createOrlistAllBookings( * /bookings: * post: * summary: Creates a new booking + * operationId: addBooking + * requestBody: + * description: Edit an existing booking related to one of your event-types + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * title: + * type: string + * example: 15min + * startTime: + * type: string + * example: 1970-01-01T17:00:00.000Z + * endTime: + * type: string + * example: 1970-01-01T17:00:00.000Z * tags: * - bookings * responses: @@ -52,18 +71,25 @@ async function createOrlistAllBookings( * description: Authorization information is missing or invalid. */ const safe = schemaBookingCreateBodyParams.safeParse(body); - if (!safe.success) throw new Error("Invalid request body"); + if (!safe.success) { + console.log(safe.error); + res.status(400).json({ message: "Bad request. Booking body is invalid." }); + return; + // throw new Error("Invalid request body"); + } const data = await prisma.booking.create({ data: { ...safe.data, userId } }); const booking = schemaBookingReadPublic.parse(data); if (booking) res.status(201).json({ booking, message: "Booking created successfully" }); else - (error: Error) => + (error: Error) => { + console.log(error); res.status(400).json({ message: "Could not create new booking", error, }); + }; } else res.status(405).json({ message: `Method ${method} not allowed` }); } diff --git a/tsconfig.json b/tsconfig.json index 4943d154a6..5622a85041 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,5 +15,5 @@ "**/*.ts", "**/*.tsx" ], - "exclude": ["node_modules", "templates"] + "exclude": ["node_modules", "templates", "auth"] }