Greasy Fork is available in English.

Wasabi Bucket Usage

Inject Wasabi CSV utilisation stats into your dashboard

// ==UserScript==
// @name         Wasabi Bucket Usage
// @namespace    https://mcgown.dev
// @version      0.1
// @description  Inject Wasabi CSV utilisation stats into your dashboard
// @author       Stewart McGown <[email protected]>
// @match        https://console.wasabisys.com/*
// @grant        none
// ==/UserScript==
function formatBytes(bytes, decimals = 2) {
    if (bytes === 0) return '0 Bytes';

    const k = 1024;
    const dm = decimals < 0 ? 0 : decimals;
    const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];

    const i = Math.floor(Math.log(bytes) / Math.log(k));

    return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
function CSVToArray( strData, strDelimiter ){
    // Check to see if the delimiter is defined. If not,
    // then default to comma.
    strDelimiter = (strDelimiter || ",");

    // Create a regular expression to parse the CSV values.
    var objPattern = new RegExp(
        (
            // Delimiters.
            "(\\" + strDelimiter + "|\\r?\\n|\\r|^)" +

            // Quoted fields.
            "(?:\"([^\"]*(?:\"\"[^\"]*)*)\"|" +

            // Standard fields.
            "([^\"\\" + strDelimiter + "\\r\\n]*))"
        ),
        "gi"
    );


    // Create an array to hold our data. Give the array
    // a default empty first row.
    var arrData = [[]];

    // Create an array to hold our individual pattern
    // matching groups.
    var arrMatches = null;


    // Keep looping over the regular expression matches
    // until we can no longer find a match.
    while (arrMatches = objPattern.exec( strData )){

        // Get the delimiter that was found.
        var strMatchedDelimiter = arrMatches[ 1 ];

        // Check to see if the given delimiter has a length
        // (is not the start of string) and if it matches
        // field delimiter. If id does not, then we know
        // that this delimiter is a row delimiter.
        if (
            strMatchedDelimiter.length &&
            strMatchedDelimiter !== strDelimiter
        ){

            // Since we have reached a new row of data,
            // add an empty row to our data array.
            arrData.push( [] );

        }

        var strMatchedValue;

        // Now that we have our delimiter out of the way,
        // let's check to see which kind of value we
        // captured (quoted or unquoted).
        if (arrMatches[ 2 ]){

            // We found a quoted value. When we capture
            // this value, unescape any double quotes.
            strMatchedValue = arrMatches[ 2 ].replace(
                new RegExp( "\"\"", "g" ),
                "\""
            );

        } else {

            // We found a non-quoted value.
            strMatchedValue = arrMatches[ 3 ];

        }


        // Now that we have our value string, let's add
        // it to the data array.
        arrData[ arrData.length - 1 ].push( strMatchedValue );
    }

    // Return the parsed data.
    return( arrData );
}

let lastBuckets = [];

(async function() {
    const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms))

    while (true) {

        await sleep(200)

        const buckets = [...document.querySelectorAll('td:nth-child(2)')]

        console.log(buckets)
        if (!buckets.length || buckets.every((b, i) => lastBuckets[i] === b)) continue;
        const auth = JSON.parse(localStorage.getItem('ba-auth'));
        const authHeader = `${auth.accessKeyId}:${auth.secretAccessKey}`

   if (!lastBuckets.length) {
       const headerRow = document.querySelector('#storageTable > thead > tr');
       const elem = headerRow.querySelector('th:last-child').cloneNode()
       const elem2 = elem.cloneNode(), elem3 = elem2.cloneNode();
       elem.id = 'egress'
       elem.innerHTML = 'Egress (month)'
       elem2.innerHTML = 'Size'
       elem3.innerHTML = 'Objects'
       headerRow.appendChild(elem)
       headerRow.appendChild(elem2)
       headerRow.appendChild(elem3)
   }


        lastBuckets = buckets.slice()


        for (const bucket of buckets) {

            if (bucket.parentElement.querySelector('#egress')) continue;

            fetch(`https://billing-service.wasabisys.com/v1/bucket/by-name/${bucket.innerText}/utilization?csv=true`, {
                "headers": {
                    "accept": "application/json, text/plain, */*",
                    "accept-language": "en-US,en;q=0.9",
                    "sec-ch-ua": "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"90\", \"Google Chrome\";v=\"90\"",
                    "sec-ch-ua-mobile": "?0",
                    "sec-fetch-dest": "empty",
                    "sec-fetch-mode": "cors",
                    "sec-fetch-site": "same-site",
                    "x-wasabi-authorization": authHeader
                },
                "referrer": "https://console.wasabisys.com/",
                "referrerPolicy": "strict-origin-when-cross-origin",
                "body": null,
                "method": "GET",
                "mode": "cors",
                "credentials": "omit",
                "cache": "force-cache"
            }).then(r => r.text()).then(async (res) => {



                const csv = CSVToArray(res);


                const totalIdx = csv[0].indexOf('BillableActiveStorageBytes')
                const objectsIdx = csv[0].indexOf('NumBillableActiveStorageObjects')
                const egressIdx = csv[0].indexOf('EgressBytes')

                const totalStorage = +csv[1][totalIdx]
                const objects = +csv[1][objectsIdx]

                let egressBytes = 0;
                for (const row of csv.slice(1, 30)) {
                    const val = +row[egressIdx];
                    if (!Number.isNaN(val) && typeof val === 'number') {
                        egressBytes += val;
                    }

                }

                const elem = document.createElement('td')
                elem.id = 'egress'
                elem.innerHTML = `${formatBytes(egressBytes)}`
    bucket.parentElement.appendChild(elem)
      const elem2 = elem.cloneNode()
      elem2.innerHTML = `${formatBytes(totalStorage)}`
    bucket.parentElement.appendChild(elem2)
      const elem3 = elem.cloneNode()
      elem3.innerHTML = `${objects}`
    bucket.parentElement.appendChild(elem3)
  })

   }

    }

})();