0
votes

In my client code I have:

...
// Upload image to storage
const task = storageRef.put(blob);
...
// When the image is fully uploaded to the storage...
task.then(async () => {
   ...
     await firebase
       .getDatabase()
       .collection("posts")
       .doc(userId)
       .collection("userPosts")
       .add(data);
}

As you an see, what I am doing is

  1. Upload image to storage

  2. Create a reference to this image in my firestore

Now, in my cloud function (where I check that the posted image has valid dimensions) I have:

// Create Google Cloud Storage
const gcs = new Storage();

// Create Google Cloud Firestore
const gcf = new Firestore();

// Validate image dimensions
exports.validateImageDimensions = functions
  .region("us-central1")
  // Increased memory, increased timeout (compared to defaults)
  .runWith({ memory: "2GB", timeoutSeconds: 540 })
  .storage.object()
  .onFinalize(async (object) => {
    // Get the bucket which contains the image
    const bucket = gcs.bucket(object.bucket);
    
    ...

    // Get the file id
    const fileId = file.id

       ...

         // Remove the files that are not images, or whose dimensions are not valid
          if (!isImage || !hasValidDimensions) {
            try {
              // Get post's owner (⚠️ Be aware that clients can choose whatever metadata that they add!!)
              const metadata = await file.getMetadata();
              const {
                metadata: { owner },
              } = metadata[0];

              await bucket.file(filePath).delete();

              console.log(
                `The image ${filePath} has been deleted because it has invalid dimensions.
                 This may be an attempt to break the security of the app made by the user ${owner}`
              );

              // Delete the firestore photo's document
              gcf
                .collection("posts")
                .doc("owner")
                .collection("userPosts")
                .doc(fileId)
                .delete()
                .catch((err) => {
                  console.log(
                    `Error deleting the document 'posts/${owner}/userPosts/${fileId}': ${err}`
                  );
                });
            } catch (err) {
              console.log(`Error deleting invalid file ${filePath}: ${err}`);
            }
          }

My problem is that in the client code, I create the firestore document after the image is uploaded, and this cloud functions runs just when the image is fully uploaded... So, there might be times where in this cloud function the document reference will not exist. Does anyone know how to solve this?

I am thinking to create a cloud function that is triggered when a photo is removed that deletes its document... but will have the same problem: the photo might be removed before the document is created.

And also I have thought to trigger this function only when the photo document is created, but will have problems if the client modifies my code and upload the image after the document is created.

1
If you have information to add to your question, you can just edit it using the edit link at the bottom instead of adding comments. - Doug Stevenson
Can't you simply use promises to chain those 2 events? - DVN-Anakin

1 Answers

2
votes

Why dont you call the Cloud Function from the client instead of using a storage trigger? You could call it when the storage upload has completed.

// Upload image to storage
const task = storageRef.put(blob);
...
// When the image is fully uploaded to the storage...
task.then(async () => {
   // Call the Cloud Function to verify the image
   callcloudfunction(storagepath)
   .then((isvalid) => {
     if(isvalid) {
       ...
       await firebase
         .getDatabase()
         .collection("posts")
         .doc(userId)
         .collection("userPosts")
         .add(data);
     }
   })
}

EDIT: if you want to prevent a hacker to bypass the check of the cloud function then change your rules to prevent any document creation in userPosts and add the document directly in the cloud function:

// Upload image to storage
const task = storageRef.put(blob);
...
// When the image is fully uploaded to the storage...
task.then(async () => {
   // Call the Cloud Function to verify the image and create the document
   callcloudfunction(storagepath, userId, data);
}