diff --git a/indexes.yaml b/indexes.yaml index baf5d6aa..656f3f3c 100644 --- a/indexes.yaml +++ b/indexes.yaml @@ -313,7 +313,7 @@ indexes: headers.value: text text: text partialFilterExpression: - searchable: true + searchable: true # ignore messages marked with \Deleted flag - collection: messages index: @@ -583,3 +583,17 @@ indexes: key: metadata.audit: 1 metadata.date: 1 + + - collection: audit.files + type: gridfs # index applies to gridfs database + index: + name: audit_files_queue + key: + metadata.info.queueId: 1 + + - collection: audit.files + type: gridfs # index applies to gridfs database + index: + name: audit_files_expire + key: + metadata.info.expires: 1 diff --git a/lib/api/messages.js b/lib/api/messages.js index 41b77fb9..32a727ae 100644 --- a/lib/api/messages.js +++ b/lib/api/messages.js @@ -1064,6 +1064,7 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler) => { } let total = await getFilteredMessageCount(filter); + log.debug('API', 'Searching %s', JSON.stringify(filter)); let opts = { limit, diff --git a/lib/audit-handler.js b/lib/audit-handler.js index 5ac50db2..fad0567b 100644 --- a/lib/audit-handler.js +++ b/lib/audit-handler.js @@ -2,6 +2,7 @@ const ObjectID = require('mongodb').ObjectID; const GridFSBucket = require('mongodb').GridFSBucket; +const log = require('npmlog'); class AuditHandler { constructor(options) { @@ -172,6 +173,60 @@ class AuditHandler { } }); } + + async updateDeliveryStatus(queueId, seq, status, info) { + await this.gridfs.collection('audit.files').update( + { 'metadata.info.queueId': queueId }, + { + $push: { + 'metadata.info.delivery': { + seq, + status, + time: new Date(), + info + } + } + } + ); + } + + async removeAudit(auditData) { + let cursor = await this.gridfs.collection('audit.files').find({ + 'metadata.audit': auditData._id + }); + + let messages = 0; + let messageData; + while ((messageData = await cursor.next())) { + try { + await this.gridstore.delete(messageData._id); + messages++; + } catch (err) { + log.error('Audit', 'Failed to delete message %s. %s', messageData._id, err.message); + } + } + await cursor.close(); + + await this.database.collection('audits').deleteOne({ _id: auditData._id }); + log.info('Audit', 'Deleted expired audit %s (%s messages)', auditData._id, messages); + } + + async cleanExpired() { + let expiredAudits = await this.database + .collection('audits') + .find({ + expires: { $lt: new Date() } + }) + .toArray(); + + for (let auditData of expiredAudits) { + try { + await this.removeAudit(auditData); + } catch (err) { + log.error('Audit', 'Failed to delete expired audit %s. %s', auditData._id, err.message); + } + } + } } module.exports = AuditHandler; diff --git a/lib/message-handler.js b/lib/message-handler.js index a7692f7d..81e5740b 100644 --- a/lib/message-handler.js +++ b/lib/message-handler.js @@ -235,7 +235,7 @@ class MessageHandler { if (maildata.attachments && maildata.attachments.length) { messageData.attachments = maildata.attachments; - messageData.ha = !!maildata.attachments.find(a => !a.related); + messageData.ha = maildata.attachments.some(a => !a.related); } else { messageData.ha = false; } diff --git a/tasks.js b/tasks.js index 9666c0c8..44f34312 100644 --- a/tasks.js +++ b/tasks.js @@ -318,7 +318,18 @@ function clearExpiredMessages() { if (deleted) { log.verbose('GC', 'Purged %s messages', deleted); } - return deleteOrphaned(next); + return deleteOrphaned(() => { + auditHandler + .cleanExpired() + .then(() => { + try { + next(); + } catch (err) { + // ignore, only needed to prevent calling next() twice + } + }) + .catch(next); + }); }); let processNext = () => {