Skip to content

Commit cba5d1d

Browse files
authored
Merge pull request #1496 from Hirobreak/backup-worker
Backup worker
2 parents 831a9a5 + 05ca477 commit cba5d1d

File tree

12 files changed

+869
-211
lines changed

12 files changed

+869
-211
lines changed

electron_app/src/BackupManager.js

Lines changed: 1 addition & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,9 @@ const zlib = require('zlib');
44
const { app } = require('electron');
55
const myAccount = require('./Account');
66
const { databasePath } = require('./database/DBEmodel');
7-
const { backupFilenameRegex } = require('./utils/RegexUtils');
8-
const { createPathRecursive, store } = require('./utils/FileUtils');
7+
const { createPathRecursive } = require('./utils/FileUtils');
98
const {
109
decryptStreamFileWithPassword,
11-
encryptStreamFile,
12-
exportEncryptDatabaseToFile,
13-
generateKeyAndIvFromPassword,
1410
importDatabaseFromFile
1511
} = require('./database/DBEexporter');
1612

@@ -21,13 +17,6 @@ const STREAM_SIZE = 512 * 1024;
2117
const TempBackupFolderName = 'BackupData';
2218
const TempBackupDirectory = path.join(databasePath, '..', TempBackupFolderName);
2319

24-
const ExportUnencryptedFilename = path.join(
25-
TempBackupDirectory,
26-
'unencrypt.exp'
27-
);
28-
const ExportZippedFilename = path.join(TempBackupDirectory, 'zipped.exp');
29-
const ExportEncryptedFilename = path.join(TempBackupDirectory, 'encrypted.exp');
30-
3120
const RestoreUnzippedFilename = path.join(TempBackupDirectory, 'unzipped.res');
3221
const RestoreUnencryptedFilename = path.join(
3322
TempBackupDirectory,
@@ -61,18 +50,6 @@ const removeTempBackupDirectoryRecursive = pathToDelete => {
6150
}
6251
};
6352

