diff --git a/.env.exemple b/.env.exemple new file mode 100644 index 0000000..a6cc32c --- /dev/null +++ b/.env.exemple @@ -0,0 +1,11 @@ +# Configuration OVH +OVH_APP_KEY= +OVH_APP_SECRET= +OVH_CONSUMER_KEY= +OVH_SMS_SERVICE_NAME= + +# Configuration SMS Factor +SMS_FACTOR_TOKEN= + +# Configuration serveur +PORT= diff --git a/package.json b/package.json index af505e7..8e6a76c 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,10 @@ }, "dependencies": { "cors": "^2.8.5", + "dotenv": "^17.2.0", "express": "^4.18.2", - "node-fetch": "^2.6.7" + "node-fetch": "^2.6.7", + "ovh": "^2.0.3" }, "devDependencies": { "nodemon": "^3.0.1" diff --git a/src/server.js b/src/server.js index 957a2a1..dd7f302 100644 --- a/src/server.js +++ b/src/server.js @@ -1,6 +1,8 @@ const express = require('express'); const cors = require('cors'); const fetch = require('node-fetch'); +const ovh = require('ovh'); +require('dotenv').config(); // Initialisation de l'application Express const app = express(); @@ -174,6 +176,220 @@ app.post('/api/v1/idnot/user/:code', async (req, res) => { } }); +//------------------------------------ SMS Section ------------------------------------ + +const config = { + // OVH config + OVH_APP_KEY: process.env.OVH_APP_KEY, + OVH_APP_SECRET: process.env.OVH_APP_SECRET, + OVH_CONSUMER_KEY: process.env.OVH_CONSUMER_KEY, + OVH_SMS_SERVICE_NAME: process.env.OVH_SMS_SERVICE_NAME, + + // SMS Factor config + SMS_FACTOR_TOKEN: process.env.SMS_FACTOR_TOKEN, + + PORT: process.env.PORT || 8080 +}; + +// Codes storage +const verificationCodes = new Map(); + +// Service SMS +class SmsService { + static generateCode() { + return Math.floor(100000 + Math.random() * 900000); + } + + // OVH Service + static sendSmsWithOvh(phoneNumber, message) { + return new Promise((resolve, reject) => { + const ovhClient = ovh({ + appKey: config.OVH_APP_KEY, + appSecret: config.OVH_APP_SECRET, + consumerKey: config.OVH_CONSUMER_KEY + }); + + ovhClient.request('POST', `/sms/${config.OVH_SMS_SERVICE_NAME}/jobs`, { + message: message, + receivers: [phoneNumber], + senderForResponse: false, + sender: "not.IT Fact", + noStopClause: true + }, (error, result) => { + if (error) { + console.error('Erreur OVH SMS:', error); + resolve({ success: false, error: 'Échec de l\'envoi du SMS via OVH' }); + } else { + resolve({ success: true }); + } + }); + }); + } + + // SMS Factor Service + static async sendSmsWithSmsFactor(phoneNumber, message) { + try { + const url = new URL('https://api.smsfactor.com/send/simulate'); + url.searchParams.append('to', phoneNumber); + url.searchParams.append('text', message); + url.searchParams.append('sender', 'LeCoffre'); + url.searchParams.append('token', config.SMS_FACTOR_TOKEN); + + const response = await fetch(url.toString()); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + return { success: true }; + } catch (error) { + console.error('Erreur SMS Factor:', error); + return { success: false, error: 'Échec de l\'envoi du SMS via SMS Factor' }; + } + } + + // Main method + static async sendSms(phoneNumber, message) { + // Try first with OVH + const ovhResult = await this.sendSmsWithOvh(phoneNumber, message); + + if (ovhResult.success) { + return ovhResult; + } + + // If OVH fails, try with SMS Factor + console.log('OVH SMS failed, trying SMS Factor...'); + return await this.sendSmsWithSmsFactor(phoneNumber, message); + } +} + +// Phone number validation middleware +const validatePhoneNumber = (req, res, next) => { + const { phoneNumber } = req.body; + + if (!phoneNumber) { + return res.status(400).json({ + success: false, + message: 'Le numéro de téléphone est requis' + }); + } + + // Validation basique du format + const phoneRegex = /^\+?[1-9]\d{1,14}$/; + if (!phoneRegex.test(phoneNumber)) { + return res.status(400).json({ + success: false, + message: 'Format de numéro de téléphone invalide' + }); + } + + next(); +}; + +// Routes +app.post('/api/send-code', validatePhoneNumber, async (req, res) => { + const { phoneNumber } = req.body; + + try { + // Check if a code already exists and is not expired + const existingVerification = verificationCodes.get(phoneNumber); + if (existingVerification) { + const timeSinceLastSend = Date.now() - existingVerification.timestamp; + if (timeSinceLastSend < 30000) { // 30 secondes + return res.status(429).json({ + success: false, + message: 'Veuillez attendre 30 secondes avant de demander un nouveau code' + }); + } + } + + // Generate a new code + const code = SmsService.generateCode(); + + // Store the code + verificationCodes.set(phoneNumber, { + code, + timestamp: Date.now(), + attempts: 0 + }); + + // Send the SMS + const message = `Votre code de vérification LeCoffre est : ${code}`; + const result = await SmsService.sendSms(phoneNumber, message); + + if (result.success) { + res.json({ + success: true, + message: 'Code envoyé avec succès', + }); + } else { + res.status(500).json({ + success: false, + message: 'Échec de l\'envoi du SMS via les deux fournisseurs' + }); + } + } catch (error) { + console.error('Erreur:', error); + res.status(500).json({ + success: false, + message: 'Erreur serveur lors de l\'envoi du code' + }); + } +}); + +app.post('/api/verify-code', validatePhoneNumber, (req, res) => { + const { phoneNumber, code } = req.body; + + if (!code) { + return res.status(400).json({ + success: false, + message: 'Le code est requis' + }); + } + + const verification = verificationCodes.get(phoneNumber); + + if (!verification) { + return res.status(400).json({ + success: false, + message: 'Aucun code n\'a été envoyé à ce numéro' + }); + } + + // Check if the code has not expired (5 minutes) + if (Date.now() - verification.timestamp > 5 * 60 * 1000) { + verificationCodes.delete(phoneNumber); + return res.status(400).json({ + success: false, + message: 'Le code a expiré' + }); + } + + // Check if the code is correct + if (verification.code.toString() === code.toString()) { + verificationCodes.delete(phoneNumber); + res.json({ + success: true, + message: 'Code vérifié avec succès' + }); + } else { + verification.attempts += 1; + + if (verification.attempts >= 3) { + verificationCodes.delete(phoneNumber); + res.status(400).json({ + success: false, + message: 'Trop de tentatives. Veuillez demander un nouveau code' + }); + } else { + res.status(400).json({ + success: false, + message: 'Code incorrect' + }); + } + } +}); + app.listen(PORT, () => { console.log(`Server is running on port ${PORT}`); });