diff --git a/apps/web/app/api/customer-card/route.ts b/apps/web/app/api/customer-card/route.ts index 4f50541985..e934b10053 100644 --- a/apps/web/app/api/customer-card/route.ts +++ b/apps/web/app/api/customer-card/route.ts @@ -7,6 +7,7 @@ import { z } from "zod"; import dayjs from "@calcom/dayjs"; import { UserRepository } from "@calcom/lib/server/repository/user"; +import { timeZoneSchema } from "@calcom/lib/dayjs/timeZone.schema"; import { userMetadata } from "@calcom/prisma/zod-utils"; import { CardComponent } from "@lib/plain/card-components"; @@ -443,7 +444,7 @@ const inputSchema = z.object({ name: z.string().optional(), email: z.string().email(), username: z.string().optional(), - timeZone: z.string().optional(), + timeZone: timeZoneSchema.optional(), emailVerified: z.boolean().optional(), identityProvider: z.string().optional(), twoFactorEnabled: z.boolean().optional(), diff --git a/packages/features/users/components/UserTable/EditSheet/EditUserForm.tsx b/packages/features/users/components/UserTable/EditSheet/EditUserForm.tsx index 942919ef96..b5a3f5073f 100644 --- a/packages/features/users/components/UserTable/EditSheet/EditUserForm.tsx +++ b/packages/features/users/components/UserTable/EditSheet/EditUserForm.tsx @@ -6,6 +6,7 @@ import { Controller, useForm, useFormContext } from "react-hook-form"; import { z } from "zod"; import { TimezoneSelect } from "@calcom/features/components/timezone-select"; +import { timeZoneSchema } from "@calcom/lib/dayjs/timeZone.schema"; import { emailSchema } from "@calcom/lib/emailSchema"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { MembershipRole } from "@calcom/prisma/enums"; @@ -58,7 +59,7 @@ const editSchema = z.object({ avatar: z.string(), bio: z.string(), role: z.enum([MembershipRole.MEMBER, MembershipRole.ADMIN, MembershipRole.OWNER]), - timeZone: z.string(), + timeZone: timeZoneSchema, // schedules: z.array(z.string()), // teams: z.array(z.string()), attributes: z.array(attributeSchema).optional(), diff --git a/packages/lib/dayjs/timeZone.schema.ts b/packages/lib/dayjs/timeZone.schema.ts new file mode 100644 index 0000000000..920ad9c225 --- /dev/null +++ b/packages/lib/dayjs/timeZone.schema.ts @@ -0,0 +1,8 @@ +import { z } from "zod"; + +import { isSupportedTimeZone } from "./index"; + +// Schema for validating IANA timezone strings compatible with Intl.DateTimeFormat +export const timeZoneSchema = z.string().refine((timeZone) => isSupportedTimeZone(timeZone), { + message: "Invalid timezone. Must be a valid IANA timezone string.", +}); diff --git a/packages/prisma/zod/custom/booking.ts b/packages/prisma/zod/custom/booking.ts index 45c0d44070..1e51576761 100644 --- a/packages/prisma/zod/custom/booking.ts +++ b/packages/prisma/zod/custom/booking.ts @@ -1,6 +1,6 @@ // `responses` is merged with it during handleNewBooking call because `responses` schema is dynamic and depends on eventType import z, { ZodNullable, ZodObject, ZodOptional } from "zod"; -import { isSupportedTimeZone } from "@calcom/lib/dayjs"; +import { timeZoneSchema } from "@calcom/lib/dayjs/timeZone.schema"; // TODO: Move this out of here. Importing from app-store is a circular package dependency. import { routingFormResponseInDbSchema } from "@calcom/app-store/routing-forms/zod"; @@ -12,7 +12,7 @@ export const bookingCreateBodySchema = z.object({ recurringEventId: z.string().optional(), rescheduledBy: z.string().email({ message: "Invalid email" }).optional(), start: z.string(), - timeZone: z.string().refine((value: string) => isSupportedTimeZone(value), { message: "Invalid timezone" }), + timeZone: timeZoneSchema, user: z.union([z.string(), z.array(z.string())]).optional(), language: z.string(), bookingUid: z.string().optional(), diff --git a/packages/trpc/server/routers/viewer/availability/schedule/update.schema.ts b/packages/trpc/server/routers/viewer/availability/schedule/update.schema.ts index e37588842c..7e8b6bfd23 100644 --- a/packages/trpc/server/routers/viewer/availability/schedule/update.schema.ts +++ b/packages/trpc/server/routers/viewer/availability/schedule/update.schema.ts @@ -1,8 +1,10 @@ import { z } from "zod"; +import { timeZoneSchema } from "@calcom/lib/dayjs/timeZone.schema"; + export const ZUpdateInputSchema = z.object({ scheduleId: z.number(), - timeZone: z.string().optional(), + timeZone: timeZoneSchema.optional(), name: z.string().optional(), isDefault: z.boolean().optional(), schedule: z diff --git a/packages/trpc/server/routers/viewer/me/updateProfile.schema.ts b/packages/trpc/server/routers/viewer/me/updateProfile.schema.ts index ab9a4a7a20..c3c75b1b62 100644 --- a/packages/trpc/server/routers/viewer/me/updateProfile.schema.ts +++ b/packages/trpc/server/routers/viewer/me/updateProfile.schema.ts @@ -1,6 +1,7 @@ import { z } from "zod"; import { FULL_NAME_LENGTH_MAX_LIMIT } from "@calcom/lib/constants"; +import { timeZoneSchema } from "@calcom/lib/dayjs/timeZone.schema"; import { bookerLayouts, userMetadata } from "@calcom/prisma/zod-utils"; export const updateUserMetadataAllowedKeys = z.object({ @@ -14,7 +15,7 @@ export const ZUpdateProfileInputSchema = z.object({ email: z.string().optional(), bio: z.string().optional(), avatarUrl: z.string().nullable().optional(), - timeZone: z.string().optional(), + timeZone: timeZoneSchema.optional(), weekStart: z.string().optional(), hideBranding: z.boolean().optional(), allowDynamicBooking: z.boolean().optional(), @@ -33,7 +34,7 @@ export const ZUpdateProfileInputSchema = z.object({ .array( z.object({ id: z.number().optional(), - timeZone: z.string(), + timeZone: timeZoneSchema, endDate: z.date().optional(), startDate: z.date(), }) diff --git a/packages/trpc/server/routers/viewer/organizations/update.schema.ts b/packages/trpc/server/routers/viewer/organizations/update.schema.ts index 42434e1fdd..b541771bd7 100644 --- a/packages/trpc/server/routers/viewer/organizations/update.schema.ts +++ b/packages/trpc/server/routers/viewer/organizations/update.schema.ts @@ -1,5 +1,6 @@ import { z } from "zod"; +import { timeZoneSchema } from "@calcom/lib/dayjs/timeZone.schema"; import { teamMetadataSchema } from "@calcom/prisma/zod-utils"; export const ZUpdateInputSchema = z.object({ @@ -24,7 +25,7 @@ export const ZUpdateInputSchema = z.object({ brandColor: z.string().optional(), darkBrandColor: z.string().optional(), theme: z.string().optional().nullable(), - timeZone: z.string().optional(), + timeZone: timeZoneSchema.optional(), weekStart: z.string().optional(), timeFormat: z.number().optional(), metadata: teamMetadataSchema.unwrap().optional(), diff --git a/packages/trpc/server/routers/viewer/organizations/updateUser.schema.ts b/packages/trpc/server/routers/viewer/organizations/updateUser.schema.ts index 04717ed112..2084e7c228 100644 --- a/packages/trpc/server/routers/viewer/organizations/updateUser.schema.ts +++ b/packages/trpc/server/routers/viewer/organizations/updateUser.schema.ts @@ -1,5 +1,7 @@ import { z } from "zod"; +import { timeZoneSchema } from "@calcom/lib/dayjs/timeZone.schema"; + import { assignUserToAttributeSchema } from "../attributes/assignUserToAttribute.schema"; export const ZUpdateUserInputSchema = z.object({ @@ -10,7 +12,7 @@ export const ZUpdateUserInputSchema = z.object({ email: z.string().optional(), avatar: z.string().optional(), role: z.enum(["ADMIN", "MEMBER", "OWNER"]), - timeZone: z.string(), + timeZone: timeZoneSchema, attributeOptions: assignUserToAttributeSchema.optional(), }); diff --git a/packages/trpc/server/routers/viewer/slots/types.ts b/packages/trpc/server/routers/viewer/slots/types.ts index f660b09a49..cc81e6525e 100644 --- a/packages/trpc/server/routers/viewer/slots/types.ts +++ b/packages/trpc/server/routers/viewer/slots/types.ts @@ -1,6 +1,8 @@ import type { IncomingMessage } from "http"; import { z } from "zod"; +import { timeZoneSchema } from "@calcom/lib/dayjs/timeZone.schema"; + export const getScheduleSchema = z .object({ // startTime ISOString @@ -12,7 +14,7 @@ export const getScheduleSchema = z // Event type slug eventTypeSlug: z.string().optional(), // invitee timezone - timeZone: z.string().optional(), + timeZone: timeZoneSchema.optional(), // or list of users (for dynamic events) usernameList: z.array(z.string()).min(1).optional(), debug: z.boolean().optional(), diff --git a/packages/trpc/server/routers/viewer/teams/getMemberAvailability.schema.ts b/packages/trpc/server/routers/viewer/teams/getMemberAvailability.schema.ts index 07b62c1296..8cc05ab10c 100644 --- a/packages/trpc/server/routers/viewer/teams/getMemberAvailability.schema.ts +++ b/packages/trpc/server/routers/viewer/teams/getMemberAvailability.schema.ts @@ -1,9 +1,11 @@ import { z } from "zod"; +import { timeZoneSchema } from "@calcom/lib/dayjs/timeZone.schema"; + export const ZGetMemberAvailabilityInputSchema = z.object({ teamId: z.number(), memberId: z.number(), - timezone: z.string(), + timezone: timeZoneSchema, dateFrom: z.string(), dateTo: z.string(), });