diff --git a/apps/web/lib/orgMigration.test.ts b/apps/web/lib/orgMigration.test.ts
index a107a8012a..080ac4131d 100644
--- a/apps/web/lib/orgMigration.test.ts
+++ b/apps/web/lib/orgMigration.test.ts
@@ -696,7 +696,7 @@ describe("orgMigration", () => {
});
describe("when user email matches orgAutoAcceptEmail", () => {
- const orgSettings = {
+ const orgMetadata = {
orgAutoAcceptEmail: "org1.com",
} as const;
@@ -725,10 +725,8 @@ describe("orgMigration", () => {
const dbOrg = await createOrg({
slug: data.targetOrg.slug,
name: data.targetOrg.name,
- organizationSettings: {
- create: {
- ...orgSettings,
- },
+ metadata: {
+ ...orgMetadata,
},
});
@@ -786,10 +784,8 @@ describe("orgMigration", () => {
const dbOrg = await createOrg({
slug: data.targetOrg.slug,
name: data.targetOrg.name,
- organizationSettings: {
- create: {
- ...orgSettings,
- },
+ metadata: {
+ ...orgMetadata,
},
});
@@ -841,11 +837,7 @@ describe("orgMigration", () => {
name: data.targetOrg.name,
metadata: {
requestedSlug: data.targetOrg.requestedSlug,
- },
- organizationSettings: {
- create: {
- ...orgSettings,
- },
+ ...orgMetadata,
},
});
@@ -909,7 +901,9 @@ describe("orgMigration", () => {
id: data.targetOrg.id,
slug: data.targetOrg.slug,
name: data.targetOrg.name,
- isOrganization: true,
+ metadata: {
+ isOrganization: true,
+ },
},
});
@@ -1570,9 +1564,9 @@ async function createOrg(
return await prismock.team.create({
data: {
...data,
- isOrganization: true,
metadata: {
...(data.metadata || {}),
+ isOrganization: true,
},
},
});
diff --git a/apps/web/lib/orgMigration.ts b/apps/web/lib/orgMigration.ts
index ef1921d840..c09304c315 100644
--- a/apps/web/lib/orgMigration.ts
+++ b/apps/web/lib/orgMigration.ts
@@ -1,6 +1,5 @@
import { getOrgUsernameFromEmail } from "@calcom/features/auth/signup/utils/getOrgUsernameFromEmail";
import { getOrgFullOrigin } from "@calcom/features/ee/organizations/lib/orgDomains";
-import { isOrganization } from "@calcom/lib/entityPermissionUtils";
import { HttpError } from "@calcom/lib/http-error";
import logger from "@calcom/lib/logger";
import { safeStringify } from "@calcom/lib/safeStringify";
@@ -47,7 +46,7 @@ export async function moveUserToOrg({
const teamMetadata = teamMetadataSchema.parse(team?.metadata);
- if (!isOrganization({ team })) {
+ if (!teamMetadata?.isOrganization) {
throw new Error(`Team with ID:${targetOrgId} is not an Org`);
}
@@ -61,7 +60,7 @@ export async function moveUserToOrg({
if (!targetOrgUsername) {
targetOrgUsername = getOrgUsernameFromEmail(
userToMoveToOrg.email,
- team.organizationSettings?.orgAutoAcceptEmail || ""
+ targetOrganization.metadata.orgAutoAcceptEmail || ""
);
}
@@ -196,7 +195,7 @@ export async function moveTeamToOrg({
const teamMetadata = teamMetadataSchema.parse(possibleOrg?.metadata);
- if (!isOrganization({ team: possibleOrg })) {
+ if (!teamMetadata?.isOrganization) {
throw new Error(`${targetOrg.id} is not an Org`);
}
@@ -205,7 +204,7 @@ export async function moveTeamToOrg({
await addTeamRedirect({
oldTeamSlug,
teamSlug: updatedTeam.slug,
- orgSlug: targetOrganization.slug || orgMetadata?.requestedSlug || null,
+ orgSlug: targetOrganization.slug || orgMetadata.requestedSlug || null,
});
await setOrgSlugIfNotSet({ slug: targetOrganization.slug }, orgMetadata, targetOrg.id);
if (moveMembers) {
@@ -329,14 +328,13 @@ async function setOrgSlugIfNotSet(
},
orgMetadata: {
requestedSlug?: string | undefined;
- } | null,
+ },
targetOrgId: number
) {
if (targetOrganization.slug) {
return;
}
-
- if (!orgMetadata?.requestedSlug) {
+ if (!orgMetadata.requestedSlug) {
throw new HttpError({
statusCode: 400,
message: `Org with id: ${targetOrgId} doesn't have a slug. Tried using requestedSlug but that's also not present. So, all migration done but failed to set the Organization slug. Please set it manually`,
@@ -373,9 +371,6 @@ async function getTeamOrThrowError(targetOrgId: number) {
where: {
id: targetOrgId,
},
- include: {
- organizationSettings: true,
- },
});
if (!team) {
@@ -625,7 +620,6 @@ async function moveTeamsWithoutMembersToOrg({
id: true,
slug: true,
metadata: true,
- isOrganization: true,
},
});
@@ -637,7 +631,7 @@ async function moveTeamsWithoutMembersToOrg({
};
})
// Remove Orgs from the list
- .filter((team) => !isOrganization({ team }));
+ .filter((team) => !team.metadata?.isOrganization);
const teamIdsToBeMovedToOrg = teamsToBeMovedToOrg.map((t) => t.id);
@@ -790,7 +784,6 @@ async function removeTeamsWithoutItsMemberFromOrg({ userToRemoveFromOrg }: { use
id: true,
slug: true,
metadata: true,
- isOrganization: true,
},
});
@@ -802,7 +795,7 @@ async function removeTeamsWithoutItsMemberFromOrg({ userToRemoveFromOrg }: { use
};
})
// Remove Orgs from the list
- .filter((team) => !isOrganization({ team }));
+ .filter((team) => !team.metadata?.isOrganization);
const teamIdsToBeRemovedFromOrg = teamsToBeRemovedFromOrg.map((t) => t.id);
diff --git a/apps/web/pages/signup.tsx b/apps/web/pages/signup.tsx
index 50b4138c7f..88298c4908 100644
--- a/apps/web/pages/signup.tsx
+++ b/apps/web/pages/signup.tsx
@@ -610,16 +610,14 @@ export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
team: {
select: {
metadata: true,
- isOrganization: true,
parentId: true,
parent: {
select: {
slug: true,
- organizationSettings: true,
+ metadata: true,
},
},
slug: true,
- organizationSettings: true,
},
},
},
@@ -668,8 +666,8 @@ export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
};
const isATeamInOrganization = tokenTeam?.parentId !== null;
+ const isOrganization = tokenTeam.metadata?.isOrganization;
// Detect if the team is an org by either the metadata flag or if it has a parent team
- const isOrganization = tokenTeam.isOrganization;
const isOrganizationOrATeamInOrganization = isOrganization || isATeamInOrganization;
// If we are dealing with an org, the slug may come from the team itself or its parent
const orgSlug = isOrganizationOrATeamInOrganization
@@ -686,7 +684,9 @@ export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
const isValidEmail = checkValidEmail(verificationToken.identifier);
const isOrgInviteByLink = isOrganizationOrATeamInOrganization && !isValidEmail;
- const parentOrgSettings = tokenTeam?.parent?.organizationSettings ?? null;
+ const parentMetaDataForSubteam = tokenTeam?.parent?.metadata
+ ? teamMetadataSchema.parse(tokenTeam.parent.metadata)
+ : null;
return {
props: {
@@ -699,15 +699,15 @@ export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
? getOrgUsernameFromEmail(
verificationToken.identifier,
(isOrganization
- ? tokenTeam.organizationSettings?.orgAutoAcceptEmail
- : parentOrgSettings?.orgAutoAcceptEmail) || ""
+ ? tokenTeam.metadata?.orgAutoAcceptEmail
+ : parentMetaDataForSubteam?.orgAutoAcceptEmail) || ""
)
: slugify(username),
}
: null,
orgSlug,
orgAutoAcceptEmail: isOrgInviteByLink
- ? tokenTeam?.organizationSettings?.orgAutoAcceptEmail ?? parentOrgSettings?.orgAutoAcceptEmail ?? null
+ ? tokenTeam?.metadata?.orgAutoAcceptEmail ?? parentMetaDataForSubteam?.orgAutoAcceptEmail ?? null
: null,
},
};
diff --git a/apps/web/pages/team/[slug].tsx b/apps/web/pages/team/[slug].tsx
index 830acfb6ef..6b80836974 100644
--- a/apps/web/pages/team/[slug].tsx
+++ b/apps/web/pages/team/[slug].tsx
@@ -74,7 +74,7 @@ function TeamPage({
return (
@@ -178,7 +178,7 @@ function TeamPage({
title: markdownStrippedBio,
profile: {
name: `${team.name}`,
- image: `${WEBAPP_URL}/${team.isOrganization ? "org" : "team"}/${team.slug}/avatar.png`,
+ image: `${WEBAPP_URL}/${team.metadata?.isOrganization ? "org" : "team"}/${team.slug}/avatar.png`,
},
}}
/>
@@ -190,7 +190,7 @@ function TeamPage({
imageSrc={
isValidOrgDomain
? `/org/${currentOrgDomain}/avatar.png`
- : `${WEBAPP_URL}/${team.isOrganization ? "org" : "team"}/${team.slug}/avatar.png`
+ : `${WEBAPP_URL}/${team.metadata?.isOrganization ? "org" : "team"}/${team.slug}/avatar.png`
}
size="lg"
/>
@@ -208,7 +208,7 @@ function TeamPage({
>
)}
- {team?.isOrganization ? (
+ {metadata?.isOrganization ? (
) : (
<>
@@ -311,7 +311,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
// Taking care of sub-teams and orgs
if (
(!isValidOrgDomain && team?.parent) ||
- (!isValidOrgDomain && !!team?.isOrganization) ||
+ (!isValidOrgDomain && !!metadata?.isOrganization) ||
!isOrganizationFeatureEnabled
) {
return { notFound: true } as const;
diff --git a/apps/web/playwright/fixtures/orgs.ts b/apps/web/playwright/fixtures/orgs.ts
index ea67350304..265fcd73a1 100644
--- a/apps/web/playwright/fixtures/orgs.ts
+++ b/apps/web/playwright/fixtures/orgs.ts
@@ -43,8 +43,8 @@ async function createOrgInDb({
data: {
name: name,
slug: slug,
- isOrganization: true,
metadata: {
+ isOrganization: true,
...(requestedSlug
? {
requestedSlug,
diff --git a/apps/web/playwright/fixtures/users.ts b/apps/web/playwright/fixtures/users.ts
index c14e0e58f9..9ef4085fd4 100644
--- a/apps/web/playwright/fixtures/users.ts
+++ b/apps/web/playwright/fixtures/users.ts
@@ -118,21 +118,18 @@ const createTeamAndAddUser = async (
const slug = `${isOrg ? "org" : "team"}-${workerInfo.workerIndex}-${Date.now()}`;
const data: PrismaType.TeamCreateInput = {
name: `user-id-${user.id}'s ${isOrg ? "Org" : "Team"}`,
- isOrganization: isOrg,
};
data.metadata = {
...(isUnpublished ? { requestedSlug: slug } : {}),
+ ...(isOrg
+ ? {
+ isOrganization: true,
+ isOrganizationVerified: !!isOrgVerified,
+ orgAutoAcceptEmail: user.email.split("@")[1],
+ isOrganizationConfigured: false,
+ }
+ : {}),
};
- if (isOrg) {
- data.organizationSettings = {
- create: {
- isOrganizationVerified: !!isOrgVerified,
- orgAutoAcceptEmail: user.email.split("@")[1],
- isOrganizationConfigured: false,
- },
- };
- }
-
data.slug = !isUnpublished ? slug : undefined;
if (isOrg && hasSubteam) {
const team = await createTeamAndAddUser({ user }, workerInfo);
@@ -537,7 +534,7 @@ const createUserFixture = (user: UserWithIncludes, page: Page) => {
},
};
})
- .find((membership) => !membership.team.isOrganization);
+ .find((membership) => !membership.team?.metadata?.isOrganization);
if (!membership) {
throw new Error("No team found for user");
}
@@ -548,7 +545,10 @@ const createUserFixture = (user: UserWithIncludes, page: Page) => {
where: {
userId: user.id,
team: {
- isOrganization: true,
+ metadata: {
+ path: ["isOrganization"],
+ equals: true,
+ },
},
},
include: { team: { include: { children: true } } },
diff --git a/packages/features/auth/lib/next-auth-options.ts b/packages/features/auth/lib/next-auth-options.ts
index 113bb510b8..0cf2530174 100644
--- a/packages/features/auth/lib/next-auth-options.ts
+++ b/packages/features/auth/lib/next-auth-options.ts
@@ -61,10 +61,20 @@ const checkIfUserShouldBelongToOrg = async (idP: IdentityProvider, email: string
if (!ORGANIZATIONS_AUTOLINK || idP !== "GOOGLE") return { orgUsername, orgId: undefined };
const existingOrg = await prisma.team.findFirst({
where: {
- organizationSettings: {
- isOrganizationVerified: true,
- orgAutoAcceptEmail: apexDomain,
- },
+ AND: [
+ {
+ metadata: {
+ path: ["isOrganizationVerified"],
+ equals: true,
+ },
+ },
+ {
+ metadata: {
+ path: ["orgAutoAcceptEmail"],
+ equals: apexDomain,
+ },
+ },
+ ],
},
select: {
id: true,
diff --git a/packages/features/auth/signup/handlers/calcomHandler.ts b/packages/features/auth/signup/handlers/calcomHandler.ts
index d95f1d5352..47e86e779a 100644
--- a/packages/features/auth/signup/handlers/calcomHandler.ts
+++ b/packages/features/auth/signup/handlers/calcomHandler.ts
@@ -14,7 +14,7 @@ import { closeComUpsertTeamUser } from "@calcom/lib/sync/SyncServiceManager";
import { validateAndGetCorrectedUsernameAndEmail } from "@calcom/lib/validateUsername";
import { prisma } from "@calcom/prisma";
import { IdentityProvider } from "@calcom/prisma/enums";
-import { signupSchema } from "@calcom/prisma/zod-utils";
+import { signupSchema, teamMetadataSchema } from "@calcom/prisma/zod-utils";
import { joinAnyChildTeamOnOrgInvite } from "../utils/organization";
import {
@@ -129,6 +129,8 @@ async function handler(req: RequestWithUsernameStatus, res: NextApiResponse) {
},
});
if (team) {
+ const teamMetadata = teamMetadataSchema.parse(team?.metadata);
+
const user = await prisma.user.upsert({
where: { email },
update: {
@@ -147,6 +149,7 @@ async function handler(req: RequestWithUsernameStatus, res: NextApiResponse) {
// Wrapping in a transaction as if one fails we want to rollback the whole thing to preventa any data inconsistencies
const { membership } = await createOrUpdateMemberships({
+ teamMetadata,
user,
team,
});
diff --git a/packages/features/auth/signup/handlers/selfHostedHandler.ts b/packages/features/auth/signup/handlers/selfHostedHandler.ts
index 3ccb2fc498..60e1111432 100644
--- a/packages/features/auth/signup/handlers/selfHostedHandler.ts
+++ b/packages/features/auth/signup/handlers/selfHostedHandler.ts
@@ -12,6 +12,7 @@ import { validateAndGetCorrectedUsernameAndEmail } from "@calcom/lib/validateUse
import prisma from "@calcom/prisma";
import { IdentityProvider } from "@calcom/prisma/enums";
import { signupSchema } from "@calcom/prisma/zod-utils";
+import { teamMetadataSchema } from "@calcom/prisma/zod-utils";
import { joinAnyChildTeamOnOrgInvite } from "../utils/organization";
import {
@@ -68,6 +69,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
},
});
if (team) {
+ const teamMetadata = teamMetadataSchema.parse(team?.metadata);
+
const user = await prisma.user.upsert({
where: { email: userEmail },
update: {
@@ -85,6 +88,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
});
const { membership } = await createOrUpdateMemberships({
+ teamMetadata,
user,
team,
});
diff --git a/packages/features/auth/signup/utils/createOrUpdateMemberships.ts b/packages/features/auth/signup/utils/createOrUpdateMemberships.ts
index 2380da13a6..593fb50ebd 100644
--- a/packages/features/auth/signup/utils/createOrUpdateMemberships.ts
+++ b/packages/features/auth/signup/utils/createOrUpdateMemberships.ts
@@ -1,16 +1,21 @@
+import type z from "zod";
+
import { prisma } from "@calcom/prisma";
import type { Team, User } from "@calcom/prisma/client";
import { MembershipRole } from "@calcom/prisma/enums";
+import type { teamMetadataSchema } from "@calcom/prisma/zod-utils";
export const createOrUpdateMemberships = async ({
+ teamMetadata,
user,
team,
}: {
user: Pick;
- team: Pick;
+ team: Pick;
+ teamMetadata: z.infer;
}) => {
return await prisma.$transaction(async (tx) => {
- if (team.isOrganization) {
+ if (teamMetadata?.isOrganization) {
await tx.user.update({
where: {
id: user.id,
diff --git a/packages/features/ee/organizations/api/subteams.ts b/packages/features/ee/organizations/api/subteams.ts
index ad935527c2..352b922bb4 100644
--- a/packages/features/ee/organizations/api/subteams.ts
+++ b/packages/features/ee/organizations/api/subteams.ts
@@ -4,6 +4,7 @@ import z from "zod";
import { HttpError } from "@calcom/lib/http-error";
import { defaultHandler, defaultResponder } from "@calcom/lib/server";
import prisma from "@calcom/prisma";
+import { teamMetadataSchema } from "@calcom/prisma/zod-utils";
const querySchema = z.object({
org: z.string({ required_error: "org slug is required" }),
@@ -19,16 +20,13 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
} = parsedQuery;
if (!slug) return res.status(400).json({ message: "Org is needed" });
- const org = await prisma.team.findFirst({
- where: { slug },
- select: { children: true, isOrganization: true },
- });
+ const org = await prisma.team.findFirst({ where: { slug }, select: { children: true, metadata: true } });
if (!org) return res.status(400).json({ message: "Org doesn't exist" });
- const isOrganization = org.isOrganization;
+ const metadata = teamMetadataSchema.parse(org?.metadata);
- if (!isOrganization) return res.status(400).json({ message: "Team is not an org" });
+ if (!metadata?.isOrganization) return res.status(400).json({ message: "Team is not an org" });
return res.status(200).json({ slugs: org.children.map((ch) => ch.slug) });
}
diff --git a/packages/features/ee/organizations/lib/orgDomains.ts b/packages/features/ee/organizations/lib/orgDomains.ts
index 6a2cbc1050..ebb0c9d73e 100644
--- a/packages/features/ee/organizations/lib/orgDomains.ts
+++ b/packages/features/ee/organizations/lib/orgDomains.ts
@@ -139,7 +139,10 @@ export function whereClauseForOrgWithSlugOrRequestedSlug(slug: string) {
},
},
],
- isOrganization: true,
+ metadata: {
+ path: ["isOrganization"],
+ equals: true,
+ },
} satisfies Prisma.TeamWhereInput;
}
diff --git a/packages/features/ee/organizations/pages/settings/admin/AdminOrgEditPage.tsx b/packages/features/ee/organizations/pages/settings/admin/AdminOrgEditPage.tsx
index 4dd43bb817..620210633e 100644
--- a/packages/features/ee/organizations/pages/settings/admin/AdminOrgEditPage.tsx
+++ b/packages/features/ee/organizations/pages/settings/admin/AdminOrgEditPage.tsx
@@ -6,7 +6,7 @@ import { z } from "zod";
import NoSSR from "@calcom/core/components/NoSSR";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { useParamsWithFallback } from "@calcom/lib/hooks/useParamsWithFallback";
-import type { orgSettingsSchema } from "@calcom/prisma/zod-utils";
+import type { teamMetadataSchema } from "@calcom/prisma/zod-utils";
import { trpc } from "@calcom/trpc/react";
import { Button, Form, Meta, TextField, showToast } from "@calcom/ui";
@@ -43,7 +43,7 @@ const OrgEditView = ({ orgId }: { orgId: number }) => {
type FormValues = {
name: Team["name"];
slug: Team["slug"];
- organizationSettings: z.infer;
+ metadata: z.infer;
};
const OrgForm = ({
@@ -81,10 +81,6 @@ const OrgForm = ({
mutation.mutate({
id: org.id,
...values,
- organizationSettings: {
- ...org.organizationSettings,
- orgAutoAcceptEmail: values.organizationSettings?.orgAutoAcceptEmail,
- },
});
};
@@ -100,7 +96,7 @@ const OrgForm = ({
label="Domain for which invitations are auto-accepted"
placeholder="abc.com"
required
- {...form.register("organizationSettings.orgAutoAcceptEmail")}
+ {...form.register("metadata.orgAutoAcceptEmail")}
/>