--- /dev/null
+node_modules/
+.env
--- /dev/null
+const mysql = require('mysql2/promise');
+// CORRECTION : On pointe simplement vers le .env à la racine du projet
+const path = require('path');
+require('dotenv').config({ path: path.resolve(__dirname, '../.env') });
+
+const db = mysql.createPool({
+ host: process.env.DB_HOST,
+ port: process.env.DB_PORT,
+ user: process.env.DB_USER,
+ password: process.env.DB_PASS,
+ database: process.env.DB_NAME,
+ waitForConnections: true,
+ connectionLimit: 10,
+ queueLimit: 0
+});
+
+module.exports = db;
--- /dev/null
+{
+ "name": "system_notification",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "system_notification",
+ "version": "1.0.0",
+ "license": "ISC",
+ "dependencies": {
+ "dotenv": "^17.2.4",
+ "mysql2": "^3.16.3",
+ "nodemailer": "^8.0.1"
+ }
+ },
+ "node_modules/aws-ssl-profiles": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz",
+ "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/denque": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
+ "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/dotenv": {
+ "version": "17.2.4",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.4.tgz",
+ "integrity": "sha512-mudtfb4zRB4bVvdj0xRo+e6duH1csJRM8IukBqfTRvHotn9+LBXB8ynAidP9zHqoRC/fsllXgk4kCKlR21fIhw==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
+ "node_modules/generate-function": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
+ "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==",
+ "license": "MIT",
+ "dependencies": {
+ "is-property": "^1.0.2"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz",
+ "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==",
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/is-property": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
+ "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==",
+ "license": "MIT"
+ },
+ "node_modules/long": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
+ "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/lru.min": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.4.tgz",
+ "integrity": "sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA==",
+ "license": "MIT",
+ "engines": {
+ "bun": ">=1.0.0",
+ "deno": ">=1.30.0",
+ "node": ">=8.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wellwelwel"
+ }
+ },
+ "node_modules/mysql2": {
+ "version": "3.16.3",
+ "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.16.3.tgz",
+ "integrity": "sha512-+3XhQEt4FEFuvGV0JjIDj4eP2OT/oIj/54dYvqhblnSzlfcxVOuj+cd15Xz6hsG4HU1a+A5+BA9gm0618C4z7A==",
+ "license": "MIT",
+ "dependencies": {
+ "aws-ssl-profiles": "^1.1.2",
+ "denque": "^2.1.0",
+ "generate-function": "^2.3.1",
+ "iconv-lite": "^0.7.2",
+ "long": "^5.3.2",
+ "lru.min": "^1.1.3",
+ "named-placeholders": "^1.1.6",
+ "seq-queue": "^0.0.5",
+ "sqlstring": "^2.3.3"
+ },
+ "engines": {
+ "node": ">= 8.0"
+ }
+ },
+ "node_modules/named-placeholders": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.6.tgz",
+ "integrity": "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==",
+ "license": "MIT",
+ "dependencies": {
+ "lru.min": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/nodemailer": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-8.0.1.tgz",
+ "integrity": "sha512-5kcldIXmaEjZcHR6F28IKGSgpmZHaF1IXLWFTG+Xh3S+Cce4MiakLtWY+PlBU69fLbRa8HlaGIrC/QolUpHkhg==",
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "license": "MIT"
+ },
+ "node_modules/seq-queue": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz",
+ "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q=="
+ },
+ "node_modules/sqlstring": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz",
+ "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ }
+ }
+}
--- /dev/null
+{
+ "name": "system_notification",
+ "version": "1.0.0",
+ "description": "",
+ "main": "test-connection.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "type": "commonjs",
+ "dependencies": {
+ "dotenv": "^17.2.4",
+ "mysql2": "^3.16.3",
+ "nodemailer": "^8.0.1"
+ }
+}
--- /dev/null
+// 1. On importe la connexion à la base de données
+const db = require('../config/db');
+
+const MySqlAlertRepository = {
+
+ /**
+ * Récupère toutes les règles d'alerte actives avec l'email de l'utilisateur
+ */
+ async findActiveRules() {
+ try {
+ // On prépare la requête SQL avec une jointure (JOIN)
+ // On récupère les colonnes de alert_rules (ar) et l'email de users (u)
+ const sql = `
+ SELECT ar.*, u.email
+ FROM alert_rules ar
+ JOIN users u ON ar.user_id = u.user_id
+ WHERE ar.enabled = 1
+ `;
+
+ // On exécute la requête
+ const [rows] = await db.execute(sql);
+
+ // On retourne les données
+ return rows;
+ } catch (error) {
+ console.error("Erreur dans findActiveRules:", error);
+ throw error; // On laisse le service gérer l'erreur si besoin
+ }
+ }
+
+
+};
+
+// 3. On exporte le repository
+module.exports = MySqlAlertRepository;
--- /dev/null
+const nodemailer = require('nodemailer');
+
+
+async function sendAlertEmail(to, signal) {
+ const transporter = nodemailer.createTransport({
+ host: process.env.MAIL_HOST,
+ port: parseInt(process.env.MAIL_PORT),
+ auth: {
+ user: process.env.MAIL_USER,
+ pass: process.env.MAIL_PASS
+ }
+ });
+
+ try {
+ const confidencePercent = (parseFloat(signal.confidence) * 100).toFixed(2);
+ const mailOptions = {
+ from: `"Wall-e-tte" <${process.env.MAIL_USER}>`,
+ to: to,
+ subject: `🚨 Alerte : ${signal.action} sur ${signal.pair}`,
+ text: `Action : ${signal.action}\nConfiance : ${confidencePercent}%`,
+ };
+
+ const info = await transporter.sendMail(mailOptions);
+ console.log("✅ Email envoyé ! ID:", info.messageId);
+ return 'SENT';
+ } catch (error) {
+ console.error("❌ Erreur d'envoi mail :", error.message);
+ return 'FAILED';
+ }
+}
+
+module.exports = { sendAlertEmail };
--- /dev/null
+const repository = require('./repositories/MySqlAlertRepository');
+
+async function test() {
+ console.log("Tentative de connexion à MariaDB...");
+
+ try {
+ const rules = await repository.findActiveRules();
+ console.log("Connexion réussie !");
+ console.log(`Nombre de règles actives trouvées : ${rules.length}`);
+
+ if (rules.length > 0) {
+ console.log("Détail de la première règle :", rules[0]);
+ } else {
+ console.log("La connexion marche, mais la table 'alert_rules' est vide;");
+ }
+
+ } catch (error) {
+ console.error("ÉCHEC du test :");
+ console.error("- Vérifie ton fichier .env (User, Password, Host)");
+ console.error("- Vérifie que tu es bien connecté au VPN si nécessaire");
+ console.error("Détail de l'erreur :", error.message);
+ } finally {
+ process.exit(); // On ferme le script proprement
+ }
+}
+
+test();
--- /dev/null
+require('dotenv').config();
+
+const repository = require('./repositories/MySqlAlertRepository');
+const { sendAlertEmail } = require('./services/channels/mailer');
+
+async function runTest() {
+ console.log("Recherche d'une règle active dans la DB...");
+
+ try {
+ const rules = await repository.findActiveRules();
+
+ if (rules.length === 0) {
+ console.log("Aucune règle trouvée. Vérifie tes tables SQL.");
+ return;
+ }
+
+ const rule = rules[0];
+ console.log(`Règle trouvée pour : ${rule.email}`);
+
+ // On simule un signal de test
+ const fakeSignal = {
+ action: 'BUY',
+ pair: 'BTC/EUR',
+ confidence: 0.85,
+ severity: 'WARNING'
+ };
+
+ console.log("✉️ Tentative d'envoi du mail...");
+ const mailerModule = require('./services/channels/mailer');
+ console.log("Contenu du module mailer :", mailerModule); // Ajoute cette ligne pour débugger
+ const status = await sendAlertEmail(rule.email, fakeSignal);
+
+ console.log(`📊 Résultat de l'envoi : ${status}`);
+
+ } catch (error) {
+ console.error("❌ Erreur pendant le test :", error);
+ } finally {
+ process.exit();
+ }
+}
+
+runTest();
+