strapi-plugin-magic-mail 3.0.4 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +192 -0
- package/README.md +625 -1336
- package/dist/admin/App-Bi6mzEff.mjs +10973 -0
- package/dist/admin/App-aaEU9cpu.js +10976 -0
- package/dist/admin/PluginSettings-BR2-PVHx.mjs +705 -0
- package/dist/admin/PluginSettings-DbTkOzCh.js +707 -0
- package/dist/admin/analyticsLogs-BCY0Zv3Y.mjs +685 -0
- package/dist/admin/analyticsLogs-DNU09hoB.js +686 -0
- package/dist/admin/de-B5bPRK6l.js +717 -0
- package/dist/admin/de-BqYqMhOn.mjs +717 -0
- package/dist/admin/en-8xANOQdO.mjs +717 -0
- package/dist/admin/en-Bk5eVrtc.js +717 -0
- package/dist/admin/es-BfPsCsam.js +717 -0
- package/dist/admin/es-DgpTufJ_.mjs +717 -0
- package/dist/admin/fr-BuotP0Gc.mjs +717 -0
- package/dist/admin/fr-IQ3KPUoS.js +717 -0
- package/dist/admin/index-QGqbGw6k.js +102 -0
- package/dist/admin/index-qvFJ-a0Q.mjs +103 -0
- package/dist/admin/index.js +2 -99
- package/dist/admin/index.mjs +2 -99
- package/dist/admin/pt-BI63Lncs.js +717 -0
- package/dist/admin/pt-DBZu2cDY.mjs +717 -0
- package/dist/server/index.js +4655 -3374
- package/dist/server/index.mjs +4655 -3374
- package/package.json +54 -48
- package/strapi-admin.js +3 -2
- package/strapi-server.js +3 -2
- package/dist/admin/App-D3TchJYB.js +0 -8028
- package/dist/admin/App-DkyWmEgv.mjs +0 -8007
- package/dist/admin/PluginSettings-B6TdA1qd.js +0 -388
- package/dist/admin/PluginSettings-DTqdZ-nW.mjs +0 -386
- package/dist/admin/StyledButtons-aq1EWrQ-.mjs +0 -433
- package/dist/admin/StyledButtons-jHF7TXal.js +0 -434
- package/dist/admin/de-DKEFnBGC.js +0 -63
- package/dist/admin/de-SKsLY2de.mjs +0 -63
- package/dist/admin/en-CvtZE1HH.js +0 -63
- package/dist/admin/en-Ds2Q7fue.mjs +0 -63
- package/dist/admin/es-BEvzLtTg.mjs +0 -63
- package/dist/admin/es-BbLLes3D.js +0 -63
- package/dist/admin/fr-CRpwImX6.mjs +0 -63
- package/dist/admin/fr-nH9uN6s9.js +0 -63
- package/dist/admin/pt-CjOe2N_n.mjs +0 -63
- package/dist/admin/pt-qt_48NxL.js +0 -63
- package/pics/EmailAccountDashboard.png +0 -0
- package/pics/Emailaddccount1.png +0 -0
- package/pics/RoutingEmailTemplates.png +0 -0
- package/pics/Templates.png +0 -0
- package/pics/codesnippet.png +0 -0
- package/pics/emailAnalytics.png +0 -0
- package/pics/emailDesigner.png +0 -0
- package/pics/emailTemplateSendTest.png +0 -0
- package/pics/emailVersionHistory.png +0 -0
- package/pics/emailaddaccount2.png +0 -0
- package/pics/emailaddaccount3.png +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,195 @@
|
|
|
1
|
+
# [4.0.0](https://github.com/Schero94/Magic-Mail/compare/v3.1.0...v4.0.0) (2026-11-07)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
* build!: harden the supply chain, packaging and release workflow ([5b8954d](https://github.com/Schero94/Magic-Mail/commit/5b8954de4d1c779902f19acdc0dd4646af079784))
|
|
5
|
+
* chore(email-only)!: drop legacy messaging columns and traces, document 4.0 hardening ([53d8ef3](https://github.com/Schero94/Magic-Mail/commit/53d8ef350b22a8568f0781cc36ebb5bb9358a4b1))
|
|
6
|
+
* fix(email)!: harden the sending path and route all native Strapi mail ([fef71e5](https://github.com/Schero94/Magic-Mail/commit/fef71e59d796d185b953857dba22120c12e575a1))
|
|
7
|
+
* fix(security)!: harden accounts, OAuth flows and credential key management ([e443839](https://github.com/Schero94/Magic-Mail/commit/e4438395a1f080569e75f501351ef476af855513))
|
|
8
|
+
* fix(templates)!: validate users-permissions templates, fix versioning and settings singleton ([14dd2b1](https://github.com/Schero94/Magic-Mail/commit/14dd2b11c99f517bc2715a23ad26cb06ffd10e5e))
|
|
9
|
+
* fix(tracking)!: harden click/open tracking, retention and analytics deletion ([015fc25](https://github.com/Schero94/Magic-Mail/commit/015fc257656cd97d7abc2f98e677e94254833931))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
* **admin:** accounts screens keyboard, focus and responsive layout ([0e041b3](https://github.com/Schero94/Magic-Mail/commit/0e041b345fd1131b527c66bea25ad9d0accd013e))
|
|
15
|
+
* **admin:** analytics and settings layout, states, focus and touch targets ([4647723](https://github.com/Schero94/Magic-Mail/commit/4647723003485ebd2a81076e3a56c5cf29737d21))
|
|
16
|
+
* **admin:** keep routing match-type badges on one line ([74eec23](https://github.com/Schero94/Magic-Mail/commit/74eec23332212260ebe5e64ad4401abdb68fc297))
|
|
17
|
+
* **admin:** render account wizard field hints through Field.Root ([759dc0b](https://github.com/Schero94/Magic-Mail/commit/759dc0b252b6476ecb1d65a8b91cdb06d0b3d7ae))
|
|
18
|
+
* **admin:** routing rules layout, form hints, focus and account matching ([e48f535](https://github.com/Schero94/Magic-Mail/commit/e48f5355ea22972b3a98cd654deb15def72566fa))
|
|
19
|
+
* **admin:** say that the default from email does not change the sender ([f039b0d](https://github.com/Schero94/Magic-Mail/commit/f039b0d9e0f04b0e6b6228c1d659113cf4ca78ea))
|
|
20
|
+
* **admin:** template list and editor keyboard, focus, contrast and layout ([2612e5f](https://github.com/Schero94/Magic-Mail/commit/2612e5f748344e1d16901e7dc62e8a8d91f48b96))
|
|
21
|
+
* **admin:** use one download icon for both template downloads ([303139d](https://github.com/Schero94/Magic-Mail/commit/303139d32fc71594507570201a6f464658c42098))
|
|
22
|
+
* **email:** evaluate equal-priority routing rules in list order ([a368301](https://github.com/Schero94/Magic-Mail/commit/a368301f11d9ef3b645c7f7e920df63eea7572b6))
|
|
23
|
+
* **i18n:** use sentence case for all stat card labels ([0c0c42c](https://github.com/Schero94/Magic-Mail/commit/0c0c42c18a4ede629b1616144f57af7d37fd2eb3))
|
|
24
|
+
* **oauth:** keep rotated refresh tokens, refresh early and retry once on 401; add editor-config route ([1600773](https://github.com/Schero94/Magic-Mail/commit/160077355d9d2d192325ea83c655fdffaaeb2238))
|
|
25
|
+
* **release:** pass the npm token as fallback to Trusted Publishing ([e3a85a6](https://github.com/Schero94/Magic-Mail/commit/e3a85a686635a32327527f073e8df0817662f797))
|
|
26
|
+
* resolve all ESLint findings ([7c5d25c](https://github.com/Schero94/Magic-Mail/commit/7c5d25cd6d749de6ec19a679bc4c9a86f9554f51))
|
|
27
|
+
* **security:** throttle rate-limit sweeps, key IPv6 by /64, close tracking-URL and log gaps ([a887ca4](https://github.com/Schero94/Magic-Mail/commit/a887ca4076c9cc9253e443ba478f8cd9b3ca422b))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
### Features
|
|
31
|
+
|
|
32
|
+
* **admin:** add dark-mode, i18n and accessibility foundation ([90903e5](https://github.com/Schero94/Magic-Mail/commit/90903e5d00766e16d95102158c997c24b155d3af))
|
|
33
|
+
* **admin:** harden the analytics and plugin settings views ([131a8ae](https://github.com/Schero94/Magic-Mail/commit/131a8ae318c1fc0fd079e557a864660ad683013f)), closes [#E2E8F0](https://github.com/Schero94/Magic-Mail/issues/E2E8F0) [#1E293B](https://github.com/Schero94/Magic-Mail/issues/1E293B)
|
|
34
|
+
* **admin:** harden the email accounts view and account wizard ([4794fa2](https://github.com/Schero94/Magic-Mail/commit/4794fa2a0460d7ad81d7ce925665b0b7ff02ce3f))
|
|
35
|
+
* **admin:** harden the email templates and template editor views ([30e9dcc](https://github.com/Schero94/Magic-Mail/commit/30e9dccb5015147d10be59c098dc3a92b14360d4))
|
|
36
|
+
* **admin:** harden the routing rules view ([be8eb62](https://github.com/Schero94/Magic-Mail/commit/be8eb6277697c5f2aa404c97dae7710013eef428))
|
|
37
|
+
* **admin:** merge view translations into all five locales ([5b5c55e](https://github.com/Schero94/Magic-Mail/commit/5b5c55ee0d649e5dab5f2e81d449a17150414257))
|
|
38
|
+
* **admin:** reconnect existing OAuth accounts from the edit wizard ([bcb810a](https://github.com/Schero94/Magic-Mail/commit/bcb810afa04923d978b3855250193529112a8f9f))
|
|
39
|
+
* **email:** support the Mailgun EU region ([cd0a837](https://github.com/Schero94/Magic-Mail/commit/cd0a83717ae0bcb509feeda17a021ec9d04f8078))
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
### BREAKING CHANGES
|
|
43
|
+
|
|
44
|
+
* Node.js >=22.12 is required, and
|
|
45
|
+
@strapi/plugin-users-permissions ^5.57.0 is now a required peer dependency.
|
|
46
|
+
|
|
47
|
+
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
48
|
+
* the routing-rule attributes whatsappFallback and whatsappPhoneField are removed; Strapi drops the columns magic_mail_routing_rules.whatsapp_fallback and whatsapp_phone_field on the first start of 4.0 (back up the database first if you need them; with database.settings.forceMigration=false they stay and can be dropped manually).
|
|
49
|
+
|
|
50
|
+
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
51
|
+
* users-permissions email templates reject unsupported placeholders, <% %> blocks and ${...} with 400; template versions now store the saved content (v1 = created) instead of the pre-save content; template list filters other than category/isActive/templateReferenceId are ignored; the template test-send response returns data.success instead of data.result. Migration steps are in CHANGELOG.md under [Unreleased].
|
|
52
|
+
|
|
53
|
+
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
54
|
+
* changing an account's provider/host/port/user/OAuth client/tenant/domain/allowInsecure requires re-entering stored secrets; secrets are shown as the constant "********"; credentials are stored in a v1 format that 3.x can not read and MAGIC_MAIL_ENCRYPTION_KEY is required outside development/test; OAuth needs a public https server.url (or URL) in production and in-flight authorizations must be restarted; Microsoft tenant ids are validated; routing rules must reference existing accounts; provider "ses" is removed; test sends are limited to 10/min per admin; test endpoints return fixed error messages. Migration steps are in CHANGELOG.md under [Unreleased].
|
|
55
|
+
|
|
56
|
+
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
57
|
+
* tracking needs a public URL (no localhost fallback; mail is
|
|
58
|
+
sent untracked otherwise); Content-API sends are click-tracked only for hosts
|
|
59
|
+
in tracking.allowedLinkHosts; tracked links expire after 180 days; tracking
|
|
60
|
+
events store truncated IPs and are deleted after 90 days; tracking hashes use
|
|
61
|
+
MAGIC_MAIL_TRACKING_SECRET / MAGIC_MAIL_ENCRYPTION_KEY (old hashes still
|
|
62
|
+
verify); email-log recipient/subject/templateId semantics changed; at most 100
|
|
63
|
+
links per email are tracked. See CHANGELOG.md for migration steps.
|
|
64
|
+
|
|
65
|
+
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
66
|
+
* Content-API send routes accept API tokens only unless contentApi.allowUserRoles is true; userId must reference an existing user. /send-message rejects the removed `channel` parameter and validates `to`; the response has no `channel` field. path/href attachments are rejected for every caller and provider (pass `content`). from/replyTo must be valid single addresses (from must match the account) and custom headers are limited to an allowlist. An explicit accountName is never replaced on quota/errors; the template category sets the email type; the global unsubscribe URL requires enableUnsubscribeHeader; defaultFromEmail no longer rewrites from; quota windows are clock-aligned (UTC default) and add two private account fields. Migration steps are in CHANGELOG.md under [Unreleased].
|
|
67
|
+
|
|
68
|
+
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
69
|
+
|
|
70
|
+
## [Unreleased]
|
|
71
|
+
|
|
72
|
+
### ⚠ BREAKING CHANGES (platform, packaging and API surface)
|
|
73
|
+
|
|
74
|
+
* **Node.js 22.12 or later is required** (`engines.node` `>=22.12.0 <=26.x.x`, the server bundle targets `node22`). Strapi 5.61 itself requires Node 22, and the HTML sanitizer (`sanitize-html` 2.18) requires 22.12. Migration: upgrade Node before upgrading the plugin.
|
|
75
|
+
* **`@strapi/plugin-users-permissions` `^5.57.0` is a required peer dependency.** The email log has a relation to users-permissions users, which Strapi cannot register without that plugin. Every default Strapi app already includes it. Migration: `npm install @strapi/plugin-users-permissions` if your app removed it.
|
|
76
|
+
* **Peer ranges aligned with the host:** `@strapi/utils` `^5.57.0` (was `^5.0.0`), `react-router-dom` `^6.30.3` (was `^6.30.6`, now the same as Strapi's own peer).
|
|
77
|
+
* **Admin API permissions are capability based.** Every admin route requires `plugin::magic-mail.read`, `.manage` or `.test-send` in addition to an authenticated admin; `plugin::magic-mail.access` only controls the menu entry. Roles that only had `access` can open the plugin but their API requests are denied. Migration: grant the capabilities as described in [docs/permission-migration.md](./docs/permission-migration.md).
|
|
78
|
+
* **MagicMail sends email only.** The non-email messaging transport is removed: the Content API routes `/send-whatsapp`, `/whatsapp/status` and `/whatsapp/check/:phoneNumber`, the matching admin routes and service methods are gone, and `/api/magic-mail/send` no longer accepts `phoneNumber` (strict validation returns 400 for unknown fields). This part already shipped in 3.1.0, which was published as a minor release; it is listed here for users upgrading from 3.0.x. New in this release: `channel` on `/send-message` and the legacy routing-rule columns (see below). Migration: remove these calls and the `phoneNumber` field.
|
|
79
|
+
* **Unused runtime dependencies removed** (`form-data`, `striptags`, `yup`, `decode-html`). Code that relied on them being installed transitively through this plugin must declare them itself.
|
|
80
|
+
|
|
81
|
+
### Features
|
|
82
|
+
|
|
83
|
+
* **designer:** new `designer` plugin config (`projectId`, `scriptUrl`, `version`) served to the editor by `GET /magic-mail/designer/editor-config` (read permission). The visual designer runs with a pinned Unlayer build, no Unlayer project by default, no Unlayer image hosting, stock images, AI or third-party placeholder images, and uploads images to the Strapi Media Library. The README documents the required CSP directives.
|
|
84
|
+
* **admin:** complete translations for English, German (formal), French, Spanish and Portuguese; dark-mode aware colours with WCAG AA contrast, accessible dialogs, labelled controls, unsaved-changes guards, locale-aware dates and numbers, reduced-motion support. The desktop look is unchanged; below 768 px the accounts, routing-rule and analytics tables become card lists, the plugin tab bar scrolls and switches grow to 48x32 touch targets.
|
|
85
|
+
* **admin:** keyboard and focus: table row actions react to Enter, Space and Tab (the Design System cell no longer swallows them); every dialog moves focus inside on open, closes with Escape and returns focus to the button that opened it (or to a stable fallback after a delete); plugin pages render inside the admin `main` landmark so *Skip to content* works.
|
|
86
|
+
* **admin:** routing-rule form and account wizard hints are rendered and linked to their inputs (they were passed as `Field.Hint` children, which Design System 2.2 ignores); rule account names match case-insensitively, like the router and the server.
|
|
87
|
+
* **admin:** tables wrap long values instead of scrolling sideways on desktop widths; stat cards line up with the page edges; contrast fixes for the dark-mode Transactional badge and the Pending delivery status.
|
|
88
|
+
* **admin:** the analytics view pages and searches on the server and clears logs in batches; the routing view shows broken account references; account edit shows how many rules reference an account.
|
|
89
|
+
* **ci:** pull requests run ESLint, the Prettier check, tests, build, export and bundle verification, `npm pack --dry-run` and a production audit on Node 22 and 24; the Dependabot auto-merge verification runs lint and the format check too.
|
|
90
|
+
* **email:** Mailgun EU region. Mailgun accounts take `config.region` (`us` or `eu`, default `us`); `eu` sends through `api.eu.mailgun.net`. The account wizard has a region field, and the server rejects any other value on create and update. Existing accounts without a region keep using `api.mailgun.net`.
|
|
91
|
+
* **build:** ESLint 10 flat config (`@eslint/js`, `eslint-plugin-react-hooks` 7, `eslint-config-prettier`) and Prettier, with `lint`, `lint:fix`, `format` and `format:check` scripts.
|
|
92
|
+
|
|
93
|
+
### Changes
|
|
94
|
+
|
|
95
|
+
* **admin:** the keep-alive polling (`useAuthRefresh`) is removed. Strapi's admin idle-session lifetime now applies while a MagicMail tab is open.
|
|
96
|
+
* **release:** GitHub actions are pinned by commit SHA; the release workflow verifies in a read-only job and publishes from a separate job with npm provenance; dependencies are installed with `--ignore-scripts` and without `--legacy-peer-deps`.
|
|
97
|
+
* **deps:** `vite` override raised to `^6.4.5`, the global `undici` override replaced by a scoped `^7.30.2` override for `@semantic-release/github`.
|
|
98
|
+
|
|
99
|
+
### ⚠ BREAKING CHANGES (sending path)
|
|
100
|
+
|
|
101
|
+
* **Content API send routes accept API tokens only.** `/api/magic-mail/send` and `/api/magic-mail/send-message` now reject users-permissions roles with 403. Migration: send with an API token, or restore the old behaviour with `config: { contentApi: { allowUserRoles: true } }` for `magic-mail` in `config/plugins.js`. With user roles allowed, the email log is attributed to the authenticated user and a body `userId` is ignored.
|
|
102
|
+
* **`userId` must reference an existing user.** API-token requests with a `userId` of a non-existent users-permissions user get a 400. Migration: send the numeric id or documentId of an existing user, or omit `userId`.
|
|
103
|
+
* **`channel` removed from `/send-message`.** The parameter is rejected (400) and the response no longer contains `channel`. Migration: drop `channel` from requests and stop reading it from responses.
|
|
104
|
+
* **`/send-message` validates `to` as an address list** (same rules as `/send`). Invalid recipients get a 400 before anything is logged.
|
|
105
|
+
* **File-path and URL attachments are rejected** for every caller and provider (native Strapi email service, `email-router` service and Content API). Previously SendGrid and Mailgun read `attachment.path` from disk while SMTP rejected it. Migration: read the file yourself and pass `content` (Buffer, or a string with `encoding: 'base64'`). Content-API requests with `path` now fail validation instead of having the path silently removed.
|
|
106
|
+
* **`from` and `replyTo` must be valid addresses.** `from` is parsed with RFC 5322 rules and must be one mailbox equal to the sending account's address; only its display name is kept. Content-API requests with any other `from`/`replyTo` format get a 400.
|
|
107
|
+
* **Custom headers use an allowlist** (`X-*` except `X-Mailer`, `X-Email-Type`, `X-Priority`, `X-Entity-Ref-ID`, `X-Content-Type-Options`, plus `List-Id`, `Precedence`, `Auto-Submitted`, `In-Reply-To`, `References`), at most 20. Other headers (for example `Return-Path`, `Disposition-Notification-To`, `Resent-*`) are dropped. MagicMail's own headers always win. Migration: rename custom headers to `X-*`.
|
|
108
|
+
* **Explicit `accountName` is never replaced.** When the requested account is over quota or fails, the send fails instead of silently using another account. `accountName` lookup is now case-insensitive.
|
|
109
|
+
* **Template category drives the email type.** A template with category `marketing` or `notification` sent without `type` is now routed and labelled with that type (marketing mail gets `List-Unsubscribe`/`Precedence: bulk` when an unsubscribe URL is available). Migration: pass `type: 'transactional'` explicitly to keep the old behaviour.
|
|
110
|
+
* **Global unsubscribe URL requires the "Unsubscribe header" toggle.** The plugin-wide `unsubscribeUrl` is only applied when `enableUnsubscribeHeader` is on. `{{email}}` in the URL is replaced with the recipient (single-recipient mails only), and `List-Unsubscribe-Post: One-Click` is only sent for https URLs.
|
|
111
|
+
* **`plugin-settings.defaultFromEmail` no longer rewrites `from`.** The sender address is always the selected account's address. `defaultFromName` is used as display name when the account has no `fromName` and the caller gave none.
|
|
112
|
+
* **Quota windows are clock-aligned (UTC by default)** and reserved atomically before dispatch; the boot-relative hourly timer and local-midnight daily timer are gone. Two private fields (`quotaHourKey`, `quotaDayKey`) are added to the email-account content type (Strapi adds the columns automatically). Configure the zone with `quotaTimezone`.
|
|
113
|
+
* **Plugin warnings and errors are always logged** (previously hidden unless `debug: true`). Info and debug output still requires `debug: true`.
|
|
114
|
+
|
|
115
|
+
### ⚠ BREAKING CHANGES (tracking and analytics)
|
|
116
|
+
|
|
117
|
+
* **No tracking without a public URL.** Tracking URLs use the *Tracking base URL* setting, then `server.url`, then `server.absoluteUrl`, plus `api.rest.prefix`; the open pixel now honours the setting too. The `http://localhost:1337` fallback is gone: in production a loopback host is never used and mail is sent untracked (one warning). Migration: set *Tracking base URL* or `server.url` to your public URL.
|
|
118
|
+
* **Content-API sends are not click-tracked by default.** Mails sent through `/api/magic-mail/send` and `/send-message` only get click tracking for hosts listed in `tracking.allowedLinkHosts` (an API token can no longer mint redirects on your domain to any site). Open tracking is unchanged. Migration: set `config.tracking.allowedLinkHosts: ['example.com']` for `magic-mail` in `config/plugins.js`. When the list is set, it also limits admin/service sends and existing tracked links.
|
|
119
|
+
* **Tracked links expire after 180 days** (`tracking.linkTtlDays`) and then show the fallback page; expired link mappings are deleted. Migration: set `linkTtlDays: 0` to keep links forever.
|
|
120
|
+
* **Tracking data retention and anonymisation.** Open/click events store the IP truncated to /24 (IPv4) or /48 (IPv6), no `location` and at most 512 characters of user agent; `X-Forwarded-For` is no longer read (enable `server.proxy` behind a proxy). Events older than 90 days are deleted (`tracking.eventRetentionDays`). On first start a one-time migration (`trackingPrivacyMigrationV1`) truncates stored IPs, clears `location` and removes sensitive click URLs from existing events. Optional `tracking.logRetentionDays` deletes old email logs. Migration: set `anonymizeIp: false` or `eventRetentionDays: 0` to keep the old behaviour.
|
|
121
|
+
* **New tracking hash secret.** Tracking hashes are signed with `MAGIC_MAIL_TRACKING_SECRET`, else `MAGIC_MAIL_ENCRYPTION_KEY`, else the first app key, instead of the raw `APP_KEYS` string. Links in mails sent before the upgrade keep working (old hashes are still verified), and rotating `APP_KEYS` no longer breaks them. Migration: optionally set `MAGIC_MAIL_TRACKING_SECRET`; when rotating it, keep old values in `MAGIC_MAIL_TRACKING_SECRET_PREVIOUS`.
|
|
122
|
+
* **Email log fields.** `recipient` is the first bare address (all recipients are kept in `metadata.recipients`), `subject` is cut to 255 characters, and `templateId` holds the numeric template reference id (a non-numeric requested id is kept in `metadata.requestedTemplateId`). Previously multi-recipient or display-name sends failed to create a log and were sent untracked.
|
|
123
|
+
* **At most 100 links per email are tracked** (`tracking.maxLinksPerEmail`); further links stay untracked.
|
|
124
|
+
* **Tracking rate limit degrades.** Above 300 requests per minute and IP, opens and clicks are no longer recorded, but the pixel is served and the click redirects; only above 3,000 per minute is a plain-text 429 returned (no JSON envelope).
|
|
125
|
+
|
|
126
|
+
### ⚠ BREAKING CHANGES (accounts, OAuth, templates, settings)
|
|
127
|
+
|
|
128
|
+
* **Stored secrets must be re-entered when the endpoint changes.** `PUT /magic-mail/accounts/:id` keeps a stored secret (SMTP password, API key, OAuth client secret, DKIM key) only while provider, `host`, `port`, `user`, `clientId`, `tenantId`, `domain` and `allowInsecure` stay the same. Otherwise a masked or omitted secret is rejected with 400 (`details.config` lists the fields). Changing the provider also requires a complete `config`; old keys of the previous provider are dropped. Migration: send the secret again whenever you change one of these fields.
|
|
129
|
+
* **Secret placeholder is a constant.** `GET /magic-mail/accounts/:id` returns `********` for every stored secret instead of `****` plus the last four characters. Only this exact value means "keep"; any other value (also one starting with `****`) is stored as the new secret. An unreadable config returns `credentialsUnreadableReason: "KEY_MISMATCH"` instead of the crypto error text. Migration: API clients that round-tripped the old `****abcd` mask must send `********` (or the real secret).
|
|
130
|
+
* **Credential encryption format and key rules.** New ciphertexts use `v1:<iv>:<tag>:<ct>` (AES-256-GCM, 12-byte IV, HKDF-SHA256 subkey). Pre-4.0 blobs still decrypt and are re-encrypted on the next save; 3.x can not read `v1` blobs, so back up the database before upgrading if you might roll back. `MAGIC_MAIL_ENCRYPTION_KEY` is now required whenever `NODE_ENV` is not unset/`development`/`test` (previously only in `production`), and the constant fallback key is gone. Rotation: set the new key in `MAGIC_MAIL_ENCRYPTION_KEY` and the old one in `MAGIC_MAIL_ENCRYPTION_KEY_PREVIOUS`, then save each account.
|
|
131
|
+
* **OAuth state signing key.** The state HMAC uses an HKDF subkey of `MAGIC_MAIL_OAUTH_STATE_SECRET`, else `MAGIC_MAIL_ENCRYPTION_KEY`, else the first app key (previously the raw `APP_KEYS` string). States are bound to the admin user who started the flow. Authorizations in progress during the upgrade must be restarted.
|
|
132
|
+
* **OAuth redirect URI in production.** The redirect URI comes from `URL` (legacy override) or Strapi's `server.url`/`server.absoluteUrl`; in production it must be public `https`, the `http://localhost:1337` fallback is gone and starting a flow fails with a 400 explaining what to set. Migration: set `server.url` (or `URL`) to the public URL and make sure it matches the redirect URI registered with Google/Microsoft/Yahoo. The `/oauth/<provider>/auth` response now includes `redirectUri`. The callback page no longer redirects to `/admin/plugins/magic-mail?oauth_code=...` when the opener tab is gone; restart the flow from the admin page.
|
|
133
|
+
* **Microsoft tenant IDs are validated** (GUID, domain, `common`/`organizations`/`consumers`; letters, digits, `.` and `-` only). `oauth/create-account` no longer accepts `config.domain`.
|
|
134
|
+
* **users-permissions email templates need users-permissions permissions.** `GET`/`PUT /magic-mail/designer/core/:type` additionally require *Users & Permissions → Email templates* read/update. Saving converts `{{ KEY }}` to `<%= KEY %>` and rejects `<% %>` blocks, `${...}` and any placeholder other than `URL`, `SERVER_URL`, `ADMIN_URL`, `CODE`, `TOKEN`, `USER`, `USER.email`, `USER.username` (400). Previously `{{URL}}` was saved as `<%URL%>`, which users-permissions does not fill in. Migration: grant the users-permissions template permissions to roles that edit these templates, and re-save templates that contain unsupported placeholders.
|
|
135
|
+
* **Users & Permissions is a required peer dependency** (see *platform and packaging* above). Core-template endpoints answer 404 when the plugin is not loaded.
|
|
136
|
+
* **Legacy routing-rule columns removed.** The unused attributes `whatsappFallback` and `whatsappPhoneField` are deleted from the routing-rule content type. Strapi drops the columns `magic_mail_routing_rules.whatsapp_fallback` and `whatsapp_phone_field` on the first start of 4.0. Migration: back up the database first if you need those values; with `database.settings.forceMigration: false` the columns stay and can be dropped manually.
|
|
137
|
+
* **Routing rules reference existing accounts.** Creating or updating a rule with an `accountName`/`fallbackAccountName` that matches no account (case-insensitive) returns 400. Renaming an account updates the rules that use it; deleting an account deactivates rules that target it and clears it as fallback (`DELETE /accounts/:id` returns the counts in `data`). `GET /routing-rules` is sorted by priority descending (evaluation order).
|
|
138
|
+
* **Provider `ses` removed** from the email-account enum (it never had an adapter).
|
|
139
|
+
* **Template versions are the saved states.** Version 1 is the created content and each content-changing save adds one version with the new content (previously the old content was snapshotted before every save, duplicating v1). Saves that only change `isActive`/category add no version. Export includes `isActive`; an import keeps it.
|
|
140
|
+
* **Template list filters are allowlisted.** `GET /designer/templates?filters[...]` only honours `category`, `isActive` and `templateReferenceId` equality; other filters are ignored.
|
|
141
|
+
* **Test sends are rate limited** to 10 per minute per admin across `POST /accounts/:id/test`, `/test-strapi-service` and `/designer/templates/:id/test-send` (429 above).
|
|
142
|
+
* **Client-facing error texts.** Account tests and test sends return fixed messages (for example "Could not connect to the mail server. Check host, port and TLS settings.") instead of raw transport errors; `/test-strapi-service` answers 502 on delivery failure and no longer returns the provider `result`; the template test-send response returns `data.success` instead of `data.result`.
|
|
143
|
+
* **Content-API send routes carry the `plugin::magic-mail.api-token-only` route policy** in addition to the controller check (403 for users-permissions roles unless `contentApi.allowUserRoles: true`).
|
|
144
|
+
|
|
145
|
+
### Bug Fixes
|
|
146
|
+
|
|
147
|
+
* **tracking:** the click-tracking link rewriter is linear-time (no catastrophic backtracking); a crafted 100 KB HTML body could block the event loop for minutes.
|
|
148
|
+
* **tracking:** more token-bearing links are excluded from click tracking (any parameter name containing `token`, `secret`, `session`, `signature`, `reset`, ...; names ending in `code`, `key`, `sig`, `otp`, `state`; long random values; URLs with credentials).
|
|
149
|
+
* **tracking:** a link is only rewritten after its mapping was stored, so a failed insert never produces a dead link; mappings are stored with bounded concurrency.
|
|
150
|
+
* **tracking:** malformed tracking ids are rejected before any database query.
|
|
151
|
+
* **analytics:** deleting an email log, or clearing all logs, also deletes its stored link mappings; clear-all reports `hasMore` correctly. `GET /analytics/emails` searches template names too and accepts `deliveryStatus`; `pageSize` is capped at 100.
|
|
152
|
+
* **templates:** `{{system.date}}`, `{{system.year}}` and `{{system.unsubscribe}}` are filled by the server (`system.webview` renders empty); callers cannot override them.
|
|
153
|
+
* **email:** native interception wraps the `@strapi/email` provider, so `sendTemplatedEmail()` (admin password reset) is routed through MagicMail too.
|
|
154
|
+
* **email:** when no active account exists, or every eligible account is over quota, native Strapi emails are sent through the original provider instead of failing. Failures after an account was attempted are returned to the caller and never re-sent.
|
|
155
|
+
* **email:** failover tries every eligible account in order (rule account, rule fallback, the accounts of further matching rules, primary, others by priority) and continues on errors that guarantee non-delivery; attempted and skipped accounts are stored on the email log.
|
|
156
|
+
* **email:** routing rules with equal priority are evaluated oldest first, the same order the admin list and `GET /routing-rules` show (previously the database order decided).
|
|
157
|
+
* **email:** recipient routing rules work with recipient arrays, CSV strings and display names.
|
|
158
|
+
* **email:** SendGrid no longer double-encodes base64 attachments and sends `cid` attachments inline; SendGrid, Mailgun and Yahoo now apply custom headers and the aligned sender display name.
|
|
159
|
+
* **email:** the outbound HTML sanitizer keeps email layout markup (`<style>` blocks with a CSS filter, `bgcolor`, `align`, `valign`, `role`, `<center>`, `<font>`, `data:image` inline images) while still removing scripts, event handlers and `javascript:` URLs.
|
|
160
|
+
* **email:** template rendering no longer decodes HTML entities across the whole body (`decode-html` removed); only Mustache tags are decoded.
|
|
161
|
+
* **email:** every email is logged, including text-only mails and mails with `enableTracking: false`.
|
|
162
|
+
* **email:** `send()` no longer mutates the caller's object, so reusing it does not leak tracking data between recipients.
|
|
163
|
+
* **email:** the account test sends through exactly the tested account (also before activation).
|
|
164
|
+
* **email:** SMTP accounts can set `allowInsecure: true` for local mail catchers (honoured outside production or for loopback hosts).
|
|
165
|
+
* **email:** Mailgun domains are validated and URL-encoded.
|
|
166
|
+
* **tracking:** the one-time sensitive tracking-link purge pages by id and runs again once (`sensitiveLinkPurgeV2`) to catch rows the earlier version skipped.
|
|
167
|
+
* **accounts:** clearing Reply-To, description or From name in the edit form clears the stored value; unlimited (0) quotas are kept.
|
|
168
|
+
* **accounts:** account test emails escape every interpolated value.
|
|
169
|
+
* **oauth:** rotated Microsoft/Yahoo refresh tokens are returned by the refresh helpers (`refreshToken`) so they can be persisted; `buildOAuthBlob()` keeps the previous refresh token when none is returned. New `POST /magic-mail/oauth/reconnect/:accountId` re-authorizes an existing account (start with `GET /oauth/<provider>/auth?accountId=<documentId>`).
|
|
170
|
+
* **oauth:** pending PKCE verifiers live in one pruned store entry (abandoned flows no longer leave rows behind; pre-4.0 `oauth:pkce:*` rows are deleted once), and concurrent flows no longer lose each other's entries.
|
|
171
|
+
* **oauth:** the callback page posts to the admin panel origin (`admin.absoluteUrl`), so split admin/API hosts work.
|
|
172
|
+
* **templates:** saving a users-permissions template no longer crashes when the store entry is empty; numeric template/version lookups use the Query Engine instead of the deprecated `entityService`; concurrent saves get distinct version numbers; downloads use a sanitized file name and a sandbox CSP.
|
|
173
|
+
* **settings:** the settings row is created once even for concurrent first sends, duplicate rows from earlier races are removed, and reads are cached for 10 seconds (cleared on update).
|
|
174
|
+
* **oauth:** expiring access tokens are refreshed 60 seconds early, rotated refresh tokens are stored (previously dropped by the router, so Microsoft/Yahoo accounts eventually lost access), concurrent sends share one refresh, and a send rejected with HTTP 401 / SMTP `EAUTH` is refreshed and retried exactly once.
|
|
175
|
+
* **build:** `npm run watch` and `watch:link` now produce the same esbuild server bundle as `npm run build` (previously the server entry required files that did not exist in `dist`); `verify:runtime` rejects an unbundled server entry.
|
|
176
|
+
* **package:** the root `strapi-admin.js`/`strapi-server.js` shims point at `dist` instead of unpublished sources, and README screenshots (about 7 MB) are no longer shipped in the npm tarball.
|
|
177
|
+
* **cleanup:** removed the unregistered debug controller (`controllers/test.js`), the unused `service.js`, and unused helpers (`getAccountWithDecryptedConfig`, `resetCounters`, `isLinkTrackingEnabled`, `isOpenTrackingEnabled`, `getTrackingBaseUrl`); the dev integration script no longer calls removed endpoints, asserts they return 404 and prints no part of the admin JWT.
|
|
178
|
+
|
|
179
|
+
# [3.1.0](https://github.com/Schero94/Magic-Mail/compare/v3.0.4...v3.1.0) (2026-10-15)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
### Bug Fixes
|
|
183
|
+
|
|
184
|
+
* align CI installs with lockfile settings ([657f8cb](https://github.com/Schero94/Magic-Mail/commit/657f8cb8f6ef9b0e35e6c3d52a987c4186d90984))
|
|
185
|
+
* keep Magic-Mail tabs in sync with URL ([e3522a6](https://github.com/Schero94/Magic-Mail/commit/e3522a64606bca39065260556d03e56449ae8a2f))
|
|
186
|
+
* make release failure notices tolerant of missing labels ([2b2403e](https://github.com/Schero94/Magic-Mail/commit/2b2403e3ad9c30b668b1b6f804373e31af682cee))
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
### Features
|
|
190
|
+
|
|
191
|
+
* make Magic-Mail email-only and harden delivery ([906c813](https://github.com/Schero94/Magic-Mail/commit/906c8133e18e54608ba2b621daf4040f579aaa2d))
|
|
192
|
+
|
|
1
193
|
## [3.0.4](https://github.com/Schero94/Magic-Mail/compare/v3.0.3...v3.0.4) (2026-07-11)
|
|
2
194
|
|
|
3
195
|
|