From abc5ff2d97d26baabd568573fe31b13b8647d775 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Omar=20L=C3=B3pez?= Date: Fri, 11 Nov 2022 12:54:44 -0700 Subject: [PATCH] Deprecated API endpoints cleanup (#5478) * Deprecated API endpoints cleanup * Linting Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- apps/web/calendso.yaml | 119 -------------- .../DisconnectStripeIntegration.tsx | 69 -------- apps/web/pages/api/_README/README.md | 7 + apps/web/pages/api/import/calendly.ts | 85 ---------- apps/web/pages/api/import/savvycal.ts | 85 ---------- apps/web/pages/api/integrations.ts | 154 ------------------ apps/web/pages/api/intent-username/index.ts | 48 ------ apps/web/pages/api/me.ts | 45 ----- apps/web/pages/api/schedule/index.ts | 55 ------- apps/web/pages/api/teams/[team]/index.ts | 60 ------- apps/web/pages/api/teams/[team]/invite.ts | 132 --------------- apps/web/pages/api/teams/[team]/membership.ts | 86 ---------- apps/web/pages/api/teams/[team]/profile.ts | 71 -------- apps/web/pages/api/upgrade.ts | 54 ------ apps/web/pages/api/user/[id].ts | 86 ---------- apps/web/pages/api/user/me.ts | 50 ------ apps/web/pages/api/user/membership.ts | 63 ------- apps/web/pages/api/user/profile.ts | 50 ------ apps/web/pages/api/v2-opt-in.ts | 28 ---- apps/web/pages/settings/billing/index.tsx | 10 -- .../larkcalendar/lib/CalendarService.ts | 13 +- .../ee/common/components/TrialBanner.tsx | 39 ----- packages/ui/UpgradeToProDialog.tsx | 54 ------ 23 files changed, 9 insertions(+), 1454 deletions(-) delete mode 100644 apps/web/components/integrations/DisconnectStripeIntegration.tsx create mode 100644 apps/web/pages/api/_README/README.md delete mode 100644 apps/web/pages/api/import/calendly.ts delete mode 100644 apps/web/pages/api/import/savvycal.ts delete mode 100644 apps/web/pages/api/integrations.ts delete mode 100644 apps/web/pages/api/intent-username/index.ts delete mode 100644 apps/web/pages/api/me.ts delete mode 100644 apps/web/pages/api/schedule/index.ts delete mode 100644 apps/web/pages/api/teams/[team]/index.ts delete mode 100644 apps/web/pages/api/teams/[team]/invite.ts delete mode 100644 apps/web/pages/api/teams/[team]/membership.ts delete mode 100644 apps/web/pages/api/teams/[team]/profile.ts delete mode 100644 apps/web/pages/api/upgrade.ts delete mode 100644 apps/web/pages/api/user/[id].ts delete mode 100644 apps/web/pages/api/user/me.ts delete mode 100644 apps/web/pages/api/user/membership.ts delete mode 100644 apps/web/pages/api/user/profile.ts delete mode 100644 apps/web/pages/api/v2-opt-in.ts delete mode 100644 packages/features/ee/common/components/TrialBanner.tsx delete mode 100644 packages/ui/UpgradeToProDialog.tsx diff --git a/apps/web/calendso.yaml b/apps/web/calendso.yaml index abd7957373..09da00cfe3 100644 --- a/apps/web/calendso.yaml +++ b/apps/web/calendso.yaml @@ -598,60 +598,6 @@ paths: summary: Gets and stores the OAuth token tags: - Integrations - /api/user/profile: - patch: - description: Updates a user's profile. - summary: Updates a user's profile - tags: - - User - requestBody: - content: - application/json: - schema: - type: object - properties: - name: - type: string - description: - type: string - avatar: - type: string - timeZone: - type: string - weekStart: - type: string - hideBranding: - type: string - theme: - type: string - responses: - "200": - description: OK - content: - application/json: - schema: - type: object - properties: - message: - type: string - "401": - description: Unauthorized - content: - application/json: - schema: - type: object - properties: - message: - type: string - "500": - description: Internal Server Error - content: - application/json: - schema: - type: object - properties: - message: - type: string /api/me: get: description: Gets current user's profile. @@ -686,71 +632,6 @@ paths: properties: message: type: string - /api/user/membership: - get: - description: Get a list of the teams the user has joined. - summary: Get the teams a user is joined to - tags: - - User - requestBody: - content: - application/json: - schema: - type: object - responses: - "200": - description: OK - content: - application/json: - schema: - type: object - properties: - message: - type: string - "401": - description: Unauthorized - content: - application/json: - schema: - type: object - properties: - message: - type: string - "500": - description: Internal Server Error - content: - application/json: - schema: - type: object - properties: - message: - type: string - patch: - description: Accept team invitation - summary: Accept team invitation. - tags: - - User - requestBody: - content: - application/json: - schema: - type: object - properties: - teamId: - type: string - delete: - description: Leave team or decline membership invite of current user - summary: Leave team or decline team invite. - tags: - - User - requestBody: - content: - application/json: - schema: - type: object - properties: - teamId: - type: string "/api/{team}": delete: description: Deletes a team diff --git a/apps/web/components/integrations/DisconnectStripeIntegration.tsx b/apps/web/components/integrations/DisconnectStripeIntegration.tsx deleted file mode 100644 index 734229bd2c..0000000000 --- a/apps/web/components/integrations/DisconnectStripeIntegration.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { useMutation } from "@tanstack/react-query"; -import { useState } from "react"; - -import { ButtonBaseProps } from "@calcom/ui/Button"; -import { Dialog } from "@calcom/ui/Dialog"; -import showToast from "@calcom/ui/v2/core/notifications"; - -import DeleteStripeDialogContent from "@components/dialog/DeleteStripeDialogContent"; - -export default function DisconnectStripeIntegration(props: { - /** Integration credential id */ - id: number; - render: (renderProps: ButtonBaseProps) => JSX.Element; - onOpenChange: (isOpen: boolean) => unknown | Promise; -}) { - const [modalOpen, setModalOpen] = useState(false); - const mutation = useMutation( - async (action: string) => { - const res = await fetch("/api/integrations", { - method: "DELETE", - body: JSON.stringify({ id: props.id, action }), - headers: { - "Content-Type": "application/json", - }, - }); - if (!res.ok) { - throw new Error("Something went wrong"); - } - return res.json(); - }, - { - async onSettled() { - await props.onOpenChange(modalOpen); - }, - onSuccess(data) { - showToast(data.message, "success"); - setModalOpen(false); - }, - } - ); - return ( - <> - - { - mutation.mutate("cancel"); - }} - onRemove={() => { - mutation.mutate("remove"); - }}> - If you have unpaid and unconfirmed bookings, you must choose to cancel them or remove the required - payment field. - - - {props.render({ - onClick() { - setModalOpen(true); - }, - disabled: modalOpen, - loading: mutation.isLoading, - })} - - ); -} diff --git a/apps/web/pages/api/_README/README.md b/apps/web/pages/api/_README/README.md new file mode 100644 index 0000000000..0db337431d --- /dev/null +++ b/apps/web/pages/api/_README/README.md @@ -0,0 +1,7 @@ +# Before adding new endpoints here + +We're deprecating adding new endpoints in favor of creating tRPC procedures. + +You can learn about [tRPC procedures in the docs](https://trpc.io/docs/v10/procedures). + +You can see our current tRPC procedures in this file `packages/trpc/server/routers/_app.ts` diff --git a/apps/web/pages/api/import/calendly.ts b/apps/web/pages/api/import/calendly.ts deleted file mode 100644 index aec448880a..0000000000 --- a/apps/web/pages/api/import/calendly.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { PrismaClient } from "@prisma/client"; -import type { NextApiRequest, NextApiResponse } from "next"; - -import { getSession } from "@lib/auth"; - -const prisma = new PrismaClient(); - -type CalendlyEventType = { - name: string; - slug: string; - duration: number; - description_plain: string; - secret: boolean; -}; - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - const session = await getSession({ req }); - const authenticatedUser = await prisma.user.findFirstOrThrow({ - where: { - id: session?.user.id, - }, - select: { - id: true, - }, - }); - if (req.method == "POST") { - const userResult = await fetch("https://api.calendly.com/users/me", { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + req.body.token, - }, - }); - - if (userResult.status == 200) { - const userData = await userResult.json(); - - await prisma.user.update({ - where: { - id: authenticatedUser.id, - }, - data: { - name: userData.resource.name, - }, - }); - - const eventTypesResult = await fetch( - "https://api.calendly.com/event_types?user=" + userData.resource.uri, - { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + req.body.token, - }, - } - ); - - const eventTypesData = await eventTypesResult.json(); - - eventTypesData.collection.forEach(async (eventType: CalendlyEventType) => { - await prisma.eventType.create({ - data: { - title: eventType.name, - slug: eventType.slug, - length: eventType.duration, - description: eventType.description_plain, - hidden: eventType.secret, - users: { - connect: { - id: authenticatedUser.id, - }, - }, - userId: authenticatedUser.id, - }, - }); - }); - - res.status(201).end(); - } else { - res.status(500).end(); - } - } else { - res.status(405).end(); - } -} diff --git a/apps/web/pages/api/import/savvycal.ts b/apps/web/pages/api/import/savvycal.ts deleted file mode 100644 index 775c95060b..0000000000 --- a/apps/web/pages/api/import/savvycal.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { PrismaClient } from "@prisma/client"; -import type { NextApiRequest, NextApiResponse } from "next"; - -import { getSession } from "@lib/auth"; - -const prisma = new PrismaClient(); - -type SavvyCalEventType = { - name: string; - slug: string; - durations: [number]; - description: string; - state: "active"; -}; - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - const session = await getSession({ req }); - const authenticatedUser = await prisma.user.findFirstOrThrow({ - where: { - id: session?.user.id, - }, - select: { - id: true, - }, - }); - if (req.method === "POST") { - const userResult = await fetch("https://api.savvycal.com/v1/me", { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + req.body.token, - }, - }); - - if (userResult.status === 200) { - const userData = await userResult.json(); - - await prisma.user.update({ - where: { - id: authenticatedUser.id, - }, - data: { - name: userData.display_name, - timeZone: userData.time_zone, - weekStart: userData.first_day_of_week === 0 ? "Sunday" : "Monday", - avatar: userData.avatar_url, - }, - }); - - const eventTypesResult = await fetch("https://api.savvycal.com/v1/links?limit=100", { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: "Bearer " + req.body.token, - }, - }); - - const eventTypesData = await eventTypesResult.json(); - - eventTypesData.entries.forEach(async (eventType: SavvyCalEventType) => { - await prisma.eventType.create({ - data: { - title: eventType.name, - slug: eventType.slug, - length: eventType.durations[0], - description: eventType.description.replace(/<[^>]*>?/gm, ""), - hidden: eventType.state === "active", - users: { - connect: { - id: authenticatedUser.id, - }, - }, - userId: authenticatedUser.id, - }, - }); - }); - - res.status(201).end(); - } else { - res.status(500).end(); - } - } else { - res.status(405).end(); - } -} diff --git a/apps/web/pages/api/integrations.ts b/apps/web/pages/api/integrations.ts deleted file mode 100644 index c947688cc5..0000000000 --- a/apps/web/pages/api/integrations.ts +++ /dev/null @@ -1,154 +0,0 @@ -import { BookingStatus, Prisma } from "@prisma/client"; -import type { NextApiRequest, NextApiResponse } from "next"; - -import prisma from "@calcom/prisma"; - -import { getSession } from "@lib/auth"; - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - if (!["GET", "DELETE"].includes(req.method || "")) { - return res.status(405).end(); - } - - // Check that user is authenticated - const session = await getSession({ req }); - const userId = session?.user?.id; - - if (!userId) { - res.status(401).json({ message: "You must be logged in to do this" }); - return; - } - - if (req.method === "GET") { - const credentials = await prisma.credential.findMany({ - where: { - userId, - }, - select: { - type: true, - }, - }); - - res.status(200).json(credentials); - } - - if (req.method == "DELETE") { - const id = req.body.id; - const data: Prisma.UserUpdateInput = { - credentials: { - delete: { - id, - }, - }, - }; - const integration = await prisma.credential.findUnique({ - where: { - id, - }, - }); - /* If the user deletes a zapier integration, we delete all his api keys as well. */ - if (integration?.appId === "zapier") { - data.apiKeys = { - deleteMany: { - userId, - appId: "zapier", - }, - }; - /* We also delete all user's zapier wehbooks */ - data.webhooks = { - deleteMany: { - userId, - appId: "zapier", - }, - }; - } - - await prisma.user.update({ - where: { - id: userId, - }, - data, - }); - - if (req.body?.action === "cancel" || req.body?.action === "remove") { - try { - const bookingIdsWithPayments = await prisma.booking - .findMany({ - where: { - userId: session?.user?.id, - paid: false, - NOT: { - payment: { - every: { - booking: null, - }, - }, - }, - }, - select: { - id: true, - }, - }) - .then((bookings) => bookings.map((booking) => booking.id)); - const deletePayments = prisma.payment.deleteMany({ - where: { - bookingId: { - in: bookingIdsWithPayments, - }, - success: false, - }, - }); - - const updateBookings = prisma.booking.updateMany({ - where: { - id: { - in: bookingIdsWithPayments, - }, - }, - data: { - status: BookingStatus.CANCELLED, - rejectionReason: "Payment provider got removed", - }, - }); - - const bookingReferences = await prisma.booking - .findMany({ - where: { - status: BookingStatus.ACCEPTED, - }, - select: { - id: true, - }, - }) - .then((bookings) => bookings.map((booking) => booking.id)); - - const deleteBookingReferences = prisma.bookingReference.deleteMany({ - where: { - bookingId: { - in: bookingReferences, - }, - }, - }); - if (req.body?.action === "cancel") { - await prisma.$transaction([deletePayments, updateBookings, deleteBookingReferences]); - } else { - const updateBookings = prisma.booking.updateMany({ - where: { - id: { - in: bookingIdsWithPayments, - }, - }, - data: { - paid: true, - }, - }); - await prisma.$transaction([deletePayments, updateBookings]); - } - } catch (e) { - console.error(e); - res.status(500).json({ message: "Integration could not be deleted" }); - } - } - res.status(200).json({ message: "Integration deleted successfully" }); - } -} diff --git a/apps/web/pages/api/intent-username/index.ts b/apps/web/pages/api/intent-username/index.ts deleted file mode 100644 index b930d928e9..0000000000 --- a/apps/web/pages/api/intent-username/index.ts +++ /dev/null @@ -1,48 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from "next"; -import { getSession } from "next-auth/react"; - -import { defaultHandler } from "@calcom/lib/server"; -import { checkUsername } from "@calcom/lib/server/checkUsername"; -import prisma from "@calcom/prisma"; -import { userMetadata as zodUserMetadata } from "@calcom/prisma/zod-utils"; - -export async function getHandler(req: NextApiRequest, res: NextApiResponse) { - const { intentUsername } = req.body; - // Check that user is authenticated - try { - const session = await getSession({ req }); - const userId = session?.user?.id; - const user = await prisma.user.findFirstOrThrow({ - select: { - id: true, - metadata: true, - }, - where: { id: userId }, - }); - const checkPremiumUsernameResult = await checkUsername(intentUsername); - - if (userId && user) { - const userMetadata = zodUserMetadata.parse(user.metadata); - - await prisma.user.update({ - where: { - id: userId, - }, - data: { - metadata: { - ...userMetadata, - intentUsername, - isIntentPremium: checkPremiumUsernameResult.premium, - }, - }, - }); - } - } catch (error) { - res.status(501).send({ message: "intent-username.save.error" }); - } - res.end(); -} - -export default defaultHandler({ - GET: Promise.resolve({ default: getHandler }), -}); diff --git a/apps/web/pages/api/me.ts b/apps/web/pages/api/me.ts deleted file mode 100644 index f2f3628e8e..0000000000 --- a/apps/web/pages/api/me.ts +++ /dev/null @@ -1,45 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from "next"; - -import prisma from "@calcom/prisma"; - -import { getSession } from "@lib/auth"; -import { defaultAvatarSrc } from "@lib/profile"; - -/** - * @deprecated Use TRCP's viewer.me query - */ -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - const session = await getSession({ req }); - if (!session) { - res.status(401).json({ message: "Not authenticated" }); - return; - } - - const user = await prisma.user.findUniqueOrThrow({ - where: { - id: session.user.id, - }, - select: { - id: true, - username: true, - name: true, - email: true, - bio: true, - timeZone: true, - weekStart: true, - startTime: true, - endTime: true, - bufferTime: true, - theme: true, - createdDate: true, - hideBranding: true, - avatar: true, - }, - }); - - user.avatar = user.avatar || defaultAvatarSrc({ email: user.email }); - - res.status(200).json({ - user, - }); -} diff --git a/apps/web/pages/api/schedule/index.ts b/apps/web/pages/api/schedule/index.ts deleted file mode 100644 index 69df404525..0000000000 --- a/apps/web/pages/api/schedule/index.ts +++ /dev/null @@ -1,55 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from "next"; - -import prisma from "@calcom/prisma"; - -import { getSession } from "@lib/auth"; -import { getAvailabilityFromSchedule } from "@lib/availability"; - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - const session = await getSession({ req }); - const userId = session?.user?.id; - if (!userId) { - res.status(401).json({ message: "Not authenticated" }); - return; - } - - if (!req.body.schedule || req.body.schedule.length !== 7) { - return res.status(400).json({ message: "Bad Request." }); - } - - const availability = getAvailabilityFromSchedule(req.body.schedule); - - if (req.method === "POST") { - try { - await prisma.user.update({ - where: { - id: userId, - }, - data: { - availability: { - /* We delete user availabilty */ - deleteMany: { - userId: { - equals: userId, - }, - }, - /* So we can replace it */ - createMany: { - data: availability.map((schedule) => ({ - days: schedule.days, - startTime: schedule.startTime, - endTime: schedule.endTime, - })), - }, - }, - }, - }); - return res.status(200).json({ - message: "created", - }); - } catch (error) { - console.error(error); - return res.status(500).json({ message: "Unable to create schedule." }); - } - } -} diff --git a/apps/web/pages/api/teams/[team]/index.ts b/apps/web/pages/api/teams/[team]/index.ts deleted file mode 100644 index 79f18236fb..0000000000 --- a/apps/web/pages/api/teams/[team]/index.ts +++ /dev/null @@ -1,60 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from "next"; - -import { getTeamWithMembers } from "@calcom/lib/server/queries/teams"; -import { closeComDeleteTeam } from "@calcom/lib/sync/SyncServiceManager"; -import prisma from "@calcom/prisma"; - -import { getSession } from "@lib/auth"; - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - const session = await getSession({ req: req }); - if (!session) { - return res.status(401).json({ message: "Not authenticated" }); - } - if (!session.user?.id) { - console.log("Received session token without a user id."); - return res.status(500).json({ message: "Something went wrong." }); - } - if (!req.query.team) { - console.log("Missing team query param."); - return res.status(500).json({ message: "Something went wrong." }); - } - - const teamId = parseInt(req.query.team as string); - - // GET /api/teams/{team} - if (req.method === "GET") { - const team = await getTeamWithMembers(teamId); - return res.status(200).json({ team }); - } - // DELETE /api/teams/{team} - if (req.method === "DELETE") { - const membership = await prisma.membership.findFirst({ - where: { - userId: session.user.id, - teamId, - }, - }); - - if (!membership || membership.role !== "OWNER") { - console.log(`User ${session.user.id} tried deleting an organization they don't own.`); - return res.status(403).json({ message: "Forbidden." }); - } - - await prisma.membership.delete({ - where: { - userId_teamId: { userId: session.user.id, teamId }, - }, - }); - const deletedTeam = await prisma.team.delete({ - where: { - id: teamId, - }, - }); - - // Sync Services: Close.com - closeComDeleteTeam(deletedTeam); - - return res.status(204).send(null); - } -} diff --git a/apps/web/pages/api/teams/[team]/invite.ts b/apps/web/pages/api/teams/[team]/invite.ts deleted file mode 100644 index 3fce9765ae..0000000000 --- a/apps/web/pages/api/teams/[team]/invite.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { MembershipRole } from "@prisma/client"; -import { randomBytes } from "crypto"; -import type { NextApiRequest, NextApiResponse } from "next"; - -import { sendTeamInviteEmail } from "@calcom/emails"; -import prisma from "@calcom/prisma"; - -import { getSession } from "@lib/auth"; -import { BASE_URL } from "@lib/config/constants"; -import slugify from "@lib/slugify"; - -import { getTranslation } from "@server/lib/i18n"; - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - const t = await getTranslation(req.body.language ?? "en", "common"); - - if (req.method !== "POST") { - return res.status(400).json({ message: "Bad request" }); - } - - const session = await getSession({ req }); - if (!session) { - return res.status(401).json({ message: "Not authenticated" }); - } - - const team = await prisma.team.findFirst({ - where: { - id: parseInt(req.query.team as string), - }, - }); - - if (!team) { - return res.status(404).json({ message: "Invalid team" }); - } - - const reqBody = req.body as { - usernameOrEmail: string; - role: MembershipRole; - sendEmailInvitation: boolean; - }; - const { role, sendEmailInvitation } = reqBody; - // liberal email match - const isEmail = (str: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(str); - const usernameOrEmail = isEmail(reqBody.usernameOrEmail) - ? reqBody.usernameOrEmail.toLowerCase() - : slugify(reqBody.usernameOrEmail); - - const invitee = await prisma.user.findFirst({ - where: { - OR: [{ username: usernameOrEmail }, { email: usernameOrEmail }], - }, - }); - - if (!invitee) { - const email = isEmail(usernameOrEmail) ? usernameOrEmail : undefined; - if (!email) { - return res.status(400).json({ - message: `Invite failed because there is no corresponding user for ${usernameOrEmail}`, - }); - } - await prisma.user.create({ - data: { - email, - teams: { - create: { - team: { - connect: { - id: parseInt(req.query.team as string), - }, - }, - role, - }, - }, - }, - }); - - const token: string = randomBytes(32).toString("hex"); - - await prisma.verificationToken.create({ - data: { - identifier: usernameOrEmail, - token, - expires: new Date(new Date().setHours(168)), // +1 week - }, - }); - - if (session?.user?.name && team?.name) { - await sendTeamInviteEmail({ - language: t, - from: session.user.name, - to: usernameOrEmail, - teamName: team.name, - joinLink: `${BASE_URL}/auth/signup?token=${token}&callbackUrl=/settings/teams}`, - }); - } - - return res.status(201).json({}); - } - - // create provisional membership - try { - await prisma.membership.create({ - data: { - teamId: parseInt(req.query.team as string), - userId: invitee.id, - role, - }, - }); - } catch (err: any) { - if (err.code === "P2002") { - // unique constraint violation - return res.status(409).json({ - message: "This user is a member of this team / has a pending invitation.", - }); - } else { - throw err; // rethrow - } - } - - // inform user of membership by email - if (sendEmailInvitation && session?.user?.name && team?.name) { - await sendTeamInviteEmail({ - language: t, - from: session.user.name, - to: usernameOrEmail, - teamName: team.name, - joinLink: BASE_URL + "/settings/teams", - }); - } - - res.status(201).json({}); -} diff --git a/apps/web/pages/api/teams/[team]/membership.ts b/apps/web/pages/api/teams/[team]/membership.ts deleted file mode 100644 index ea86bd7617..0000000000 --- a/apps/web/pages/api/teams/[team]/membership.ts +++ /dev/null @@ -1,86 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from "next"; - -import { closeComDeleteTeamMembership } from "@calcom/lib/sync/SyncServiceManager"; -import prisma from "@calcom/prisma"; - -import { getSession } from "@lib/auth"; - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - const session = await getSession({ req }); - - if (!session) { - res.status(401).json({ message: "Not authenticated" }); - return; - } - - const isTeamOwner = !!(await prisma.membership.findFirst({ - where: { - userId: session.user?.id, - teamId: parseInt(req.query.team as string), - role: "OWNER", - }, - })); - - if (!isTeamOwner) { - res.status(403).json({ message: "You are not authorized to manage this team" }); - return; - } - - // List members - if (req.method === "GET") { - const memberships = await prisma.membership.findMany({ - where: { - teamId: parseInt(req.query.team as string), - }, - }); - - let members = await prisma.user.findMany({ - where: { - id: { - in: memberships.map((membership) => membership.userId), - }, - }, - select: { - id: true, - username: true, - name: true, - email: true, - bio: true, - avatar: true, - timeZone: true, - }, - }); - - members = members.map((member) => { - const membership = memberships.find((membership) => member.id === membership.userId); - return { - ...member, - role: membership?.accepted ? membership?.role : "INVITEE", - }; - }); - - return res.status(200).json({ members: members }); - } - - // Cancel a membership (invite) - if (req.method === "DELETE") { - const user = await prisma.user.findFirst({ - where: { - id: req.body.userId, - }, - }); - await prisma.membership.delete({ - where: { - userId_teamId: { userId: req.body.userId, teamId: parseInt(req.query.team as string) }, - }, - }); - // Sync Services: Close.com - closeComDeleteTeamMembership(user); - - return res.status(204).send(null); - } - - // Promote or demote a member of the team - - res.status(200).json({}); -} diff --git a/apps/web/pages/api/teams/[team]/profile.ts b/apps/web/pages/api/teams/[team]/profile.ts deleted file mode 100644 index 34b0f6192b..0000000000 --- a/apps/web/pages/api/teams/[team]/profile.ts +++ /dev/null @@ -1,71 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from "next"; - -import prisma from "@calcom/prisma"; - -import { getSession } from "@lib/auth"; - -// @deprecated - USE TRPC -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - const session = await getSession({ req }); - if (!session?.user?.id) { - return res.status(401).json({ message: "Not authenticated" }); - } - - const isTeamOwner = !!(await prisma.membership.findFirst({ - where: { - userId: session.user.id, - teamId: parseInt(req.query.team as string), - role: "OWNER", - }, - })); - - if (!isTeamOwner) { - res.status(403).json({ message: "You are not authorized to manage this team" }); - return; - } - - // PATCH /api/teams/profile/{team} - if (req.method === "PATCH") { - const team = await prisma.team.findFirst({ - where: { - id: parseInt(req.query.team as string), - }, - }); - - if (!team) { - return res.status(404).json({ message: "Invalid team" }); - } - - const username = req.body.username; - const userConflict = await prisma.team.findMany({ - where: { - slug: username, - }, - }); - const teamId = Number(req.query.team); - if (userConflict.some((team) => team.id !== teamId)) { - return res.status(409).json({ message: "Team username already taken" }); - } - - const name = req.body.name; - const slug = req.body.username; - const bio = req.body.description; - const logo = req.body.logo; - const hideBranding = req.body.hideBranding; - - await prisma.team.update({ - where: { - id: team.id, - }, - data: { - name, - slug, - logo, - bio, - hideBranding, - }, - }); - - return res.status(200).json({ message: "Team updated successfully" }); - } -} diff --git a/apps/web/pages/api/upgrade.ts b/apps/web/pages/api/upgrade.ts deleted file mode 100644 index ed2929fcc3..0000000000 --- a/apps/web/pages/api/upgrade.ts +++ /dev/null @@ -1,54 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from "next"; - -import { getStripeCustomerId } from "@calcom/app-store/stripepayment/lib/customer"; -import prisma from "@calcom/prisma"; - -import { getSession } from "@lib/auth"; -import { WEBSITE_URL } from "@lib/config/constants"; -import { HttpError as HttpCode } from "@lib/core/http/error"; - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - const session = await getSession({ req }); - if (!session?.user?.id) { - return res.status(401).json({ message: "Not authenticated" }); - } - - if (!["GET", "POST"].includes(req.method || "")) { - throw new HttpCode({ statusCode: 405, message: "Method Not Allowed" }); - } - - const user = await prisma.user.findUniqueOrThrow({ - where: { - id: session.user.id, - }, - select: { - email: true, - metadata: true, - }, - }); - - const stripeCustomerId = await getStripeCustomerId(user); - - try { - const response = await fetch(`${WEBSITE_URL}/api/upgrade`, { - method: "POST", - credentials: "include", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - stripeCustomerId, - email: user.email, - fromApp: true, - }), - }); - const data = await response.json(); - - if (!data.url) throw new HttpCode({ statusCode: 401, message: data.message }); - - res.redirect(303, data.url); - } catch (error) { - console.error(`error`, error); - res.redirect(303, req.headers.origin || "/"); - } -} diff --git a/apps/web/pages/api/user/[id].ts b/apps/web/pages/api/user/[id].ts deleted file mode 100644 index 2818178797..0000000000 --- a/apps/web/pages/api/user/[id].ts +++ /dev/null @@ -1,86 +0,0 @@ -import { pick } from "lodash"; -import type { NextApiRequest, NextApiResponse } from "next"; -import z from "zod"; - -import prisma from "@calcom/prisma"; - -import { getSession } from "@lib/auth"; - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - const session = await getSession({ req }); - - if (!session?.user.id) { - return res.status(401).json({ message: "Not authenticated" }); - } - - const querySchema = z.object({ - id: z.string().transform((val) => parseInt(val)), - }); - - const parsedQuery = querySchema.safeParse(req.query); - const userId = parsedQuery.success ? parsedQuery.data.id : null; - - if (!userId) { - return res.status(400).json({ message: "No user id provided" }); - } - - const authenticatedUser = await prisma.user.findFirstOrThrow({ - where: { - id: session.user.id, - }, - select: { - id: true, - }, - }); - - if (userId !== authenticatedUser.id) { - return res.status(401).json({ message: "Unauthorized" }); - } - - if (req.method === "GET") { - return res.status(405).json({ message: "Method Not Allowed" }); - } - - if (req.method === "PATCH") { - const updatedUser = await prisma.user.update({ - where: { - id: authenticatedUser.id, - }, - data: { - ...pick(req.body.data, [ - "username", - "name", - "avatar", - "timeZone", - "timeFormat", - "weekStart", - "hideBranding", - "theme", - "completedOnboarding", - ]), - bio: req.body.description ?? req.body.data?.bio, - }, - select: { - id: true, - username: true, - name: true, - email: true, - emailVerified: true, - bio: true, - avatar: true, - timeZone: true, - timeFormat: true, - weekStart: true, - startTime: true, - endTime: true, - bufferTime: true, - hideBranding: true, - theme: true, - createdDate: true, - plan: true, - completedOnboarding: true, - }, - }); - return res.status(200).json({ message: "User Updated", data: updatedUser }); - } -} diff --git a/apps/web/pages/api/user/me.ts b/apps/web/pages/api/user/me.ts deleted file mode 100644 index 37ef14487f..0000000000 --- a/apps/web/pages/api/user/me.ts +++ /dev/null @@ -1,50 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from "next"; - -import { deleteStripeCustomer } from "@calcom/app-store/stripepayment/lib/customer"; -import { deleteWebUser as syncServicesDeleteWebUser } from "@calcom/lib/sync/SyncServiceManager"; -import prisma from "@calcom/prisma"; - -import { getSession } from "@lib/auth"; - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - const session = await getSession({ req }); - - if (!session?.user.id) { - return res.status(401).json({ message: "Not authenticated" }); - } - - if (req.method !== "DELETE") { - return res.status(405).json({ message: "Method Not Allowed" }); - } - - if (req.method === "DELETE") { - // Get user - const user = await prisma.user.findUniqueOrThrow({ - where: { - id: session.user?.id, - }, - select: { - id: true, - email: true, - metadata: true, - username: true, - createdDate: true, - name: true, - plan: true, - }, - }); - // Delete from stripe - await deleteStripeCustomer(user).catch(console.warn); - // Delete from Cal - await prisma.user.delete({ - where: { - id: session?.user.id, - }, - }); - - // Sync Services - syncServicesDeleteWebUser(user); - - return res.status(204).end(); - } -} diff --git a/apps/web/pages/api/user/membership.ts b/apps/web/pages/api/user/membership.ts deleted file mode 100644 index 4a5613629e..0000000000 --- a/apps/web/pages/api/user/membership.ts +++ /dev/null @@ -1,63 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from "next"; - -import prisma from "@calcom/prisma"; - -import { getSession } from "@lib/auth"; - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - const session = await getSession({ req: req }); - if (!session || !session.user?.id) { - return res.status(401).json({ message: "Not authenticated" }); - } - - if (req.method === "GET") { - const memberships = await prisma.membership.findMany({ - where: { - userId: session.user.id, - }, - }); - - const teams = await prisma.team.findMany({ - where: { - id: { - in: memberships.map((membership) => membership.teamId), - }, - }, - }); - - return res.status(200).json({ - membership: memberships.map((membership) => ({ - role: membership.accepted ? membership.role : "INVITEE", - ...teams.find((team) => team.id === membership.teamId), - })), - }); - } - - if (!req.body.teamId) { - return res.status(400).json({ message: "Bad request" }); - } - - // Leave team or decline membership invite of current user - if (req.method === "DELETE") { - await prisma.membership.delete({ - where: { - userId_teamId: { userId: session.user.id, teamId: req.body.teamId }, - }, - }); - return res.status(204).send(null); - } - - // Accept team invitation - if (req.method === "PATCH") { - await prisma.membership.update({ - where: { - userId_teamId: { userId: session.user.id, teamId: req.body.teamId }, - }, - data: { - accepted: true, - }, - }); - - return res.status(204).send(null); - } -} diff --git a/apps/web/pages/api/user/profile.ts b/apps/web/pages/api/user/profile.ts deleted file mode 100644 index 91e69bec7d..0000000000 --- a/apps/web/pages/api/user/profile.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { PrismaClientKnownRequestError } from "@prisma/client/runtime"; -import { pick } from "lodash"; -import type { NextApiRequest, NextApiResponse } from "next"; - -import prisma from "@calcom/prisma"; - -import { getSession } from "@lib/auth"; - -import { resizeBase64Image } from "@server/lib/resizeBase64Image"; - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - const session = await getSession({ req: req }); - - if (!session) { - res.status(401).json({ message: "Not authenticated" }); - return; - } - - try { - const avatar = req.body.avatar ? await resizeBase64Image(req.body.avatar) : undefined; - await prisma.user.update({ - where: { - id: session.user.id, - }, - data: { - ...pick(req.body, [ - "username", - "name", - "timeZone", - "weekStart", - "hideBranding", - "theme", - "completedOnboarding", - "locale", - ]), - avatar, - bio: req.body.description, - }, - }); - } catch (e) { - if (e instanceof PrismaClientKnownRequestError) { - if (e.code === "P2002") { - return res.status(409).json({ message: "Username already taken" }); - } - } - throw e; - } - - return res.status(200).json({ message: "Profile updated successfully" }); -} diff --git a/apps/web/pages/api/v2-opt-in.ts b/apps/web/pages/api/v2-opt-in.ts deleted file mode 100644 index cd9fcca0b6..0000000000 --- a/apps/web/pages/api/v2-opt-in.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from "next"; - -import { ensureSession } from "@calcom/lib/auth"; -import { defaultHandler, defaultResponder } from "@calcom/lib/server"; - -async function handler(req: NextApiRequest, res: NextApiResponse): Promise { - // Only logged in users can opt-in/out - await ensureSession({ req }); - - // If has the cookie, Opt-out of V2 - if ("calcom-v2-early-access" in req.cookies && req.cookies["calcom-v2-early-access"] === "1") { - res.setHeader("Set-Cookie", `calcom-v2-early-access=0; Max-Age=0; Path=/`); - } else { - /* Opt-int to V2 */ - res.setHeader("Set-Cookie", "calcom-v2-early-access=1; Path=/"); - } - - let redirectUrl = "/"; - - // We take you back where you came from if possible - if (typeof req.headers["referer"] === "string") redirectUrl = req.headers["referer"]; - - res.redirect(redirectUrl); -} - -export default defaultHandler({ - GET: Promise.resolve({ default: defaultResponder(handler) }), -}); diff --git a/apps/web/pages/settings/billing/index.tsx b/apps/web/pages/settings/billing/index.tsx index 40764a6f16..b2b1d0f8ba 100644 --- a/apps/web/pages/settings/billing/index.tsx +++ b/apps/web/pages/settings/billing/index.tsx @@ -52,16 +52,6 @@ const BillingView = () => { <>
- {!isPro && ( - -
- -
-
- )} - { - const { t } = useLocale(); - const query = useMeQuery(); - const user = query.data; - - if (!user || user.plan !== "TRIAL") return null; - - const trialDaysLeft = user.trialEndsAt - ? dayjs(user.trialEndsAt).add(1, "day").diff(dayjs(), "day") - : dayjs(user.createdDate) - .add(TRIAL_LIMIT_DAYS + 1, "day") - .diff(dayjs(), "day"); - - return ( -
-
- {trialDaysLeft > 0 ? t("trial_days_left", { days: trialDaysLeft }) : t("trial_expired")} -
- - -
- ); -}; - -export default TrialBanner; diff --git a/packages/ui/UpgradeToProDialog.tsx b/packages/ui/UpgradeToProDialog.tsx deleted file mode 100644 index b5c481ece5..0000000000 --- a/packages/ui/UpgradeToProDialog.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { Trans } from "next-i18next"; - -import { useLocale } from "@calcom/lib/hooks/useLocale"; - -import Button from "./Button"; -import { Dialog, DialogClose, DialogContent } from "./Dialog"; -import { Icon } from "./Icon"; - -export function UpgradeToProDialog({ - modalOpen, - setModalOpen, - children, -}: { - modalOpen: boolean; - setModalOpen: (open: boolean) => void; - children: React.ReactNode; -}) { - const { t } = useLocale(); - return ( - - -
-
-
-
- -
-
-
-

{children}

-

- - You can - - upgrade here - - . - -

-
-
- - - -
-
-
- ); -}