+1








5c0da23b97
* Pass organization name & logo * Overflow hidden * Show org icon on public page * Add org logo to large user avatars * Clean up * Add org name and logo to context * Get org logo from /avatar.png endpoint * Do not query for logo * Remove name and logo from session middleware * Type fix * Set user onboarding org logo * feat: organization avatar component (#10788) Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com> * Type fixes * Type fix * Transition to org slug for organization avatar * Address feedback * Clean up * Clean up * Type fix * fix: set avatar cache control (#11163) * test: Integration tests for handleNewBooking (#11044) Co-authored-by: Shivam Kalra <shivamkalra98@gmail.com> Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com> * fix: booking_paid webhook and added new payment metadata (#11093) * app store improvements, logos, dark mode, added screenshots, fixed author names (#11164) * fix: mobile event types and avatars (#11184) * New Crowdin translations by Github Action * fix: updateProfile metadata overwrite (#11188) Co-authored-by: alannnc <alannnc@gmail.com> * New Crowdin translations by Github Action --------- Co-authored-by: Sean Brydon <sean@cal.com> Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com> Co-authored-by: Omar López <zomars@me.com> Co-authored-by: Hariom Balhara <hariombalhara@gmail.com> Co-authored-by: Shivam Kalra <shivamkalra98@gmail.com> Co-authored-by: alannnc <alannnc@gmail.com> Co-authored-by: Peer Richelsen <peeroke@gmail.com> Co-authored-by: Leo Giovanetti <hello@leog.me> Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
120 lines
3.1 KiB
TypeScript
120 lines
3.1 KiB
TypeScript
import type { NextApiRequest, NextApiResponse } from "next";
|
|
import { z } from "zod";
|
|
|
|
import { getSlugOrRequestedSlug, orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
|
|
import { AVATAR_FALLBACK } from "@calcom/lib/constants";
|
|
import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage";
|
|
import prisma from "@calcom/prisma";
|
|
|
|
const querySchema = z
|
|
.object({
|
|
username: z.string(),
|
|
teamname: z.string(),
|
|
orgSlug: z.string(),
|
|
/**
|
|
* Allow fetching avatar of a particular organization
|
|
* Avatars being public, we need not worry about others accessing it.
|
|
*/
|
|
orgId: z.string().transform((s) => Number(s)),
|
|
})
|
|
.partial();
|
|
|
|
async function getIdentityData(req: NextApiRequest) {
|
|
const { username, teamname, orgId, orgSlug } = querySchema.parse(req.query);
|
|
const { currentOrgDomain, isValidOrgDomain } = orgDomainConfig(req.headers.host ?? "");
|
|
|
|
const org = isValidOrgDomain ? currentOrgDomain : null;
|
|
|
|
const orgQuery = orgId
|
|
? {
|
|
id: orgId,
|
|
}
|
|
: org
|
|
? getSlugOrRequestedSlug(org)
|
|
: null;
|
|
|
|
if (username) {
|
|
const user = await prisma.user.findFirst({
|
|
where: {
|
|
username,
|
|
organization: orgQuery,
|
|
},
|
|
select: { avatar: true, email: true },
|
|
});
|
|
return {
|
|
name: username,
|
|
email: user?.email,
|
|
avatar: user?.avatar,
|
|
org,
|
|
};
|
|
}
|
|
if (teamname) {
|
|
const team = await prisma.team.findFirst({
|
|
where: {
|
|
slug: teamname,
|
|
parent: orgQuery,
|
|
},
|
|
select: { logo: true },
|
|
});
|
|
return {
|
|
org,
|
|
name: teamname,
|
|
email: null,
|
|
avatar: getPlaceholderAvatar(team?.logo, teamname),
|
|
};
|
|
}
|
|
if (orgSlug) {
|
|
const org = await prisma.team.findFirst({
|
|
where: getSlugOrRequestedSlug(orgSlug),
|
|
select: {
|
|
slug: true,
|
|
logo: true,
|
|
name: true,
|
|
},
|
|
});
|
|
return {
|
|
org: org?.slug,
|
|
name: org?.name,
|
|
email: null,
|
|
avatar: getPlaceholderAvatar(org?.logo, org?.name),
|
|
};
|
|
}
|
|
}
|
|
|
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|
const identity = await getIdentityData(req);
|
|
const img = identity?.avatar;
|
|
// We cache for one day
|
|
res.setHeader("Cache-Control", "s-maxage=86400, stale-while-revalidate=60");
|
|
// If image isn't set or links to this route itself, use default avatar
|
|
if (!img) {
|
|
if (identity?.org) {
|
|
res.setHeader("x-cal-org", identity.org);
|
|
}
|
|
res.writeHead(302, {
|
|
Location: AVATAR_FALLBACK,
|
|
});
|
|
|
|
return res.end();
|
|
}
|
|
|
|
if (!img.includes("data:image")) {
|
|
if (identity.org) {
|
|
res.setHeader("x-cal-org", identity.org);
|
|
}
|
|
res.writeHead(302, { Location: img });
|
|
return res.end();
|
|
}
|
|
|
|
const decoded = img.toString().replace("data:image/png;base64,", "").replace("data:image/jpeg;base64,", "");
|
|
const imageResp = Buffer.from(decoded, "base64");
|
|
if (identity.org) {
|
|
res.setHeader("x-cal-org", identity.org);
|
|
}
|
|
res.writeHead(200, {
|
|
"Content-Type": "image/png",
|
|
"Content-Length": imageResp.length,
|
|
});
|
|
res.end(imageResp);
|
|
}
|