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

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,