Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- Frontend (don't mind the fieldTypes):
- export async function processActivities(
- // eslint-disable-next-line u/typescript-eslint/no-explicit-any
- data: any[],
- fieldTypes: Record<string, string>
- ): Promise<void> {
- try {
- await setDoc(doc(firestore, '_tools_', 'activitiesProcessor'), fieldTypes, { merge: true });
- const limit = pLimit(5); // only 5 in flight
- const promises = [];
- const processActivity = httpsCallable(backendFunctions, 'processActivity');
- for (const activityData of data) {
- promises.push(limit(() => processActivity({ activityData, fieldTypes })));
- }
- await Promise.all(promises);
- console.log('Activities processed successfully');
- } catch (err) {
- console.error('Failed to process activities', err);
- }
- }
- --------------------------------------------------------------------------------------------
- Backend (again, don't mind the fieldTypes. activityData is mostly strings and pictures (3 to 5 per activity) that needs processing):
- import * as admin from "firebase-admin";
- import * as functions from "firebase-functions/v2/https";
- import { getDownloadURL } from "firebase-admin/storage";
- import * as sharp from "sharp";
- import { v4 as uuidv4 } from "uuid";
- export const processActivity = functions.onCall(
- {
- memory: "4GiB",
- timeoutSeconds: 3600,
- },
- async (request) => {
- const { activityData, fieldTypes } = request.data;
- const activityId = activityData["custom ID"];
- const picturesArray = activityData.pictures
- .split(",")
- .map((url: string) => url.trim());
- const hashTagArray = activityData.hashtag
- .split(",")
- .map((hashtag: string) => hashtag.trim());
- console.log("activityData", activityData);
- console.log("fieldTypes", fieldTypes);
- console.log("activityId", activityId);
- console.log("picturesArray", picturesArray);
- console.log("hashTagArray", hashTagArray);
- const storage = admin.storage();
- const db = admin.firestore();
- const processedUrls: string[] = []; // for frontend (includes errors)
- const uploadedUrls: string[] = []; // for Firestore (only successful uploads)
- for (const url of picturesArray) {
- try {
- // download original image
- const response = await fetch(url);
- const buffer = Buffer.from(await response.arrayBuffer());
- // process image with sharp
- const processedBuffer = await sharp(buffer)
- .resize({ height: 2000, withoutEnlargement: true })
- .webp({ quality: 80 })
- .toBuffer();
- // generate unique file name
- const fileName = `resizedPicturesTest/${activityId}/${uuidv4()}.webp`;
- console.log("fileName", fileName);
- const file = storage.bucket().file(fileName);
- // upload to Firebase Storage
- await file.save(processedBuffer, {
- metadata: { contentType: "image/webp" },
- });
- // get download URL from Storage (admin SDK)
- const downloadUrl = await getDownloadURL(file);
- console.log("downloadUrl", downloadUrl);
- // push to both arrays
- processedUrls.push(downloadUrl);
- uploadedUrls.push(downloadUrl);
- } catch (err) {
- console.error("Failed to process", url, err);
- // add an error message only for frontend
- processedUrls.push(`Failed to process image: ${url}`);
- }
- }
- console.log("processedUrls", processedUrls);
- // write only successful uploads to Firestore
- if (activityId && uploadedUrls.length > 0) {
- try {
- const docRef = db.collection("activities_test").doc(activityId);
- await docRef.set(
- { pictures: admin.firestore.FieldValue.arrayUnion(...uploadedUrls) },
- { merge: true }
- );
- console.log(`Firestore updated for document: ${activityId}`);
- } catch (err) {
- console.error("Failed to write URLs to Firestore", err);
- }
- }
- return { urls: processedUrls };
- }
- );
Advertisement
Add Comment
Please, Sign In to add comment