33 files changed
+740
-271
No files matched your search
+7
-1
@@ -72,4 +72,10 @@ dist
|
||||
lint-results
|
||||
|
||||
# Yarn
|
||||
yarn-error.log
|
||||
yarn-error.log
|
||||
|
||||
.turbo
|
||||
.next
|
||||
.husky
|
||||
.vscode
|
||||
.env
|
||||
@@ -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.
|
||||
+182
@@ -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<FormValues>();
|
||||
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<FormValues> = 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 (
|
||||
<div
|
||||
className="flex min-h-screen flex-col justify-center bg-gray-50 py-12 sm:px-6 lg:px-8"
|
||||
aria-labelledby="modal-title"
|
||||
role="dialog"
|
||||
aria-modal="true">
|
||||
<HeadSeo title={t("sign_up")} description={t("sign_up")} />
|
||||
<div className="sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<h2 className="font-cal text-center text-3xl font-extrabold text-gray-900">
|
||||
{t("create_your_account")}
|
||||
</h2>
|
||||
</div>
|
||||
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<div className="mx-2 bg-white px-4 py-8 shadow sm:rounded-lg sm:px-10">
|
||||
<FormProvider {...methods}>
|
||||
<form onSubmit={methods.handleSubmit(signUp)} className="space-y-6 bg-white">
|
||||
{errors.apiError && <Alert severity="error" message={errors.apiError?.message} />}
|
||||
<div className="space-y-2">
|
||||
<TextField
|
||||
addOnLeading={
|
||||
<span className="inline-flex items-center rounded-l-sm border border-r-0 border-gray-300 bg-gray-50 px-3 text-sm text-gray-500">
|
||||
{process.env.NEXT_PUBLIC_WEBSITE_URL}/
|
||||
</span>
|
||||
}
|
||||
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
|
||||
/>
|
||||
<EmailField
|
||||
{...register("email")}
|
||||
className="mt-1 block w-full rounded-md border border-gray-300 bg-gray-100 px-3 py-2 shadow-sm focus:border-black focus:outline-none focus:ring-black sm:text-sm"
|
||||
/>
|
||||
<PasswordField
|
||||
labelProps={{
|
||||
className: "block text-sm font-medium text-gray-700",
|
||||
}}
|
||||
{...register("password")}
|
||||
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"
|
||||
/>
|
||||
<PasswordField
|
||||
label={t("confirm_password")}
|
||||
labelProps={{
|
||||
className: "block text-sm font-medium text-gray-700",
|
||||
}}
|
||||
{...register("passwordcheck", {
|
||||
validate: (value) =>
|
||||
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"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex space-x-2 rtl:space-x-reverse">
|
||||
<Button loading={isSubmitting} className="w-7/12 justify-center">
|
||||
{t("create_account")}
|
||||
</Button>
|
||||
<Button
|
||||
color="secondary"
|
||||
className="w-5/12 justify-center"
|
||||
onClick={() =>
|
||||
signIn("Cal.com", {
|
||||
callbackUrl: (`${WEBAPP_URL}/${router.query.callbackUrl}` || "") as string,
|
||||
})
|
||||
}>
|
||||
{t("login_instead")}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</FormProvider>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
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(),
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -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();
|
||||
|
||||
@@ -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));
|
||||
@@ -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,
|
||||
|
||||
+10
-10
@@ -1,4 +1,4 @@
|
||||
{
|
||||
{
|
||||
"name": "@calcom/api",
|
||||
"version": "1.0.0",
|
||||
"description": "Public API for Cal.com",
|
||||
@@ -13,27 +13,27 @@
|
||||
"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"
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
|
||||
@@ -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<AttendeesResponse | AttendeeResponse>
|
||||
) {
|
||||
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,
|
||||
|
||||
@@ -18,7 +18,10 @@ export async function availabilityById(
|
||||
res: NextApiResponse<AvailabilityResponse>
|
||||
) {
|
||||
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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { BookingModel } from "@/../../packages/prisma/zod";
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
import prisma from "@calcom/prisma";
|
||||
@@ -20,95 +19,138 @@ 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
|
||||
* 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
|
||||
* 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) {
|
||||
console.log(safeBody.error);
|
||||
res.status(400).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 +158,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 +169,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")(
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -30,13 +30,14 @@ export async function bookingById(
|
||||
* /bookings/{id}:
|
||||
* get:
|
||||
* summary: Find a booking
|
||||
* operationId: getBookingById
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* schema:
|
||||
* type: integer
|
||||
* required: true
|
||||
* description: Numeric ID of the booking to get
|
||||
* description: ID of the booking to get
|
||||
* tags:
|
||||
* - bookings
|
||||
* responses:
|
||||
@@ -64,13 +65,31 @@ 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
|
||||
* schema:
|
||||
* type: integer
|
||||
* required: true
|
||||
* description: Numeric ID of the booking to edit
|
||||
* description: ID of the booking to edit
|
||||
* tags:
|
||||
* - bookings
|
||||
* responses:
|
||||
@@ -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,13 +127,14 @@ export async function bookingById(
|
||||
* /bookings/{id}:
|
||||
* delete:
|
||||
* summary: Remove an existing booking
|
||||
* operationId: removeBookingById
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* schema:
|
||||
* type: integer
|
||||
* required: true
|
||||
* description: Numeric ID of the booking to delete
|
||||
* description: ID of the booking to delete
|
||||
* tags:
|
||||
* - bookings
|
||||
* responses:
|
||||
|
||||
@@ -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` });
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
+20
-3
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
+108
-68
@@ -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<any>) {
|
||||
export async function userById(
|
||||
{ method, query, body, userId }: NextApiRequest,
|
||||
res: NextApiResponse<UserResponse>
|
||||
) {
|
||||
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 } })
|
||||
|
||||
+32
-16
@@ -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<UsersResponse>) {
|
||||
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<UsersResponse | UserResponse>
|
||||
) {
|
||||
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);
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
+1
-1
@@ -15,5 +15,5 @@
|
||||
"**/*.ts",
|
||||
"**/*.tsx"
|
||||
],
|
||||
"exclude": ["node_modules", "templates"]
|
||||
"exclude": ["node_modules", "templates", "auth"]
|
||||
}
|
||||
Reference in new issue
Block a user