Passer au contenu principal

Documentation Index

Fetch the complete documentation index at: https://docs.lovi.ai/llms.txt

Use this file to discover all available pages before exploring further.

Introduction

Ce guide couvre les bonnes pratiques de sécurité, l’optimisation des performances et les directives d’intégration pour vous aider à construire des applications robustes avec l’API Lovi.

🔒 Bonnes Pratiques de Sécurité

Gestion des Clés API

✅ À FAIRE :
  • Stockez les clés API dans des variables d’environnement ou un système de gestion de configuration sécurisé
  • Utilisez des clés API différentes pour chaque environnement (développement, staging, production)
  • Renouvelez les clés API régulièrement (recommandé : tous les 90 jours)
  • Surveillez l’utilisation des clés API et configurez des alertes pour les activités inhabituelles
  • Appliquez le principe du moindre privilège (clés séparées pour différentes fonctions si disponible)
❌ À NE PAS FAIRE :
  • Coder en dur les clés API dans le code source ou les enregistrer dans le contrôle de version
  • Partager les clés API par e-mail, chat ou autres canaux non sécurisés
  • Utiliser les clés API de production dans les environnements de développement
  • Enregistrer les clés API complètes dans les logs applicatifs

Sécurité de l’Authentification

// ✅ Good - Store in environment variables
const ACCESS_KEY = process.env.LOVI_ACCESS_KEY;

// ❌ Bad - Hardcoded in source
const ACCESS_KEY = 'your-actual-api-key-here';

Sécurité des Requêtes

Utilisez toujours HTTPS :
// ✅ Good - HTTPS only
const BASE_URL = 'https://cloud.lovi.ai';

// ❌ Bad - Insecure HTTP
const BASE_URL = 'http://cloud.lovi.ai';
Validez les données d’entrée :
function validatePhoneNumber(number) {
  // Remove any non-digit characters
  const cleaned = number.replace(/\D/g, '');

  // Check if it's a valid international format
  if (!/^\d{10,15}$/.test(cleaned)) {
    throw new Error('Invalid phone number format');
  }

  return cleaned;
}

⚡ Optimisation des Performances

Gestion des Connexions

Réutilisez les connexions HTTP :
// ✅ Good - Reuse connection pool
const https = require('https');
const agent = new https.Agent({
  keepAlive: true,
  maxSockets: 10
});

const fetch = require('node-fetch');

async function makeRequest(url, data) {
  return fetch(url, {
    method: 'POST',
    agent: agent,
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(data)
  });
}

Traitement par Lots

Traitez les notifications par lots :
async function sendNotificationsBatch(notifications, batchSize = 10) {
  const results = [];

  for (let i = 0; i < notifications.length; i += batchSize) {
    const batch = notifications.slice(i, i + batchSize);
    const batchPromises = batch.map(notification =>
      sendNotification(notification)
    );

    const batchResults = await Promise.allSettled(batchPromises);
    results.push(...batchResults);

    // Rate limiting: wait between batches
    if (i + batchSize < notifications.length) {
      await sleep(100); // 100ms delay
    }
  }

  return results;
}

Stratégies de Mise en Cache

Mettez en cache les informations des templates :
class TemplateCache {
  constructor(ttl = 3600000) { // 1 hour TTL
    this.cache = new Map();
    this.ttl = ttl;
  }

  async getTemplate(templateName) {
    const cached = this.cache.get(templateName);

    if (cached && Date.now() - cached.timestamp < this.ttl) {
      return cached.data;
    }

    const template = await fetchTemplateFromAPI(templateName);
    this.cache.set(templateName, {
      data: template,
      timestamp: Date.now()
    });

    return template;
  }
}

📊 Limitation et Contrôle du Débit

Comprendre les Limites de Débit

OpérationLimiteFenêtre
Authentification10 requêtes1 minute
Notifications100 requêtes1 minute
Récupération de templates50 requêtes1 minute
Création de templates5 requêtes1 heure

Gestion des Limites de Débit

class RateLimitHandler {
  constructor() {
    this.requestQueue = [];
    this.processing = false;
  }

  async makeRequest(url, data) {
    return new Promise((resolve, reject) => {
      this.requestQueue.push({ url, data, resolve, reject });
      this.processQueue();
    });
  }

  async processQueue() {
    if (this.processing || this.requestQueue.length === 0) return;

    this.processing = true;

    while (this.requestQueue.length > 0) {
      const request = this.requestQueue.shift();

      try {
        const response = await this.executeRequest(request);
        request.resolve(response);
      } catch (error) {
        if (error.status === 429) {
          // Rate limited - put request back and wait
          this.requestQueue.unshift(request);
          await sleep(error.retryAfter * 1000);
          continue;
        }
        request.reject(error);
      }

      // Small delay between requests
      await sleep(100);
    }

    this.processing = false;
  }
}

