fix: team metadata restriction for stripe (#23327)
This commit is contained in:
@@ -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<Params
|
||||
if (!team) {
|
||||
const prevTeam = await prisma.team.findFirstOrThrow({ where: { id } });
|
||||
|
||||
metadata = teamMetadataSchema.safeParse(prevTeam.metadata);
|
||||
metadata = teamMetadataStrictSchema.safeParse(prevTeam.metadata);
|
||||
if (!metadata.success) {
|
||||
throw new HttpError({ statusCode: 400, message: "Invalid team metadata" });
|
||||
}
|
||||
@@ -80,7 +80,7 @@ async function getHandler(req: NextRequest, { params }: { params: Promise<Params
|
||||
}
|
||||
|
||||
if (!metadata) {
|
||||
metadata = teamMetadataSchema.safeParse(team.metadata);
|
||||
metadata = teamMetadataStrictSchema.safeParse(team.metadata);
|
||||
if (!metadata.success) {
|
||||
throw new HttpError({ statusCode: 400, message: "Invalid team metadata" });
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import type { Team, User } from "@calcom/prisma/client";
|
||||
import { RedirectType } from "@calcom/prisma/client";
|
||||
import { Prisma } from "@calcom/prisma/client";
|
||||
import type { MembershipRole } from "@calcom/prisma/enums";
|
||||
import { teamMetadataSchema } from "@calcom/prisma/zod-utils";
|
||||
import { teamMetadataSchema, teamMetadataStrictSchema } from "@calcom/prisma/zod-utils";
|
||||
|
||||
const log = logger.getSubLogger({ prefix: ["orgMigration"] });
|
||||
|
||||
@@ -588,7 +588,7 @@ async function dbRemoveTeamFromOrg({ teamId }: { teamId: number }) {
|
||||
});
|
||||
}
|
||||
|
||||
const teamMetadata = teamMetadataSchema.parse(team?.metadata);
|
||||
const teamMetadata = teamMetadataStrictSchema.parse(team?.metadata);
|
||||
try {
|
||||
return await prisma.team.update({
|
||||
where: {
|
||||
|
||||
@@ -10,14 +10,14 @@ import { Redirect } from "@calcom/lib/redirect";
|
||||
import { safeStringify } from "@calcom/lib/safeStringify";
|
||||
import { OrganizationOnboardingRepository } from "@calcom/lib/server/repository/organizationOnboarding";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { teamMetadataSchema } from "@calcom/prisma/zod-utils";
|
||||
import { teamMetadataStrictSchema } from "@calcom/prisma/zod-utils";
|
||||
|
||||
import billing from "..";
|
||||
import { TeamBillingPublishResponseStatus, type TeamBilling, type TeamBillingInput } from "./team-billing";
|
||||
|
||||
const log = logger.getSubLogger({ prefix: ["TeamBilling"] });
|
||||
|
||||
const teamPaymentMetadataSchema = teamMetadataSchema.unwrap();
|
||||
const teamPaymentMetadataSchema = teamMetadataStrictSchema.unwrap();
|
||||
|
||||
export class InternalTeamBilling implements TeamBilling {
|
||||
private _team!: Omit<TeamBillingInput, "metadata"> & {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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<typeof teamMetadataSchema>;
|
||||
existingMetadata: z.infer<typeof teamMetadataStrictSchema>;
|
||||
}) {
|
||||
return await prisma.team.update({
|
||||
where: { id, isOrganization: true },
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
Reference in New Issue
Block a user