Deleting Wasabi CDN Bucket 204 Error Not Deleting

427 Views Asked by At

So I'm trying to programmatically delete Wasabi CDN objects from one of my buckets. My request is sending back 204 and showing success but nothing is being moved/deleted. I'm using node/javascript to do this.

Here is my function that is supposed to delete the bucket.

import expressAsyncHandler from 'express-async-handler'
import User from '../../models/User.js'
import axios from 'axios'
import aws4 from 'aws4'

/**
 * @desc:   THIS is going to be a testing function that will be added into admin delete user and all related docs.
 * @route:  DELETE
 * @access: Private - Admin Route for when deleting user will delete the CDN username bucket aswell
 * @goalHere: The goal of this function is to delete the user bucket from CDN. So that if we d
 * @comment: This is a testing function that will be added into deleteVideo.js. Unless we just await this function in deleteVideo.js.
 */

export const deleteUserBucket = expressAsyncHandler(async (req, res, next) => {
  try {
    const user = await User.findOne({ username: req.user.username })
    const username = user.username

    let request = {
      host: process.env.CDN_HOST,
      method: 'DELETE',
      url: `https://s3.wasabisys.com/truthcasting/${username}?force_delete=true`,
      path: `/truthcasting/${username}?force_delete=true`,
      headers: {
        'Content-Type': 'application/json',
      },
      service: 's3',
      region: 'us-east-1',
      maxContentLength: Infinity,
      maxBodyLength: Infinity,
    }

    let signedRequest = aws4.sign(request, {
      accessKeyId: process.env.CDN_KEY,
      secretAccessKey: process.env.CDN_SECRET,
    })

    //delete the Host and Content-Length headers
    delete signedRequest.headers.Host
    delete signedRequest.headers['Content-Length']

    const response = await axios(signedRequest)
    console.log(response.data)
    console.log('successfully deleted user bucket', response.status)

    return res.status(200).json({
      message: `Successfully deleted user bucket`,
    })
  } catch (error) {
    console.log(error)

    return res.status(500).json({
      message: `Problem with deleting user bucket`,
    })
  }
})

export default deleteUserBucket

When I send the http DELETE request in POSTMAN to {{dev}}api/admin/deleteuserbucket it then gives me a response of 204 ok and this is the response.

{
    "message": "Successfully deleted user bucket"
}

I then go to my Wasabi CDN Buckets to check if it is deleted, in this case it's goodstock and it's still there. Feel like I'm missing something dumb here.

2

There are 2 best solutions below

0
On BEST ANSWER

So for deleting contents that're inside of your root bucket, you need to point it to the complete object. That being said the way I had it set up in the original post code was returning 204("which is expected from Wasabi API") and not deleting anything due to the fact that I wasn't pointing it to the complete object path. Also I've found out that if you want to do batch delete instead of deleting one file, one by one you can use the aws-sdk node package to do a get request to your object, then use that response to loop through the object and remove what you need.. Here is an example. Hopefully this can help someone in the near future.

import expressAsyncHandler from 'express-async-handler'
import User from '../../models/User.js'
import axios from 'axios'
import aws4 from 'aws4'
import errorHandler from '../../middleware/error.js'
import AWS from 'aws-sdk'

/**
 * @desc:   THIS is going to be a testing function that will be added into admin delete user and all related docs.
 * @route:  DELETE
 * @access: Private - Admin Route for when deleting user will delete the CDN username bucket aswell
 * @goalHere: The goal of this function is to delete the user bucket from CDN. So that if we d
 * @comment: This is a testing function that will be added into deleteVideo.js. Unless we just await this function in deleteVideo.js.
 */

export const deleteUserBucket = expressAsyncHandler(async (req, res, next) => {
  const username = req.body.username
  try {
    // Connection
    // This is how you can use the .aws credentials file to fetch the credentials
    const credentials = new AWS.SharedIniFileCredentials({
      profile: 'wasabi',
    })
    AWS.config.credentials = credentials

    // This is a configuration to directly use a profile from aws credentials file.
    AWS.config.credentials.accessKeyId = process.env.CDN_KEY
    AWS.config.credentials.secretAccessKey = process.env.CDN_SECRET

    // Set the AWS region. us-east-1 is default for IAM calls.
    AWS.config.region = 'us-east-1'

    // Set an endpoint.
    const ep = new AWS.Endpoint('s3.wasabisys.com')

    // Create an S3 client
    const s3 = new AWS.S3({ endpoint: ep })

    // The following example retrieves an object for an S3 bucket.
    // set the details for the bucket and key
    const object_get_params = {
      Bucket: 'truthcasting',
      Prefix: `${username}/`,
      //Key: `cnfishead/videos/4:45:14-PM-5-6-2022-VIDDYOZE-Logo-Drop.mp4`,
      // Key: `cnfishead/images/headshot.04f99695-photo.jpg`,
    }

    // get the object that we just uploaded.
    // get the uploaded test_file
    // s3.getObject(object_get_params, function (err, data) {
    //   if (err) console.log(err, err.stack) // an error occurred
    //   else console.log(data) // successful response
    // })

    // get the object that we just uploaded.
    // get the uploaded test_file
    await s3.listObjectsV2(object_get_params, (err, data) => {
      if (err) {
        console.log(err)
        return res.status(500).json({
          message: 'Error getting object',
          error: err,
        })
      } else {
        console.log(data)
        //TODO Change this for loop to a async for loop. Like this: for await (const file of data.Contents) { }
        for (let i = 0; i < data.Contents.length; i++) {
          const object_delete_params = {
            Bucket: 'truthcasting',
            Key: data.Contents[i].Key,
          }
          s3.deleteObject(object_delete_params, (err, data) => {
            if (err) {
              console.log(err)
              return res.status(500).json({
                message: 'Error deleting object',
                error: err,
              })
            } else {
              console.log(data)
            }
          })
        }
        if (data.IsTruncated) {
          console.log('Truncated')
          getObjectFromBucket(req, res, next)
        }
        //console.log('Not Truncated')
        res.status(200).json({
          message: `Successfully retrieved + deleted ${data.Contents.length} objects`,
          data: data,
        })
      }
    })
  } catch (error) {
    console.log(error)
    errorHandler(error, req, res)
  }
})

export default deleteUserBucket

5
On

In S3, the delete bucket API call return 204 No content and an empty response body on successful delete.

With that URL, you are making a delete request on an object and not the bucket:

URL: `https://s3.wasabisys.com/truthcasting/${username}?force_delete=true`

The username passed in this URL will be interpreted as a key and S3 will look for an object in the root of the bucket.

Also why not using the AWS SDK to delete the bucket instead of reimplementing all of this. Check the AWS docs for this.