From 665eb9fbbdc569f063c5bac585aea2641f2aa339 Mon Sep 17 00:00:00 2001 From: titanism <101466223+titanism@users.noreply.github.com> Date: Fri, 30 Aug 2024 00:28:46 -0500 Subject: [PATCH] fix: fixed IMAP response (per ), fixed backup check in worker, wip quota merge --- app/models/aliases.js | 12 ++++++++++++ app/models/domains.js | 22 +++++++++++++++------- app/models/users.js | 8 +++++++- app/views/about/index.md | 2 +- helpers/on-auth.js | 16 ++++------------ helpers/worker.js | 2 +- 6 files changed, 40 insertions(+), 22 deletions(-) diff --git a/app/models/aliases.js b/app/models/aliases.js index f1f35ee34..f6ed0ffba 100644 --- a/app/models/aliases.js +++ b/app/models/aliases.js @@ -6,6 +6,7 @@ const Boom = require('@hapi/boom'); const RE2 = require('re2'); const _ = require('lodash'); +const bytes = require('bytes'); const captainHook = require('captain-hook'); const isFQDN = require('is-fqdn'); const isSANB = require('is-string-and-not-blank'); @@ -92,6 +93,17 @@ APS.plugin(mongooseCommonPlugin, { }); const Aliases = new mongoose.Schema({ + // alias specific max quota (set by admins only) + max_quota: { + type: Number, + default: config.maxQuotaPerAlias, + min: 0, + // + // NOTE: hard-coded max of 100 GB (safeguard) + // + max: bytes('100GB') + }, + // apple push notification support // aps: [APS], diff --git a/app/models/domains.js b/app/models/domains.js index 83842231b..e4189a299 100644 --- a/app/models/domains.js +++ b/app/models/domains.js @@ -222,6 +222,16 @@ Invite.plugin(mongooseCommonPlugin, { }); const Domains = new mongoose.Schema({ + // domain specific max quota per alias + max_quota_per_alias: { + type: Number, + default: config.maxQuotaPerAlias, + min: 0, + // + // NOTE: hard-coded max of 100 GB (safeguard) + // + max: bytes('100GB') + }, // URL to POST to when a bounce is detected from outbound SMTP servers // (NOTE: see additional validation below where we prevent localhost and private IP's from being webhooks) bounce_webhook: { @@ -2216,12 +2226,6 @@ async function getMaxQuota(_id, locale = i18n.config.defaultLocale) { .lean() .exec(); - if (!domain) { - throw Boom.badRequest( - i18n.translateError('DOMAIN_DOES_NOT_EXIST_ANYWHERE', locale) - ); - } - if (!domain) throw Boom.badRequest( i18n.translateError('DOMAIN_DOES_NOT_EXIST_ANYWHERE', locale) @@ -2276,6 +2280,10 @@ async function getMaxQuota(_id, locale = i18n.config.defaultLocale) { ); } + // TODO: add in form and API endpoint and API docs + // TODO: if the alias had a limit set on `alias.max_quota` by an admin + // TODO: if the domain had an alias-wide limit set on `domain.max_quota_per_alias` + // go through all admins and get the max value const max = _.max( adminMembers.map((member) => @@ -2288,7 +2296,7 @@ async function getMaxQuota(_id, locale = i18n.config.defaultLocale) { // // NOTE: hard-coded max of 100 GB (safeguard) // - return _.clamp(max, config.maxQuotaPerAlias, bytes('100GB')); + return _.clamp(max, 0, bytes('100GB')); } Domains.statics.getMaxQuota = getMaxQuota; diff --git a/app/models/users.js b/app/models/users.js index 13d5fe33a..4e44be273 100644 --- a/app/models/users.js +++ b/app/models/users.js @@ -5,6 +5,7 @@ const Boom = require('@hapi/boom'); const _ = require('lodash'); +const bytes = require('bytes'); const captainHook = require('captain-hook'); const countryList = require('country-list'); const cryptoRandomString = require('crypto-random-string'); @@ -152,7 +153,12 @@ const object = {}; object[config.userFields.maxQuotaPerAlias] = { type: Number, - default: config.maxQuotaPerAlias + default: config.maxQuotaPerAlias, + min: 0, + // + // NOTE: hard-coded max of 100 GB (safeguard) + // + max: bytes('100GB') }; object[config.userFields.smtpLimit] = { diff --git a/app/views/about/index.md b/app/views/about/index.md index bab681ca3..afa217504 100644 --- a/app/views/about/index.md +++ b/app/views/about/index.md @@ -56,6 +56,6 @@ Throughout March to July 2024, we released major optimizations and improvements In July 2024, [we added iOS Push support](https://github.com/nodemailer/wildduck/issues/711#issuecomment-2254114016) since Apple Mail on iOS does not support IMAP `IDLE` command. Now users can get real-time notifications of new mail on their Apple iOS devices. We also added time to inbox ("TTI") monitoring for our own service – as well as Yahoo/AOL (now that they support app-generated passwords again) to the footer of every page on our website. Additionally, we now allow users to encrypt their plaintext DNS TXT records even on the free plan at no cost. Privacy should not be a feature, it should be inherently built-in to all aspects of a product. As highly requested in a [Privacy Guides discussion](https://discuss.privacyguides.net/t/forward-email-email-provider/13370) and on [our GitHub issues](https://github.com/forwardemail/forwardemail.net/issues/254) we've added this. Lastly, we added the ability for aliases to either quietly reject `250`, soft reject `421`, or hard reject `550` if they are disabled. Previously, disabled aliases only routed to a blackhole (e.g. `/dev/null`) and it appeared to senders as if their messages to these disabled aliases succeeded. -In August 2024, we added support for exporting mailboxes as [EML](https://en.wikipedia.org/wiki/Email#Filename_extensions) and [Mbox](https://en.wikipedia.org/wiki/Mbox) formats (in addition to the [SQLite](https://en.wikipedia.org/wiki/SQLite) export format already supported) – and we also added [webhook signature support](https://forwardemail.net/faq#do-you-support-webhooks:\~:text=If%20you%27re%20on%20a%20paid%20plan%2C%20then%20go%20to%20My%20Account%20%E2%86%92%20Domains%20%E2%86%92%20Settings%20%E2%86%92%20Webhook%20Signature%20Payload%20Verification%20Key%20to%20obtain%20your%20webhook%20key.) via `X-Webhook-Signature` header. We also [added support for bounce webhooks](/faq#do-you-support-bounce-webhooks) and now allow users to send newsletters, announcements, and email marketing through our outbound SMTP service. +In August 2024, we added support for exporting mailboxes as [EML](https://en.wikipedia.org/wiki/Email#Filename_extensions) and [Mbox](https://en.wikipedia.org/wiki/Mbox) formats (in addition to the [SQLite](https://en.wikipedia.org/wiki/SQLite) export format already supported). [Webhook signature support](https://forwardemail.net/faq#do-you-support-webhooks:\~:text=If%20you%27re%20on%20a%20paid%20plan%2C%20then%20go%20to%20My%20Account%20%E2%86%92%20Domains%20%E2%86%92%20Settings%20%E2%86%92%20Webhook%20Signature%20Payload%20Verification%20Key%20to%20obtain%20your%20webhook%20key.) via `X-Webhook-Signature` header was added. [Bounce webhook support was added](/faq#do-you-support-bounce-webhooks) and we now allow users to send newsletters, announcements, and email marketing through our outbound SMTP service. We also added the ability to set domain-wide and alias-specific storage quotas for IMAP/POP3/CalDAV. [arc]: https://en.wikipedia.org/wiki/Authenticated_Received_Chain diff --git a/helpers/on-auth.js b/helpers/on-auth.js index 602e4ae14..6ae71cd8e 100644 --- a/helpers/on-auth.js +++ b/helpers/on-auth.js @@ -675,26 +675,18 @@ async function onAuth(auth, session, fn) { .catch((err) => this.logger.warn(err, { session })); } } catch (err) { + // // NOTE: if err.response === 'NO' then WildDuck POP3 will return error message too - // NOTE: if err.response is a message then WildDuck IMAP will return the error message // // NOTE: we should actually share error message if it was not a code bug // (otherwise it won't be intuitive to users if they're late on payment) // (and we now do this via "ALERT" `imapResponse` code set in refineAndLogError // // - const error = refineAndLogError( - err, - session, - this.server instanceof IMAPServer, - this + // + fn( + refineAndLogError(err, session, this.server instanceof IMAPServer, this) ); - if (this.server instanceof IMAPServer) { - error.response = error.message; - fn(error); - } else { - fn(error); - } } } diff --git a/helpers/worker.js b/helpers/worker.js index bab7bbc3f..bacf38510 100644 --- a/helpers/worker.js +++ b/helpers/worker.js @@ -787,7 +787,7 @@ async function backup(payload) { // NOTE: if the SQLite file is 2x larger than the backup, then we // should run a VACUUM since auto vacuum isn't optimal // - if (payload.format === 'sqlite' && tmp) { + if (payload.format === 'sqlite' && tmp && backup) { try { // check how much space is remaining on storage location const storagePath = getPathToDatabase({