diff --git a/package-lock.json b/package-lock.json
index 24ae2a6a10..6f7731af67 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 6e124be633..ff3526a0ba 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 a17e0675b1..9bdcce843e 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 d5430d30b2..a5c5b0c13f 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 27658f39dd..d8d8cfa9a9 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 97a2f8de89..1cbe4cd83f 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 877a9b38b8..160b6e2a21 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 c7908524f3..808331ff93 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
};