feat(analytics-mailer): replace mailgun with stalwart smtp
Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 3s
Monorepo Pipeline / 🧹 Lint (push) Failing after 1m23s
Monorepo Pipeline / 🧪 Test (push) Successful in 40s
Monorepo Pipeline / 🏗️ Build (push) Successful in 2m7s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped

This commit is contained in:
2026-07-21 10:46:51 +02:00
parent 53ad9f23d8
commit b6e8628970
4 changed files with 49 additions and 49 deletions

View File

@@ -28,9 +28,11 @@ jobs:
UMAMI_BASE_URL: ${{ secrets.UMAMI_BASE_URL }}
UMAMI_USERNAME: ${{ secrets.UMAMI_USERNAME }}
UMAMI_PASSWORD: ${{ secrets.UMAMI_PASSWORD }}
MAILGUN_API_KEY: ${{ secrets.MAILGUN_API_KEY }}
MAILGUN_DOMAIN: ${{ secrets.MAILGUN_DOMAIN }}
MAILGUN_SENDER: ${{ secrets.MAILGUN_SENDER }}
SMTP_HOST: ${{ secrets.SMTP_HOST }}
SMTP_PORT: ${{ secrets.SMTP_PORT }}
SMTP_USER: ${{ secrets.SMTP_USER }}
SMTP_PASS: ${{ secrets.SMTP_PASS }}
SMTP_SENDER: ${{ secrets.SMTP_SENDER }}
steps:
- name: Checkout
uses: actions/checkout@v4

View File

