Skip to content

Commit 989a748

Browse files
committed
initial commit
0 parents  commit 989a748

File tree

6 files changed

+1459
-0
lines changed

6 files changed

+1459
-0
lines changed

LICENSE

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
2+
Version 2, December 2004
3+
4+
Copyright (C) 2021 Simon Csaba <sc.de@gmx.de>
5+
6+
Everyone is permitted to copy and distribute verbatim or modified
7+
copies of this license document, and changing it is allowed as long
8+
as the name is changed.
9+
10+
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
11+
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
12+
13+
0. You just DO WHAT THE FUCK YOU WANT TO.

README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Simple directory to Google Drive Backup (SdtGDB)
2+
## Features
3+
* This system will backup folders and upload them to Google Drive
4+
* Every backup is encrypted using [cryptify](https://www.npmjs.com/package/cryptify)
5+
* Backups can automatically done every hour
6+
7+
## Should I use this?
8+
Probably not tbh. It's quite complicated to set up, and the project only hase a very small and specific use-case.
9+
`Why is it called "Simple.." if it's complicated to set up? ~ I am very glad you asked, it's because the code is quite simple and it took not a very long time to develop this "thing".`
10+
11+
## Installation
12+
1. Clone this repo: `git clone https://github.com/SCDerox/simple-directory-to-gdrive-backup.git`
13+
2. Install dependencies `npm ci`
14+
3. Enabled the Google-Drive-API in your developer console, create and download the Oauth-Desktop-App-credentials and save them as `credentials.json` in the cloned directory.
15+
4. Create a configuration-file called `config.json` in the cloned directory and change the configure parameters (explained below).
16+
5. Then start the script as described below. The system should now ask you to follow a link and enter a token which goolge will issue you after finishing the authentication. Just follow the simple instructions in your console.
17+
18+
## Start the system
19+
* If you only want to back up once run `npm start` in the cloned directory
20+
* To ensure that backups are performed hourly, I suggest to use [pm2](https://pm2.keymetrics.io/): `pm2 start index.js`
21+
22+
## Configure
23+
You can change these parameters in the `config.json` you created earlier.
24+
* `key`: Password with which the ZIP should be encrypted
25+
* `prefix`: Optional prefix which should be put before every filenname
26+
* `folderID`: Your google-drive-folder-ID
27+
* `enabledHourlyUpload`: If enabled the script will backup your files hourly
28+
* `limitHoursTo`: Array of strings; Hours to limit the hourly upload to
29+
* `folders`: Array of the following objects:
30+
* `localFolder`: Path to your local folder to back up
31+
* `zipFolder`: Path to the backuped folder inside the ZIP
32+
33+
## How do I decrypt the encrypted file?
34+
You can simply use the [cryptify](https://www.npmjs.com/package/cryptify) -cli and remove the `.crypt`-extension from the filename.
35+
36+
## Acknowledgements
37+
The `drive.js` file is simply the module [drive-upload](https://www.npmjs.com/package/drive-upload), but I changed some things because it didn't always work on my system.

drive.js

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/*
2+
This file was "borrowed" from a module called drive-upload (https://www.npmjs.com/package/drive-upload) and modified a little bit, because it didn't work on my system
3+
*/
4+
const fs = require('fs');
5+
const readline = require('readline');
6+
const {google} = require('googleapis');
7+
const FileType = require('file-type');
8+
9+
const SCOPES = ['https://www.googleapis.com/auth/drive'];
10+
11+
let options = {
12+
credentials: './credentials.json',
13+
token: './token.json',
14+
permissions: {
15+
'type': 'anyone',
16+
'role': 'reader'
17+
}
18+
};
19+
20+
exports.setOptions = opts => {
21+
if (opts.driveFolder) {
22+
options['driveFolder'] = opts.driveFolder;
23+
}
24+
if (opts.permissions && Object.keys(opts.permissions).length > 0) {
25+
options['permissions'] = opts.permissions;
26+
}
27+
};
28+
29+
exports.store = (srcFile, destFile, callback) => {
30+
fs.readFile(options.credentials, (err, content) => {
31+
if (err) return console.log('Error loading client secret file:', err);
32+
authorize(JSON.parse(content), auth => {
33+
FileType.fromFile(srcFile)
34+
.then(fileData => {
35+
fileData = {ext: 'zip', mime: 'application/zip'};
36+
const drive = google.drive({version: 'v3', auth});
37+
const fileMetadata = {
38+
'name': destFile ? destFile.split('/').pop() : srcFile.split('/').pop(),
39+
parents: options.driveFolder ? [options.driveFolder] : null
40+
};
41+
const media = {
42+
mimeType: fileData.mime,
43+
body: fs.createReadStream(srcFile)
44+
};
45+
drive.files.create({
46+
resource: fileMetadata,
47+
media: media,
48+
fields: 'id'
49+
}, function (err, file) {
50+
if (err) {
51+
console.error(err);
52+
} else {
53+
const trans = options.permissions.role === 'owner';
54+
drive.permissions.create({
55+
fileId: file.data.id,
56+
requestBody: options.permissions,
57+
transferOwnership: trans
58+
});
59+
drive.files.get({
60+
fileId: file.data.id,
61+
fields: 'id,name,mimeType,parents,webContentLink,webViewLink,thumbnailLink,createdTime,size,imageMediaMetadata'
62+
}).then(response => {
63+
response.data['webLink'] = response.data.webContentLink.replace('&export=download', '');
64+
if (callback && typeof callback === 'function') {
65+
callback(response.data);
66+
} else {
67+
console.log('fileData:', response.data);
68+
}
69+
}).catch(err => console.log('Drive error:', err));
70+
}
71+
});
72+
})
73+
.catch(err => {
74+
});
75+
});
76+
});
77+
};
78+
79+
function authorize(credentials, callback) {
80+
const {client_secret, client_id, redirect_uris} = credentials.installed;
81+
const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uris[0]);
82+
fs.readFile(options.token, (err, token) => {
83+
if (err) {
84+
return getAccessToken(oAuth2Client, callback);
85+
}
86+
oAuth2Client.setCredentials(JSON.parse(token));
87+
callback(oAuth2Client);
88+
});
89+
}
90+
91+
function getAccessToken(oAuth2Client, callback) {
92+
const authUrl = oAuth2Client.generateAuthUrl({
93+
access_type: 'offline',
94+
scope: SCOPES
95+
});
96+
console.log('Authorize this app by visiting this url:', authUrl);
97+
const rl = readline.createInterface({
98+
input: process.stdin,
99+
output: process.stdout
100+
});
101+
rl.question('Enter the code from that page here: ', (code) => {
102+
rl.close();
103+
oAuth2Client.getToken(code, (err, token) => {
104+
if (err) return console.error('Error retrieving access token', err);
105+
oAuth2Client.setCredentials(token);
106+
107+
fs.writeFile(options.token, JSON.stringify(token), (err) => {
108+
if (err) return console.error(err);
109+
});
110+
callback(oAuth2Client);
111+
});
112+
});
113+
}

index.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
const driveUpload = require('./drive');
2+
const Zip = require('adm-zip');
3+
const config = require('./config.json');
4+
const Cryptify = require('cryptify');
5+
const schedule = require('node-schedule');
6+
7+
if (config.enabledHourlyUpload) {
8+
schedule.scheduleJob('1 * * * *', function () {
9+
if (config.limitHoursTo && config.limitHoursTo.length !== 0) {
10+
if (config.limitHoursTo.includes(new Date().getHours().toString())) backup();
11+
}else backup();
12+
})
13+
}
14+
15+
driveUpload.setOptions({
16+
driveFolder: config.folderID,
17+
permissions: {}
18+
});
19+
20+
async function backup() {
21+
return new Promise((async resolve => {
22+
const zip = new Zip();
23+
console.log('Backing up...');
24+
const date = new Date();
25+
const filename = `backup-${config.prefix}-${date.getDate()}.${date.getMonth() + 1}.${date.getFullYear()}-${date.getHours()}:${date.getMinutes()}.zip`;
26+
config.folders.forEach(f => {
27+
console.log(`Adding ${f.localFolder} to zip...`)
28+
zip.addLocalFolder(f.localFolder, f.zipFolder);
29+
});
30+
console.log(`Saving zip...`)
31+
zip.writeZip(`./tmp/${filename}`, async () => {
32+
console.log('Encrypting file..\n')
33+
await new Cryptify(`./tmp/${filename}`, config.key).encrypt();
34+
console.log('Encrypted successfully, uploading to GDrive...')
35+
driveUpload.store(`./tmp/${filename}`, `${filename}.crypt`, file => {
36+
console.log('Uploaded successfully');
37+
resolve(file);
38+
});
39+
});
40+
}));
41+
}
42+
43+
if (config.backupOnStart) backup();

0 commit comments

Comments
 (0)