🏗️ Modèles d’Architecture

Modèle Repository

class NotificationRepository {
  constructor(apiClient) {
    this.apiClient = apiClient;
  }

  async sendWhatsAppNotification(notification) {
    const payload = this.formatWhatsAppPayload(notification);
    return await this.apiClient.post('/notify', payload);
  }

  async sendVoiceNotification(notification) {
    const payload = this.formatVoicePayload(notification);
    return await this.apiClient.post('/notify/voice', payload);
  }

  formatWhatsAppPayload(notification) {
    return {
      contact: {
        number: this.validatePhoneNumber(notification.phoneNumber),
        name: notification.contactName
      },
      language_template: notification.language,
      name_template: notification.templateName,
      recipient_id: notification.phoneNumber,
      notification_type: notification.type,
      campaign_name: notification.campaign
    };
  }
}

Modèle Factory pour les Notifications

class NotificationFactory {
  static create(type, data) {
    switch (type) {
      case 'whatsapp':
        return new WhatsAppNotification(data);
      case 'voice':
        return new VoiceNotification(data);
      default:
        throw new Error(`Unknown notification type: ${type}`);
    }
  }
}

class WhatsAppNotification {
  constructor(data) {
    this.contact = { number: data.phoneNumber, name: data.name };
    this.template = data.templateName;
    this.type = 'whatsapp';
  }

  toPayload() {
    return {
      contact: this.contact,
      name_template: this.template,
      // ... other properties
    };
  }
}

🧪 Stratégies de Tests

Tests Unitaires

// Mock API responses for testing
const mockAPIClient = {
  post: jest.fn()
};

describe('NotificationService', () => {
  test('should send WhatsApp notification successfully', async () => {
    mockAPIClient.post.mockResolvedValue({
      success: true,
      notification_id: 'test-123'
    });

    const service = new NotificationService(mockAPIClient);
    const result = await service.sendWhatsApp({
      phoneNumber: '34666033135',
      templateName: 'welcome',
      contactName: 'John Doe'
    });

    expect(result.success).toBe(true);
    expect(mockAPIClient.post).toHaveBeenCalledWith('/notify', expect.any(Object));
  });
});

Tests d’Intégration

// Use test environment with real API calls
describe('Integration Tests', () => {
  const testApiKey = process.env.TEST_API_KEY;
  const apiClient = new LoviAPIClient(testApiKey);

  test('should handle authentication flow', async () => {
    const authResult = await apiClient.authenticate(
      process.env.TEST_EMAIL,
      process.env.TEST_PASSWORD
    );

    expect(authResult.access_token).toBeDefined();
    expect(authResult.token_type).toBe('bearer');
  });
});

🔍 Surveillance et Journalisation

Journalisation Structurée

const logger = require('winston');

logger.configure({
  format: logger.format.combine(
    logger.format.timestamp(),
    logger.format.errors({ stack: true }),
    logger.format.json()
  ),
  transports: [
    new logger.transports.File({ filename: 'api-calls.log' })
  ]
});

async function loggedAPICall(endpoint, data) {
  const requestId = generateRequestId();

  logger.info('API request started', {
    request_id: requestId,
    endpoint: endpoint,
    method: 'POST',
    timestamp: new Date().toISOString()
  });

  try {
    const response = await makeAPICall(endpoint, data);

    logger.info('API request completed', {
      request_id: requestId,
      endpoint: endpoint,
      status_code: response.status,
      success: true,
      duration: Date.now() - startTime
    });

    return response;
  } catch (error) {
    logger.error('API request failed', {
      request_id: requestId,
      endpoint: endpoint,
      error: error.message,
      status_code: error.statusCode,
      duration: Date.now() - startTime
    });

    throw error;
  }
}

Vérifications de Santé

class APIHealthMonitor {
  constructor(apiClient) {
    this.apiClient = apiClient;
    this.healthStatus = { healthy: true, lastCheck: null };
  }

  async checkHealth() {
    try {
      const start = Date.now();
      await this.apiClient.get('/templates?limit=1');
      const duration = Date.now() - start;

      this.healthStatus = {
        healthy: true,
        lastCheck: new Date().toISOString(),
        responseTime: duration,
        status: 'operational'
      };
    } catch (error) {
      this.healthStatus = {
        healthy: false,
        lastCheck: new Date().toISOString(),
        error: error.message,
        status: 'degraded'
      };
    }

    return this.healthStatus;
  }
}

