gcharanteja commited on
Commit
d6293df
·
1 Parent(s): 1e8fcff
Files changed (4) hide show
  1. Dockerfile +0 -1
  2. nginx-smtp.conf +0 -8
  3. server.js +53 -38
  4. test-email.mjs +41 -25
Dockerfile CHANGED
@@ -11,7 +11,6 @@ COPY . .
11
 
12
  ENV PORT=7860
13
  ENV DATA_DIR=/data
14
- ENV SMTP_PROXY=http://nginx:2587
15
  EXPOSE 7860
16
 
17
  VOLUME ["/data"]
 
11
 
12
  ENV PORT=7860
13
  ENV DATA_DIR=/data
 
14
  EXPOSE 7860
15
 
16
  VOLUME ["/data"]
nginx-smtp.conf DELETED
@@ -1,8 +0,0 @@
1
- stream {
2
- server {
3
- listen 2587;
4
- proxy_pass smtp-relay.brevo.com:587;
5
- proxy_connect_timeout 30s;
6
- proxy_timeout 60s;
7
- }
8
- }
 
 
 
 
 
 
 
 
 
server.js CHANGED
@@ -4,7 +4,6 @@ import path from "node:path";
4
  import { fileURLToPath } from "node:url";
5
  import express from "express";
6
  import { firefox } from "playwright";
7
- import nodemailer from "nodemailer";
8
 
9
  const __filename = fileURLToPath(import.meta.url);
10
  const __dirname = path.dirname(__filename);
@@ -27,6 +26,7 @@ const SECRETS = [
27
  "SMTP_PASS",
28
  "FROM_EMAIL",
29
  "TO_EMAIL",
 
30
  ];
31
 
32
  app.get("/hello", (req, res) => {
@@ -68,14 +68,6 @@ function fetchSecret(key) {
68
  });
69
  }
70
 
