From c98c06062b165609b5d405d5b147ef34468240fe Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Tue, 1 Jul 2025 14:38:32 -0400 Subject: [PATCH] revert: Process Base64 Avatar Image (#22185) * Revert "chore: add test for updateProfilePhotoGoogle (#22169)" This reverts commit 67032ddaf1048f36e5d56614907bb3e5af597c56. * Revert "fix: Process base64 avatar image (#22165)" This reverts commit d8d8bc4d0045f7ede8f814adc524f54619488046. --- .../oauth/updateProfilePhotoGoogle.test.ts | 114 ------------------ .../_utils/oauth/updateProfilePhotoGoogle.ts | 20 +-- .../app-store/googlecalendar/api/callback.ts | 15 +-- packages/lib/isBase64Image.ts | 12 -- .../viewer/me/updateProfile.handler.ts | 3 +- .../viewer/organizations/update.handler.ts | 15 ++- .../organizations/updateUser.handler.ts | 3 +- .../routers/viewer/teams/create.handler.ts | 3 +- .../routers/viewer/teams/update.handler.ts | 3 +- 9 files changed, 23 insertions(+), 165 deletions(-) delete mode 100644 packages/app-store/_utils/oauth/updateProfilePhotoGoogle.test.ts delete mode 100644 packages/lib/isBase64Image.ts diff --git a/packages/app-store/_utils/oauth/updateProfilePhotoGoogle.test.ts b/packages/app-store/_utils/oauth/updateProfilePhotoGoogle.test.ts deleted file mode 100644 index a2cf7819f1..0000000000 --- a/packages/app-store/_utils/oauth/updateProfilePhotoGoogle.test.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { oauth2_v2 } from "@googleapis/oauth2"; -import type { OAuth2Client } from "googleapis-common"; -import { describe, it, expect, vi, beforeEach } from "vitest"; - -import logger from "@calcom/lib/logger"; -import { uploadAvatar } from "@calcom/lib/server/avatar"; -import { UserRepository } from "@calcom/lib/server/repository/user"; -import { resizeBase64Image } from "@calcom/lib/server/resizeBase64Image"; - -import { updateProfilePhotoGoogle } from "./updateProfilePhotoGoogle"; - -vi.mock("@googleapis/oauth2", () => ({ - oauth2_v2: { - Oauth2: vi.fn(), - }, -})); - -vi.mock("@calcom/lib/logger", () => ({ - default: { - error: vi.fn(), - }, -})); - -vi.mock("@calcom/lib/server/avatar", () => ({ - uploadAvatar: vi.fn(), -})); - -vi.mock("@calcom/lib/server/resizeBase64Image", () => ({ - resizeBase64Image: vi.fn(), -})); - -vi.mock("@calcom/lib/server/repository/user", () => ({ - UserRepository: { - updateAvatar: vi.fn(), - }, -})); - -describe("updateProfilePhotoGoogle", () => { - const mockOAuth2Client = {} as OAuth2Client; - const userId = 123; - - let mockOauth2Instance: any; - - beforeEach(() => { - vi.clearAllMocks(); - - mockOauth2Instance = { - userinfo: { - get: vi.fn(), - }, - }; - - (oauth2_v2.Oauth2 as any).mockImplementation(() => mockOauth2Instance); - (uploadAvatar as any).mockResolvedValue("/api/avatar/processed-123.png"); - (resizeBase64Image as any).mockResolvedValue("data:image/png;base64,resized-data"); - (UserRepository.updateAvatar as any).mockResolvedValue(undefined); - }); - - it("should update avatar with valid URL", async () => { - const validUrl = "https://example.com/avatar.jpg"; - mockOauth2Instance.userinfo.get.mockResolvedValue({ - data: { picture: validUrl }, - }); - - await updateProfilePhotoGoogle(mockOAuth2Client, userId); - - expect(UserRepository.updateAvatar).toHaveBeenCalledWith({ - id: userId, - avatarUrl: validUrl, - }); - expect(uploadAvatar).not.toHaveBeenCalled(); - }); - - it("should process base64 data", async () => { - const base64Data = - "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII="; - mockOauth2Instance.userinfo.get.mockResolvedValue({ - data: { picture: base64Data }, - }); - - await updateProfilePhotoGoogle(mockOAuth2Client, userId); - - expect(resizeBase64Image).toHaveBeenCalledWith(base64Data); - expect(uploadAvatar).toHaveBeenCalledWith({ - avatar: "data:image/png;base64,resized-data", - userId, - }); - expect(UserRepository.updateAvatar).toHaveBeenCalledWith({ - id: userId, - avatarUrl: "/api/avatar/processed-123.png", - }); - }); - - it("should do nothing when no picture provided", async () => { - mockOauth2Instance.userinfo.get.mockResolvedValue({ - data: {}, - }); - - await updateProfilePhotoGoogle(mockOAuth2Client, userId); - - expect(UserRepository.updateAvatar).not.toHaveBeenCalled(); - expect(uploadAvatar).not.toHaveBeenCalled(); - }); - - it("should handle errors gracefully", async () => { - const error = new Error("OAuth API failed"); - mockOauth2Instance.userinfo.get.mockRejectedValue(error); - - await updateProfilePhotoGoogle(mockOAuth2Client, userId); - - expect(logger.error).toHaveBeenCalledWith("Error updating avatarUrl from google calendar connect", error); - expect(UserRepository.updateAvatar).not.toHaveBeenCalled(); - }); -}); diff --git a/packages/app-store/_utils/oauth/updateProfilePhotoGoogle.ts b/packages/app-store/_utils/oauth/updateProfilePhotoGoogle.ts index 2773e4ae80..6f59810d13 100644 --- a/packages/app-store/_utils/oauth/updateProfilePhotoGoogle.ts +++ b/packages/app-store/_utils/oauth/updateProfilePhotoGoogle.ts @@ -1,32 +1,16 @@ import { oauth2_v2 } from "@googleapis/oauth2"; import type { OAuth2Client } from "googleapis-common"; -import { isBase64Image } from "@calcom/lib/isBase64Image"; import logger from "@calcom/lib/logger"; -import { uploadAvatar } from "@calcom/lib/server/avatar"; import { UserRepository } from "@calcom/lib/server/repository/user"; -import { resizeBase64Image } from "@calcom/lib/server/resizeBase64Image"; export async function updateProfilePhotoGoogle(oAuth2Client: OAuth2Client, userId: number) { try { const oauth2 = new oauth2_v2.Oauth2({ auth: oAuth2Client }); const userDetails = await oauth2.userinfo.get(); - const avatarUrl = userDetails.data?.picture; - if (!avatarUrl) { - return; + if (userDetails.data?.picture) { + await UserRepository.updateAvatar({ id: userId, avatarUrl: userDetails.data.picture }); } - - // Handle base64 data - if (isBase64Image(avatarUrl)) { - const resizedAvatarUrl = await uploadAvatar({ - avatar: await resizeBase64Image(avatarUrl), - userId, - }); - await UserRepository.updateAvatar({ id: userId, avatarUrl: resizedAvatarUrl }); - return; - } - - await UserRepository.updateAvatar({ id: userId, avatarUrl }); } catch (error) { logger.error("Error updating avatarUrl from google calendar connect", error); } diff --git a/packages/app-store/googlecalendar/api/callback.ts b/packages/app-store/googlecalendar/api/callback.ts index 85c3ba0be6..9d9d545a90 100644 --- a/packages/app-store/googlecalendar/api/callback.ts +++ b/packages/app-store/googlecalendar/api/callback.ts @@ -4,12 +4,7 @@ import type { NextApiRequest, NextApiResponse } from "next"; import GoogleCalendarService from "@calcom/app-store/googlecalendar/lib/CalendarService"; import { renewSelectedCalendarCredentialId } from "@calcom/lib/connectedCalendar"; -import { - GOOGLE_CALENDAR_SCOPES, - SCOPE_USERINFO_PROFILE, - WEBAPP_URL, - WEBAPP_URL_FOR_OAUTH, -} from "@calcom/lib/constants"; +import { GOOGLE_CALENDAR_SCOPES, WEBAPP_URL, WEBAPP_URL_FOR_OAUTH } from "@calcom/lib/constants"; import { getSafeRedirectUrl } from "@calcom/lib/getSafeRedirectUrl"; import { HttpError } from "@calcom/lib/http-error"; import { defaultHandler } from "@calcom/lib/server/defaultHandler"; @@ -19,7 +14,6 @@ import { Prisma } from "@calcom/prisma/client"; import getInstalledAppPath from "../../_utils/getInstalledAppPath"; import { decodeOAuthState } from "../../_utils/oauth/decodeOAuthState"; -import { updateProfilePhotoGoogle } from "../../_utils/oauth/updateProfilePhotoGoogle"; import { getGoogleAppKeys } from "../lib/getGoogleAppKeys"; async function getHandler(req: NextApiRequest, res: NextApiResponse) { @@ -101,9 +95,10 @@ async function getHandler(req: NextApiRequest, res: NextApiResponse) { } // Only attempt to update the user's profile photo if the user has granted the required scope - if (grantedScopes.includes(SCOPE_USERINFO_PROFILE)) { - await updateProfilePhotoGoogle(oAuth2Client, req.session.user.id); - } + // TODO: Use the avatarUrl when setting the profile picture + // if (grantedScopes.includes(SCOPE_USERINFO_PROFILE)) { + // await updateProfilePhotoGoogle(oAuth2Client, req.session.user.id); + // } const selectedCalendarWhereUnique = { userId: req.session.user.id, diff --git a/packages/lib/isBase64Image.ts b/packages/lib/isBase64Image.ts deleted file mode 100644 index 1fdb482b3d..0000000000 --- a/packages/lib/isBase64Image.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Checks if a string is a supported base64 image format - */ -export function isBase64Image(input: string | null | undefined): boolean { - if (!input) return false; - - return ( - input.startsWith("data:image/png;base64,") || - input.startsWith("data:image/jpeg;base64,") || - input.startsWith("data:image/jpg;base64,") - ); -} diff --git a/packages/trpc/server/routers/viewer/me/updateProfile.handler.ts b/packages/trpc/server/routers/viewer/me/updateProfile.handler.ts index 7a00418512..ae92ff9ca4 100644 --- a/packages/trpc/server/routers/viewer/me/updateProfile.handler.ts +++ b/packages/trpc/server/routers/viewer/me/updateProfile.handler.ts @@ -9,7 +9,6 @@ import { StripeBillingService } from "@calcom/features/ee/billing/stripe-billlin import { FeaturesRepository } from "@calcom/features/flags/features.repository"; import hasKeyInMetadata from "@calcom/lib/hasKeyInMetadata"; import { HttpError } from "@calcom/lib/http-error"; -import { isBase64Image } from "@calcom/lib/isBase64Image"; import logger from "@calcom/lib/logger"; import { uploadAvatar } from "@calcom/lib/server/avatar"; import { checkUsername } from "@calcom/lib/server/checkUsername"; @@ -153,7 +152,7 @@ export const updateProfileHandler = async ({ ctx, input }: UpdateProfileOptions) } // if defined AND a base 64 string, upload and update the avatar URL - if (input.avatarUrl && isBase64Image(input.avatarUrl)) { + if (input.avatarUrl && input.avatarUrl.startsWith("data:image/png;base64,")) { data.avatarUrl = await uploadAvatar({ avatar: await resizeBase64Image(input.avatarUrl), userId: user.id, diff --git a/packages/trpc/server/routers/viewer/organizations/update.handler.ts b/packages/trpc/server/routers/viewer/organizations/update.handler.ts index 98460451ea..5f1beff0e0 100644 --- a/packages/trpc/server/routers/viewer/organizations/update.handler.ts +++ b/packages/trpc/server/routers/viewer/organizations/update.handler.ts @@ -2,7 +2,6 @@ import type { Prisma } from "@prisma/client"; import { IS_TEAM_BILLING_ENABLED } from "@calcom/lib/constants"; import { getMetadataHelpers } from "@calcom/lib/getMetadataHelpers"; -import { isBase64Image } from "@calcom/lib/isBase64Image"; import { uploadLogo } from "@calcom/lib/server/avatar"; import { isOrganisationAdmin } from "@calcom/lib/server/queries/organisations"; import { resizeBase64Image } from "@calcom/lib/server/resizeBase64Image"; @@ -162,7 +161,12 @@ export const updateHandler = async ({ ctx, input }: UpdateOptions) => { metadata: mergeMetadata({ ...input.metadata }), }; - if (input.banner && isBase64Image(input.banner)) { + if ( + input.banner && + (input.banner.startsWith("data:image/png;base64,") || + input.banner.startsWith("data:image/jpeg;base64,") || + input.banner.startsWith("data:image/jpg;base64,")) + ) { const banner = await resizeBase64Image(input.banner, { maxSize: 1500 }); data.bannerUrl = await uploadLogo({ logo: banner, @@ -173,7 +177,12 @@ export const updateHandler = async ({ ctx, input }: UpdateOptions) => { data.bannerUrl = null; } - if (input.logoUrl && isBase64Image(input.logoUrl)) { + if ( + input.logoUrl && + (input.logoUrl.startsWith("data:image/png;base64,") || + input.logoUrl.startsWith("data:image/jpeg;base64,") || + input.logoUrl.startsWith("data:image/jpg;base64,")) + ) { data.logoUrl = await uploadLogo({ logo: await resizeBase64Image(input.logoUrl), teamId: currentOrgId, diff --git a/packages/trpc/server/routers/viewer/organizations/updateUser.handler.ts b/packages/trpc/server/routers/viewer/organizations/updateUser.handler.ts index c0d49b7ea2..2e8150b7b3 100644 --- a/packages/trpc/server/routers/viewer/organizations/updateUser.handler.ts +++ b/packages/trpc/server/routers/viewer/organizations/updateUser.handler.ts @@ -2,7 +2,6 @@ import type { Prisma, PrismaPromise, User, Membership, Profile } from "@prisma/c import { ensureOrganizationIsReviewed } from "@calcom/ee/organizations/lib/ensureOrganizationIsReviewed"; import { checkAdminOrOwner } from "@calcom/features/auth/lib/checkAdminOrOwner"; -import { isBase64Image } from "@calcom/lib/isBase64Image"; import { uploadAvatar } from "@calcom/lib/server/avatar"; import { checkRegularUsername } from "@calcom/lib/server/checkRegularUsername"; import { isOrganisationAdmin, isOrganisationOwner } from "@calcom/lib/server/queries/organisations"; @@ -120,7 +119,7 @@ export const updateUserHandler = async ({ ctx, input }: UpdateUserOptions) => { timeZone: input.timeZone, }; - if (input.avatar && isBase64Image(input.avatar)) { + if (input.avatar && input.avatar.startsWith("data:image/png;base64,")) { const avatar = await resizeBase64Image(input.avatar); data.avatarUrl = await uploadAvatar({ avatar, diff --git a/packages/trpc/server/routers/viewer/teams/create.handler.ts b/packages/trpc/server/routers/viewer/teams/create.handler.ts index 1313d4b4d3..1f06448f9c 100644 --- a/packages/trpc/server/routers/viewer/teams/create.handler.ts +++ b/packages/trpc/server/routers/viewer/teams/create.handler.ts @@ -1,6 +1,5 @@ import { generateTeamCheckoutSession } from "@calcom/features/ee/teams/lib/payments"; import { IS_TEAM_BILLING_ENABLED, WEBAPP_URL } from "@calcom/lib/constants"; -import { isBase64Image } from "@calcom/lib/isBase64Image"; import { uploadLogo } from "@calcom/lib/server/avatar"; import { ProfileRepository } from "@calcom/lib/server/repository/profile"; import { resizeBase64Image } from "@calcom/lib/server/resizeBase64Image"; @@ -107,7 +106,7 @@ export const createHandler = async ({ ctx, input }: CreateOptions) => { }, }); // Upload logo, create doesn't allow logo removal - if (input.logo && isBase64Image(input.logo)) { + if (input.logo && input.logo.startsWith("data:image/png;base64,")) { const logoUrl = await uploadLogo({ logo: await resizeBase64Image(input.logo), teamId: createdTeam.id, diff --git a/packages/trpc/server/routers/viewer/teams/update.handler.ts b/packages/trpc/server/routers/viewer/teams/update.handler.ts index aa08d7c4a2..6b525e6b6a 100644 --- a/packages/trpc/server/routers/viewer/teams/update.handler.ts +++ b/packages/trpc/server/routers/viewer/teams/update.handler.ts @@ -4,7 +4,6 @@ import { getOrgFullOrigin } from "@calcom/ee/organizations/lib/orgDomains"; import { IS_TEAM_BILLING_ENABLED } from "@calcom/lib/constants"; import type { IntervalLimit } from "@calcom/lib/intervalLimits/intervalLimitSchema"; import { validateIntervalLimitOrder } from "@calcom/lib/intervalLimits/validateIntervalLimitOrder"; -import { isBase64Image } from "@calcom/lib/isBase64Image"; import { uploadLogo } from "@calcom/lib/server/avatar"; import { isTeamAdmin } from "@calcom/lib/server/queries/teams"; import { prisma } from "@calcom/prisma"; @@ -71,7 +70,7 @@ export const updateHandler = async ({ ctx, input }: UpdateOptions) => { rrTimestampBasis: input.rrTimestampBasis, }; - if (input.logo && isBase64Image(input.logo)) { + if (input.logo && input.logo.startsWith("data:image/png;base64,")) { data.logoUrl = await uploadLogo({ teamId: input.id, logo: input.logo }); } else if (typeof input.logo !== "undefined" && !input.logo) { data.logoUrl = null;