64-
const cleanPreviousBackupFilesInFolder = pathToClean => {
65-
try {
66-
const files = fs.readdirSync(pathToClean);
67-
const filtered = files.filter(name => backupFilenameRegex.test(name));
68-
filtered.forEach(filename => {
69-
fs.unlinkSync(path.join(pathToClean, filename));
70-
});
71-
} catch (cleanErr) {
72-
return cleanErr;
73-
}
74-
};
75-
7653
/* Default Directory
7754
----------------------------- */
7855
function getUsername() {
@@ -96,19 +73,6 @@ const createDefaultBackupFolder = () => {
9673
/* Methods
9774
----------------------------- */
9875

99-
const zipStreamFile = ({ inputFile, outputFile }) => {
100-
return new Promise((resolve, reject) => {
101-
const highWaterMark = STREAM_SIZE;
102-
const reader = fs.createReadStream(inputFile, { highWaterMark });
103-
const writer = fs.createWriteStream(outputFile);
104-
reader
105-
.pipe(zlib.createGzip())
106-
.pipe(writer)
107-
.on('error', reject)
108-
.on('finish', resolve);
109-
});
110-
};
111-
11276
const unzipStreamFile = ({ inputFile, outputFile }) => {
11377
return new Promise((resolve, reject) => {
11478
const highWaterMark = STREAM_SIZE;
@@ -131,95 +95,6 @@ const prepareBackupFiles = () => {
13195
}
13296
};
13397

134-
const getFileSizeInBytes = filename => {
135-
try {
136-
const stats = fs.statSync(filename);
137-
return stats.size;
138-
} catch (error) {
139-
return 0;
140-
}
141-
};
142-
143-
/* Export Backup
144-
----------------------------- */
145-
const exportBackupUnencrypted = async ({
146-
backupPath,
147-
accountObj,
148-
progressCallback
149-
}) => {
150-
try {
151-
try {
152-
await exportEncryptDatabaseToFile({
153-
outputPath: ExportUnencryptedFilename,
154-
accountObj,
155-
progressCallback
156-
});
157-
} catch (dbErr) {
158-
throw new Error('Failed to export database');
159-
}
160-
// Compress backup file
161-
try {
162-
await zipStreamFile({
163-
inputFile: ExportUnencryptedFilename,
164-
outputFile: ExportZippedFilename
165-
});
166-
} catch (zipErr) {
167-
throw new Error('Failed to compress backup file');
168-
}
169-
// Move to destination
170-
try {
171-
cleanPreviousBackupFilesInFolder(path.join(backupPath, '..'));
172-
await store(backupPath, fs.readFileSync(ExportZippedFilename));
173-
} catch (fileErr) {
174-
throw new Error('Failed to move backup file');
175-
}
176-
return getFileSizeInBytes(backupPath);
177-
} catch (exportBackupError) {
178-
throw exportBackupError;
179-
} finally {
180-
removeTempBackupDirectoryRecursive(TempBackupDirectory);
181-
}
182-
};
183-
184-
const exportBackupEncrypted = async ({ backupPath, password, accountObj }) => {
185-
try {
186-
// Export database
187-
try {
188-
await exportEncryptDatabaseToFile({
189-
outputPath: ExportUnencryptedFilename,
190-
accountObj
191-
});
192-
} catch (dbErr) {
193-
throw new Error('Failed to export database');
194-
}
195-
// GZip & Encrypt
196-
try {
197-
const { key, iv, salt } = generateKeyAndIvFromPassword(password);
198-
await encryptStreamFile({
199-
inputFile: ExportUnencryptedFilename,
200-
outputFile: ExportEncryptedFilename,
201-
key,
202-
iv,
203-
salt
204-
});
205-
} catch (encryptErr) {
206-
throw new Error('Failed to encrypt backup');
207-
}
208-
// Move to destination
209-
try {
210-
cleanPreviousBackupFilesInFolder(path.join(backupPath, '..'));
211-
await store(backupPath, fs.readFileSync(ExportEncryptedFilename));
212-
} catch (fileErr) {
213-
throw new Error('Failed to create backup file');
214-
}
215-
return getFileSizeInBytes(backupPath);
216-
} catch (exportBackupError) {
217-
throw exportBackupError;
218-
} finally {
219-
removeTempBackupDirectoryRecursive(TempBackupDirectory);
220-
}
221-
};
222-
22398
/* Restore Backup
22499
----------------------------- */
225100
const restoreUnencryptedBackup = async ({ filePath }) => {
@@ -280,8 +155,6 @@ const restoreEncryptedBackup = async ({ filePath, password }) => {
280155
module.exports = {
281156
createDefaultBackupFolder,
282157
getDefaultBackupFolder,
283-
exportBackupUnencrypted,
284-
exportBackupEncrypted,
285158
prepareBackupFiles,
286159
restoreUnencryptedBackup,
287160
restoreEncryptedBackup
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
const fs = require('fs');
2+
const crypto = require('crypto');
3+
const zlib = require('zlib');
4+
5+
const CIPHER_ALGORITHM = 'aes-128-cbc';
6+
const STREAM_SIZE = 512 * 1024;
7+
const DEFAULT_KEY_LENGTH = 16;
8+
9+
const zipStreamFile = ({ inputFile, outputFile }) => {
10+
return new Promise((resolve, reject) => {
11+
const highWaterMark = STREAM_SIZE;
12+
const reader = fs.createReadStream(inputFile, { highWaterMark });
13+
const writer = fs.createWriteStream(outputFile);
14+
reader
15+
.pipe(zlib.createGzip())
16+
.pipe(writer)
17+
.on('error', reject)
18+
.on('finish', resolve);
19+
});
20+
};
21+
22+
const encryptStreamFile = ({ inputFile, outputFile, key, iv, salt }) => {
23+
return new Promise((resolve, reject) => {
24+
const reader = fs.createReadStream(inputFile, {
25+
highWaterMark: STREAM_SIZE
26+
});
27+
const writer = fs.createWriteStream(outputFile);
28+
if (salt) writer.write(salt);
29+
writer.write(iv);
30+
31+
reader
32+
.pipe(zlib.createGzip())
33+
.pipe(crypto.createCipheriv(CIPHER_ALGORITHM, key, iv))
34+
.pipe(writer)
35+
.on('error', reject)
36+
.on('finish', resolve);
37+
});
38+
};
39+
40+
const generateKeyAndIvFromPassword = (password, customSalt) => {
41+
const salt = customSalt || crypto.randomBytes(8);
42+
const iterations = 10000;
43+
const pbkdf2Name = 'sha256';
44+
const key = crypto.pbkdf2Sync(
45+
Buffer.from(password, 'utf8'),
46+
salt,
47+
iterations,
48+
DEFAULT_KEY_LENGTH,
49+
pbkdf2Name
50+
);
51+
const iv = crypto.randomBytes(DEFAULT_KEY_LENGTH);
52+
return { key, iv, salt };
53+
};
54+
55+
module.exports = {
56+
zipStreamFile,
57+
generateKeyAndIvFromPassword,
58+
encryptStreamFile
59+
};
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
const fs = require('fs');
2+
const path = require('path');
3+
4+
const removeTempBackupDirectoryRecursive = pathToDelete => {
5+
if (fs.existsSync(pathToDelete)) {
6+
fs.readdirSync(pathToDelete).forEach(file => {
7+
const currentPath = path.join(pathToDelete, file);
8+
if (fs.lstatSync(currentPath).isDirectory()) {
9+
removeTempBackupDirectoryRecursive(currentPath);
10+
} else {
11+
fs.unlinkSync(currentPath);
12+
}
13+
});
14+
fs.rmdirSync(pathToDelete);
15+
}
16+
};
17+
18+
const checkTempBackupDirectory = tempBackupDirectory => {
19+
try {
20+
if (fs.existsSync(tempBackupDirectory)) {
21+
removeTempBackupDirectoryRecursive(tempBackupDirectory);
22+
}
23+
fs.mkdirSync(tempBackupDirectory);
24+
} catch (e) {
25+
throw new Error('Unable to create temp folder');
26+
}
27+
};
28+
29+
const getFileSizeInBytes = filename => {
30+
try {
31+
const stats = fs.statSync(filename);
32+
return stats.size;
33+
} catch (error) {
34+
return 0;
35+
}
36+
};
37+
38+
const copyDatabase = (dbPath, tempBackupDirectory) => {
39+
const dbshm = `${dbPath}-shm`;
40+
const dbwal = `${dbPath}-wal`;
41+
42+
if (!fs.existsSync(dbPath)) {
43+
throw new Error('No Database Found');
44+
}
45+
46+
fs.copyFileSync(dbPath, path.join(tempBackupDirectory, 'CriptextEncrypt.db'));
47+
48+
if (fs.existsSync(dbshm)) {
49+
fs.copyFileSync(
50+
dbshm,
51+
path.join(tempBackupDirectory, 'CriptextEncrypt.db-shm')
52+
);
53+
}
54+
if (fs.existsSync(dbwal)) {
55+
fs.copyFileSync(
56+
dbwal,
57+
path.join(tempBackupDirectory, 'CriptextEncrypt.db-wal')
58+
);
59+
}
60+
};
61+
62+
module.exports = {
63+
getFileSizeInBytes,
64+
checkTempBackupDirectory,
65+
removeTempBackupDirectoryRecursive,
66+
copyDatabase
67+
};

0 commit comments

Comments
 (0)