Skip to content
This repository has been archived by the owner on Jan 23, 2024. It is now read-only.

Commit

Permalink
feat: Support moving a user and it's teams to an org as temporary app…
Browse files Browse the repository at this point in the history
…roach (calcom#11892)
  • Loading branch information
hariombalhara authored Oct 17, 2023
1 parent d46e80c commit 225055f
Show file tree
Hide file tree
Showing 9 changed files with 166 additions and 20 deletions.
44 changes: 44 additions & 0 deletions apps/web/lib/getTemporaryOrgRedirect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import logger from "@calcom/lib/logger";
import { safeStringify } from "@calcom/lib/safeStringify";
import type { RedirectType } from "@calcom/prisma/client";

const log = logger.getChildLogger({ prefix: ["lib", "getTemporaryOrgRedirect"] });
export const getTemporaryOrgRedirect = async ({
slug,
redirectType,
eventTypeSlug,
}: {
slug: string;
redirectType: RedirectType;
eventTypeSlug: string | null;
}) => {
const prisma = (await import("@calcom/prisma")).default;
log.debug(
`Looking for redirect for`,
safeStringify({
slug,
redirectType,
eventTypeSlug,
})
);
const redirect = await prisma.tempOrgRedirect.findUnique({
where: {
from_type_fromOrgId: {
type: redirectType,
from: slug,
fromOrgId: 0,
},
},
});

if (redirect) {
log.debug(`Redirecting ${slug} to ${redirect.toUrl}`);
return {
redirect: {
permanent: false,
destination: eventTypeSlug ? `${redirect.toUrl}/${eventTypeSlug}` : redirect.toUrl,
},
} as const;
}
return null;
};
20 changes: 18 additions & 2 deletions apps/web/pages/[user].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import useTheme from "@calcom/lib/hooks/useTheme";
import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
import { stripMarkdown } from "@calcom/lib/stripMarkdown";
import prisma from "@calcom/prisma";
import type { EventType, User } from "@calcom/prisma/client";
import { RedirectType, type EventType, type User } from "@calcom/prisma/client";
import { baseEventTypeSelect } from "@calcom/prisma/selects";
import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
import { HeadSeo, UnpublishedEntity } from "@calcom/ui";
Expand All @@ -35,6 +35,8 @@ import PageWrapper from "@components/PageWrapper";

import { ssrInit } from "@server/lib/ssr";

import { getTemporaryOrgRedirect } from "../lib/getTemporaryOrgRedirect";

export function UserPage(props: InferGetServerSidePropsType<typeof getServerSideProps>) {
const { users, profile, eventTypes, markdownStrippedBio, entity } = props;

Expand Down Expand Up @@ -261,20 +263,22 @@ export const getServerSideProps: GetServerSideProps<UserPageProps> = async (cont
context.params?.orgSlug
);
const usernameList = getUsernameList(context.query.user as string);
const isOrgContext = isValidOrgDomain && currentOrgDomain;
const dataFetchStart = Date.now();
const usersWithoutAvatar = await prisma.user.findMany({
where: {
username: {
in: usernameList,
},
organization: isValidOrgDomain && currentOrgDomain ? getSlugOrRequestedSlug(currentOrgDomain) : null,
organization: isOrgContext ? getSlugOrRequestedSlug(currentOrgDomain) : null,
},
select: {
id: true,
username: true,
email: true,
name: true,
bio: true,
metadata: true,
brandColor: true,
darkBrandColor: true,
organizationId: true,
Expand Down Expand Up @@ -312,6 +316,18 @@ export const getServerSideProps: GetServerSideProps<UserPageProps> = async (cont
avatar: `/${user.username}/avatar.png`,
}));

if (!isOrgContext) {
const redirect = await getTemporaryOrgRedirect({
slug: usernameList[0],
redirectType: RedirectType.User,
eventTypeSlug: null,
});

if (redirect) {
return redirect;
}
}

if (!users.length || (!isValidOrgDomain && !users.some((user) => user.organizationId === null))) {
return {
notFound: true,
Expand Down
25 changes: 21 additions & 4 deletions apps/web/pages/[user]/[type].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,15 @@ import { orgDomainConfig, userOrgQuery } from "@calcom/features/ee/organizations
import { getUsernameList } from "@calcom/lib/defaultEvents";
import slugify from "@calcom/lib/slugify";
import prisma from "@calcom/prisma";
import { RedirectType } from "@calcom/prisma/client";

import type { inferSSRProps } from "@lib/types/inferSSRProps";
import type { EmbedProps } from "@lib/withEmbedSsr";

import PageWrapper from "@components/PageWrapper";

import { getTemporaryOrgRedirect } from "../../lib/getTemporaryOrgRedirect";

export type PageProps = inferSSRProps<typeof getServerSideProps> & EmbedProps;

export default function Type({
Expand Down Expand Up @@ -93,7 +96,7 @@ async function getDynamicGroupPageProps(context: GetServerSidePropsContext) {
if (!users.length) {
return {
notFound: true,
};
} as const;
}
const org = isValidOrgDomain ? currentOrgDomain : null;

Expand All @@ -115,7 +118,7 @@ async function getDynamicGroupPageProps(context: GetServerSidePropsContext) {
if (!eventData) {
return {
notFound: true,
};
} as const;
}

return {
Expand Down Expand Up @@ -150,6 +153,20 @@ async function getUserPageProps(context: GetServerSidePropsContext) {
context.params?.orgSlug
);

const isOrgContext = currentOrgDomain && isValidOrgDomain;

if (!isOrgContext) {
const redirect = await getTemporaryOrgRedirect({
slug: usernames[0],
redirectType: RedirectType.User,
eventTypeSlug: slug,
});

if (redirect) {
return redirect;
}
}

const { ssrInit } = await import("@server/lib/ssr");
const ssr = await ssrInit(context);
const user = await prisma.user.findFirst({
Expand All @@ -167,7 +184,7 @@ async function getUserPageProps(context: GetServerSidePropsContext) {
if (!user) {
return {
notFound: true,
};
} as const;
}

let booking: GetBookingType | null = null;
Expand All @@ -189,7 +206,7 @@ async function getUserPageProps(context: GetServerSidePropsContext) {
if (!eventData) {
return {
notFound: true,
};
} as const;
}

return {
Expand Down
16 changes: 2 additions & 14 deletions apps/web/pages/org/[orgSlug]/[user]/[type]/embed.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,7 @@
import type { GetServerSidePropsContext } from "next";
import withEmbedSsr from "@lib/withEmbedSsr";

import { getServerSideProps as _getServerSideProps } from "../[type]";

export { default } from "../[type]";

export const getServerSideProps = async (context: GetServerSidePropsContext) => {
const ssrResponse = await _getServerSideProps(context);
if (ssrResponse.notFound) {
return ssrResponse;
}
return {
...ssrResponse,
props: {
...ssrResponse.props,
isEmbed: true,
},
};
};
export const getServerSideProps = withEmbedSsr(_getServerSideProps);
18 changes: 18 additions & 0 deletions apps/web/pages/team/[slug].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import slugify from "@calcom/lib/slugify";
import { stripMarkdown } from "@calcom/lib/stripMarkdown";
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
import prisma from "@calcom/prisma";
import { RedirectType } from "@calcom/prisma/client";
import { teamMetadataSchema } from "@calcom/prisma/zod-utils";
import { Avatar, AvatarGroup, Button, HeadSeo, UnpublishedEntity } from "@calcom/ui";
import { ArrowRight } from "@calcom/ui/components/icon";
Expand All @@ -30,6 +31,8 @@ import Team from "@components/team/screens/Team";

import { ssrInit } from "@server/lib/ssr";

import { getTemporaryOrgRedirect } from "../../lib/getTemporaryOrgRedirect";

export type PageProps = inferSSRProps<typeof getServerSideProps>;

function TeamPage({
Expand Down Expand Up @@ -272,13 +275,28 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
context.req.headers.host ?? "",
context.params?.orgSlug
);
const isOrgContext = isValidOrgDomain && currentOrgDomain;

const flags = await getFeatureFlagMap(prisma);
const team = await getTeamWithMembers({
slug: slugify(slug ?? ""),
orgSlug: currentOrgDomain,
isTeamView: true,
isOrgView: isValidOrgDomain && context.resolvedUrl === "/",
});

if (!isOrgContext && slug) {
const redirect = await getTemporaryOrgRedirect({
slug: slug,
redirectType: RedirectType.Team,
eventTypeSlug: null,
});

if (redirect) {
return redirect;
}
}

const ssr = await ssrInit(context);
const metadata = teamMetadataSchema.parse(team?.metadata ?? {});
console.info("gSSP, team/[slug] - ", {
Expand Down
16 changes: 16 additions & 0 deletions apps/web/pages/team/[slug]/[type].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@ import { getSlugOrRequestedSlug } from "@calcom/features/ee/organizations/lib/or
import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
import slugify from "@calcom/lib/slugify";
import prisma from "@calcom/prisma";
import { RedirectType } from "@calcom/prisma/client";

import type { inferSSRProps } from "@lib/types/inferSSRProps";
import type { EmbedProps } from "@lib/withEmbedSsr";

import PageWrapper from "@components/PageWrapper";

import { getTemporaryOrgRedirect } from "../../../lib/getTemporaryOrgRedirect";

export type PageProps = inferSSRProps<typeof getServerSideProps> & EmbedProps;

export default function Type({
Expand Down Expand Up @@ -75,6 +78,19 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
context.req.headers.host ?? "",
context.params?.orgSlug
);
const isOrgContext = currentOrgDomain && isValidOrgDomain;

if (!isOrgContext) {
const redirect = await getTemporaryOrgRedirect({
slug: teamSlug,
redirectType: RedirectType.Team,
eventTypeSlug: meetingSlug,
});

if (redirect) {
return redirect;
}
}

const team = await prisma.team.findFirst({
where: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
-- CreateEnum
CREATE TYPE "RedirectType" AS ENUM ('user-event-type', 'team-event-type', 'user', 'team');

-- CreateTable
CREATE TABLE "TempOrgRedirect" (
"id" SERIAL NOT NULL,
"from" TEXT NOT NULL,
"fromOrgId" INTEGER NOT NULL,
"type" "RedirectType" NOT NULL,
"toUrl" TEXT NOT NULL,
"enabled" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,

CONSTRAINT "TempOrgRedirect_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE UNIQUE INDEX "TempOrgRedirect_from_type_fromOrgId_key" ON "TempOrgRedirect"("from", "type", "fromOrgId");
21 changes: 21 additions & 0 deletions packages/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -972,3 +972,24 @@ model CalendarCache {
@@id([credentialId, key])
@@unique([credentialId, key])
}

enum RedirectType {
UserEventType @map("user-event-type")
TeamEventType @map("team-event-type")
User @map("user")
Team @map("team")
}

model TempOrgRedirect {
id Int @id @default(autoincrement())
// Better would be to have fromOrgId and toOrgId as well and then we should have just to instead toUrl
from String
// 0 would mean it is non org
fromOrgId Int
type RedirectType
toUrl String
enabled Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([from, type, fromOrgId])
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ export const listHandler = async ({ ctx }: ListHandlerInput) => {
},
});

if (!membership) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "You do not have a membership to your organization",
});
}

const metadata = teamMetadataSchema.parse(membership?.team.metadata);

return {
Expand Down

0 comments on commit 225055f

Please sign in to comment.