onedrive cleaner

i used to use onedrive for a while

can not site i do like it but i'm fine

the problem: from time to time i'm taking "backups" from ongoing demos to onedrive and because i'm just drag and dropping folders from my desktop to onedrive it floods with all possible unwanted directories (aka .git)

to solve that afterwards i have created small script which traverses all directories in onedrive and removes those which should not live there

here is what i have ended up with:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>OneDriveCleaner</title>
    <script>
        HTMLElement.prototype.withAttribute = function (qualifiedName, value) {
            if (typeof value === 'string') {
                this.setAttribute(qualifiedName, value)
            } else {
                this[qualifiedName] = value
            }
            return this
        }

        HTMLElement.prototype.withAttributes = function (props) {
            for (const [key, value] of Object.entries(props)) {
                this.withAttribute(key, value)
            }
            return this
        }

        HTMLElement.prototype.withChild = function () {
            for(const child of arguments) {
                if (Array.isArray(child)) {
                    for(const item of child) {
                        this.append(item)
                    }
                } else {
                    this.append(child)
                }
            }
            return this
        }

        HTMLElement.prototype.withStyle = function (style, value) {
            this.style[style] = value
            return this
        }

        HTMLElement.prototype.withStyles = function (styles) {
            for (const [key, value] of Object.entries(styles)) {
                this.withStyle(key, value)
            }
            return this
        }

        HTMLElement.prototype.clear = function () {
            while (this.firstChild) {
                this.removeChild(this.lastChild);
            }
            return this
        }
    </script>
</head>
<body>
    <h3>OneDrive Cleaner</h3>
    <p><a href="https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/~/Overview/quickStartType~/null/sourceType/Microsoft_AAD_IAM/appId/afeca029-dbde-4adc-a730-01631aea7c09/objectId/5e24a0a6-955a-441a-9549-b42acc62f409/isMSAApp~/false/defaultBlade/Overview/appSignInAudience/PersonalMicrosoftAccount">app</a> - created only for personal accounts, after creation in authentication singla page applications redirection added and both checkboxes are checked so we can grab access token</p>

    <ol id="ol"></ol>

    <script>
        const ol = document.getElementById('ol')
        if (!document.location.hash) {
            var url = new URL('https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize')
            url.searchParams.set('client_id', 'afeca029-dbde-4adc-a730-01631aea7c09')
            url.searchParams.set('redirect_uri', 'http://localhost:3000/')
            url.searchParams.set('response_type', 'id_token token')
            url.searchParams.set('scope', 'openid Files.Read.All Files.ReadWrite.All')
            url.searchParams.set('access_type', 'online')
            url.searchParams.set('nonce', 123)
            window.location = url.toString()
        } else {
            const t = Date.now()
            // folders().then(print) // [{id: 'xxxxxxxxx', name: 'Backup'}, ...]
            iterate('45960A0985A834F0!861023')
        }

        async function iterate(id) {
            const lookup = ['node_modules', '.idea', '.git', '.vs', '.vscode', '.DS_Store', 'bin', 'obj', '.cache', 'build', 'publish', '.allure', '.gradle', '.ipynb_checkpoints', '__pycache__', 'TestResults']
            const items = await list(id)
            const el = (id ? document.getElementById(id)?.querySelector('OL') : ol) || ol
            for(const item of items) {
                el.appendChild(createElement('LI', {id: item.id, title: `${item.parentReference.path}/${item.name}`}, item.name))
                if (lookup.includes(item.name)) {
                    document.getElementById(item.id).appendChild(createElement('SPAN', {style: 'color:red'}, 'found'))
                    if (true || confirm(`delete ${item.parentReference.path}/${item.name}?`)) {
                        remove(id)
                    }
                } else if ('folder' in item) {
                    document.getElementById(item.id).appendChild(createElement('OL'))
                    try {
                        await iterate(item.id)
                    } catch(error) {
                        console.log('error', error, item)
                    }
                }
                window.scrollTo(0, document.body.scrollHeight)
            }
        }

        function print(items) {
            console.table(items.map(({id, name}) => ({id, name})))
        }

        function folders(id) {
            return list(id).then(items => items.filter(item => 'folder' in item))
        }

        async function list(id) {
            const access_token = new URLSearchParams(document.location.hash.substring(1)).get('access_token')
            const authorization = `Bearer ${access_token}`
            const url = id
                ? `https://graph.microsoft.com/v1.0/me/drive/items/${id}/children`
                : 'https://graph.microsoft.com/v1.0/me/drive/root/children'

            const response = await fetch(url, {headers: {authorization}})
            const json = await response.json()
            const {value} = json
            return value
        }

        function remove(id) {
            const access_token = new URLSearchParams(document.location.hash.substring(1)).get('access_token')
            const authorization = `Bearer ${access_token}`
            const url = `https://graph.microsoft.com/v1.0/me/drive/items/${id}`

            fetch(url, {method: 'DELETE', headers: {authorization}})
                // .then(r => r.json())
                .then(console.log)
                .catch(console.log)
        }

        function createElement(name, props = {}, children = undefined) {
            const el = document.createElement(name)
            for (const [key, value] of Object.entries(props)) {
                el.setAttribute(key, value)
            }
            if (!children) {
                return el
            }
            children = Array.isArray(children) ? children : [children]
            for(const child of children) {
                if (typeof child === 'function') {
                    el.appendChild(child())
                } else {
                    el.appendChild(document.createTextNode(child))
                }
            }
            return el
        }
    </script>
</body>
</html>

to run such app we need some kind of server thats why it requires node and express, aka:

const express = require('express')

const app = express()

app.use(express.static('.'))

app.listen(3000)