71
- async function getSmtpConfig() {
72
- const [host, port, user, pass, from, to] = await Promise.all(
73
- SECRETS.map(fetchSecret),
74
- );
75
-
76
- return { host, port: Number(port), user, pass, from, to };
77
- }
78
-
79
  function readState() {
80
  try {
81
  return JSON.parse(fs.readFileSync(STATE_FILE, "utf8"));
@@ -88,38 +80,61 @@ function writeState(state) {
88
  fs.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
89
  }
90
 
91
- const SMTP_PROXY = process.env.SMTP_PROXY || "";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
 
93
  async function sendEmail(subject, body) {
94
- log("Fetching SMTP config...");
95
- const cfg = await getSmtpConfig();
96
- log(`SMTP config loaded (host=${cfg.host}, port=${cfg.port}, from=${cfg.from}, to=${cfg.to})`);
97
-
98
- const opts = {
99
- host: cfg.host,
100
- port: cfg.port,
101
- secure: cfg.port === 465,
102
- auth: { user: cfg.user, pass: cfg.pass },
103
- connectionTimeout: 30000,
104
- greetingTimeout: 30000,
105
- socketTimeout: 60000,
106
- };
107
- if (SMTP_PROXY) {
108
- opts.proxy = SMTP_PROXY;
109
- log(`Using SMTP proxy: ${SMTP_PROXY}`);
110
  }
111
-
112
- const transporter = nodemailer.createTransport(opts);
113
-
114
- log("Sending email...");
115
- const result = await transporter.sendMail({
116
- from: cfg.from,
117
- to: cfg.to,
118
- subject,
119
- text: body,
120
- });
121
- log(`Email sent (messageId=${result.messageId})`);
122
- return result;
123
  }
124
 
125
  function valuesChanged(oldVals, newVals) {
 
4
  import { fileURLToPath } from "node:url";
5
  import express from "express";
6
  import { firefox } from "playwright";
 
7
 
8
  const __filename = fileURLToPath(import.meta.url);
9
  const __dirname = path.dirname(__filename);
 
26
  "SMTP_PASS",
27
  "FROM_EMAIL",
28
  "TO_EMAIL",
29
+ "BREVO_API_KEY",
30
  ];
31
 
32
  app.get("/hello", (req, res) => {
 
68
  });
69
  }
70
 
 
 
 
 
 
 
 
 
71
  function readState() {
72
  try {
73
  return JSON.parse(fs.readFileSync(STATE_FILE, "utf8"));
 
80
  fs.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
81
  }
82
 
83
+ async function sendEmailViaApi(subject, body) {
84
+ log("Fetching email config...");
85
+ const [from, to, apiKey] = await Promise.all([
86
+ fetchSecret("FROM_EMAIL"),
87
+ fetchSecret("TO_EMAIL"),
88
+ fetchSecret("BREVO_API_KEY"),
89
+ ]);
90
+ log(`Sending via Brevo API (from=${from}, to=${to})`);
91
+
92
+ const payload = JSON.stringify({
93
+ sender: { email: from },
94
+ to: [{ email: to }],
95
+ subject,
96
+ textContent: body,
97
+ });
98
+
99
+ return new Promise((resolve, reject) => {
100
+ const req = https.request(
101
+ {
102
+ hostname: "api.brevo.com",
103
+ port: 443,
104
+ path: "/v3/smtp/email",
105
+ method: "POST",
106
+ headers: {
107
+ "api-key": apiKey,
108
+ "Content-Type": "application/json",
109
+ "Content-Length": Buffer.byteLength(payload),
110
+ },
111
+ },
112
+ (res) => {
113
+ let data = "";
114
+ res.on("data", (c) => (data += c));
115
+ res.on("end", () => {
116
+ if (res.statusCode === 201 || res.statusCode === 200) {
117
+ log("Email sent via Brevo API");
118
+ resolve();
119
+ } else {
120
+ reject(new Error(`Brevo API ${res.statusCode}: ${data}`));
121
+ }
122
+ });
123
+ },
124
+ );
125
+ req.on("error", reject);
126
+ req.write(payload);
127
+ req.end();
128
+ });
129
+ }
130
 
131
  async function sendEmail(subject, body) {
132
+ try {
133
+ await sendEmailViaApi(subject, body);
134
+ } catch (error) {
135
+ err(`Failed to send email: ${error.message}`);
136
+ throw error;
 
 
 
 
 
 
 
 
 
 
 
137
  }
 
 
 
 
 
 
 
 
 
 
 
 
138
  }
139
 
140
  function valuesChanged(oldVals, newVals) {
test-email.mjs CHANGED
@@ -1,36 +1,52 @@
1
- import https from "node:https";
2
-
3
  const API_BASE = "https://maxxcarl-keyvault.hf.space/secrets";
4
  const API_KEY = "Azure@123";
5
- const SECRETS = ["SMTP_HOST","SMTP_PORT","SMTP_USER","SMTP_PASS","FROM_EMAIL","TO_EMAIL"];
6
 
7
- function fetchSecret(key) {
 
 
8
  return new Promise((resolve, reject) => {
9
- https.get(`${API_BASE}/${key}`, { headers: { "X-API-Key": API_KEY } }, res => {
10
- let data = "";
11
- res.on("data", c => data += c);
12
- res.on("end", () => resolve(JSON.parse(data).value));
13
- }).on("error", reject);
 
14
  });
15
  }
16
 
17
- const [host, port, user, pass, from, to] = await Promise.all(SECRETS.map(fetchSecret));
18
-
19
- const { createTransport } = await import("nodemailer");
20
- const t = createTransport({
21
- host, port: Number(port), secure: false,
22
- auth: { user, pass },
23
- connectionTimeout: 15000, greetingTimeout: 15000,
24
- });
25
 
26
- await t.verify();
27
- console.log("SMTP connection OK, sending...");
28
 
29
- await t.sendMail({
30
- from, to,
31
- subject: "Test from local machine",
32
- text: `If you see this, SMTP works!\n\nhost=${host} port=${port} user=${user}`,
 
33
  });
34
 
35
- console.log("Email sent!");
36
- process.exit(0);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  const API_BASE = "https://maxxcarl-keyvault.hf.space/secrets";
2
  const API_KEY = "Azure@123";
 
3
 
4
+ const { request } = await import("node:https");
5
+
6
+ function fetchSecret(k) {
7
  return new Promise((resolve, reject) => {
8
+ const url = `${API_BASE}/${k}`;
9
+ request(url, { headers: { "X-API-Key": API_KEY, accept: "application/json" } }, (res) => {
10
+ let d = "";
11
+ res.on("data", (c) => d += c);
12
+ res.on("end", () => resolve(JSON.parse(d).value));
13
+ }).on("error", reject).end();
14
  });
15
  }
16
 
17
+ const [from, to, apiKey] = await Promise.all([
18
+ fetchSecret("FROM_EMAIL"),
19
+ fetchSecret("TO_EMAIL"),
20
+ fetchSecret("BREVO_API_KEY"),
21
+ ]);
 
 
 
22
 
23
+ console.log(`from=${from} to=${to} apiKey=${apiKey.substring(0, 10)}...`);
 
24
 
25
+ const payload = JSON.stringify({
26
+ sender: { email: from },
27
+ to: [{ email: to }],
28
+ subject: "API Test",
29
+ textContent: "Sent via Brevo REST API",
30
  });
31
 
32
+ const req = request({
33
+ hostname: "api.brevo.com",
34
+ port: 443,
35
+ path: "/v3/smtp/email",
36
+ method: "POST",
37
+ headers: {
38
+ "api-key": apiKey,
39
+ "Content-Type": "application/json",
40
+ "Content-Length": Buffer.byteLength(payload),
41
+ },
42
+ }, (res) => {
43
+ let d = "";
44
+ res.on("data", (c) => d += c);
45
+ res.on("end", () => {
46
+ console.log(res.statusCode, d.substring(0, 200));
47
+ process.exit(res.statusCode === 201 || res.statusCode === 200 ? 0 : 1);
48
+ });
49
+ });
50
+ req.on("error", (e) => { console.error(e.message); process.exit(1); });
51
+ req.write(payload);
52
+ req.end();