Files
calendar/packages/trpc/server/routers/viewer/teams/update.handler.ts
T
luzpazandGitHub 97ef32f7ca fix: typos in several package/ directories (#19495)
* fix: typos in packages/embeds
Found via codespell

* fix: typos in packages/lib
Found via codespell

* fix: typos in packages/trpc
Found via codespell
2025-02-24 14:44:08 +00:00

152 lines
4.3 KiB
TypeScript

import type { Prisma } from "@prisma/client";
import { getOrgFullOrigin } from "@calcom/ee/organizations/lib/orgDomains";
import { validateIntervalLimitOrder } from "@calcom/lib";
import { IS_TEAM_BILLING_ENABLED } from "@calcom/lib/constants";
import { uploadLogo } from "@calcom/lib/server/avatar";
import { isTeamAdmin } from "@calcom/lib/server/queries/teams";
import { prisma } from "@calcom/prisma";
import { RedirectType } from "@calcom/prisma/enums";
import { teamMetadataSchema } from "@calcom/prisma/zod-utils";
import type { IntervalLimit } from "@calcom/types/Calendar";
import { TRPCError } from "@trpc/server";
import type { TrpcSessionUser } from "../../../trpc";
import type { TUpdateInputSchema } from "./update.schema";
type UpdateOptions = {
ctx: {
user: NonNullable<TrpcSessionUser>;
};
input: TUpdateInputSchema;
};
export const updateHandler = async ({ ctx, input }: UpdateOptions) => {
const isOrgAdmin = ctx.user?.organization?.isOrgAdmin;
if (!isOrgAdmin) {
if (!(await isTeamAdmin(ctx.user?.id, input.id))) {
throw new TRPCError({ code: "UNAUTHORIZED" });
}
}
if (input.slug) {
const userConflict = await prisma.team.findMany({
where: {
slug: input.slug,
},
});
if (userConflict.some((t) => t.id !== input.id)) return;
}
const prevTeam = await prisma.team.findFirst({
where: {
id: input.id,
},
});
if (!prevTeam) throw new TRPCError({ code: "NOT_FOUND", message: "Team not found." });
if (input.bookingLimits) {
const isValid = validateIntervalLimitOrder(input.bookingLimits);
if (!isValid)
throw new TRPCError({ code: "BAD_REQUEST", message: "Booking limits must be in ascending order." });
}
const data: Prisma.TeamUpdateArgs["data"] = {
name: input.name,
bio: input.bio,
hideBranding: input.hideBranding,
isPrivate: input.isPrivate,
hideBookATeamMember: input.hideBookATeamMember,
brandColor: input.brandColor,
darkBrandColor: input.darkBrandColor,
theme: input.theme,
bookingLimits: input.bookingLimits ?? undefined,
includeManagedEventsInLimits: input.includeManagedEventsInLimits ?? undefined,
};
if (input.logo && input.logo.startsWith("data:image/png;base64,")) {
data.logoUrl = await uploadLogo({ teamId: input.id, logo: input.logo });
} else if (typeof input.logo !== "undefined" && !input.logo) {
data.logoUrl = null;
}
if (
input.slug &&
IS_TEAM_BILLING_ENABLED &&
/** If the team doesn't have a slug we can assume that it hasn't been published yet. */
!prevTeam.slug
) {
// Save it on the metadata so we can use it later
data.metadata = {
requestedSlug: input.slug,
};
} else {
data.slug = input.slug;
// If we save slug, we don't need the requestedSlug anymore
const metadataParse = teamMetadataSchema.safeParse(prevTeam.metadata);
if (metadataParse.success) {
const { requestedSlug: _, ...cleanMetadata } = metadataParse.data || {};
data.metadata = {
...cleanMetadata,
};
}
}
const updatedTeam = await prisma.team.update({
where: { id: input.id },
data,
});
if (updatedTeam.parentId && prevTeam.slug) {
// No changes made lets skip this logic
if (updatedTeam.slug === prevTeam.slug) return;
// Fetch parent team slug to construct toUrl
const parentTeam = await prisma.team.findUnique({
where: {
id: updatedTeam.parentId,
},
select: {
slug: true,
},
});
if (!parentTeam?.slug) {
throw new Error(`Parent team with slug: ${parentTeam?.slug} not found`);
}
const orgUrlPrefix = getOrgFullOrigin(parentTeam.slug);
const toUrlOld = `${orgUrlPrefix}/${prevTeam.slug}`;
const toUrlNew = `${orgUrlPrefix}/${updatedTeam.slug}`;
await prisma.tempOrgRedirect.updateMany({
where: {
type: RedirectType.Team,
toUrl: toUrlOld,
},
data: {
toUrl: toUrlNew,
},
});
}
return {
logoUrl: updatedTeam.logoUrl,
name: updatedTeam.name,
bio: updatedTeam.bio,
slug: updatedTeam.slug,
theme: updatedTeam.theme,
brandColor: updatedTeam.brandColor,
darkBrandColor: updatedTeam.darkBrandColor,
bookingLimits: updatedTeam.bookingLimits as IntervalLimit,
includeManagedEventsInLimits: updatedTeam.includeManagedEventsInLimits,
};
};
export default updateHandler;