From 71fd513bc01baf03b352cb64bfc3ced993c1d5eb Mon Sep 17 00:00:00 2001 From: ClaraCrazy Date: Thu, 3 Oct 2024 01:22:10 +0200 Subject: [PATCH] Refactor email purge time configuration and usage to be more precise. - New purgeTIme now allows to configure a purge to be every X minutes, hours or days. - Also remove a bit more trust by pulling footer deletion time from config. - TODO: implement 'convertUp' function, converting numbers up to the biggest possible value where `i > 2 (so 48hrs still works as per slogan and domain)`. I.e. 72hrs = 3 days, 360minutes = 6hrs, 1440minutes to 24hrs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Johannes Bülow --- application/config.sample.js | 6 +++- application/helper.js | 41 ++++++++++++++++++++++++++ application/imap-service.js | 27 +++++++++++------ application/mail-processing-service.js | 13 +++----- infrastructure/web/routes/inbox.js | 11 +++++++ infrastructure/web/routes/login.js | 7 +++++ infrastructure/web/views/layout.twig | 2 +- 7 files changed, 87 insertions(+), 20 deletions(-) create mode 100644 application/helper.js diff --git a/application/config.sample.js b/application/config.sample.js index 3d2dd2c..c5405ad 100644 --- a/application/config.sample.js +++ b/application/config.sample.js @@ -1,7 +1,11 @@ const config = { email: { domains: process.env.EMAIL_DOMAINS, - deleteMailsOlderThanDays: process.env.EMAIL_DELETE_MAILS_OLDER_THAN_DAYS || 2 + purgeTime: process.env.PURGE_TIME || { + time: 48, + unit: 'hours', // minutes, hours, days + convert: true, // Convert to highest sensible unit + } }, imap: { user: process.env.IMAP_USER, diff --git a/application/helper.js b/application/helper.js new file mode 100644 index 0000000..9441979 --- /dev/null +++ b/application/helper.js @@ -0,0 +1,41 @@ +const config = require('./config') +const moment = require('moment') + +class Helper { + + /** + * Normalize our config into a proper timestamp, so we know what emails to purge + * @returns {Date} + */ + purgeTimeStamp() { + return moment() + .subtract(config.email.purgeTime.time, config.email.purgeTime.unit) + .toDate() + } + + /** + * Check if time difference between now and purgeTimeStamp is more than one day + * @param {Date} now + * @param {Date} past + * @returns {Boolean} + */ + moreThanOneDay(now, past) { + const DAY_IN_MS = 24 * 60 * 60 * 1000; + if((now - past) / DAY_IN_MS >= 1){ + return true + } else { + return false + } + } + + /** + * Convert time to highest possible unit where i > 2 + * @returns {Date} + */ + convertUp(time, unit) { + // TODO: Implement + return time +` ${unit}` + } +}; + +module.exports = Helper diff --git a/application/imap-service.js b/application/imap-service.js index 2567f8c..eb45f0a 100644 --- a/application/imap-service.js +++ b/application/imap-service.js @@ -8,7 +8,8 @@ const debug = require('debug')('48hr-email:imap') const _ = require('lodash') const moment = require('moment') const Mail = require('../domain/mail') - +const Helper = require('./helper') +const helper = new(Helper) // Just adding some missing functions to imap-simple... :-) @@ -199,22 +200,30 @@ class ImapService extends EventEmitter { * @param {Date} deleteMailsBefore delete mails before this date instance */ async deleteOldMails(deleteMailsBefore) { - let uids = await this._searchWithoutFetch([ - ['!DELETED'], - ['BEFORE', deleteMailsBefore] - ]) + let uids = [] + if (helper.moreThanOneDay(moment(), deleteMailsBefore)) { + //fetch mails from date -1day (calculated in MS) to avoid wasting resources + deleteMailsBefore = deleteMailsBefore - 24 * 60 * 60 * 1000 + uids = await this._searchWithoutFetch([ + ['!DELETED'], + ['BEFORE', deleteMailsBefore] + ]) + } else { + uids = await this._searchWithoutFetch([ + ['!DELETED'] + ]) + } + if (uids.length === 0) { return } - const DeleteOlderThan = moment() - .subtract(this.config.email.deleteMailsOlderThanDays, 'days') - .toDate() + const DeleteOlderThan = helper.purgeTimeStamp() const uidsWithHeaders = await this._getMailHeaders(uids) uidsWithHeaders.forEach(mail => { if (mail['attributes'].date > DeleteOlderThan || this.config.http.examples.uids.includes(parseInt(mail['attributes'].id))) { - uids.filter(uid => uid !== mail['attributes'].uid) + uids = uids.filter(uid => uid !== mail['attributes'].uid) } }) diff --git a/application/mail-processing-service.js b/application/mail-processing-service.js index 763faa2..2b63da3 100644 --- a/application/mail-processing-service.js +++ b/application/mail-processing-service.js @@ -3,6 +3,9 @@ const debug = require('debug')('48hr-email:imap-manager') const mem = require('mem') const moment = require('moment') const ImapService = require('./imap-service') +const Helper = require('./helper') +const helper = new(Helper) + class MailProcessingService extends EventEmitter { constructor(mailRepository, imapService, clientNotification, config) { @@ -71,15 +74,7 @@ class MailProcessingService extends EventEmitter { async _deleteOldMails() { try { - await this.imapService.deleteOldMails( - moment() - // Because of how we have to handle the times (IMAP isnt time-aware), we need to subtract one day - // to get all mails in their last few hours before technical purge - // - // This is a bit of a hack, but it works. See imap-service.js#deleteOldMails (L211-227) for more info - .subtract(this.config.email.deleteMailsOlderThanDays - 1, 'days') - .toDate() - ) + await this.imapService.deleteOldMails(helper.purgeTimeStamp()) } catch (error) { console.log('can not delete old messages', error) } diff --git a/infrastructure/web/routes/inbox.js b/infrastructure/web/routes/inbox.js index c344ecf..ca6cf40 100644 --- a/infrastructure/web/routes/inbox.js +++ b/infrastructure/web/routes/inbox.js @@ -3,6 +3,12 @@ const express = require('express') const router = new express.Router() const {param} = require('express-validator') const config = require('../../../application/config') +const Helper = require('../../../application/helper') +const helper = new(Helper) + +const purgeTime = config.email.purgeTime.convert ? helper.convertUp(config.email.purgeTime.time, config.email.purgeTime.unit) + : config.email.purgeTime.time +` ${config.email.purgeTime.unit}`; + const sanitizeAddress = param('address').customSanitizer( (value, {req}) => { return req.params.address @@ -15,6 +21,7 @@ router.get('^/:address([^@/]+@[^@/]+)', sanitizeAddress, (req, res, _next) => { const mailProcessingService = req.app.get('mailProcessingService') res.render('inbox', { title: `${config.http.branding[0]} | ` + req.params.address, + purgeTime: purgeTime, address: req.params.address, mailSummaries: mailProcessingService.getMailSummaries(req.params.address), branding: config.http.branding, @@ -41,6 +48,7 @@ router.get( res.set('Cache-Control', 'private, max-age=600') res.render('mail', { title: mail.subject + " | " + req.params.address, + purgeTime: purgeTime, address: req.params.address, mail, uid: req.params.uid, @@ -50,6 +58,7 @@ router.get( res.render( 'error', { + purgeTime: purgeTime, address: req.params.address, message: 'This mail could not be found. It either does not exist or has been deleted from our servers!', branding: config.http.branding @@ -124,6 +133,7 @@ router.get( res.render( 'error', { + purgeTime: purgeTime, address: req.params.address, message: 'This attachment could not be found. It either does not exist or has been deleted from our servers!', branding: config.http.branding, @@ -163,6 +173,7 @@ router.get( res.render( 'error', { + purgeTime: purgeTime, address: req.params.address, message: 'This mail could not be found. It either does not exist or has been deleted from our servers!', branding: config.http.branding, diff --git a/infrastructure/web/routes/login.js b/infrastructure/web/routes/login.js index ab5c1b8..06db523 100644 --- a/infrastructure/web/routes/login.js +++ b/infrastructure/web/routes/login.js @@ -4,11 +4,17 @@ const router = new express.Router() const randomWord = require('random-word') const {check, validationResult} = require('express-validator') const config = require('../../../application/config') +const Helper = require('../../../application/helper') +const helper = new(Helper) + +const purgeTime = config.email.purgeTime.convert ? helper.convertUp(config.email.purgeTime.time, config.email.purgeTime.unit) + : config.email.purgeTime.time +` ${config.email.purgeTime.unit}`; router.get('/', (req, res, _next) => { res.render('login', { title: `${config.http.branding[0]} | Your temporary Inbox`, username: randomWord(), + purgeTime: purgeTime, domains: config.email.domains, branding: config.http.branding, }) @@ -39,6 +45,7 @@ router.post( return res.render('login', { userInputError: true, title: `${config.http.branding[0]} | Your temporary Inbox`, + purgeTime: purgeTime, username: randomWord(), branding: config.http.branding, }) diff --git a/infrastructure/web/views/layout.twig b/infrastructure/web/views/layout.twig index 2dcac94..0210f95 100644 --- a/infrastructure/web/views/layout.twig +++ b/infrastructure/web/views/layout.twig @@ -28,7 +28,7 @@ {% block footer %} {% endblock %}