@@ -12,13 +12,13 @@
"@mintel/mail": "workspace:*",
"axios": "^1.7.9",
"dotenv": "^16.4.7",
"form-data": "^4.0.1",
"mailgun.js": "^10.3.0"
"nodemailer": "^9.0.3"
},
"devDependencies": {
"@mintel/eslint-config": "workspace:*",
"@mintel/tsconfig": "workspace:*",
"@types/node": "^22.19.10",
"@types/nodemailer": "^8.0.1",
"tsup": "^8.3.5",
"tsx": "^4.19.2",
"typescript": "^5.7.2",

View File

@@ -4,8 +4,7 @@ import axios from "axios";
import { render } from "@mintel/mail";
import { AnalyticsTemplate } from "@mintel/mail";
import { getLast7Days, getThisMonth, getThisYear } from "./utils/time-calculator";
import Mailgun from "mailgun.js";
import formData from "form-data";
import nodemailer from "nodemailer";
import * as dotenv from "dotenv";
dotenv.config();
@@ -15,10 +14,11 @@ const UMAMI_USERNAME = process.env.UMAMI_USERNAME;
const UMAMI_PASSWORD = process.env.UMAMI_PASSWORD;
const UMAMI_API_KEY = process.env.UMAMI_API_KEY;
const MAILGUN_API_KEY = process.env.MAILGUN_API_KEY;
const MAILGUN_DOMAIN = process.env.MAILGUN_DOMAIN;
const MAILGUN_SENDER = process.env.MAILGUN_SENDER || "reports@mintel.me";
const MAILGUN_URL = process.env.MAILGUN_URL || "https://api.eu.mailgun.net";
const SMTP_HOST = process.env.SMTP_HOST || "mail.infra.mintel.me";
const SMTP_PORT = parseInt(process.env.SMTP_PORT || "587", 10);
const SMTP_USER = process.env.SMTP_USER;
const SMTP_PASS = process.env.SMTP_PASS;
const SMTP_SENDER = process.env.SMTP_SENDER || "reports@mintel.me";
interface ConfigClient {
websiteId: string;
@@ -116,9 +116,16 @@ async function main() {
const httpsAgent = new https.Agent({ rejectUnauthorized: false });
const api = axios.create({ baseURL: `${UMAMI_BASE_URL}/api`, headers, httpsAgent });
// Setup Mailgun
const mailgun = new Mailgun(formData);
const mg = mailgun.client({ username: "api", key: MAILGUN_API_KEY || "dummy", url: MAILGUN_URL });
// Setup SMTP Transporter (Nodemailer)
const transporter = nodemailer.createTransport({
host: SMTP_HOST,
port: SMTP_PORT,
secure: SMTP_PORT === 465,
auth: SMTP_USER && SMTP_PASS ? {
user: SMTP_USER,
pass: SMTP_PASS,
} : undefined,
});
const timeRanges = [getLast7Days(), getThisMonth(), getThisYear()];
@@ -153,12 +160,12 @@ async function main() {
await fs.writeFile(debugPath, html);
console.log(`[DRY-RUN] Saved HTML preview to ${debugPath}`);
} else {
if (!MAILGUN_API_KEY) {
throw new Error("MAILGUN_API_KEY is missing for live run.");
if (!SMTP_HOST) {
throw new Error("SMTP_HOST is missing for live run.");
}
await mg.messages.create(MAILGUN_DOMAIN!, {
from: MAILGUN_SENDER,
to: [client.clientEmail],
await transporter.sendMail({
from: SMTP_SENDER,
to: client.clientEmail,
subject,
html,
});

51
pnpm-lock.yaml generated
View File

@@ -115,12 +115,9 @@ importers:
dotenv:
specifier: ^16.4.7
version: 16.6.1
form-data:
specifier: ^4.0.1
version: 4.0.5
mailgun.js:
specifier: ^10.3.0
version: 10.4.0
nodemailer:
specifier: ^9.0.3
version: 9.0.3
devDependencies:
'@mintel/eslint-config':
specifier: workspace:*
@@ -131,6 +128,9 @@ importers:
'@types/node':
specifier: ^22.19.10
version: 22.19.10
'@types/nodemailer':
specifier: ^8.0.1
version: 8.0.1
tsup:
specifier: ^8.3.5
version: 8.5.1(@swc/core@1.15.11(@swc/helpers@0.5.18))(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)
@@ -3599,6 +3599,9 @@ packages:
'@types/node@22.19.10':
resolution: {integrity: sha512-tF5VOugLS/EuDlTBijk0MqABfP8UxgYazTLo3uIn3b4yJgg26QRbVYJYsDtHrjdDUIRfP70+VfhTTc+CE1yskw==}
'@types/nodemailer@8.0.1':
resolution: {integrity: sha512-PxpaInm8V1JQDd4j0ds5HfvWQk8JupS1C0Picb96QJsrrRDjBH+DlK7L4ZdNSqNULhiZRQHc40nLVShaGxXAMw==}
'@types/parse-json@4.0.2':
resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==}
@@ -4303,9 +4306,6 @@ packages:
bare-url@2.3.2:
resolution: {integrity: sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==}
base-64@1.0.0:
resolution: {integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==}
base64-arraybuffer@1.0.2:
resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==}
engines: {node: '>= 0.6.0'}
@@ -6478,10 +6478,6 @@ packages:
resolution: {integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==}
engines: {node: '>=12'}
mailgun.js@10.4.0:
resolution: {integrity: sha512-YrdaZEAJwwjXGBTfZTNQ1LM7tmkdUaz2NpZEu7+zULcG4Wrlhd7cWSNZW0bxT3bP48k5N0mZWz8C2f9gc2+Geg==}
engines: {node: '>=18.0.0'}
map-stream@0.1.0:
resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==}
@@ -6777,6 +6773,10 @@ packages:
node-releases@2.0.27:
resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==}
nodemailer@9.0.3:
resolution: {integrity: sha512-n+YP+NKwR5zRWa60k3GiQ6Q3B4KXCoAw40dAKeCtYn020iNN74aWK2liXIC3ZEATeGql7we3tE3t8QwhY0eskw==}
engines: {node: '>=6.0.0'}
normalize-path@3.0.0:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'}
@@ -8368,9 +8368,6 @@ packages:
uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
url-join@4.0.1:
resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==}
use-context-selector@2.0.0:
resolution: {integrity: sha512-owfuSmUNd3eNp3J9CdDl0kMgfidV+MkDvHPpvthN5ThqM+ibMccNE0k+Iq7TWC6JPFvGZqanqiGCuQx6DyV24g==}
peerDependencies:
@@ -11655,6 +11652,10 @@ snapshots:
dependencies:
undici-types: 6.21.0
'@types/nodemailer@8.0.1':
dependencies:
'@types/node': 20.19.33
'@types/parse-json@4.0.2': {}
'@types/pg-pool@2.0.7':
@@ -12049,7 +12050,7 @@ snapshots:
pathe: 2.0.3
sirv: 3.0.2
tinyglobby: 0.2.15
tinyrainbow: 3.0.3
tinyrainbow: 3.1.0
vitest: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@20.19.33)(@vitest/ui@4.0.18)(happy-dom@20.5.3)(jiti@2.6.1)(jsdom@27.4.0(canvas@3.2.1))(lightningcss@1.30.2)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)
optional: true
@@ -12465,8 +12466,6 @@ snapshots:
dependencies:
bare-path: 3.0.0
base-64@1.0.0: {}
base64-arraybuffer@1.0.2: {}
base64-js@0.0.8: {}
@@ -14143,7 +14142,7 @@ snapshots:
happy-dom@20.5.3:
dependencies:
'@types/node': 20.19.33
'@types/node': 22.19.10
'@types/whatwg-mimetype': 3.0.2
'@types/ws': 8.18.1
entities: 6.0.1
@@ -14950,14 +14949,6 @@ snapshots:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
mailgun.js@10.4.0:
dependencies:
axios: 1.13.5
base-64: 1.0.0
url-join: 4.0.1
transitivePeerDependencies:
- debug
map-stream@0.1.0: {}
marked@14.0.0: {}
@@ -15197,6 +15188,8 @@ snapshots:
node-releases@2.0.27: {}
nodemailer@9.0.3: {}
normalize-path@3.0.0: {}
normalize-svg-path@1.1.0:
@@ -17165,8 +17158,6 @@ snapshots:
dependencies:
punycode: 2.3.1
url-join@4.0.1: {}
use-context-selector@2.0.0(react@19.2.4)(scheduler@0.25.0):
dependencies:
react: 19.2.4