Backing up Files with CodiMD

I recently started renting a VPS server to host some of the ethical alternatives to the typical host of surveillance services.

One service that I have enjoyed in the past is Dropbox Paper. However this “free” (as in beer, not in freedom) service preys on your personal data, just like its big brother Dropbox. To me this is not acceptable, so I decided to give CodiMD a try. CodiMD is an evolution of HackMD, a free software alternative inspired in its free software cousin Etherpad. CodiMD is licensed under Affero General Public License¹. CodiMD has a demo site where you can go to play and test all of its (extensive) features.

CodiMD features

I am still discovering the intricacies of this software, and I am already in awe of the quality of the product. The feature that I am missing is the ability to download my MD documents, so I can archive them in my home NAS. For this reason I have created this small nodejs script, that I want to share with you as AGPL as well. This code is a bit messy but I wanted something quick that didn’t have any NPM dependency to run, only nodejs core.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#!/usr/bin/node

const { request } = require('https');
const querystring = require('querystring');
const { writeFileSync } = require('fs');

const host = process.argv[2];
const email = process.argv[3];
const password = process.argv[4];

if (!host || !email || !password) {
  console.error(`Usage ${process.argv[1]} <host> <username> <password>`);
  process.exit(1);
}

const baseOptions = { host, port: 443 };

const postData = querystring.stringify({ email, password });

const req = request({
    ...baseOptions,
    path: '/login',
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
      'Content-Length': Buffer.byteLength(postData)
    }
  }, (res) => {
  const cookie = res.headers['set-cookie'][0];
  baseOptions.headers = { Cookie: cookie };
  const req2 = request({
    ...baseOptions,
    path: '/history',
  }, res2 => {
    let body = '';
    res2.on('data', chunk => { body += chunk });
    res2.on('end', () => {
      let parsed = {};
      try {
        parsed = JSON.parse(body);
      }
      catch (e) {
        console.error('Invalid credentials');
        process.exit(2);
      }
      const reqs = parsed.history.map(({ text, id }) => {
        const fileName = `${text.toLowerCase().replace(/\s+/g, '-')}.md`;
        return request({
          ...baseOptions,
          path: `/${id}/download`,
        }, rsp => {
          let bd = '';
          rsp.on('data', chunk => { bd += chunk });
          rsp.on('end', () => {
            writeFileSync(fileName, bd);
            console.log(`Written file "${fileName}".`);
          });
        });
      });
      reqs.map(r => r.end());
    });
  });
  req2.end();
  res.setEncoding('utf8');
});

req.on('error', (e) => {
  console.error(`problem with request: ${e.message}`);
});

// Write data to request body
req.write(postData);
req.end();

Executing this script will download the files to your local:

1
node backup-notes.js text.mateuaguilo.com email@example.org fooBarIsPassword

¹ AGPL is considered one of the most freedom friendly licenses. Companies like Google prohibit their use because of its nature.

👋 Subscribe!

If you like this content, you might consider subscribing to this site's RSS feed. This is the best way to stay up to date with new content on the site. If you don't know how to subscribe, you can check this tutorial.

Comments