2024-02-17 21:13:04 +02:00
import sql = require ( "./sql" ) ;
import revisionService = require ( "./revisions" ) ;
import log = require ( "./log" ) ;
import entityChangesService = require ( "./entity_changes" ) ;
import optionService = require ( "./options" ) ;
import dateUtils = require ( "./date_utils" ) ;
import sqlInit = require ( "./sql_init" ) ;
import cls = require ( "./cls" ) ;
import { EntityChange } from "./entity_changes_interface" ;
function eraseNotes ( noteIdsToErase : string [ ] ) {
2023-07-27 23:57:12 +02:00
if ( noteIdsToErase . length === 0 ) {
return ;
}
sql . executeMany ( ` DELETE FROM notes WHERE noteId IN (???) ` , noteIdsToErase ) ;
setEntityChangesAsErased ( sql . getManyRows ( ` SELECT * FROM entity_changes WHERE entityName = 'notes' AND entityId IN (???) ` , noteIdsToErase ) ) ;
// we also need to erase all "dependent" entities of the erased notes
2024-02-17 21:13:04 +02:00
const branchIdsToErase = sql . getManyRows < { branchId : string } > ( ` SELECT branchId FROM branches WHERE noteId IN (???) ` , noteIdsToErase )
2023-07-27 23:57:12 +02:00
. map ( row = > row . branchId ) ;
eraseBranches ( branchIdsToErase ) ;
2024-02-17 21:13:04 +02:00
const attributeIdsToErase = sql . getManyRows < { attributeId : string } > ( ` SELECT attributeId FROM attributes WHERE noteId IN (???) ` , noteIdsToErase )
2023-07-27 23:57:12 +02:00
. map ( row = > row . attributeId ) ;
eraseAttributes ( attributeIdsToErase ) ;
2024-02-17 21:13:04 +02:00
const revisionIdsToErase = sql . getManyRows < { revisionId : string } > ( ` SELECT revisionId FROM revisions WHERE noteId IN (???) ` , noteIdsToErase )
2023-07-27 23:57:12 +02:00
. map ( row = > row . revisionId ) ;
2023-12-04 00:11:24 +01:00
eraseRevisions ( revisionIdsToErase ) ;
2023-07-27 23:57:12 +02:00
log . info ( ` Erased notes: ${ JSON . stringify ( noteIdsToErase ) } ` ) ;
}
2024-02-17 21:13:04 +02:00
function setEntityChangesAsErased ( entityChanges : EntityChange [ ] ) {
2023-07-27 23:57:12 +02:00
for ( const ec of entityChanges ) {
ec . isErased = true ;
2023-12-30 00:34:46 +01:00
// we're not changing hash here, not sure if good or not
// content hash check takes isErased flag into account, though
2023-07-29 21:59:20 +02:00
ec . utcDateChanged = dateUtils . utcNowDateTime ( ) ;
2023-07-27 23:57:12 +02:00
2023-09-05 00:30:09 +02:00
entityChangesService . putEntityChangeWithForcedChange ( ec ) ;
2023-07-27 23:57:12 +02:00
}
}
2024-02-17 21:13:04 +02:00
function eraseBranches ( branchIdsToErase : string [ ] ) {
2023-07-27 23:57:12 +02:00
if ( branchIdsToErase . length === 0 ) {
return ;
}
sql . executeMany ( ` DELETE FROM branches WHERE branchId IN (???) ` , branchIdsToErase ) ;
setEntityChangesAsErased ( sql . getManyRows ( ` SELECT * FROM entity_changes WHERE entityName = 'branches' AND entityId IN (???) ` , branchIdsToErase ) ) ;
log . info ( ` Erased branches: ${ JSON . stringify ( branchIdsToErase ) } ` ) ;
}
2024-02-17 21:13:04 +02:00
function eraseAttributes ( attributeIdsToErase : string [ ] ) {
2023-07-27 23:57:12 +02:00
if ( attributeIdsToErase . length === 0 ) {
return ;
}
sql . executeMany ( ` DELETE FROM attributes WHERE attributeId IN (???) ` , attributeIdsToErase ) ;
setEntityChangesAsErased ( sql . getManyRows ( ` SELECT * FROM entity_changes WHERE entityName = 'attributes' AND entityId IN (???) ` , attributeIdsToErase ) ) ;
log . info ( ` Erased attributes: ${ JSON . stringify ( attributeIdsToErase ) } ` ) ;
}
2024-02-17 21:13:04 +02:00
function eraseAttachments ( attachmentIdsToErase : string [ ] ) {
2023-07-27 23:57:12 +02:00
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 ) } ` ) ;
}
2024-02-17 21:13:04 +02:00
function eraseRevisions ( revisionIdsToErase : string [ ] ) {
2023-12-04 00:11:24 +01:00
if ( revisionIdsToErase . length === 0 ) {
return ;
}
sql . executeMany ( ` DELETE FROM revisions WHERE revisionId IN (???) ` , revisionIdsToErase ) ;
setEntityChangesAsErased ( sql . getManyRows ( ` SELECT * FROM entity_changes WHERE entityName = 'revisions' AND entityId IN (???) ` , revisionIdsToErase ) ) ;
log . info ( ` Removed revisions: ${ JSON . stringify ( revisionIdsToErase ) } ` ) ;
}
2023-07-27 23:57:12 +02:00
function eraseUnusedBlobs() {
const unusedBlobIds = sql . getColumn ( `
SELECT blobs . blobId
FROM blobs
LEFT JOIN notes ON notes . blobId = blobs . blobId
LEFT JOIN attachments ON attachments . blobId = blobs . blobId
LEFT JOIN revisions ON revisions . blobId = blobs . blobId
WHERE notes . noteId IS NULL
AND attachments . attachmentId IS NULL
AND revisions . revisionId IS NULL ` );
if ( unusedBlobIds . length === 0 ) {
return ;
}
sql . executeMany ( ` DELETE FROM blobs WHERE blobId IN (???) ` , unusedBlobIds ) ;
// blobs are not marked as erased in entity_changes, they are just purged completely
// this is because technically every keystroke can create a new blob and there would be just too many
sql . executeMany ( ` DELETE FROM entity_changes WHERE entityName = 'blobs' AND entityId IN (???) ` , unusedBlobIds ) ;
log . info ( ` Erased unused blobs: ${ JSON . stringify ( unusedBlobIds ) } ` ) ;
}
2024-02-17 21:13:04 +02:00
function eraseDeletedEntities ( eraseEntitiesAfterTimeInSeconds : number | null = null ) {
2023-07-27 23:57:12 +02:00
// this is important also so that the erased entity changes are sent to the connected clients
sql . transactional ( ( ) = > {
if ( eraseEntitiesAfterTimeInSeconds === null ) {
eraseEntitiesAfterTimeInSeconds = optionService . getOptionInt ( 'eraseEntitiesAfterTimeInSeconds' ) ;
}
const cutoffDate = new Date ( Date . now ( ) - eraseEntitiesAfterTimeInSeconds * 1000 ) ;
2024-02-17 21:13:04 +02:00
const noteIdsToErase = sql . getColumn < string > ( "SELECT noteId FROM notes WHERE isDeleted = 1 AND utcDateModified <= ?" , [ dateUtils . utcDateTimeStr ( cutoffDate ) ] ) ;
2023-07-27 23:57:12 +02:00
eraseNotes ( noteIdsToErase ) ;
2024-02-17 21:13:04 +02:00
const branchIdsToErase = sql . getColumn < string > ( "SELECT branchId FROM branches WHERE isDeleted = 1 AND utcDateModified <= ?" , [ dateUtils . utcDateTimeStr ( cutoffDate ) ] ) ;
2023-07-27 23:57:12 +02:00
eraseBranches ( branchIdsToErase ) ;
2024-02-17 21:13:04 +02:00
const attributeIdsToErase = sql . getColumn < string > ( "SELECT attributeId FROM attributes WHERE isDeleted = 1 AND utcDateModified <= ?" , [ dateUtils . utcDateTimeStr ( cutoffDate ) ] ) ;
2023-07-27 23:57:12 +02:00
eraseAttributes ( attributeIdsToErase ) ;
2024-02-17 21:13:04 +02:00
const attachmentIdsToErase = sql . getColumn < string > ( "SELECT attachmentId FROM attachments WHERE isDeleted = 1 AND utcDateModified <= ?" , [ dateUtils . utcDateTimeStr ( cutoffDate ) ] ) ;
2023-07-27 23:57:12 +02:00
eraseAttachments ( attachmentIdsToErase ) ;
eraseUnusedBlobs ( ) ;
} ) ;
}
2024-02-17 21:13:04 +02:00
function eraseNotesWithDeleteId ( deleteId : string ) {
const noteIdsToErase = sql . getColumn < string > ( "SELECT noteId FROM notes WHERE isDeleted = 1 AND deleteId = ?" , [ deleteId ] ) ;
2023-07-27 23:57:12 +02:00
eraseNotes ( noteIdsToErase ) ;
2024-02-17 21:13:04 +02:00
const branchIdsToErase = sql . getColumn < string > ( "SELECT branchId FROM branches WHERE isDeleted = 1 AND deleteId = ?" , [ deleteId ] ) ;
2023-07-27 23:57:12 +02:00
eraseBranches ( branchIdsToErase ) ;
2024-02-17 21:13:04 +02:00
const attributeIdsToErase = sql . getColumn < string > ( "SELECT attributeId FROM attributes WHERE isDeleted = 1 AND deleteId = ?" , [ deleteId ] ) ;
2023-07-27 23:57:12 +02:00
eraseAttributes ( attributeIdsToErase ) ;
2024-02-17 21:13:04 +02:00
const attachmentIdsToErase = sql . getColumn < string > ( "SELECT attachmentId FROM attachments WHERE isDeleted = 1 AND deleteId = ?" , [ deleteId ] ) ;
2023-07-27 23:57:12 +02:00
eraseAttachments ( attachmentIdsToErase ) ;
eraseUnusedBlobs ( ) ;
}
function eraseDeletedNotesNow() {
eraseDeletedEntities ( 0 ) ;
}
function eraseUnusedAttachmentsNow() {
eraseScheduledAttachments ( 0 ) ;
}
2024-02-17 21:13:04 +02:00
function eraseScheduledAttachments ( eraseUnusedAttachmentsAfterSeconds : number | null = null ) {
2023-07-27 23:57:12 +02:00
if ( eraseUnusedAttachmentsAfterSeconds === null ) {
eraseUnusedAttachmentsAfterSeconds = optionService . getOptionInt ( 'eraseUnusedAttachmentsAfterSeconds' ) ;
}
const cutOffDate = dateUtils . utcDateTimeStr ( new Date ( Date . now ( ) - ( eraseUnusedAttachmentsAfterSeconds * 1000 ) ) ) ;
2024-02-17 21:13:04 +02:00
const attachmentIdsToErase = sql . getColumn < string > ( 'SELECT attachmentId FROM attachments WHERE utcDateScheduledForErasureSince < ?' , [ cutOffDate ] ) ;
2023-07-27 23:57:12 +02:00
eraseAttachments ( attachmentIdsToErase ) ;
}
sqlInit . dbReady . then ( ( ) = > {
// first cleanup kickoff 5 minutes after startup
setTimeout ( cls . wrap ( ( ) = > eraseDeletedEntities ( ) ) , 5 * 60 * 1000 ) ;
setTimeout ( cls . wrap ( ( ) = > eraseScheduledAttachments ( ) ) , 6 * 60 * 1000 ) ;
setInterval ( cls . wrap ( ( ) = > eraseDeletedEntities ( ) ) , 4 * 3600 * 1000 ) ;
setInterval ( cls . wrap ( ( ) = > eraseScheduledAttachments ( ) ) , 3600 * 1000 ) ;
} ) ;
2024-02-17 21:13:04 +02:00
export = {
2023-07-27 23:57:12 +02:00
eraseDeletedNotesNow ,
eraseUnusedAttachmentsNow ,
eraseNotesWithDeleteId ,
2023-09-21 11:38:38 +02:00
eraseUnusedBlobs ,
2023-12-04 00:11:24 +01:00
eraseAttachments ,
eraseRevisions
2023-07-27 23:57:12 +02:00
} ;