erasing unused attachments

This commit is contained in:
zadam 2023-04-24 21:22:34 +02:00
parent 4b074365e7
commit 49fb913eab
9 changed files with 133 additions and 70 deletions

115
package-lock.json generated
View File

@ -83,24 +83,24 @@
"electron-builder": "23.6.0",
"electron-packager": "17.1.1",
"electron-rebuild": "3.2.9",
"eslint": "^8.38.0",
"eslint": "8.39.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-jsonc": "^2.7.0",
"eslint-plugin-prettier": "^4.2.1",
"esm": "3.2.25",
"husky": "^8.0.3",
"husky": "8.0.3",
"jasmine": "4.6.0",
"jsdoc": "4.0.2",
"jsonc-eslint-parser": "^2.2.0",
"lint-staged": "^13.2.1",
"lorem-ipsum": "2.0.8",
"nodemon": "^2.0.22",
"prettier": "2.8.7",
"nodemon": "2.0.22",
"prettier": "2.8.8",
"rcedit": "3.0.1",
"webpack": "5.80.0",
"webpack-cli": "5.0.1"
"webpack-cli": "5.0.2"
},
"optionalDependencies": {
"electron-installer-debian": "3.1.0"
@ -453,9 +453,9 @@
}
},
"node_modules/@eslint/js": {
"version": "8.38.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.38.0.tgz",
"integrity": "sha512-IoD2MfUnOV58ghIHCiil01PcohxjbYR/qCxsoC+xNgUwh1EY8jOOrYmu3d3a71+tJJ23uscEV4X2HJWMsPJu4g==",
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.39.0.tgz",
"integrity": "sha512-kf9RB0Fg7NZfap83B3QOqOGg9QmD9yBudqQXzzOtn3i4y7ZUXe5ONeW34Gwi+TxhH4mvj72R1Zc300KUMa9Bng==",
"dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@ -1580,9 +1580,9 @@
}
},
"node_modules/@webpack-cli/serve": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.1.tgz",
"integrity": "sha512-0G7tNyS+yW8TdgHwZKlDWYXFA6OJQnoLCQvYKkQP0Q2X205PSQ6RNUj0M+1OB/9gRQaUZ/ccYfaxd0nhaWKfjw==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.2.tgz",
"integrity": "sha512-S9h3GmOmzUseyeFW3tYNnWS7gNUuwxZ3mmMq0JyW78Vx1SGKPSkt5bT4pB0rUnVfHjP0EL9gW2bOzmtiTfQt0A==",
"dev": true,
"engines": {
"node": ">=14.15.0"
@ -5173,15 +5173,15 @@
}
},
"node_modules/eslint": {
"version": "8.38.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.38.0.tgz",
"integrity": "sha512-pIdsD2jwlUGf/U38Jv97t8lq6HpaU/G9NKbYmpWpZGw3LdTNhZLbJePqxOXGB5+JEKfOPU/XLxYxFh03nr1KTg==",
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.39.0.tgz",
"integrity": "sha512-mwiok6cy7KTW7rBpo05k6+p4YVZByLNjAZ/ACB9DRCu4YDRwjXI01tWHp6KAUWelsBetTxKK/2sHB0vdS8Z2Og==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.4.0",
"@eslint/eslintrc": "^2.0.2",
"@eslint/js": "8.38.0",
"@eslint/js": "8.39.0",
"@humanwhocodes/config-array": "^0.11.8",
"@humanwhocodes/module-importer": "^1.0.1",
"@nodelib/fs.walk": "^1.2.8",
@ -5191,7 +5191,7 @@
"debug": "^4.3.2",
"doctrine": "^3.0.0",
"escape-string-regexp": "^4.0.0",
"eslint-scope": "^7.1.1",
"eslint-scope": "^7.2.0",
"eslint-visitor-keys": "^3.4.0",
"espree": "^9.5.1",
"esquery": "^1.4.2",
@ -5492,9 +5492,9 @@
}
},
"node_modules/eslint/node_modules/eslint-scope": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz",
"integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==",
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz",
"integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==",
"dev": true,
"dependencies": {
"esrecurse": "^4.3.0",
@ -5502,6 +5502,9 @@
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint/node_modules/find-up": {
@ -10256,9 +10259,9 @@
}
},
"node_modules/prettier": {
"version": "2.8.7",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.7.tgz",
"integrity": "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==",
"version": "2.8.8",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
"dev": true,
"bin": {
"prettier": "bin-prettier.js"
@ -12710,17 +12713,17 @@
}
},
"node_modules/webpack-cli": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.0.1.tgz",
"integrity": "sha512-S3KVAyfwUqr0Mo/ur3NzIp6jnerNpo7GUO6so51mxLi1spqsA17YcMXy0WOIJtBSnj748lthxC6XLbNKh/ZC+A==",
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.0.2.tgz",
"integrity": "sha512-4y3W5Dawri5+8dXm3+diW6Mn1Ya+Dei6eEVAdIduAmYNLzv1koKVAqsfgrrc9P2mhrYHQphx5htnGkcNwtubyQ==",
"dev": true,
"dependencies": {
"@discoveryjs/json-ext": "^0.5.0",
"@webpack-cli/configtest": "^2.0.1",
"@webpack-cli/info": "^2.0.1",
"@webpack-cli/serve": "^2.0.1",
"@webpack-cli/serve": "^2.0.2",
"colorette": "^2.0.14",
"commander": "^9.4.1",
"commander": "^10.0.1",
"cross-spawn": "^7.0.3",
"envinfo": "^7.7.3",
"fastest-levenshtein": "^1.0.12",
@ -12755,12 +12758,12 @@
}
},
"node_modules/webpack-cli/node_modules/commander": {
"version": "9.4.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-9.4.1.tgz",
"integrity": "sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==",
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
"integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
"dev": true,
"engines": {
"node": "^12.20.0 || >=14"
"node": ">=14"
}
},
"node_modules/webpack-merge": {
@ -13409,9 +13412,9 @@
}
},
"@eslint/js": {
"version": "8.38.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.38.0.tgz",
"integrity": "sha512-IoD2MfUnOV58ghIHCiil01PcohxjbYR/qCxsoC+xNgUwh1EY8jOOrYmu3d3a71+tJJ23uscEV4X2HJWMsPJu4g==",
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.39.0.tgz",
"integrity": "sha512-kf9RB0Fg7NZfap83B3QOqOGg9QmD9yBudqQXzzOtn3i4y7ZUXe5ONeW34Gwi+TxhH4mvj72R1Zc300KUMa9Bng==",
"dev": true
},
"@excalidraw/excalidraw": {
@ -14329,9 +14332,9 @@
"requires": {}
},
"@webpack-cli/serve": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.1.tgz",
"integrity": "sha512-0G7tNyS+yW8TdgHwZKlDWYXFA6OJQnoLCQvYKkQP0Q2X205PSQ6RNUj0M+1OB/9gRQaUZ/ccYfaxd0nhaWKfjw==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.2.tgz",
"integrity": "sha512-S9h3GmOmzUseyeFW3tYNnWS7gNUuwxZ3mmMq0JyW78Vx1SGKPSkt5bT4pB0rUnVfHjP0EL9gW2bOzmtiTfQt0A==",
"dev": true,
"requires": {}
},
@ -17123,15 +17126,15 @@
}
},
"eslint": {
"version": "8.38.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.38.0.tgz",
"integrity": "sha512-pIdsD2jwlUGf/U38Jv97t8lq6HpaU/G9NKbYmpWpZGw3LdTNhZLbJePqxOXGB5+JEKfOPU/XLxYxFh03nr1KTg==",
"version": "8.39.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.39.0.tgz",
"integrity": "sha512-mwiok6cy7KTW7rBpo05k6+p4YVZByLNjAZ/ACB9DRCu4YDRwjXI01tWHp6KAUWelsBetTxKK/2sHB0vdS8Z2Og==",
"dev": true,
"requires": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.4.0",
"@eslint/eslintrc": "^2.0.2",
"@eslint/js": "8.38.0",
"@eslint/js": "8.39.0",
"@humanwhocodes/config-array": "^0.11.8",
"@humanwhocodes/module-importer": "^1.0.1",
"@nodelib/fs.walk": "^1.2.8",
@ -17141,7 +17144,7 @@
"debug": "^4.3.2",
"doctrine": "^3.0.0",
"escape-string-regexp": "^4.0.0",
"eslint-scope": "^7.1.1",
"eslint-scope": "^7.2.0",
"eslint-visitor-keys": "^3.4.0",
"espree": "^9.5.1",
"esquery": "^1.4.2",
@ -17177,9 +17180,9 @@
"dev": true
},
"eslint-scope": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz",
"integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==",
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz",
"integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==",
"dev": true,
"requires": {
"esrecurse": "^4.3.0",
@ -20911,9 +20914,9 @@
"integrity": "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA=="
},
"prettier": {
"version": "2.8.7",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.7.tgz",
"integrity": "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==",
"version": "2.8.8",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
"dev": true
},
"prettier-linter-helpers": {
@ -22786,17 +22789,17 @@
}
},
"webpack-cli": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.0.1.tgz",
"integrity": "sha512-S3KVAyfwUqr0Mo/ur3NzIp6jnerNpo7GUO6so51mxLi1spqsA17YcMXy0WOIJtBSnj748lthxC6XLbNKh/ZC+A==",
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.0.2.tgz",
"integrity": "sha512-4y3W5Dawri5+8dXm3+diW6Mn1Ya+Dei6eEVAdIduAmYNLzv1koKVAqsfgrrc9P2mhrYHQphx5htnGkcNwtubyQ==",
"dev": true,
"requires": {
"@discoveryjs/json-ext": "^0.5.0",
"@webpack-cli/configtest": "^2.0.1",
"@webpack-cli/info": "^2.0.1",
"@webpack-cli/serve": "^2.0.1",
"@webpack-cli/serve": "^2.0.2",
"colorette": "^2.0.14",
"commander": "^9.4.1",
"commander": "^10.0.1",
"cross-spawn": "^7.0.3",
"envinfo": "^7.7.3",
"fastest-levenshtein": "^1.0.12",
@ -22807,9 +22810,9 @@
},
"dependencies": {
"commander": {
"version": "9.4.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-9.4.1.tgz",
"integrity": "sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==",
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
"integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
"dev": true
}
}

