Electron Adventures: Episode 5: Display Free Disk Space
Tags: electron, javascript
Published: 2021-07-29 15:21:09

The approach used in previous episode, of constructing an enormous `data` URL, is not very scalable.

Electron offers surprisingly many alternatives, and unfortunately you'll need to know more than one, because the more powerful ones (which we'll get to eventually) need some careful work to remain secure.

### Find free disk space

If you run `df` command, you get list of disks and their used and total space, but in some insane format, with many systems showing data in 512 byte blocks, which haven't been used by any disk format since the 1970s or so.

Passing an option like `-k` (kilobytes) or `-m` (megabytes) or `-h` (human readable) to `df` restores a bit of sanity. But with any of them `df` is still hard to parse, and dependent on operating system.

So first we'll need to tweak the command to `df -kP`. Then we'll need to apply a bunch of regular expressions to its outputs to split it into columns.

If you search Stack Overflow, you'll see many wrong ways to do this, which just split on spaces would work, but paths can absolutely contain spaces, and will do that on most operating systems these days.
I got this regular expression technique from `node-df` package, and it's good enough for what we do here. Unfortunately that package [is insecure and has some other issues](https://github.com/adriano-di-giovanni/node-df/pulls), so I don't want to depend on it.

```javascript
let child_process = require("child_process")

let runCommand = (command) => {
  return child_process.execSync(command).toString().trim()
}

function df() {
  let output = runCommand("df -kP")
  let rows = output
    .split(/[\r\n]+/)
    .slice(1)
  return rows.map(row => (
    row
      .replace(/\s+(\d+)/g, '\t$1')
      .replace(/\s+\//g, '\t/')
      .split(/\t/)
  ))
}
```

### Send data to the frontend

A fairly secure way to send data to the frontend, is for backend to get any data, serialize it to JSON, and then execute callback function in the frontend process.

As it's one way going from more controller to less controlled environment, it is relatively easy to get right, even if it's fairly limited.

```javascript
let { app, BrowserWindow } = require("electron")

function createWindow() {
  let payload = JSON.stringify(df())
  let win = new BrowserWindow({})
  win.maximize()
  win.loadFile("index.html")

  win.webContents.on('did-finish-load', function () {
    win.webContents.executeJavaScript(`displayFreeDiskSpace(${payload})`);
  });
}

app.on("ready", createWindow)

app.on("window-all-closed", () => {
  app.quit()
})
```

### index.html

This one will be really boring, and taken almost directly from one of the previous episodes:

```html
<!DOCTYPE html>
<html>
  <body>
    <h1>Free disk space</h1>
    <div id="info"></div>
    <script src="app.js"></script>
  </body>
</html>
```

### Display received data

And finally `app.js`, which gets data as a callback, and formats it to HTML.

```javascript
function displayFreeDiskSpace(data) {
  let info = document.querySelector("#info")
  for (let row of data) {
    let total = parseInt(row[1])
    let used = parseInt(row[2])
    let path = row[5]
    let percent
    if (total !== 0) {
      percent = Math.round(100 * used / total)
    } else {
      percent = 0
    }
    let div = document.createElement("div")
    div.append(`${path}: ${percent}% used (${used}/${total})`)
    info.append(div)
  }
}
```

### Result

And here's what we got:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qj0xgifvdeu3g8l1tuw1.png)

[All the code for the episode is here](https://github.com/taw/electron-adventures/tree/master/episode-05-display-free-disk-space).

We have just two problems.

First problem is that especially on OSX everything is so heavily virtualized, that none of the numbers we get mean all that much anymore.

And the second problem is that this is a terribly boring way to visualize disk space.

We'll ignore the first one, and we'll be fixing the second one in the next episode.