diff --git a/apps/web/lib/orgMigration.ts b/apps/web/lib/orgMigration.ts index a315be2928..68d8863af1 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"; @@ -48,7 +47,7 @@ export async function moveUserToOrg({ const teamMetadata = teamMetadataSchema.parse(team?.metadata); - if (!isOrganization({ team })) { + if (!team.isOrganization) { throw new Error(`Team with ID:${targetOrgId} is not an Org`); } @@ -197,7 +196,7 @@ export async function moveTeamToOrg({ const teamMetadata = teamMetadataSchema.parse(possibleOrg?.metadata); - if (!isOrganization({ team: possibleOrg })) { + if (!possibleOrg.isOrganization) { throw new Error(`${targetOrg.id} is not an Org`); } @@ -667,7 +666,7 @@ async function moveTeamsWithoutMembersToOrg({ }; }) // Remove Orgs from the list - .filter((team) => !isOrganization({ team })); + .filter((team) => !team.isOrganization); const teamIdsToBeMovedToOrg = teamsToBeMovedToOrg.map((t) => t.id); @@ -832,7 +831,7 @@ async function removeTeamsWithoutItsMemberFromOrg({ userToRemoveFromOrg }: { use }; }) // Remove Orgs from the list - .filter((team) => !isOrganization({ team })); + .filter((team) => !team.isOrganization); const teamIdsToBeRemovedFromOrg = teamsToBeRemovedFromOrg.map((t) => t.id); diff --git a/apps/web/playwright/change-username.e2e.ts b/apps/web/playwright/change-username.e2e.ts index 46bf03e778..d92b58196d 100644 --- a/apps/web/playwright/change-username.e2e.ts +++ b/apps/web/playwright/change-username.e2e.ts @@ -2,6 +2,9 @@ import { expect } from "@playwright/test"; import stripe from "@calcom/features/ee/payments/server/stripe"; import { WEBAPP_URL } from "@calcom/lib/constants"; +import { MembershipRole } from "@calcom/prisma/enums"; + +import { moveUserToOrg } from "@lib/orgMigration"; import { test } from "./lib/fixtures"; @@ -110,4 +113,54 @@ test.describe("Change username on settings", () => { await expect(page).toHaveURL(/.*checkout.stripe.com/); }); + + test("User can't take a username that has been migrated to a different username in an organization", async ({ + users, + orgs, + page, + }) => { + const existingUser = + await test.step("Migrate user to a different username in an organization", async () => { + const org = await orgs.create({ + name: "TestOrg", + }); + + const existingUser = await users.create({ + username: "john", + emailDomain: org.organizationSettings?.orgAutoAcceptEmail ?? "", + name: "John Outside Organization", + }); + + await moveUserToOrg({ + user: existingUser, + targetOrg: { + // Changed username. After this there is no user with username equal to {existingUser.username} + username: `${existingUser.username}-org`, + id: org.id, + membership: { + role: MembershipRole.MEMBER, + accepted: true, + }, + }, + shouldMoveTeams: false, + }); + return existingUser; + }); + + await test.step("Changing username for another user to the previous username of migrated user - shouldn't be allowed", async () => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const previousUsername = existingUser.username!; + + const user = await users.create(); + await user.apiLogin(); + + await page.goto("/settings/my-account/profile"); + const usernameInput = page.locator("[data-testid=username-input]"); + + await usernameInput.fill(previousUsername); + await page.waitForLoadState("networkidle"); + await expect(page.locator("[data-testid=update-username-btn]").nth(0)).toBeHidden(); + await expect(page.locator("[data-testid=update-username-btn]").nth(1)).toBeHidden(); + }); + }); }); diff --git a/apps/web/playwright/fixtures/users.ts b/apps/web/playwright/fixtures/users.ts index b5f85b35b6..faa18a20ed 100644 --- a/apps/web/playwright/fixtures/users.ts +++ b/apps/web/playwright/fixtures/users.ts @@ -604,15 +604,33 @@ const createUserFixture = (user: UserWithIncludes, page: Page) => { return membership; }, getOrgMembership: async () => { - return prisma.membership.findFirstOrThrow({ + const membership = await prisma.membership.findFirstOrThrow({ where: { userId: user.id, team: { isOrganization: true, }, }, - include: { team: { include: { children: true } } }, + include: { + team: { + include: { + children: true, + organizationSettings: true, + }, + }, + }, }); + if (!membership) { + return membership; + } + + return { + ...membership, + team: { + ...membership.team, + metadata: teamMetadataSchema.parse(membership.team.metadata), + }, + }; }, getFirstEventAsOwner: async () => prisma.eventType.findFirstOrThrow({ @@ -670,6 +688,7 @@ type CustomUserOpts = Partial> & { roleInOrganization?: MembershipRole; schedule?: Schedule; password?: string | null; + emailDomain?: string; }; // creates the actual user in the db. @@ -687,10 +706,11 @@ const createUser = ( ? opts.username : `${opts?.username || "user"}-${workerInfo.workerIndex}-${Date.now()}`; + const emailDomain = opts?.emailDomain || "example.com"; return { username: uname, name: opts?.name, - email: opts?.email ?? `${uname}@example.com`, + email: opts?.email ?? `${uname}@${emailDomain}`, password: { create: { hash: hashPassword(uname), diff --git a/apps/web/playwright/organization/organization-invitation.e2e.ts b/apps/web/playwright/organization/organization-invitation.e2e.ts index 1b3eac6949..80e275b9c3 100644 --- a/apps/web/playwright/organization/organization-invitation.e2e.ts +++ b/apps/web/playwright/organization/organization-invitation.e2e.ts @@ -2,6 +2,9 @@ import type { Browser, Page } from "@playwright/test"; import { expect } from "@playwright/test"; import prisma from "@calcom/prisma"; +import { MembershipRole } from "@calcom/prisma/client"; + +import { moveUserToOrg } from "@lib/orgMigration"; import { test } from "../lib/fixtures"; import { getInviteLink } from "../lib/testUtils"; @@ -16,7 +19,7 @@ test.afterEach(async ({ users, orgs }) => { test.describe("Organization", () => { test.describe("Email not matching orgAutoAcceptEmail", () => { - test("Org Invitation", async ({ browser, page, users, emails }) => { + test("nonexisting user invited to an organization", async ({ browser, page, users, emails }) => { const orgOwner = await users.create(undefined, { hasTeam: true, isOrg: true }); const { team: org } = await orgOwner.getOrgMembership(); await orgOwner.apiLogin(); @@ -83,7 +86,17 @@ test.describe("Organization", () => { }); }); - test("Team invitation", async ({ browser, page, users, emails }) => { + // This test is already covered by booking.e2e.ts where existing user is invited and his booking links are tested. + // We can re-test here when we want to test some more scenarios. + // eslint-disable-next-line @typescript-eslint/no-empty-function + test("existing user invited to an organization", () => {}); + + test("nonexisting user invited to a Team inside organization", async ({ + browser, + page, + users, + emails, + }) => { const orgOwner = await users.create(undefined, { hasTeam: true, isOrg: true, hasSubteam: true }); await orgOwner.apiLogin(); const { team } = await orgOwner.getFirstTeamMembership(); @@ -183,7 +196,7 @@ test.describe("Organization", () => { }); test.describe("Email matching orgAutoAcceptEmail and a Verified Organization", () => { - test("Org Invitation", async ({ browser, page, users, emails }) => { + test("nonexisting user is invited to Org", async ({ browser, page, users, emails }) => { const orgOwner = await users.create(undefined, { hasTeam: true, isOrg: true, isOrgVerified: true }); const { team: org } = await orgOwner.getOrgMembership(); await orgOwner.apiLogin(); @@ -248,7 +261,52 @@ test.describe("Organization", () => { }); }); - test("Team Invitation", async ({ browser, page, users, emails }) => { + // Such a user has user.username changed directly in addition to having the new username in the profile.username + test("existing user migrated to an organization", async ({ users, page, emails }) => { + const orgOwner = await users.create(undefined, { hasTeam: true, isOrg: true, isOrgVerified: true }); + const { team: org } = await orgOwner.getOrgMembership(); + await orgOwner.apiLogin(); + const { existingUser } = await test.step("Invite an existing user to an organization", async () => { + const existingUser = await users.create({ + username: "john", + emailDomain: org.organizationSettings?.orgAutoAcceptEmail ?? "", + name: "John Outside Organization", + }); + + await moveUserToOrg({ + user: existingUser, + targetOrg: { + username: `${existingUser.username}-org`, + id: org.id, + membership: { + role: MembershipRole.MEMBER, + accepted: true, + }, + }, + shouldMoveTeams: false, + }); + return { existingUser }; + }); + + await test.step("Signing up with the previous username of the migrated user - shouldn't be allowed", async () => { + await page.goto("/signup"); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await page.locator('input[name="username"]').fill(existingUser.username!); + await page + .locator('input[name="email"]') + .fill(`${existingUser.username}-differnet-email@example.com`); + await page.locator('input[name="password"]').fill("Password99!"); + await page.waitForLoadState("networkidle"); + await expect(page.locator('button[type="submit"]')).toBeDisabled(); + }); + }); + + test("nonexisting user is invited to a team inside organization", async ({ + browser, + page, + users, + emails, + }) => { const orgOwner = await users.create(undefined, { hasTeam: true, isOrg: true, diff --git a/packages/features/auth/signup/handlers/calcomHandler.ts b/packages/features/auth/signup/handlers/calcomHandler.ts index 6104c71660..8cec77bde4 100644 --- a/packages/features/auth/signup/handlers/calcomHandler.ts +++ b/packages/features/auth/signup/handlers/calcomHandler.ts @@ -8,6 +8,7 @@ import { createOrUpdateMemberships } from "@calcom/features/auth/signup/utils/cr import { WEBAPP_URL } from "@calcom/lib/constants"; import { getLocaleFromRequest } from "@calcom/lib/getLocaleFromRequest"; import { HttpError } from "@calcom/lib/http-error"; +import logger from "@calcom/lib/logger"; import { usernameHandler, type RequestWithUsernameStatus } from "@calcom/lib/server/username"; import { createWebUser as syncServicesCreateWebUser } from "@calcom/lib/sync/SyncServiceManager"; import { closeComUpsertTeamUser } from "@calcom/lib/sync/SyncServiceManager"; @@ -23,6 +24,8 @@ import { validateAndGetCorrectedUsernameForTeam, } from "../utils/token"; +const log = logger.getSubLogger({ prefix: ["signupCalcomHandler"] }); + async function handler(req: RequestWithUsernameStatus, res: NextApiResponse) { const { email: _email, @@ -35,6 +38,9 @@ async function handler(req: RequestWithUsernameStatus, res: NextApiResponse) { token: true, }) .parse(req.body); + + log.debug("handler", { email: _email }); + let username: string | null = req.usernameStatus.requestedUserName; let checkoutSessionId: string | null = null; diff --git a/packages/features/auth/signup/handlers/selfHostedHandler.ts b/packages/features/auth/signup/handlers/selfHostedHandler.ts index 9fd6373496..d24b750044 100644 --- a/packages/features/auth/signup/handlers/selfHostedHandler.ts +++ b/packages/features/auth/signup/handlers/selfHostedHandler.ts @@ -6,6 +6,7 @@ import { sendEmailVerification } from "@calcom/features/auth/lib/verifyEmail"; import { createOrUpdateMemberships } from "@calcom/features/auth/signup/utils/createOrUpdateMemberships"; import { IS_PREMIUM_USERNAME_ENABLED } from "@calcom/lib/constants"; import logger from "@calcom/lib/logger"; +import { isUsernameReservedDueToMigration } from "@calcom/lib/server/username"; import slugify from "@calcom/lib/slugify"; import { closeComUpsertTeamUser } from "@calcom/lib/sync/SyncServiceManager"; import { validateAndGetCorrectedUsernameAndEmail } from "@calcom/lib/validateUsername"; @@ -78,7 +79,19 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) organizationSettings: true, }, }); + if (team) { + const isInviteForATeamInOrganization = !!team.parent; + const isCheckingUsernameInGlobalNamespace = !team.isOrganization && !isInviteForATeamInOrganization; + + if (isCheckingUsernameInGlobalNamespace) { + const isUsernameAvailable = !(await isUsernameReservedDueToMigration(correctedUsername)); + if (!isUsernameAvailable) { + res.status(409).json({ message: "A user exists with that username" }); + return; + } + } + const user = await prisma.user.upsert({ where: { email: userEmail }, update: { @@ -123,6 +136,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) }, }); } else { + const isUsernameAvailable = !(await isUsernameReservedDueToMigration(correctedUsername)); + if (!isUsernameAvailable) { + res.status(409).json({ message: "A user exists with that username" }); + return; + } if (IS_PREMIUM_USERNAME_ENABLED) { const checkUsername = await checkPremiumUsername(correctedUsername); if (checkUsername.premium) { diff --git a/packages/features/ee/teams/components/TeamListItem.tsx b/packages/features/ee/teams/components/TeamListItem.tsx index e66c8cadd6..f3beadc1ec 100644 --- a/packages/features/ee/teams/components/TeamListItem.tsx +++ b/packages/features/ee/teams/components/TeamListItem.tsx @@ -6,7 +6,6 @@ import InviteLinkSettingsModal from "@calcom/ee/teams/components/InviteLinkSetti import MemberInvitationModal from "@calcom/ee/teams/components/MemberInvitationModal"; import classNames from "@calcom/lib/classNames"; import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage"; -import { isOrganization } from "@calcom/lib/entityPermissionUtils"; import { getTeamUrlSync } from "@calcom/lib/getBookerUrl/client"; import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams"; import { useLocale } from "@calcom/lib/hooks/useLocale"; @@ -77,7 +76,7 @@ export default function TeamListItem(props: Props) { utils.viewer.teams.listInvites.invalidate(); const userOrganizationId = user?.profile?.organization?.id; const isSubTeamOfDifferentOrg = team.parentId ? team.parentId != userOrganizationId : false; - const isDifferentOrg = isOrganization({ team }) && team.id !== userOrganizationId; + const isDifferentOrg = team.isOrganization && team.id !== userOrganizationId; // If the user team being accepted is a sub-team of different organization or the different organization itself then page must be reloaded to let the session change reflect reliably everywhere. if (variables.accept && (isSubTeamOfDifferentOrg || isDifferentOrg)) { window.location.reload(); @@ -101,7 +100,7 @@ export default function TeamListItem(props: Props) { const { hideDropdown, setHideDropdown } = props; if (!team) return <>; - const teamUrl = isOrganization({ team }) + const teamUrl = team.isOrganization ? getTeamUrlSync({ orgSlug: team.slug, teamSlug: null }) : getTeamUrlSync({ orgSlug: team.parent ? team.parent.slug : null, teamSlug: team.slug }); const teamInfo = ( diff --git a/packages/features/ee/teams/components/TeamsListing.tsx b/packages/features/ee/teams/components/TeamsListing.tsx index b10fc62374..6565939568 100644 --- a/packages/features/ee/teams/components/TeamsListing.tsx +++ b/packages/features/ee/teams/components/TeamsListing.tsx @@ -2,7 +2,6 @@ import { useRouter } from "next/navigation"; import { useEffect, useMemo, useState } from "react"; import { APP_NAME, WEBAPP_URL } from "@calcom/lib/constants"; -import { isOrganization } from "@calcom/lib/entityPermissionUtils"; import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { trpc } from "@calcom/trpc/react"; @@ -46,14 +45,11 @@ export function TeamsListing() { }, }); - const teams = useMemo(() => data?.filter((m) => m.accepted && !isOrganization({ team: m })) || [], [data]); + const teams = useMemo(() => data?.filter((m) => m.accepted && !m.isOrganization) || [], [data]); - const teamInvites = useMemo( - () => data?.filter((m) => !m.accepted && !isOrganization({ team: m })) || [], - [data] - ); + const teamInvites = useMemo(() => data?.filter((m) => !m.accepted && !m.isOrganization) || [], [data]); - const organizationInvites = (data?.filter((m) => !m.accepted && isOrganization({ team: m })) || []).filter( + const organizationInvites = (data?.filter((m) => !m.accepted && m.isOrganization) || []).filter( (orgInvite) => { const isThereASubTeamOfTheOrganizationInInvites = teamInvites.find( (teamInvite) => teamInvite.parentId === orgInvite.id diff --git a/packages/features/ee/teams/lib/payments.ts b/packages/features/ee/teams/lib/payments.ts index e6f4dae9e2..ed543d2120 100644 --- a/packages/features/ee/teams/lib/payments.ts +++ b/packages/features/ee/teams/lib/payments.ts @@ -4,7 +4,6 @@ import { getStripeCustomerIdFromUserId } from "@calcom/app-store/stripepayment/l import stripe from "@calcom/app-store/stripepayment/lib/server"; import { WEBAPP_URL } from "@calcom/lib/constants"; import { ORGANIZATION_MIN_SEATS } from "@calcom/lib/constants"; -import { isOrganization } from "@calcom/lib/entityPermissionUtils"; import prisma from "@calcom/prisma"; import { teamMetadataSchema } from "@calcom/prisma/zod-utils"; @@ -157,7 +156,7 @@ export const updateQuantitySubscriptionFromStripe = async (teamId: number) => { )?.quantity; if (!subscriptionQuantity) throw new Error("Subscription not found"); - if (!!isOrganization({ team }) && membershipCount < ORGANIZATION_MIN_SEATS) { + if (team.isOrganization && membershipCount < ORGANIZATION_MIN_SEATS) { console.info( `Org ${teamId} has less members than the min ${ORGANIZATION_MIN_SEATS}, skipping updating subscription.` ); diff --git a/packages/lib/entityPermissionUtils.ts b/packages/lib/entityPermissionUtils.ts index d08dce51dd..6031c0a50b 100644 --- a/packages/lib/entityPermissionUtils.ts +++ b/packages/lib/entityPermissionUtils.ts @@ -1,4 +1,4 @@ -import type { Membership, Team } from "@calcom/prisma/client"; +import type { Membership } from "@calcom/prisma/client"; import { MembershipRole } from "@calcom/prisma/enums"; export const enum ENTITY_PERMISSION_LEVEL { @@ -19,10 +19,6 @@ export function canEditEntity( ); } -export function isOrganization({ team }: { team: Pick }) { - return team.isOrganization; -} - export function getEntityPermissionLevel( entity: { userId: number | null; diff --git a/packages/lib/server/checkRegularUsername.ts b/packages/lib/server/checkRegularUsername.ts index e039091268..964c8c43bf 100644 --- a/packages/lib/server/checkRegularUsername.ts +++ b/packages/lib/server/checkRegularUsername.ts @@ -1,8 +1,10 @@ import slugify from "@calcom/lib/slugify"; import { ProfileRepository } from "./repository/profile"; +import { isUsernameReservedDueToMigration } from "./username"; export async function checkRegularUsername(_username: string, currentOrgDomain?: string | null) { + const isCheckingUsernameInGlobalNamespace = !currentOrgDomain; const username = slugify(_username); const premium = !!process.env.NEXT_PUBLIC_IS_E2E && username.length < 5; @@ -23,8 +25,13 @@ export async function checkRegularUsername(_username: string, currentOrgDomain?: message: "A user exists with that username", }; } + + const isUsernameAvailable = isCheckingUsernameInGlobalNamespace + ? !(await isUsernameReservedDueToMigration(username)) + : true; + return { - available: true as const, + available: isUsernameAvailable, premium, }; } diff --git a/packages/lib/server/checkUsername.ts b/packages/lib/server/checkUsername.ts index 6c1c31f1c5..1d57927f1e 100644 --- a/packages/lib/server/checkUsername.ts +++ b/packages/lib/server/checkUsername.ts @@ -3,4 +3,6 @@ import { IS_PREMIUM_USERNAME_ENABLED } from "@calcom/lib/constants"; import { checkRegularUsername } from "./checkRegularUsername"; import { usernameCheck as checkPremiumUsername } from "./username"; +// TODO: Replace `lib/checkPremiumUsername` with `usernameCheck` and then import checkPremiumUsername directly here. +// We want to remove dependency on website for signup stuff as signup is now part of app. export const checkUsername = !IS_PREMIUM_USERNAME_ENABLED ? checkRegularUsername : checkPremiumUsername; diff --git a/packages/lib/server/repository/user.ts b/packages/lib/server/repository/user.ts index 1864439d0f..5f2ed07f4a 100644 --- a/packages/lib/server/repository/user.ts +++ b/packages/lib/server/repository/user.ts @@ -4,7 +4,6 @@ import { Prisma } from "@calcom/prisma/client"; import type { User as UserType } from "@calcom/prisma/client"; import type { UpId, UserProfile } from "@calcom/types/UserProfile"; -import { isOrganization } from "../../entityPermissionUtils"; import logger from "../../logger"; import { safeStringify } from "../../safeStringify"; import { ProfileRepository } from "./profile"; @@ -92,8 +91,8 @@ export class UserRepository { userId, }); - const acceptedOrgMemberships = acceptedTeamMemberships.filter((membership) => - isOrganization({ team: membership.team }) + const acceptedOrgMemberships = acceptedTeamMemberships.filter( + (membership) => membership.team.isOrganization ); const organizations = acceptedOrgMemberships.map((membership) => membership.team); diff --git a/packages/lib/server/username.ts b/packages/lib/server/username.ts index c8bd09df24..b4a01bc580 100644 --- a/packages/lib/server/username.ts +++ b/packages/lib/server/username.ts @@ -2,10 +2,13 @@ import type { NextApiRequest, NextApiResponse } from "next"; import slugify from "@calcom/lib/slugify"; import prisma from "@calcom/prisma"; +import { RedirectType } from "@calcom/prisma/enums"; import { IS_PREMIUM_USERNAME_ENABLED } from "../constants"; +import logger from "../logger"; import notEmpty from "../notEmpty"; +const log = logger.getSubLogger({ prefix: ["server/username"] }); const cachedData: Set = new Set(); export type RequestWithUsernameStatus = NextApiRequest & { @@ -106,7 +109,9 @@ const usernameHandler = return handler(req, res); }; -const usernameCheck = async (usernameRaw: string) => { +const usernameCheck = async (usernameRaw: string, currentOrgDomain?: string | null) => { + log.debug("usernameCheck", { usernameRaw, currentOrgDomain }); + const isCheckingUsernameInGlobalNamespace = !currentOrgDomain; const response = { available: true, premium: false, @@ -129,6 +134,10 @@ const usernameCheck = async (usernameRaw: string) => { if (user) { response.available = false; + } else { + response.available = isCheckingUsernameInGlobalNamespace + ? !(await isUsernameReservedDueToMigration(username)) + : true; } if (await isPremiumUserName(username)) { @@ -158,6 +167,20 @@ const usernameCheck = async (usernameRaw: string) => { return response; }; +/** + * Should be used when in global namespace(i.e. outside of an organization) + */ +export const isUsernameReservedDueToMigration = async (username: string) => + !!(await prisma.tempOrgRedirect.findUnique({ + where: { + from_type_fromOrgId: { + type: RedirectType.User, + from: username, + fromOrgId: 0, + }, + }, + })); + /** * It is a bit different from usernameCheck because it also check if the user signing up is the same user that has a pending invitation to organization * So, it uses email to uniquely identify the user and then also checks if the username requested by that user is available for taking or not. @@ -207,12 +230,13 @@ const usernameCheckForSignup = async ({ const isClaimingAlreadySetUsername = user.username === username; const isClaimingUnsetUsername = !user.username; response.available = isClaimingUnsetUsername || isClaimingAlreadySetUsername; - // There are no premium users outside an organization only + // There are premium users outside an organization only response.premium = await isPremiumUserName(username); } + // If user isn't found, it's a direct signup and that can't be of an organization } else { - // If user isn't found, it's a new signup and that can't be of an organization response.premium = await isPremiumUserName(username); + response.available = !(await isUsernameReservedDueToMigration(username)); } // get list of similar usernames in the db diff --git a/packages/trpc/server/routers/viewer/eventTypes/getByViewer.handler.ts b/packages/trpc/server/routers/viewer/eventTypes/getByViewer.handler.ts index 0e4fea76bd..eff78baadb 100644 --- a/packages/trpc/server/routers/viewer/eventTypes/getByViewer.handler.ts +++ b/packages/trpc/server/routers/viewer/eventTypes/getByViewer.handler.ts @@ -3,7 +3,6 @@ import { orderBy } from "lodash"; import { hasFilter } from "@calcom/features/filters/lib/hasFilter"; import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowError"; -import { isOrganization } from "@calcom/lib/entityPermissionUtils"; import { getOrgAvatarUrl, getTeamAvatarUrl, getUserAvatarUrl } from "@calcom/lib/getAvatarUrl"; import { getBookerBaseUrlSync } from "@calcom/lib/getBookerUrl/client"; import { getBookerBaseUrl } from "@calcom/lib/getBookerUrl/server"; @@ -214,7 +213,7 @@ export const getByViewerHandler = async ({ ctx, input }: GetByViewerOptions) => await Promise.all( memberships .filter((mmship) => { - if (isOrganization({ team: mmship.team })) { + if (mmship.team.isOrganization) { return false; } else { if (!input?.filters || !hasFilter(input?.filters)) { diff --git a/packages/trpc/server/routers/viewer/teams/acceptOrLeave.handler.ts b/packages/trpc/server/routers/viewer/teams/acceptOrLeave.handler.ts index c15af40206..963c53f5b6 100644 --- a/packages/trpc/server/routers/viewer/teams/acceptOrLeave.handler.ts +++ b/packages/trpc/server/routers/viewer/teams/acceptOrLeave.handler.ts @@ -1,5 +1,4 @@ import { createAProfileForAnExistingUser } from "@calcom/lib/createAProfileForAnExistingUser"; -import { isOrganization } from "@calcom/lib/entityPermissionUtils"; import { updateNewTeamMemberEventTypes } from "@calcom/lib/server/queries"; import { closeComUpsertTeamUser } from "@calcom/lib/sync/SyncServiceManager"; import { prisma } from "@calcom/prisma"; @@ -45,9 +44,8 @@ export const acceptOrLeaveHandler = async ({ ctx, input }: AcceptOrLeaveOptions) }); } - const isAnOrganization = isOrganization({ team }); const isASubteam = team.parentId !== null; - const idOfOrganizationInContext = isAnOrganization ? team.id : isASubteam ? team.parentId : null; + const idOfOrganizationInContext = team.isOrganization ? team.id : isASubteam ? team.parentId : null; const needProfileUpdate = !!idOfOrganizationInContext; if (needProfileUpdate) { await createAProfileForAnExistingUser({ diff --git a/packages/trpc/server/routers/viewer/teams/inviteMember/inviteMember.handler.ts b/packages/trpc/server/routers/viewer/teams/inviteMember/inviteMember.handler.ts index f6f3a48ed6..5b991dd958 100644 --- a/packages/trpc/server/routers/viewer/teams/inviteMember/inviteMember.handler.ts +++ b/packages/trpc/server/routers/viewer/teams/inviteMember/inviteMember.handler.ts @@ -2,7 +2,6 @@ import { updateQuantitySubscriptionFromStripe } from "@calcom/features/ee/teams/ import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowError"; import { IS_TEAM_BILLING_ENABLED } from "@calcom/lib/constants"; import { createAProfileForAnExistingUser } from "@calcom/lib/createAProfileForAnExistingUser"; -import { isOrganization } from "@calcom/lib/entityPermissionUtils"; import logger from "@calcom/lib/logger"; import { safeStringify } from "@calcom/lib/safeStringify"; import { getTranslation } from "@calcom/lib/server/i18n"; @@ -174,7 +173,7 @@ async function handleExistingUsersInvites({ } const translation = await getTranslation(input.language ?? "en", "common"); - if (!isOrganization({ team })) { + if (!team.isOrganization) { const [autoJoinUsers, regularUsers] = groupUsersByJoinability({ existingUsersWithMembersips, team, diff --git a/packages/trpc/server/routers/viewer/teams/inviteMember/utils.ts b/packages/trpc/server/routers/viewer/teams/inviteMember/utils.ts index be6b047ce4..dc3adb8025 100644 --- a/packages/trpc/server/routers/viewer/teams/inviteMember/utils.ts +++ b/packages/trpc/server/routers/viewer/teams/inviteMember/utils.ts @@ -3,7 +3,6 @@ import type { TFunction } from "next-i18next"; import { sendTeamInviteEmail } from "@calcom/emails"; import { ENABLE_PROFILE_SWITCHER, WEBAPP_URL } from "@calcom/lib/constants"; -import { isOrganization } from "@calcom/lib/entityPermissionUtils"; import logger from "@calcom/lib/logger"; import { safeStringify } from "@calcom/lib/safeStringify"; import { isTeamAdmin } from "@calcom/lib/server/queries"; @@ -452,7 +451,7 @@ export function getAutoJoinStatus({ invitee: UserWithMembership; connectionInfoMap: Record>; }) { - const isRegularTeam = !isOrganization({ team }) && !team.parentId; + const isRegularTeam = !team.isOrganization && !team.parentId; if (isRegularTeam) { // There are no-auto join in regular teams ever