mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 18:36:30 +01:00 
			
		
		
		
	scheduled erasure of attachments WIP
This commit is contained in:
		| @@ -10,9 +10,12 @@ CREATE TABLE IF NOT EXISTS "attachments" | |||||||
|     blobId    TEXT DEFAULT null, |     blobId    TEXT DEFAULT null, | ||||||
|     dateModified TEXT NOT NULL, |     dateModified TEXT NOT NULL, | ||||||
|     utcDateModified TEXT not null, |     utcDateModified TEXT not null, | ||||||
|     utcDateScheduledForDeletionSince TEXT DEFAULT NULL, |     utcDateScheduledForErasureSince TEXT DEFAULT NULL, | ||||||
|     isDeleted    INT  not null, |     isDeleted    INT  not null, | ||||||
|     deleteId    TEXT DEFAULT NULL); |     deleteId    TEXT DEFAULT NULL); | ||||||
|  |  | ||||||
| CREATE INDEX IDX_attachments_parentId_role | CREATE INDEX IDX_attachments_parentId_role | ||||||
|     on attachments (parentId, role); |     on attachments (parentId, role); | ||||||
|  |  | ||||||
|  | CREATE INDEX IDX_attachments_utcDateScheduledForErasureSince | ||||||
|  |     on attachments (utcDateScheduledForErasureSince); | ||||||
|   | |||||||
| @@ -121,7 +121,7 @@ CREATE TABLE IF NOT EXISTS "attachments" | |||||||
|     blobId    TEXT DEFAULT null, |     blobId    TEXT DEFAULT null, | ||||||
|     dateModified TEXT NOT NULL, |     dateModified TEXT NOT NULL, | ||||||
|     utcDateModified TEXT not null, |     utcDateModified TEXT not null, | ||||||
|     utcDateScheduledForDeletionSince TEXT DEFAULT NULL, |     utcDateScheduledForErasureSince TEXT DEFAULT NULL, | ||||||
|     isDeleted    INT  not null, |     isDeleted    INT  not null, | ||||||
|     deleteId    TEXT DEFAULT NULL); |     deleteId    TEXT DEFAULT NULL); | ||||||
| CREATE INDEX IDX_attachments_parentId_role | CREATE INDEX IDX_attachments_parentId_role | ||||||
|   | |||||||
							
								
								
									
										662
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										662
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										14
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								package.json
									
									
									
									
									
								
							| @@ -33,10 +33,10 @@ | |||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@braintree/sanitize-url": "6.0.2", |     "@braintree/sanitize-url": "6.0.2", | ||||||
