@runnerpro/backend 1.23.0 → 1.23.1
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.
|
@@ -14,18 +14,40 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.sendMail = void 0;
|
|
16
16
|
const nodemailer_1 = __importDefault(require("nodemailer"));
|
|
17
|
+
/*
|
|
18
|
+
Transporter con POOL.
|
|
19
|
+
|
|
20
|
+
Sin `pool`, nodemailer abre conexión + AUTH nuevos en CADA sendMail. Un cron que
|
|
21
|
+
envía cientos de correos seguidos encadena cientos de logins contra Gmail y acaba
|
|
22
|
+
en `454-4.7.0 Too many login attempts`, que además deja la cuenta bloqueada un rato
|
|
23
|
+
y tumba también a los demás correos de esa ventana (incidente T-172:
|
|
24
|
+
sendWorkoutsByMail, 728 correos de golpe, caído todos los domingos desde el
|
|
25
|
+
05/07/2026).
|
|
26
|
+
|
|
27
|
+
Con pool la conexión se reutiliza: un solo AUTH para toda la tanda.
|
|
28
|
+
- maxConnections 1: una sola conexión viva, así el número de logins no depende del
|
|
29
|
+
número de correos.
|
|
30
|
+
- maxMessages 100: Gmail corta la sesión por número de mensajes; al llegar al tope
|
|
31
|
+
nodemailer reconecta solo (728 correos ≈ 8 logins, no 728).
|
|
32
|
+
- rateDelta/rateLimit: como mucho 2 correos por segundo, para no disparar tampoco
|
|
33
|
+
los límites por volumen.
|
|
34
|
+
*/
|
|
17
35
|
const transporter = nodemailer_1.default.createTransport({
|
|
18
36
|
service: 'gmail',
|
|
19
37
|
auth: {
|
|
20
38
|
user: process.env.GMAIL_EMAIL_USER,
|
|
21
39
|
pass: process.env.GMAIL_EMAIL_PWD,
|
|
22
40
|
},
|
|
41
|
+
pool: true,
|
|
42
|
+
maxConnections: 1,
|
|
43
|
+
maxMessages: 100,
|
|
44
|
+
rateDelta: 1000,
|
|
45
|
+
rateLimit: 2,
|
|
23
46
|
connectionTimeout: 10000,
|
|
24
47
|
greetingTimeout: 10000,
|
|
25
48
|
socketTimeout: 20000,
|
|
26
49
|
});
|
|
27
50
|
const sendMail = ({ subject, title, body, to, link, attachments, bcc, signoff }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
28
|
-
var _a;
|
|
29
51
|
const toMapped = process.env.NODE_ENV === 'PROD' ? to.map((correo) => correo).join(',') : process.env.GMAIL_EMAIL_USER;
|
|
30
52
|
if ((toMapped === null || toMapped === void 0 ? void 0 : toMapped.length) === 0)
|
|
31
53
|
return;
|
|
@@ -39,29 +61,28 @@ const sendMail = ({ subject, title, body, to, link, attachments, bcc, signoff })
|
|
|
39
61
|
html: bodyHTML,
|
|
40
62
|
attachments: attachments || [],
|
|
41
63
|
});
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
transport.once('close', onClose);
|
|
56
|
-
sendPromise.finally(() => {
|
|
57
|
-
transport.removeListener('error', onError);
|
|
58
|
-
transport.removeListener('close', onClose);
|
|
59
|
-
});
|
|
60
|
-
});
|
|
64
|
+
/*
|
|
65
|
+
Antes había aquí una red de seguridad que escuchaba 'error'/'close' del transporte
|
|
66
|
+
para no quedarse colgado si el socket moría. Se quita porque:
|
|
67
|
+
|
|
68
|
+
- Con `pool`, el SMTPPool solo emite 'idle': los fallos de conexión los devuelve por
|
|
69
|
+
el callback del propio mensaje, así que `sendPromise` ya se resuelve o rechaza
|
|
70
|
+
siempre; el timeout de abajo sigue siendo el tope duro.
|
|
71
|
+
- Aquel código creaba una cadena huérfana (`sendPromise.finally(...)`) que heredaba
|
|
72
|
+
el rechazo de `sendPromise` y no la escuchaba nadie: CADA envío fallido dejaba un
|
|
73
|
+
`[FATAL] Unhandled Rejection` en el log, que es justo lo que se veía en el
|
|
74
|
+
incidente T-172 junto al `454-4.7.0 Too many login attempts`.
|
|
75
|
+
*/
|
|
76
|
+
let timeoutId;
|
|
61
77
|
const timeoutPromise = new Promise((_, reject) => {
|
|
62
|
-
setTimeout(() => reject(new Error('sendMail timeout')), 30000);
|
|
78
|
+
timeoutId = setTimeout(() => reject(new Error('sendMail timeout')), 30000);
|
|
63
79
|
});
|
|
64
|
-
|
|
80
|
+
try {
|
|
81
|
+
return yield Promise.race([sendPromise, timeoutPromise]);
|
|
82
|
+
}
|
|
83
|
+
finally {
|
|
84
|
+
clearTimeout(timeoutId);
|
|
85
|
+
}
|
|
65
86
|
});
|
|
66
87
|
exports.sendMail = sendMail;
|
|
67
88
|
function getBodyHTML(title, body, link, signoff) {
|
|
@@ -21,6 +21,6 @@ interface Mail {
|
|
|
21
21
|
*/
|
|
22
22
|
signoff?: string;
|
|
23
23
|
}
|
|
24
|
-
declare const sendMail: ({ subject, title, body, to, link, attachments, bcc, signoff }: Mail) => Promise<import("nodemailer/lib/smtp-
|
|
24
|
+
declare const sendMail: ({ subject, title, body, to, link, attachments, bcc, signoff }: Mail) => Promise<import("nodemailer/lib/smtp-pool").SentMessageInfo>;
|
|
25
25
|
export { sendMail };
|
|
26
26
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/sendMail/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/sendMail/index.ts"],"names":[],"mappings":"AAoCA,UAAU,IAAI;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,EAAE,CAAC;IACb,IAAI,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IACzD,WAAW,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACjE,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IACf;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,QAAA,MAAM,QAAQ,kEAAyE,IAAI,gEAmC1F,CAAC;AAiEF,OAAO,EAAE,QAAQ,EAAE,CAAC"}
|