diff --git a/package-lock.json b/package-lock.json
index 24ae2a6a1..6f7731af6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -79,7 +79,7 @@
},
"devDependencies": {
"cross-env": "7.0.3",
- "electron": "25.9.1",
+ "electron": "25.9.2",
"electron-builder": "24.6.4",
"electron-packager": "17.1.2",
"electron-rebuild": "3.2.9",
@@ -4276,9 +4276,9 @@
}
},
"node_modules/electron": {
- "version": "25.9.1",
- "resolved": "https://registry.npmjs.org/electron/-/electron-25.9.1.tgz",
- "integrity": "sha512-Uo/Fh7igjoUXA/f90iTATZJesQEArVL1uLA672JefNWTLymdKSZkJKiCciu/Xnd0TS6qvdIOUGuJFSTQnKskXQ==",
+ "version": "25.9.2",
+ "resolved": "https://registry.npmjs.org/electron/-/electron-25.9.2.tgz",
+ "integrity": "sha512-hVBN5rsrL99BKNHvzMeYy2PkAmewuIobu4U3o3EzVz4MDoLmMfW4yTH5GZ4RbJrpokoEky5IzGtRR/ggPzL6Fw==",
"hasInstallScript": true,
"dependencies": {
"@electron/get": "^2.0.0",
@@ -16633,9 +16633,9 @@
}
},
"electron": {
- "version": "25.9.1",
- "resolved": "https://registry.npmjs.org/electron/-/electron-25.9.1.tgz",
- "integrity": "sha512-Uo/Fh7igjoUXA/f90iTATZJesQEArVL1uLA672JefNWTLymdKSZkJKiCciu/Xnd0TS6qvdIOUGuJFSTQnKskXQ==",
+ "version": "25.9.2",
+ "resolved": "https://registry.npmjs.org/electron/-/electron-25.9.2.tgz",
+ "integrity": "sha512-hVBN5rsrL99BKNHvzMeYy2PkAmewuIobu4U3o3EzVz4MDoLmMfW4yTH5GZ4RbJrpokoEky5IzGtRR/ggPzL6Fw==",
"requires": {
"@electron/get": "^2.0.0",
"@types/node": "^18.11.18",
diff --git a/package.json b/package.json
index 6e124be63..ff3526a0b 100644
--- a/package.json
+++ b/package.json
@@ -97,7 +97,7 @@
},
"devDependencies": {
"cross-env": "7.0.3",
- "electron": "25.9.1",
+ "electron": "25.9.2",
"electron-builder": "24.6.4",
"electron-packager": "17.1.2",
"electron-rebuild": "3.2.9",
diff --git a/src/public/app/widgets/type_widgets/options/advanced/database_anonymization.js b/src/public/app/widgets/type_widgets/options/advanced/database_anonymization.js
index a17e0675b..9bdcce843 100644
--- a/src/public/app/widgets/type_widgets/options/advanced/database_anonymization.js
+++ b/src/public/app/widgets/type_widgets/options/advanced/database_anonymization.js
@@ -20,6 +20,10 @@ const TPL = `
You can decide yourself if you want to provide a fully or lightly anonymized database. Even fully anonymized DB is very useful, however in some cases lightly anonymized database can speed up the process of bug identification and fixing.
Save lightly anonymized database
+
+ Existing anonymized databases
+
+
`;
export default class DatabaseAnonymizationOptions extends OptionsWidget {
@@ -38,6 +42,8 @@ export default class DatabaseAnonymizationOptions extends OptionsWidget {
else {
toastService.showMessage(`Created fully anonymized database in ${resp.anonymizedFilePath}`, 10000);
}
+
+ this.refresh();
});
this.$anonymizeLightButton.on('click', async () => {
@@ -51,6 +57,24 @@ export default class DatabaseAnonymizationOptions extends OptionsWidget {
else {
toastService.showMessage(`Created lightly anonymized database in ${resp.anonymizedFilePath}`, 10000);
}
+
+ this.refresh();
+ });
+
+ this.$existingAnonymizedDatabases = this.$widget.find(".existing-anonymized-databases");
+ }
+
+ optionsLoaded(options) {
+ server.get("database/anonymized-databases").then(anonymizedDatabases => {
+ this.$existingAnonymizedDatabases.empty();
+
+ if (!anonymizedDatabases.length) {
+ anonymizedDatabases = [{filePath: "no anonymized database yet"}];
+ }
+
+ for (const {filePath} of anonymizedDatabases) {
+ this.$existingAnonymizedDatabases.append($("").text(filePath));
+ }
});
}
}
diff --git a/src/public/app/widgets/type_widgets/options/backup.js b/src/public/app/widgets/type_widgets/options/backup.js
index d5430d30b..a5c5b0c13 100644
--- a/src/public/app/widgets/type_widgets/options/backup.js
+++ b/src/public/app/widgets/type_widgets/options/backup.js
@@ -37,6 +37,12 @@ const TPL = `
Backup database now
+
+
`;
export default class BackupOptions extends OptionsWidget {
@@ -49,6 +55,8 @@ export default class BackupOptions extends OptionsWidget {
const {backupFile} = await server.post('database/backup-database');
toastService.showMessage(`Database has been backed up to ${backupFile}`, 10000);
+
+ this.refresh();
});
this.$dailyBackupEnabled = this.$widget.find(".daily-backup-enabled");
@@ -63,11 +71,25 @@ export default class BackupOptions extends OptionsWidget {
this.$monthlyBackupEnabled.on('change', () =>
this.updateCheckboxOption('monthlyBackupEnabled', this.$monthlyBackupEnabled));
+
+ this.$existingBackupList = this.$widget.find(".existing-backup-list");
}
optionsLoaded(options) {
this.setCheckboxState(this.$dailyBackupEnabled, options.dailyBackupEnabled);
this.setCheckboxState(this.$weeklyBackupEnabled, options.weeklyBackupEnabled);
this.setCheckboxState(this.$monthlyBackupEnabled, options.monthlyBackupEnabled);
+
+ server.get("database/backups").then(backupFiles => {
+ this.$existingBackupList.empty();
+
+ if (!backupFiles.length) {
+ backupFiles = [{filePath: "no backup yet"}];
+ }
+
+ for (const {filePath} of backupFiles) {
+ this.$existingBackupList.append($(" ").text(filePath));
+ }
+ });
}
}
diff --git a/src/routes/api/database.js b/src/routes/api/database.js
index 27658f39d..d8d8cfa9a 100644
--- a/src/routes/api/database.js
+++ b/src/routes/api/database.js
@@ -6,8 +6,8 @@ const backupService = require('../../services/backup');
const anonymizationService = require('../../services/anonymization');
const consistencyChecksService = require('../../services/consistency_checks');
-async function anonymize(req) {
- return await anonymizationService.createAnonymizedCopy(req.params.type);
+function getExistingBackups() {
+ return backupService.getExistingBackups();
}
async function backupDatabase() {
@@ -22,6 +22,18 @@ function vacuumDatabase() {
log.info("Database has been vacuumed.");
}
+function findAndFixConsistencyIssues() {
+ consistencyChecksService.runOnDemandChecks(true);
+}
+
+function getExistingAnonymizedDatabases() {
+ return anonymizationService.getExistingAnonymizedDatabases();
+}
+
+async function anonymize(req) {
+ return await anonymizationService.createAnonymizedCopy(req.params.type);
+}
+
function checkIntegrity() {
const results = sql.getRows("PRAGMA integrity_check");
@@ -32,14 +44,12 @@ function checkIntegrity() {
};
}
-function findAndFixConsistencyIssues() {
- consistencyChecksService.runOnDemandChecks(true);
-}
-
module.exports = {
+ getExistingBackups,
backupDatabase,
vacuumDatabase,
findAndFixConsistencyIssues,
+ getExistingAnonymizedDatabases,
anonymize,
checkIntegrity
};
diff --git a/src/routes/routes.js b/src/routes/routes.js
index 97a2f8de8..1cbe4cd83 100644
--- a/src/routes/routes.js
+++ b/src/routes/routes.js
@@ -289,9 +289,11 @@ function register(app) {
apiRoute(GET, '/api/sql/schema', sqlRoute.getSchema);
apiRoute(PST, '/api/sql/execute/:noteId', sqlRoute.execute);
route(PST, '/api/database/anonymize/:type', [auth.checkApiAuthOrElectron, csrfMiddleware], databaseRoute.anonymize, apiResultHandler, false);
+ apiRoute(GET, '/api/database/anonymized-databases', databaseRoute.getExistingAnonymizedDatabases);
// backup requires execution outside of transaction
route(PST, '/api/database/backup-database', [auth.checkApiAuthOrElectron, csrfMiddleware], databaseRoute.backupDatabase, apiResultHandler, false);
+ apiRoute(GET, '/api/database/backups', databaseRoute.getExistingBackups);
// VACUUM requires execution outside of transaction
route(PST, '/api/database/vacuum-database', [auth.checkApiAuthOrElectron, csrfMiddleware], databaseRoute.vacuumDatabase, apiResultHandler, false);
diff --git a/src/services/anonymization.js b/src/services/anonymization.js
index 877a9b38b..160b6e2a2 100644
--- a/src/services/anonymization.js
+++ b/src/services/anonymization.js
@@ -4,6 +4,7 @@ const dataDir = require("./data_dir");
const dateUtils = require("./date_utils");
const Database = require("better-sqlite3");
const sql = require("./sql");
+const path = require("path");
function getFullAnonymizationScript() {
// we want to delete all non-builtin attributes because they can contain sensitive names and values
@@ -70,7 +71,21 @@ async function createAnonymizedCopy(type) {
};
}
+function getExistingAnonymizedDatabases() {
+ if (!fs.existsSync(dataDir.ANONYMIZED_DB_DIR)) {
+ return [];
+ }
+
+ return fs.readdirSync(dataDir.ANONYMIZED_DB_DIR)
+ .filter(fileName => fileName.includes("anonymized"))
+ .map(fileName => ({
+ fileName: fileName,
+ filePath: path.resolve(dataDir.ANONYMIZED_DB_DIR, fileName)
+ }));
+}
+
module.exports = {
getFullAnonymizationScript,
- createAnonymizedCopy
+ createAnonymizedCopy,
+ getExistingAnonymizedDatabases
}
diff --git a/src/services/backup.js b/src/services/backup.js
index c7908524f..808331ff9 100644
--- a/src/services/backup.js
+++ b/src/services/backup.js
@@ -8,6 +8,20 @@ const log = require('./log');
const syncMutexService = require('./sync_mutex');
const cls = require('./cls');
const sql = require('./sql');
+const path = require('path');
+
+function getExistingBackups() {
+ if (!fs.existsSync(dataDir.BACKUP_DIR)) {
+ return [];
+ }
+
+ return fs.readdirSync(dataDir.BACKUP_DIR)
+ .filter(fileName => fileName.includes("backup"))
+ .map(fileName => ({
+ fileName: fileName,
+ filePath: path.resolve(dataDir.BACKUP_DIR, fileName)
+ }));
+}
function regularBackup() {
cls.init(() => {
@@ -58,6 +72,7 @@ if (!fs.existsSync(dataDir.BACKUP_DIR)) {
}
module.exports = {
+ getExistingBackups,
backupNow,
regularBackup
};