|     "@electron/remote": "2.0.9", |     "@electron/remote": "2.0.9", | ||||||
|     "@excalidraw/excalidraw": "0.14.2", |     "@excalidraw/excalidraw": "0.15.2", | ||||||
|     "archiver": "5.3.1", |     "archiver": "5.3.1", | ||||||
|     "async-mutex": "0.4.0", |     "async-mutex": "0.4.0", | ||||||
|     "axios": "1.3.5", |     "axios": "1.3.6", | ||||||
|     "better-sqlite3": "7.4.5", |     "better-sqlite3": "7.4.5", | ||||||
|     "chokidar": "3.5.3", |     "chokidar": "3.5.3", | ||||||
|     "cls-hooked": "4.2.2", |     "cls-hooked": "4.2.2", | ||||||
| @@ -51,13 +51,13 @@ | |||||||
|     "electron-debug": "3.2.0", |     "electron-debug": "3.2.0", | ||||||
|     "electron-dl": "3.5.0", |     "electron-dl": "3.5.0", | ||||||
|     "electron-window-state": "5.0.3", |     "electron-window-state": "5.0.3", | ||||||
|     "escape-html": "^1.0.3", |     "escape-html": "1.0.3", | ||||||
|     "express": "4.18.2", |     "express": "4.18.2", | ||||||
|     "express-partial-content": "1.0.2", |     "express-partial-content": "1.0.2", | ||||||
|     "express-rate-limit": "6.7.0", |     "express-rate-limit": "6.7.0", | ||||||
|     "express-session": "1.17.3", |     "express-session": "1.17.3", | ||||||
|     "fs-extra": "11.1.1", |     "fs-extra": "11.1.1", | ||||||
|     "helmet": "6.1.2", |     "helmet": "6.1.5", | ||||||
|     "html": "1.0.0", |     "html": "1.0.0", | ||||||
|     "html2plaintext": "2.1.4", |     "html2plaintext": "2.1.4", | ||||||
|     "http-proxy-agent": "5.0.0", |     "http-proxy-agent": "5.0.0", | ||||||
| @@ -71,7 +71,7 @@ | |||||||
|     "jsdom": "21.1.1", |     "jsdom": "21.1.1", | ||||||
|     "mime-types": "2.1.35", |     "mime-types": "2.1.35", | ||||||
|     "multer": "1.4.5-lts.1", |     "multer": "1.4.5-lts.1", | ||||||
|     "node-abi": "3.35.0", |     "node-abi": "3.40.0", | ||||||
|     "normalize-strings": "1.1.1", |     "normalize-strings": "1.1.1", | ||||||
|     "open": "8.4.1", |     "open": "8.4.1", | ||||||
|     "rand-token": "1.0.1", |     "rand-token": "1.0.1", | ||||||
| @@ -83,7 +83,7 @@ | |||||||
|     "sanitize-filename": "1.6.3", |     "sanitize-filename": "1.6.3", | ||||||
|     "sanitize-html": "2.10.0", |     "sanitize-html": "2.10.0", | ||||||
|     "sax": "1.2.4", |     "sax": "1.2.4", | ||||||
|     "semver": "7.3.8", |     "semver": "7.5.0", | ||||||
|     "serve-favicon": "2.5.0", |     "serve-favicon": "2.5.0", | ||||||
|     "session-file-store": "1.5.0", |     "session-file-store": "1.5.0", | ||||||
|     "stream-throttle": "0.1.3", |     "stream-throttle": "0.1.3", | ||||||
| @@ -117,7 +117,7 @@ | |||||||
|     "prettier": "2.8.7", |     "prettier": "2.8.7", | ||||||
|     "nodemon": "^2.0.22", |     "nodemon": "^2.0.22", | ||||||
|     "rcedit": "3.0.1", |     "rcedit": "3.0.1", | ||||||
|     "webpack": "5.78.0", |     "webpack": "5.80.0", | ||||||
|     "webpack-cli": "5.0.1" |     "webpack-cli": "5.0.1" | ||||||
|   }, |   }, | ||||||
|   "optionalDependencies": { |   "optionalDependencies": { | ||||||
|   | |||||||
| @@ -20,7 +20,7 @@ class BAttachment extends AbstractBeccaEntity { | |||||||
|     static get entityName() { return "attachments"; } |     static get entityName() { return "attachments"; } | ||||||
|     static get primaryKeyName() { return "attachmentId"; } |     static get primaryKeyName() { return "attachmentId"; } | ||||||
|     static get hashedProperties() { return ["attachmentId", "parentId", "role", "mime", "title", "blobId", |     static get hashedProperties() { return ["attachmentId", "parentId", "role", "mime", "title", "blobId", | ||||||
|                                             "utcDateScheduledForDeletionSince", "utcDateModified"]; } |                                             "utcDateScheduledForErasureSince", "utcDateModified"]; } | ||||||
|  |  | ||||||
|     constructor(row) { |     constructor(row) { | ||||||
|         super(); |         super(); | ||||||
| @@ -56,7 +56,7 @@ class BAttachment extends AbstractBeccaEntity { | |||||||
|         /** @type {string} */ |         /** @type {string} */ | ||||||
|         this.utcDateModified = row.utcDateModified; |         this.utcDateModified = row.utcDateModified; | ||||||
|         /** @type {string} */ |         /** @type {string} */ | ||||||
|         this.utcDateScheduledForDeletionSince = row.utcDateScheduledForDeletionSince; |         this.utcDateScheduledForErasureSince = row.utcDateScheduledForErasureSince; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** @returns {BAttachment} */ |     /** @returns {BAttachment} */ | ||||||
| @@ -68,7 +68,7 @@ class BAttachment extends AbstractBeccaEntity { | |||||||
|             title: this.title, |             title: this.title, | ||||||
|             blobId: this.blobId, |             blobId: this.blobId, | ||||||
|             isProtected: this.isProtected, |             isProtected: this.isProtected, | ||||||
|             utcDateScheduledForDeletionSince: this.utcDateScheduledForDeletionSince |             utcDateScheduledForErasureSince: this.utcDateScheduledForErasureSince | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -171,7 +171,7 @@ class BAttachment extends AbstractBeccaEntity { | |||||||
|             isDeleted: false, |             isDeleted: false, | ||||||
|             dateModified: this.dateModified, |             dateModified: this.dateModified, | ||||||
|             utcDateModified: this.utcDateModified, |             utcDateModified: this.utcDateModified, | ||||||
|             utcDateScheduledForDeletionSince: this.utcDateScheduledForDeletionSince |             utcDateScheduledForErasureSince: this.utcDateScheduledForErasureSince | ||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -21,7 +21,7 @@ class FAttachment { | |||||||
|         /** @type {string} */ |         /** @type {string} */ | ||||||
|         this.utcDateModified = row.utcDateModified; |         this.utcDateModified = row.utcDateModified; | ||||||
|         /** @type {string} */ |         /** @type {string} */ | ||||||
|         this.utcDateScheduledForDeletionSince = row.utcDateScheduledForDeletionSince; |         this.utcDateScheduledForErasureSince = row.utcDateScheduledForErasureSince; | ||||||
|  |  | ||||||
|         this.froca.attachments[this.attachmentId] = this; |         this.froca.attachments[this.attachmentId] = this; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -27,6 +27,39 @@ function formatTimeWithSeconds(date) { | |||||||
|     return `${padNum(date.getHours())}:${padNum(date.getMinutes())}:${padNum(date.getSeconds())}`; |     return `${padNum(date.getHours())}:${padNum(date.getMinutes())}:${padNum(date.getSeconds())}`; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | function formatTimeInterval(ms) { | ||||||
|  |     const seconds = Math.round(ms / 1000); | ||||||
|  |     const minutes = Math.floor(seconds / 60); | ||||||
|  |     const hours = Math.floor(minutes / 60); | ||||||
|  |     const days = Math.floor(hours / 24); | ||||||
|  |     const plural = (count, name) => `${count} ${name}${count > 1 ? 's' : ''}`; | ||||||
|  |     const segments = []; | ||||||
|  |  | ||||||
|  |     if (days > 0) { | ||||||
|  |         segments.push(plural(days, 'day')); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (days < 2) { | ||||||
|  |         if (hours % 24 > 0) { | ||||||
|  |             segments.push(plural(hours % 24, 'hour')); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (hours < 4) { | ||||||
|  |             if (minutes % 60 > 0) { | ||||||
|  |                 segments.push(plural(minutes % 60, 'minute')); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if (minutes < 5) { | ||||||
|  |                 if (seconds % 60 > 0) { | ||||||
|  |                     segments.push(plural(seconds % 60, 'second')); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return segments.join(", "); | ||||||
|  | } | ||||||
|  |  | ||||||
| // this is producing local time! | // this is producing local time! | ||||||
| function formatDate(date) { | function formatDate(date) { | ||||||
|     //    return padNum(date.getDate()) + ". " + padNum(date.getMonth() + 1) + ". " + date.getFullYear(); |     //    return padNum(date.getDate()) + ". " + padNum(date.getMonth() + 1) + ". " + date.getFullYear(); | ||||||
| @@ -489,6 +522,7 @@ export default { | |||||||
|     formatDate, |     formatDate, | ||||||
|     formatDateISO, |     formatDateISO, | ||||||
|     formatDateTime, |     formatDateTime, | ||||||
|  |     formatTimeInterval, | ||||||
|     formatSize, |     formatSize, | ||||||
|     localNowDateTime, |     localNowDateTime, | ||||||
|     now, |     now, | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ import utils from "../services/utils.js"; | |||||||
| import AttachmentActionsWidget from "./buttons/attachments_actions.js"; | import AttachmentActionsWidget from "./buttons/attachments_actions.js"; | ||||||
| import BasicWidget from "./basic_widget.js"; | import BasicWidget from "./basic_widget.js"; | ||||||
| import server from "../services/server.js"; | import server from "../services/server.js"; | ||||||
|  | import options from "../services/options.js"; | ||||||
|  |  | ||||||
| const TPL = ` | const TPL = ` | ||||||
| <div class="attachment-detail"> | <div class="attachment-detail"> | ||||||
| @@ -44,6 +45,10 @@ const TPL = ` | |||||||
|             max-width: 90%;  |             max-width: 90%;  | ||||||
|             object-fit: contain; |             object-fit: contain; | ||||||
|         } |         } | ||||||
|  |          | ||||||
|  |         .attachment-detail-wrapper.scheduled-for-deletion .attachment-content img { | ||||||
|  |             filter: contrast(10%); | ||||||
|  |         } | ||||||
|     </style> |     </style> | ||||||
|  |  | ||||||
|     <div class="attachment-detail-wrapper"> |     <div class="attachment-detail-wrapper"> | ||||||
| @@ -54,6 +59,8 @@ const TPL = ` | |||||||
|             <div class="attachment-actions-container"></div> |             <div class="attachment-actions-container"></div> | ||||||
|         </div> |         </div> | ||||||
|          |          | ||||||
|  |         <div class="attachment-deletion-warning alert alert-info"></div> | ||||||
|  |          | ||||||
|         <div class="attachment-content"></div> |         <div class="attachment-content"></div> | ||||||
|     </div> |     </div> | ||||||
| </div>`; | </div>`; | ||||||
| @@ -100,15 +107,29 @@ export default class AttachmentDetailWidget extends BasicWidget { | |||||||
|                 .text(this.attachment.title); |                 .text(this.attachment.title); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         const {utcDateScheduledForDeletionSince} = this.attachment; |         const $deletionWarning = this.$wrapper.find('.attachment-deletion-warning'); | ||||||
|  |         const {utcDateScheduledForErasureSince} = this.attachment; | ||||||
|  |  | ||||||
|         if (utcDateScheduledForDeletionSince) { |         if (utcDateScheduledForErasureSince) { | ||||||
|             const scheduledSinceTimestamp = utils.parseDate(utcDateScheduledForDeletionSince)?.getTime(); |             this.$wrapper.addClass("scheduled-for-deletion"); | ||||||
|             const interval = 3600 * 1000; |  | ||||||
|             const deletionTimestamp = scheduledSinceTimestamp + interval; |  | ||||||
|             const willBeDeletedInSeconds = Math.round((deletionTimestamp - Date.now()) / 1000); |  | ||||||
|  |  | ||||||
|             this.$wrapper.find('.attachment-title').append(`Will be deleted in ${willBeDeletedInSeconds} seconds.`); |             const scheduledSinceTimestamp = utils.parseDate(utcDateScheduledForErasureSince)?.getTime(); | ||||||
|  |             const intervalMs = options.getInt('eraseUnusedImageAttachmentsAfterSeconds') * 1000; | ||||||
|  |             const deletionTimestamp = scheduledSinceTimestamp + intervalMs; | ||||||
|  |             const willBeDeletedInMs = deletionTimestamp - Date.now(); | ||||||
|  |  | ||||||
|  |             $deletionWarning.show(); | ||||||
|  |  | ||||||
|  |             if (willBeDeletedInMs >= 60000) { | ||||||
|  |                 $deletionWarning.text(`This attachment will be deleted in ${utils.formatTimeInterval(willBeDeletedInMs)}`); | ||||||
|  |             } else { | ||||||
|  |                 $deletionWarning.text(`This attachment will be deleted soon`); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             $deletionWarning.append(", because the image attachment is not used. To prevent deletion, add the image back into the note."); | ||||||
|  |         } else { | ||||||
|  |             this.$wrapper.removeClass("scheduled-for-deletion"); | ||||||
|  |             $deletionWarning.hide(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         this.$wrapper.find('.attachment-details') |         this.$wrapper.find('.attachment-details') | ||||||
|   | |||||||
| @@ -61,7 +61,8 @@ const ALLOWED_OPTIONS = new Set([ | |||||||
|     'downloadImagesAutomatically', |     'downloadImagesAutomatically', | ||||||
|     'minTocHeadings', |     'minTocHeadings', | ||||||
|     'checkForUpdates', |     'checkForUpdates', | ||||||
|     'disableTray' |     'disableTray', | ||||||
|  |     'eraseUnusedImageAttachmentsAfterSeconds' | ||||||
| ]); | ]); | ||||||
|  |  | ||||||
| function getOptions() { | function getOptions() { | ||||||
|   | |||||||
| @@ -21,6 +21,7 @@ const htmlSanitizer = require("./html_sanitizer"); | |||||||
| const ValidationError = require("../errors/validation_error"); | const ValidationError = require("../errors/validation_error"); | ||||||
| const noteTypesService = require("./note_types"); | const noteTypesService = require("./note_types"); | ||||||
| const fs = require("fs"); | const fs = require("fs"); | ||||||
|  | const BAttachment = require("../becca/entities/battachment"); | ||||||
|  |  | ||||||
| /** @param {BNote} parentNote */ | /** @param {BNote} parentNote */ | ||||||
| function getNewNotePosition(parentNote) { | function getNewNotePosition(parentNote) { | ||||||
| @@ -342,17 +343,17 @@ function checkImageAttachments(note, content) { | |||||||
|     let match; |     let match; | ||||||
|  |  | ||||||
|     while (match = re.exec(content)) { |     while (match = re.exec(content)) { | ||||||
|         foundAttachmentIds.push(match[1]); |         foundAttachmentIds.add(match[1]); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     for (const attachment of note.getAttachmentByRole('image')) { |     for (const attachment of note.getAttachmentByRole('image')) { | ||||||
|         const imageInContent = foundAttachmentIds.has(attachment.attachmentId); |         const imageInContent = foundAttachmentIds.has(attachment.attachmentId); | ||||||
|  |  | ||||||
|         if (attachment.utcDateScheduledForDeletionSince && imageInContent) { |         if (attachment.utcDateScheduledForErasureSince && imageInContent) { | ||||||
|             attachment.utcDateScheduledForDeletionSince = null; |             attachment.utcDateScheduledForErasureSince = null; | ||||||
|             attachment.save(); |             attachment.save(); | ||||||
|         } else if (!attachment.utcDateScheduledForDeletionSince && !imageInContent) { |         } else if (!attachment.utcDateScheduledForErasureSince && !imageInContent) { | ||||||
|             attachment.utcDateScheduledForDeletionSince = dateUtils.utcNowDateTime(); |             attachment.utcDateScheduledForErasureSince = dateUtils.utcNowDateTime(); | ||||||
|             attachment.save(); |             attachment.save(); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -841,6 +842,33 @@ function eraseAttributes(attributeIdsToErase) { | |||||||
|     log.info(`Erased attributes: ${JSON.stringify(attributeIdsToErase)}`); |     log.info(`Erased attributes: ${JSON.stringify(attributeIdsToErase)}`); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | function eraseAttachments(attachmentIdsToErase) { | ||||||
|  |     if (attachmentIdsToErase.length === 0) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     sql.executeMany(`DELETE FROM attachments WHERE attachmentId IN (???)`, attachmentIdsToErase); | ||||||
|  |  | ||||||
|  |     setEntityChangesAsErased(sql.getManyRows(`SELECT * FROM entity_changes WHERE entityName = 'attachments' AND entityId IN (???)`, attachmentIdsToErase)); | ||||||
|  |  | ||||||
|  |     log.info(`Erased attachments: ${JSON.stringify(attachmentIdsToErase)}`); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function eraseUnusedBlobs() { | ||||||
|  |     const unusedBlobIds = sql.getColumn(` | ||||||
|  |         SELECT blobId | ||||||
|  |         FROM blobs | ||||||
|  |         LEFT JOIN notes ON notes.blobId = blobs.blobId | ||||||
|  |         LEFT JOIN attachments ON attachments.blobId = blobs.blobId | ||||||
|  |         WHERE notes.noteId IS NULL AND attachments.attachmentId IS NULL`); | ||||||
|  |  | ||||||
|  |     sql.executeMany(`DELETE FROM blobs WHERE blobId IN (???)`, unusedBlobIds); | ||||||
|  |  | ||||||
|  |     setEntityChangesAsErased(sql.getManyRows(`SELECT * FROM entity_changes WHERE entityName = 'blobs' AND entityId IN (???)`, unusedBlobIds)); | ||||||
|  |  | ||||||
|  |     log.info(`Erased unused blobs: ${JSON.stringify(unusedBlobIds)}`); | ||||||
|  | } | ||||||
|  |  | ||||||
| function eraseDeletedEntities(eraseEntitiesAfterTimeInSeconds = null) { | function eraseDeletedEntities(eraseEntitiesAfterTimeInSeconds = null) { | ||||||
|     // this is important also so that the erased entity changes are sent to the connected clients |     // this is important also so that the erased entity changes are sent to the connected clients | ||||||
|     sql.transactional(() => { |     sql.transactional(() => { | ||||||
| @@ -861,6 +889,12 @@ function eraseDeletedEntities(eraseEntitiesAfterTimeInSeconds = null) { | |||||||
|         const attributeIdsToErase = sql.getColumn("SELECT attributeId FROM attributes WHERE isDeleted = 1 AND utcDateModified <= ?", [dateUtils.utcDateTimeStr(cutoffDate)]); |         const attributeIdsToErase = sql.getColumn("SELECT attributeId FROM attributes WHERE isDeleted = 1 AND utcDateModified <= ?", [dateUtils.utcDateTimeStr(cutoffDate)]); | ||||||
|  |  | ||||||
|         eraseAttributes(attributeIdsToErase); |         eraseAttributes(attributeIdsToErase); | ||||||
|  |  | ||||||
|  |         const attachmentIdsToErase = sql.getColumn("SELECT attachmentId FROM attachments WHERE isDeleted = 1 AND utcDateModified <= ?", [dateUtils.utcDateTimeStr(cutoffDate)]); | ||||||
|  |  | ||||||
|  |         eraseAttachments(attachmentIdsToErase); | ||||||
|  |  | ||||||
|  |         eraseUnusedBlobs(); | ||||||
|     }); |     }); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -1013,11 +1047,21 @@ function getNoteIdMapping(origNote) { | |||||||
|     return noteIdMapping; |     return noteIdMapping; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | function eraseScheduledAttachments() { | ||||||
|  |     const eraseIntervalSeconds = optionService.getOptionInt('eraseUnusedImageAttachmentsAfterSeconds'); | ||||||
|  |     const cutOffDate = dateUtils.utcDateTimeStr(new Date(Date.now() - (eraseIntervalSeconds * 1000))); | ||||||
|  |     const attachmentIdsToErase = sql.getColumn('SELECT attachmentId FROM attachments WHERE utcDateScheduledForErasureSince < ?', [cutOffDate]); | ||||||
|  |  | ||||||
|  |     eraseAttachments(attachmentIdsToErase); | ||||||
|  | } | ||||||
|  |  | ||||||
| sqlInit.dbReady.then(() => { | sqlInit.dbReady.then(() => { | ||||||
|     // first cleanup kickoff 5 minutes after startup |     // first cleanup kickoff 5 minutes after startup | ||||||
|     setTimeout(cls.wrap(() => eraseDeletedEntities()), 5 * 60 * 1000); |     setTimeout(cls.wrap(() => eraseDeletedEntities()), 5 * 60 * 1000); | ||||||
|  |     setTimeout(cls.wrap(() => eraseScheduledAttachments()), 6 * 60 * 1000); | ||||||
|  |  | ||||||
|     setInterval(cls.wrap(() => eraseDeletedEntities()), 4 * 3600 * 1000); |     setInterval(cls.wrap(() => eraseDeletedEntities()), 4 * 3600 * 1000); | ||||||
|  |     setInterval(cls.wrap(() => eraseScheduledAttachments()), 3600 * 1000); | ||||||
| }); | }); | ||||||
|  |  | ||||||
| module.exports = { | module.exports = { | ||||||
|   | |||||||
| @@ -88,6 +88,7 @@ const defaultOptions = [ | |||||||
|     { name: 'minTocHeadings', value: '5', isSynced: true }, |     { name: 'minTocHeadings', value: '5', isSynced: true }, | ||||||
|     { name: 'checkForUpdates', value: 'true', isSynced: true }, |     { name: 'checkForUpdates', value: 'true', isSynced: true }, | ||||||
|     { name: 'disableTray', value: 'false', isSynced: false }, |     { name: 'disableTray', value: 'false', isSynced: false }, | ||||||
|  |     { name: 'eraseUnusedImageAttachmentsAfterSeconds', value: '86400', isSynced: false }, | ||||||
| ]; | ]; | ||||||
|  |  | ||||||
| function initStartupOptions() { | function initStartupOptions() { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user