revert: Process Base64 Avatar Image (#22185)

* Revert "chore: add test for updateProfilePhotoGoogle (#22169)"

This reverts commit 67032ddaf1.

* Revert "fix: Process base64 avatar image (#22165)"

This reverts commit d8d8bc4d00.
This commit is contained in:
Benny Joo authored and GitHub committed 2025-07-01 18:38:32 +00:00
1 parent 4963d247dc
commit c98c06062b
9 files changed
+23 -165

No files matched your search

@@ -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();
});
});
@@ -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);
}
@@ -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,
-12
View File
@@ -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,")
);
}
@@ -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,
@@ -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,
@@ -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,
@@ -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,
@@ -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;