* fix: prevent base64 logo/banner storage in organization onboarding - Add processOnboardingBrandAssets helper method to BaseOnboardingService - Process base64 images and upload them before storing in database - Use uploadAvatar with userId to avoid foreign key issues before Team exists - Handle both create and update/resume flows - Ensure OrganizationOnboarding and Team records store regular URLs not base64 - Fixes header size issues when logoUrl is added to session cookies Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * fix: use processed URLs from OrganizationOnboarding record - Update SelfHostedOnboardingService to use organizationOnboarding.logo/bannerUrl instead of raw input - Update BillingEnabledOrgOnboardingService payment intent to use processed URLs - Ensures Team records receive processed URLs, not base64 data Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * refactor: remove unused variables and imports Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * refactor: extract image processing to helper method and add tests - Created private processImageField() method to eliminate code duplication - Uses regex for more robust data URI and URL detection - Processes logo and bannerUrl in parallel with Promise.all - Added 2 important tests for base64 image processing: 1. Verifies base64 conversion with correct resize options (bannerUrl uses maxSize: 1500) 2. Validates undefined vs null semantics (no-op vs explicit clear) - Fixed pre-existing lint warnings by replacing 'any' types with proper types Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * Improve code * Fixup organizationId * simplify --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Alex van Andel <me@alexvanandel.com>
41 lines
1.2 KiB
TypeScript
41 lines
1.2 KiB
TypeScript
import jimp from "jimp";
|
|
|
|
export function isBase64Image(value: string): boolean {
|
|
return /^data:image\/(png|jpe?g);base64,/i.test(value);
|
|
}
|
|
|
|
export async function resizeBase64Image(
|
|
base64OrUrl: string,
|
|
opts?: {
|
|
maxSize?: number;
|
|
}
|
|
) {
|
|
if (!base64OrUrl.startsWith("data:")) {
|
|
// might be a `https://` or something
|
|
return base64OrUrl;
|
|
}
|
|
const mimeMatch = base64OrUrl.match(/^data:(\w+\/\w+);/);
|
|
const mimetype = mimeMatch?.[1];
|
|
if (!mimetype) {
|
|
throw new Error(`Could not distinguish mimetype`);
|
|
}
|
|
const buffer = Buffer.from(base64OrUrl.replace(/^data:image\/\w+;base64,/, ""), "base64");
|
|
|
|
const {
|
|
// 96px is the height of the image on https://cal.com/peer
|
|
maxSize = 96 * 4,
|
|
} = opts ?? {};
|
|
const image = await jimp.read(buffer);
|
|
if (image.getHeight() !== image.getHeight()) {
|
|
// this could be handled later
|
|
throw new Error("Image is not a square");
|
|
}
|
|
const currentSize = Math.max(image.getWidth(), image.getHeight());
|
|
if (currentSize > maxSize) {
|
|
image.resize(jimp.AUTO, maxSize);
|
|
}
|
|
const newBuffer = await image.getBufferAsync(mimetype);
|
|
|
|
return `data:${mimetype};base64,${newBuffer.toString("base64")}`;
|
|
}
|