View File

@ -101,24 +101,24 @@
"electron-builder": "23.6.0",
"electron-packager": "17.1.1",
"electron-rebuild": "3.2.9",
"eslint": "^8.38.0",
"eslint": "8.39.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-jsonc": "^2.7.0",
"eslint-plugin-prettier": "^4.2.1",
"esm": "3.2.25",
"husky": "^8.0.3",
"husky": "8.0.3",
"jsonc-eslint-parser": "^2.2.0",
"lint-staged": "^13.2.1",
"jasmine": "4.6.0",
"jsdoc": "4.0.2",
"lorem-ipsum": "2.0.8",
"prettier": "2.8.7",
"nodemon": "^2.0.22",
"prettier": "2.8.8",
"nodemon": "2.0.22",
"rcedit": "3.0.1",
"webpack": "5.80.0",
"webpack-cli": "5.0.1"
"webpack-cli": "5.0.2"
},
"optionalDependencies": {
"electron-installer-debian": "3.1.0"

View File

@ -28,6 +28,7 @@ import ConsistencyChecksOptions from "./options/advanced/consistency_checks.js";
import VacuumDatabaseOptions from "./options/advanced/vacuum_database.js";
import DatabaseAnonymizationOptions from "./options/advanced/database_anonymization.js";
import BackendLogWidget from "./content/backend_log.js";
import AttachmentErasureTimeoutOptions from "./options/other/attachment_erasure_timeout.js";
const TPL = `<div class="note-detail-content-widget note-detail-printable">
<style>
@ -77,6 +78,7 @@ const CONTENT_WIDGETS = {
_optionsOther: [
TrayOptions,
NoteErasureTimeoutOptions,
AttachmentErasureTimeoutOptions,
NoteRevisionsSnapshotIntervalOptions,
NetworkConnectionsOptions
],

View File

@ -0,0 +1,38 @@
import OptionsWidget from "../options_widget.js";
import server from "../../../../services/server.js";
import toastService from "../../../../services/toast.js";
const TPL = `
<div class="options-section">
<h4>Attachment erasure timeout</h4>
<p>Attachment images get automatically deleted (and erased) if they are not referenced by their note anymore after a defined time out.</p>
<div class="form-group">
<label>Erase image attachments after X seconds of not being used in its note</label>
<input class="erase-unused-attachments-after-time-in-seconds form-control" type="number" min="0">
</div>
<p>You can also trigger erasing manually (without considering the timeout defined above):</p>
<button class="erase-unused-attachments-now-button btn">Erase unused attachment notes now</button>
</div>`;
export default class AttachmentErasureTimeoutOptions extends OptionsWidget {
doRender() {
this.$widget = $(TPL);
this.$eraseUnusedAttachmentsAfterTimeInSeconds = this.$widget.find(".erase-unused-attachments-after-time-in-seconds");
this.$eraseUnusedAttachmentsAfterTimeInSeconds.on('change', () => this.updateOption('eraseUnusedImageAttachmentsAfterSeconds', this.$eraseUnusedAttachmentsAfterTimeInSeconds.val()));
this.$eraseDeletedNotesButton = this.$widget.find(".erase-unused-attachments-now-button");
this.$eraseDeletedNotesButton.on('click', () => {
server.post('notes/erase-unused-attachments-now').then(() => {
toastService.showMessage("Unused image attachments have been erased.");
});
});
}
async optionsLoaded(options) {
this.$eraseUnusedAttachmentsAfterTimeInSeconds.val(options.eraseUnusedImageAttachmentsAfterSeconds);
}
}

View File

@ -16,7 +16,7 @@ const TPL = `
<input class="erase-entities-after-time-in-seconds form-control" type="number" min="0">
</div>
<p>You can also trigger erasing manually:</p>
<p>You can also trigger erasing manually (without considering the timeout defined above):</p>
<button class="erase-deleted-notes-now-button btn">Erase deleted notes now</button>
</div>`;

View File

@ -982,7 +982,7 @@ button.close:hover {
}
.options-section h4 {
margin-top: 15px;
margin-top: 25px;
margin-bottom: 15px;
}

View File

@ -168,6 +168,10 @@ function eraseDeletedNotesNow() {
noteService.eraseDeletedNotesNow();
}
function eraseUnusedAttachmentsNow() {
noteService.eraseUnusedAttachmentsNow();
}
function getDeleteNotesPreview(req) {
const {branchIdsToDelete, deleteAllClones} = req.body;
@ -275,6 +279,7 @@ module.exports = {
changeTitle,
duplicateSubtree,
eraseDeletedNotesNow,
eraseUnusedAttachmentsNow,
getDeleteNotesPreview,
uploadModifiedFile,
forceSaveNoteRevision

View File

@ -293,6 +293,7 @@ function register(app) {
apiRoute(PST, '/api/relation-map', relationMapApiRoute.getRelationMap);
apiRoute(PST, '/api/notes/erase-deleted-notes-now', notesApiRoute.eraseDeletedNotesNow);
apiRoute(PST, '/api/notes/erase-unused-attachments-now', notesApiRoute.eraseUnusedAttachmentsNow);
apiRoute(GET, '/api/similar-notes/:noteId', similarNotesRoute.getSimilarNotes);
apiRoute(GET, '/api/backend-log', backendLogRoute.getBackendLog);
apiRoute(GET, '/api/stats/note-size/:noteId', statsRoute.getNoteSize);

View File

@ -899,23 +899,33 @@ function eraseDeletedEntities(eraseEntitiesAfterTimeInSeconds = null) {
}
function eraseNotesWithDeleteId(deleteId) {
const noteIdsToErase = sql.getColumn("SELECT noteId FROM notes WHERE deleteId = ?", [deleteId]);
const noteIdsToErase = sql.getColumn("SELECT noteId FROM notes WHERE isDeleted = 1 AND deleteId = ?", [deleteId]);
eraseNotes(noteIdsToErase);
const branchIdsToErase = sql.getColumn("SELECT branchId FROM branches WHERE deleteId = ?", [deleteId]);
const branchIdsToErase = sql.getColumn("SELECT branchId FROM branches WHERE isDeleted = 1 AND deleteId = ?", [deleteId]);
eraseBranches(branchIdsToErase);
const attributeIdsToErase = sql.getColumn("SELECT attributeId FROM attributes WHERE deleteId = ?", [deleteId]);
const attributeIdsToErase = sql.getColumn("SELECT attributeId FROM attributes WHERE isDeleted = 1 AND deleteId = ?", [deleteId]);
eraseAttributes(attributeIdsToErase);
const attachmentIdsToErase = sql.getColumn("SELECT attachmentId FROM attachments WHERE isDeleted = 1 AND deleteId = ?", [deleteId]);
eraseAttachments(attachmentIdsToErase);
eraseUnusedBlobs();
}
function eraseDeletedNotesNow() {
eraseDeletedEntities(0);
}
function eraseUnusedAttachmentsNow() {
eraseScheduledAttachments(0);
}
// do a replace in str - all keys should be replaced by the corresponding values
function replaceByMap(str, mapObj) {
const re = new RegExp(Object.keys(mapObj).join("|"),"g");
@ -962,7 +972,7 @@ function duplicateSubtreeWithoutRoot(origNoteId, newNoteId) {
function duplicateSubtreeInner(origNote, origBranch, newParentNoteId, noteIdMapping) {
if (origNote.isProtected && !protectedSessionService.isProtectedSessionAvailable()) {
throw new Error(`Cannot duplicate note=${origNote.noteId} because it is protected and protected session is not available. Enter protected session and try again.`);
throw new Error(`Cannot duplicate note '${origNote.noteId}' because it is protected and protected session is not available. Enter protected session and try again.`);
}
const newNoteId = noteIdMapping[origNote.noteId];
@ -1047,9 +1057,12 @@ function getNoteIdMapping(origNote) {
return noteIdMapping;
}
function eraseScheduledAttachments() {
const eraseIntervalSeconds = optionService.getOptionInt('eraseUnusedImageAttachmentsAfterSeconds');
const cutOffDate = dateUtils.utcDateTimeStr(new Date(Date.now() - (eraseIntervalSeconds * 1000)));
function eraseScheduledAttachments(eraseUnusedImageAttachmentsAfterSeconds = null) {
if (eraseUnusedImageAttachmentsAfterSeconds === null) {
eraseUnusedImageAttachmentsAfterSeconds = optionService.getOptionInt('eraseUnusedImageAttachmentsAfterSeconds');
}
const cutOffDate = dateUtils.utcDateTimeStr(new Date(Date.now() - (eraseUnusedImageAttachmentsAfterSeconds * 1000)));
const attachmentIdsToErase = sql.getColumn('SELECT attachmentId FROM attachments WHERE utcDateScheduledForErasureSince < ?', [cutOffDate]);
eraseAttachments(attachmentIdsToErase);
@ -1075,6 +1088,7 @@ module.exports = {
getUndeletedParentBranchIds,
triggerNoteTitleChanged,
eraseDeletedNotesNow,
eraseUnusedAttachmentsNow,
eraseNotesWithDeleteId,
saveNoteRevisionIfNeeded,
downloadImages,