diff --git a/apps/web/app/api/teams/[team]/upgrade/route.ts b/apps/web/app/api/teams/[team]/upgrade/route.ts index 1f8b2eef01..57bd326498 100644 --- a/apps/web/app/api/teams/[team]/upgrade/route.ts +++ b/apps/web/app/api/teams/[team]/upgrade/route.ts @@ -12,7 +12,7 @@ import stripe from "@calcom/features/ee/payments/server/stripe"; import { WEBAPP_URL } from "@calcom/lib/constants"; import { HttpError } from "@calcom/lib/http-error"; import prisma from "@calcom/prisma"; -import { teamMetadataSchema } from "@calcom/prisma/zod-utils"; +import { teamMetadataStrictSchema } from "@calcom/prisma/zod-utils"; import { buildLegacyRequest } from "@lib/buildLegacyCtx"; @@ -50,7 +50,7 @@ async function getHandler(req: NextRequest, { params }: { params: Promise & { diff --git a/packages/features/ee/organizations/lib/server/createOrganizationFromOnboarding.ts b/packages/features/ee/organizations/lib/server/createOrganizationFromOnboarding.ts index 9aec6570e5..d6dec3481b 100644 --- a/packages/features/ee/organizations/lib/server/createOrganizationFromOnboarding.ts +++ b/packages/features/ee/organizations/lib/server/createOrganizationFromOnboarding.ts @@ -30,7 +30,7 @@ import { orgOnboardingInvitedMembersSchema, orgOnboardingTeamsSchema, } from "@calcom/prisma/zod-utils"; -import { teamMetadataSchema } from "@calcom/prisma/zod-utils"; +import { teamMetadataStrictSchema } from "@calcom/prisma/zod-utils"; import { createTeamsHandler } from "@calcom/trpc/server/routers/viewer/organizations/createTeams.handler"; import { inviteMembersWithNoInviterPermissionCheck } from "@calcom/trpc/server/routers/viewer/teams/inviteMember/inviteMember.handler"; @@ -344,7 +344,7 @@ async function backwardCompatibilityForSubscriptionDetails({ return organization; } - const existingMetadata = teamMetadataSchema.parse(organization.metadata); + const existingMetadata = teamMetadataStrictSchema.parse(organization.metadata); const updatedOrganization = await OrganizationRepository.updateStripeSubscriptionDetails({ id: organization.id, stripeSubscriptionId: paymentSubscriptionId, diff --git a/packages/lib/server/repository/organization.ts b/packages/lib/server/repository/organization.ts index 37ead73e84..fb6dceaf7e 100644 --- a/packages/lib/server/repository/organization.ts +++ b/packages/lib/server/repository/organization.ts @@ -6,6 +6,7 @@ import { safeStringify } from "@calcom/lib/safeStringify"; import { prisma } from "@calcom/prisma"; import { MembershipRole } from "@calcom/prisma/enums"; import type { CreationSource } from "@calcom/prisma/enums"; +import type { teamMetadataStrictSchema } from "@calcom/prisma/zod-utils"; import { teamMetadataSchema } from "@calcom/prisma/zod-utils"; import { createAProfileForAnExistingUser } from "../../createAProfileForAnExistingUser"; @@ -448,7 +449,7 @@ export class OrganizationRepository { id: number; stripeSubscriptionId: string; stripeSubscriptionItemId: string; - existingMetadata: z.infer; + existingMetadata: z.infer; }) { return await prisma.team.update({ where: { id, isOrganization: true }, diff --git a/packages/prisma/zod-utils.ts b/packages/prisma/zod-utils.ts index 53002f41a1..c2f34e24c4 100644 --- a/packages/prisma/zod-utils.ts +++ b/packages/prisma/zod-utils.ts @@ -372,24 +372,41 @@ export enum BillingPeriod { ANNUALLY = "ANNUALLY", } -export const teamMetadataSchema = z - .object({ - defaultConferencingApp: schemaDefaultConferencingApp.optional(), - requestedSlug: z.string().or(z.null()), - paymentId: z.string(), - subscriptionId: z.string().nullable(), - subscriptionItemId: z.string().nullable(), - orgSeats: z.number().nullable(), - orgPricePerSeat: z.number().nullable(), - migratedToOrgFrom: z - .object({ - teamSlug: z.string().or(z.null()).optional(), - lastMigrationTime: z.string().optional(), - reverted: z.boolean().optional(), - lastRevertTime: z.string().optional(), +const baseTeamMetadataSchema = z.object({ + defaultConferencingApp: schemaDefaultConferencingApp.optional(), + requestedSlug: z.string().or(z.null()), + paymentId: z.string(), + subscriptionId: z.string().nullable(), + subscriptionItemId: z.string().nullable(), + orgSeats: z.number().nullable(), + orgPricePerSeat: z.number().nullable(), + migratedToOrgFrom: z + .object({ + teamSlug: z.string().or(z.null()).optional(), + lastMigrationTime: z.string().optional(), + reverted: z.boolean().optional(), + lastRevertTime: z.string().optional(), + }) + .optional(), + billingPeriod: z.nativeEnum(BillingPeriod).optional(), +}); + +export const teamMetadataSchema = baseTeamMetadataSchema.partial().nullable(); + +export const teamMetadataStrictSchema = baseTeamMetadataSchema + .extend({ + subscriptionId: z + .string() + .refine((val) => val.startsWith("sub_"), { + message: "subscriptionId must start with 'sub_'", }) - .optional(), - billingPeriod: z.nativeEnum(BillingPeriod).optional(), + .nullable(), + subscriptionItemId: z + .string() + .refine((val) => val.startsWith("si_"), { + message: "subscriptionItemId must start with 'si_'", + }) + .nullable(), }) .partial() .nullable(); diff --git a/packages/trpc/server/routers/viewer/organizations/adminUpdate.handler.ts b/packages/trpc/server/routers/viewer/organizations/adminUpdate.handler.ts index b4383b3c67..6ccb199042 100644 --- a/packages/trpc/server/routers/viewer/organizations/adminUpdate.handler.ts +++ b/packages/trpc/server/routers/viewer/organizations/adminUpdate.handler.ts @@ -4,7 +4,7 @@ import { renameDomain } from "@calcom/lib/domainManager/organization"; import { getMetadataHelpers } from "@calcom/lib/getMetadataHelpers"; import { HttpError } from "@calcom/lib/http-error"; import { prisma } from "@calcom/prisma"; -import { teamMetadataSchema } from "@calcom/prisma/zod-utils"; +import { teamMetadataStrictSchema } from "@calcom/prisma/zod-utils"; import type { TrpcSessionUser } from "../../../types"; import type { TAdminUpdate } from "./adminUpdate.schema"; @@ -34,7 +34,7 @@ export const adminUpdateHandler = async ({ input }: AdminUpdateOptions) => { }); } - const { mergeMetadata } = getMetadataHelpers(teamMetadataSchema.unwrap(), existingOrg.metadata || {}); + const { mergeMetadata } = getMetadataHelpers(teamMetadataStrictSchema.unwrap(), existingOrg.metadata || {}); const data: Prisma.TeamUpdateArgs["data"] = restInput; diff --git a/packages/trpc/server/routers/viewer/organizations/createTeams.handler.ts b/packages/trpc/server/routers/viewer/organizations/createTeams.handler.ts index 2b0b7860bd..33ddd488c9 100644 --- a/packages/trpc/server/routers/viewer/organizations/createTeams.handler.ts +++ b/packages/trpc/server/routers/viewer/organizations/createTeams.handler.ts @@ -9,7 +9,7 @@ import slugify from "@calcom/lib/slugify"; import { prisma } from "@calcom/prisma"; import type { CreationSource } from "@calcom/prisma/enums"; import { MembershipRole, RedirectType } from "@calcom/prisma/enums"; -import { teamMetadataSchema } from "@calcom/prisma/zod-utils"; +import { teamMetadataSchema, teamMetadataStrictSchema } from "@calcom/prisma/zod-utils"; import { TRPCError } from "@trpc/server"; @@ -295,7 +295,7 @@ async function tryToCancelSubscription(subscriptionId: string) { } function getSubscriptionId(metadata: Prisma.JsonValue) { - const parsedMetadata = teamMetadataSchema.safeParse(metadata); + const parsedMetadata = teamMetadataStrictSchema.safeParse(metadata); if (parsedMetadata.success) { const subscriptionId = parsedMetadata.data?.subscriptionId; if (!subscriptionId) { diff --git a/packages/trpc/server/routers/viewer/organizations/publish.handler.ts b/packages/trpc/server/routers/viewer/organizations/publish.handler.ts index 914ca644f6..a6f270af1f 100644 --- a/packages/trpc/server/routers/viewer/organizations/publish.handler.ts +++ b/packages/trpc/server/routers/viewer/organizations/publish.handler.ts @@ -3,7 +3,7 @@ import { purchaseTeamOrOrgSubscription } from "@calcom/features/ee/teams/lib/pay import { IS_TEAM_BILLING_ENABLED, WEBAPP_URL } from "@calcom/lib/constants"; import { isOrganisationAdmin } from "@calcom/lib/server/queries/organisations"; import { prisma } from "@calcom/prisma"; -import { teamMetadataSchema } from "@calcom/prisma/zod-utils"; +import { teamMetadataStrictSchema } from "@calcom/prisma/zod-utils"; import { TRPCError } from "@trpc/server"; @@ -31,7 +31,7 @@ export const publishHandler = async ({ ctx }: PublishOptions) => { if (!prevTeam) throw new TRPCError({ code: "NOT_FOUND", message: "Organization not found." }); - const metadata = teamMetadataSchema.safeParse(prevTeam.metadata); + const metadata = teamMetadataStrictSchema.safeParse(prevTeam.metadata); if (!metadata.success) throw new TRPCError({ code: "BAD_REQUEST", message: "Invalid team metadata" }); // Since this is an ORG we need to make sure ORG members are scyned with the team. Every time a user is added to the TEAM, we need to add them to the ORG diff --git a/packages/trpc/server/routers/viewer/organizations/update.handler.ts b/packages/trpc/server/routers/viewer/organizations/update.handler.ts index e112f055a1..5957c63b0c 100644 --- a/packages/trpc/server/routers/viewer/organizations/update.handler.ts +++ b/packages/trpc/server/routers/viewer/organizations/update.handler.ts @@ -9,7 +9,7 @@ import { resizeBase64Image } from "@calcom/lib/server/resizeBase64Image"; import type { PrismaClient } from "@calcom/prisma"; import { prisma } from "@calcom/prisma"; import { MembershipRole } from "@calcom/prisma/enums"; -import { teamMetadataSchema } from "@calcom/prisma/zod-utils"; +import { teamMetadataStrictSchema } from "@calcom/prisma/zod-utils"; import { TRPCError } from "@trpc/server"; @@ -165,7 +165,10 @@ export const updateHandler = async ({ ctx, input }: UpdateOptions) => { if (!prevOrganisation) throw new TRPCError({ code: "NOT_FOUND", message: "Organisation not found." }); - const { mergeMetadata } = getMetadataHelpers(teamMetadataSchema.unwrap(), prevOrganisation.metadata ?? {}); + const { mergeMetadata } = getMetadataHelpers( + teamMetadataStrictSchema.unwrap(), + prevOrganisation.metadata ?? {} + ); const data: Prisma.TeamUpdateArgs["data"] = { logoUrl: input.logoUrl, diff --git a/packages/trpc/server/routers/viewer/organizations/update.schema.ts b/packages/trpc/server/routers/viewer/organizations/update.schema.ts index 48bb92153a..0dcb8df399 100644 --- a/packages/trpc/server/routers/viewer/organizations/update.schema.ts +++ b/packages/trpc/server/routers/viewer/organizations/update.schema.ts @@ -1,7 +1,7 @@ import { z } from "zod"; import { timeZoneSchema } from "@calcom/lib/dayjs/timeZone.schema"; -import { teamMetadataSchema } from "@calcom/prisma/zod-utils"; +import { teamMetadataStrictSchema } from "@calcom/prisma/zod-utils"; export const ZUpdateInputSchema = z.object({ name: z.string().optional(), @@ -28,7 +28,7 @@ export const ZUpdateInputSchema = z.object({ timeZone: timeZoneSchema.optional(), weekStart: z.string().optional(), timeFormat: z.number().optional(), - metadata: teamMetadataSchema.unwrap().optional(), + metadata: teamMetadataStrictSchema.unwrap().optional(), lockEventTypeCreation: z.boolean().optional(), lockEventTypeCreationOptions: z.enum(["DELETE", "HIDE"]).optional(), adminGetsNoSlotsNotification: z.boolean().optional(), diff --git a/packages/trpc/server/routers/viewer/teams/update.handler.ts b/packages/trpc/server/routers/viewer/teams/update.handler.ts index 75dd5d92f1..ded1d4e8fe 100644 --- a/packages/trpc/server/routers/viewer/teams/update.handler.ts +++ b/packages/trpc/server/routers/viewer/teams/update.handler.ts @@ -8,7 +8,7 @@ import { uploadLogo } from "@calcom/lib/server/avatar"; import { isTeamAdmin } from "@calcom/lib/server/queries/teams"; import { prisma } from "@calcom/prisma"; import { RedirectType, RRTimestampBasis } from "@calcom/prisma/enums"; -import { teamMetadataSchema } from "@calcom/prisma/zod-utils"; +import { teamMetadataStrictSchema } from "@calcom/prisma/zod-utils"; import { TRPCError } from "@trpc/server"; @@ -95,7 +95,7 @@ export const updateHandler = async ({ ctx, input }: UpdateOptions) => { data.slug = input.slug; // If we save slug, we don't need the requestedSlug anymore - const metadataParse = teamMetadataSchema.safeParse(prevTeam.metadata); + const metadataParse = teamMetadataStrictSchema.safeParse(prevTeam.metadata); if (metadataParse.success) { const { requestedSlug: _, ...cleanMetadata } = metadataParse.data || {}; data.metadata = {