📱 Intégration Mobile et Frontend

Exemple de Hook React

import { useState, useEffect } from 'react';

function useLoviNotification() {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  const sendNotification = async (notificationData) => {
    setLoading(true);
    setError(null);

    try {
      const response = await fetch('/api/send-notification', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(notificationData)
      });

      if (!response.ok) {
        throw new Error('Failed to send notification');
      }

      const result = await response.json();
      return result;
    } catch (err) {
      setError(err.message);
      throw err;
    } finally {
      setLoading(false);
    }
  };

  return { sendNotification, loading, error };
}

🔧 Gestion de la Configuration

Configuration par Environnement

// config/lovi.js
module.exports = {
  development: {
    baseURL: 'https://cloud.lovi.ai',
    accessKey: process.env.LOVI_DEV_ACCESS_KEY,
    rateLimits: {
      requests: 10,
      window: 60000 // 1 minute
    },
    retries: 3,
    timeout: 30000
  },
  production: {
    baseURL: 'https://cloud.lovi.ai',
    accessKey: process.env.LOVI_PROD_ACCESS_KEY,
    rateLimits: {
      requests: 100,
      window: 60000
    },
    retries: 5,
    timeout: 60000
  }
};

Configuration du Client

class LoviAPIClient {
  constructor(config) {
    this.baseURL = config.baseURL;
    this.accessKey = config.accessKey;
    this.timeout = config.timeout;
    this.rateLimiter = new RateLimiter(config.rateLimits);
    this.retryCount = config.retries;
  }

  async request(endpoint, options = {}) {
    await this.rateLimiter.acquire();

    const url = `${this.baseURL}${endpoint}?access_key=${this.accessKey}`;
    const config = {
      timeout: this.timeout,
      ...options
    };

    return this.retryRequest(url, config);
  }
}

📚 Documentation et Directives d’Équipe

Documentation du Code

/**
 * Send a WhatsApp notification using a template
 *
 * @param {Object} notification - Notification data
 * @param {string} notification.phoneNumber - Recipient phone number (international format, no +)
 * @param {string} notification.templateName - Name of approved template
 * @param {string} notification.language - Language code (e.g., 'es_ES', 'en_US')
 * @param {Object} notification.variables - Template variables
 * @param {string} [notification.scheduledTime] - ISO 8601 datetime for scheduling
 * @param {string} [notification.timezone] - IANA timezone code
 *
 * @returns {Promise<Object>} API response with notification_id
 *
 * @throws {ValidationError} When required parameters are missing
 * @throws {APIError} When API request fails
 *
 * @example
 * const result = await sendWhatsAppNotification({
 *   phoneNumber: '34666033135',
 *   templateName: 'welcome_user',
 *   language: 'es_ES',
 *   variables: { name: 'Juan', course: 'JavaScript' }
 * });
 */
async function sendWhatsAppNotification(notification) {
  // Implementation...
}

Standards d’Équipe

Liste de Contrôle pour l’Intégration API :
  • ✅ Implémenter une gestion des erreurs appropriée
  • ✅ Ajouter la journalisation des requêtes/réponses
  • ✅ Inclure des tests unitaires
  • ✅ Valider les paramètres d’entrée
  • ✅ Gérer les limites de débit
  • ✅ Utiliser des configurations spécifiques à l’environnement
  • ✅ Documenter toutes les fonctions et classes
  • ✅ Implémenter des vérifications de santé
  • ✅ Ajouter la surveillance et les alertes

🚨 Pièges Courants et Solutions

Piège : Valeurs Codées en Dur

// ❌ Bad
const templateName = 'welcome_template_final_v2';

// ✅ Good
const templateName = config.templates.welcome;

Piège : Mauvaise Gestion des Erreurs

// ❌ Bad
try {
  await sendNotification(data);
} catch (error) {
  console.log('Error:', error);
}

// ✅ Good
try {
  await sendNotification(data);
} catch (error) {
  logger.error('Notification failed', {
    error: error.message,
    data: sanitizeForLog(data),
    stack: error.stack
  });

  throw new NotificationError(
    'Failed to send notification',
    error.statusCode,
    error.requestId
  );
}

Piège : Ignorer les Limites de Débit

// ❌ Bad - Send all at once
notifications.forEach(notification => {
  sendNotification(notification);
});

// ✅ Good - Respect rate limits
await sendNotificationsBatch(notifications, 10);
N’oubliez pas : suivre ces bonnes pratiques vous aidera à construire des intégrations plus fiables, sécurisées et maintenables avec l’API Lovi.