2017-10-22 09:10:33 +08:00
|
|
|
"use strict";
|
|
|
|
|
2018-04-03 08:46:46 +08:00
|
|
|
const dateUtils = require('./date_utils');
|
2018-04-02 09:27:46 +08:00
|
|
|
const optionService = require('./options');
|
2017-10-15 11:31:44 +08:00
|
|
|
const fs = require('fs-extra');
|
2017-10-24 11:30:23 +08:00
|
|
|
const dataDir = require('./data_dir');
|
2017-10-25 10:17:48 +08:00
|
|
|
const log = require('./log');
|
2018-04-02 09:27:46 +08:00
|
|
|
const syncMutexService = require('./sync_mutex');
|
2020-06-07 16:20:48 +08:00
|
|
|
const attributeService = require('./attributes');
|
2018-03-29 11:41:22 +08:00
|
|
|
const cls = require('./cls');
|
2020-06-21 05:09:34 +08:00
|
|
|
const Database = require('better-sqlite3');
|
2017-10-15 11:31:44 +08:00
|
|
|
|
2020-06-20 18:31:38 +08:00
|
|
|
function regularBackup() {
|
2020-07-04 06:20:23 +08:00
|
|
|
cls.init(() => {
|
|
|
|
periodBackup('lastDailyBackupDate', 'daily', 24 * 3600);
|
2018-07-30 22:40:50 +08:00
|
|
|
|
2020-07-04 06:20:23 +08:00
|
|
|
periodBackup('lastWeeklyBackupDate', 'weekly', 7 * 24 * 3600);
|
2018-07-30 22:40:50 +08:00
|
|
|
|
2020-07-04 06:20:23 +08:00
|
|
|
periodBackup('lastMonthlyBackupDate', 'monthly', 30 * 24 * 3600);
|
|
|
|
});
|
2018-07-30 22:40:50 +08:00
|
|
|
}
|
|
|
|
|
2020-06-20 18:31:38 +08:00
|
|
|
function periodBackup(optionName, fileName, periodInSeconds) {
|
2017-12-11 01:56:59 +08:00
|
|
|
const now = new Date();
|
2020-06-20 18:31:38 +08:00
|
|
|
const lastDailyBackupDate = dateUtils.parseDateTime(optionService.getOption(optionName));
|
2017-10-15 11:31:44 +08:00
|
|
|
|
2018-07-30 22:40:50 +08:00
|
|
|
if (now.getTime() - lastDailyBackupDate.getTime() > periodInSeconds * 1000) {
|
2020-06-20 18:31:38 +08:00
|
|
|
backupNow(fileName);
|
2017-12-11 01:56:59 +08:00
|
|
|
|
2020-06-20 18:31:38 +08:00
|
|
|
optionService.setOption(optionName, dateUtils.utcNowDateTime());
|
2017-10-15 11:31:44 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-21 05:09:34 +08:00
|
|
|
async function copyFile(backupFile) {
|
2020-05-30 03:55:08 +08:00
|
|
|
const sql = require('./sql');
|
|
|
|
|
2020-06-03 05:13:55 +08:00
|
|
|
try {
|
|
|
|
fs.unlinkSync(backupFile);
|
|
|
|
} catch (e) {
|
|
|
|
} // unlink throws exception if the file did not exist
|
|
|
|
|
2020-06-21 05:09:34 +08:00
|
|
|
await sql.dbConnection.backup(backupFile);
|
2020-06-03 05:13:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
async function backupNow(name) {
|
2018-01-05 10:37:36 +08:00
|
|
|
// we don't want to backup DB in the middle of sync with potentially inconsistent DB state
|
2020-06-21 05:09:34 +08:00
|
|
|
return await syncMutexService.doExclusively(async () => {
|
2018-07-30 22:40:50 +08:00
|
|
|
const backupFile = `${dataDir.BACKUP_DIR}/backup-${name}.db`;
|
2017-10-25 10:17:48 +08:00
|
|
|
|
2020-06-21 05:09:34 +08:00
|
|
|
await copyFile(backupFile);
|
2020-05-30 03:55:08 +08:00
|
|
|
|
2020-06-21 05:09:34 +08:00
|
|
|
log.info("Created backup at " + backupFile);
|
2017-10-15 11:31:44 +08:00
|
|
|
|
2020-05-30 03:55:08 +08:00
|
|
|
return backupFile;
|
2017-10-15 11:31:44 +08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-06-21 05:09:34 +08:00
|
|
|
async function anonymize() {
|
2020-06-03 05:13:55 +08:00
|
|
|
if (!fs.existsSync(dataDir.ANONYMIZED_DB_DIR)) {
|
|
|
|
fs.mkdirSync(dataDir.ANONYMIZED_DB_DIR, 0o700);
|
|
|
|
}
|
|
|
|
|
|
|
|
const anonymizedFile = dataDir.ANONYMIZED_DB_DIR + "/" + "anonymized-" + dateUtils.getDateTimeForFile() + ".db";
|
|
|
|
|
2020-06-21 05:09:34 +08:00
|
|
|
await copyFile(anonymizedFile);
|
2020-06-03 05:13:55 +08:00
|
|
|
|
2020-06-21 05:09:34 +08:00
|
|
|
const db = new Database(anonymizedFile);
|
2020-06-03 05:13:55 +08:00
|
|
|
|
2020-06-21 05:09:34 +08:00
|
|
|
db.prepare("UPDATE api_tokens SET token = 'API token value'").run();
|
|
|
|
db.prepare("UPDATE notes SET title = 'title'").run();
|
|
|
|
db.prepare("UPDATE note_contents SET content = 'text' WHERE content IS NOT NULL").run();
|
|
|
|
db.prepare("UPDATE note_revisions SET title = 'title'").run();
|
|
|
|
db.prepare("UPDATE note_revision_contents SET content = 'text' WHERE content IS NOT NULL").run();
|
2020-06-07 16:20:48 +08:00
|
|
|
|
|
|
|
// we want to delete all non-builtin attributes because they can contain sensitive names and values
|
|
|
|
// on the other hand builtin/system attrs should not contain any sensitive info
|
2020-06-21 05:09:34 +08:00
|
|
|
const builtinAttrs = attributeService
|
|
|
|
.getBuiltinAttributeNames()
|
2020-06-21 05:24:34 +08:00
|
|
|
.map(name => "'" + name + "'").join(', ');
|
2020-06-21 05:09:34 +08:00
|
|
|
|
|
|
|
db.prepare(`UPDATE attributes SET name = 'name', value = 'value' WHERE type = 'label' AND name NOT IN(${builtinAttrs})`).run();
|
|
|
|
db.prepare(`UPDATE attributes SET name = 'name' WHERE type = 'relation' AND name NOT IN (${builtinAttrs})`).run();
|
|
|
|
db.prepare("UPDATE branches SET prefix = 'prefix' WHERE prefix IS NOT NULL").run();
|
|
|
|
db.prepare(`UPDATE options SET value = 'anonymized' WHERE name IN
|
2020-06-08 05:55:55 +08:00
|
|
|
('documentId', 'documentSecret', 'encryptedDataKey',
|
|
|
|
'passwordVerificationHash', 'passwordVerificationSalt',
|
|
|
|
'passwordDerivedKeySalt', 'username', 'syncServerHost', 'syncProxy')
|
2020-06-21 05:09:34 +08:00
|
|
|
AND value != ''`).run();
|
|
|
|
db.prepare("VACUUM").run();
|
2020-06-03 05:13:55 +08:00
|
|
|
|
2020-06-20 18:31:38 +08:00
|
|
|
db.close();
|
2020-06-03 05:13:55 +08:00
|
|
|
|
|
|
|
return {
|
|
|
|
success: true,
|
|
|
|
anonymizedFilePath: anonymizedFile
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2017-10-29 00:23:11 +08:00
|
|
|
if (!fs.existsSync(dataDir.BACKUP_DIR)) {
|
|
|
|
fs.mkdirSync(dataDir.BACKUP_DIR, 0o700);
|
|
|
|
}
|
|
|
|
|
2017-10-15 11:31:44 +08:00
|
|
|
module.exports = {
|
2020-06-03 05:13:55 +08:00
|
|
|
backupNow,
|
2020-07-03 04:57:17 +08:00
|
|
|
anonymize,
|
|
|
|
regularBackup
|
2020-05-30 03:55:08 +08:00
|
|
|
};
|