# RelaisSMS - Lot 0 - Decisions techniques Date : 2026-06-11 ## Objectif du lot Ce document fixe les decisions de cadrage avant le demarrage du socle applicatif. Il complete le cahier des charges et sert de reference pour les lots suivants. ## Decisions actees | Sujet | Decision | Impact | | --- | --- | --- | | Source CardDAV/Nextcloud | CardDAV/Nextcloud est le premier systeme d'import/synchronisation distant a implementer. | Le lot 3 porte sur `CardDavSourceConnector` et ne depend pas de Galette. | | Source Galette | Galette est prepare comme source future, mais le connecteur n'est pas implemente tant que l'instance n'est pas mise a jour. | Galette est reporte au dernier lot. Le socle et CardDAV/Nextcloud avancent sans dependance Galette. | | Nommage des sources | CardDAV/Nextcloud et Galette sont deux sources separees, meme si Galette expose ou exporte un format vCard. | Les connecteurs, configurations, cles de source et libelles UI restent distincts. Pas de regroupement sous un meme lot ou nommage. | | Version Galette cible | Galette 1.2.1 cote projet. La documentation `latest` consultee affiche 1.2.2. | Les endpoints exacts seront revalides apres upgrade, avant implementation du dernier lot Galette. | | Fournisseur SMS | OVHcloud SMS via API REST OVH Europe. | Le connecteur initial depend de l'API OVH, isole derriere `SmsProviderInterface`. | | SDK OVH | Ne pas imposer le SDK au lot 0. Preferer un adaptateur HTTP Laravel au demarrage, sauf validation explicite du SDK avec la version PHP cible. | Moins de dependance externe et controle clair de la signature API. | | Roles | Un seul role applicatif au MVP : administrateur complet. | Pas de RBAC fin au lot 1. Tous les utilisateurs authentifies ont les memes droits. | | Suppression contacts synchronises | Suppression logique par `deleted_at`, pas suppression physique. | L'historique SMS reste auditable meme si un contact disparait d'une source. | | Suppression contacts locaux | Jamais supprimes par synchronisation. | Un contact local reste sous controle applicatif. | | Refus SMS | Flag manuel `sms_opt_out` au niveau contact. | Les contacts opt-out sont exclus au moment de la resolution des destinataires. | | Limite d'envoi | 100 SMS par envoi par defaut, configurable. | Validation cote serveur obligatoire avant creation de campagne. | | Conservation historique | 3 ans par defaut, configurable. | `sms_campaigns.expires_at` est calcule a la creation. | | Programmation | Prevue dans le modele, activee par configuration. | Le lot 7 pourra utiliser le scheduler sans revoir le modele. | ## CardDAV/Nextcloud ### Etat retenu CardDAV/Nextcloud est la source distante prioritaire pour l'import/synchronisation. Elle est traitee comme un systeme autonome : son connecteur lit des carnets CardDAV, des vCards, des UID et des ETags, sans reutiliser le contrat Galette. ### Contrat cible pour RelaisSMS Le connecteur CardDAV devra fournir au cache local : | Donnee RelaisSMS | Source CardDAV attendue | | --- | --- | | Identifiant distant | UID vCard, complete par l'URL ou l'adresse du carnet si necessaire. | | Nom affiche | `FN` vCard. | | Nom / prenom | Champs structures vCard si presents. | | Telephones | Proprietes `TEL`, avec conservation du type si disponible. | | Groupes | Categories vCard si presentes. | | Synchronisation incrementale | ETag, `ctag` ou horodatage distant selon les capacites du serveur. | Decision : nommer cette source `carddav` ou `carddav_nextcloud` cote configuration/UI, jamais `galette_vcard`. ## Galette ### Etat retenu Galette est laisse de cote pour l'implementation immediate, conformement au cadrage projet : l'upgrade est planifie mais pas encore effectif. Le lot 0 documente donc le contrat attendu sans figer un endpoint qui pourrait changer au moment de l'upgrade. Le connecteur Galette sera le dernier lot. ### Documentation consultee - Documentation Galette fournie : - La page consultee est une documentation Doxygen de classes PHP, pas une specification REST/OpenAPI. - Elements reperes utiles au cadrage : - `Galette\Controllers\Crud\MembersController::list` - `Galette\Controllers\Crud\MembersController::ajaxList` - `Galette\Controllers\Crud\MembersController::show` - `Galette\Controllers\Crud\MembersController::vcard` - `Galette\Controllers\CsvController::membersExport` ### Contrat cible pour RelaisSMS Le connecteur Galette devra fournir au cache local : | Donnee RelaisSMS | Source Galette attendue | | --- | --- | | Identifiant distant | ID adherent Galette, par exemple `Numéro adhérent` dans l'export actuel. | | Nom | Champ nom. | | Prenom | Champ prenom. | | Structure | Personne morale si presente. | | Email | Courriel. | | Telephones | Telephone et GSM, avec conservation du numero original. | | Groupes/statuts | Statut adherent au minimum ; groupes Galette a confirmer apres upgrade. | | Date de modification | Date de modification si exposee par API/export. | ### Endpoints a valider apres upgrade Avant le lot 9, valider sur l'instance Galette 1.2.1 : | Usage | Endpoint ou mecanisme attendu | Statut | | --- | --- | --- | | Liste complete des adherents | Endpoint HTTP ou export membres base sur `membersExport` | A confirmer | | Detail adherent | Route detail membre ou endpoint dedie | A confirmer | | Fiche/export vCard Galette | Route `vcard` si exploitable dans le connecteur Galette | Optionnel, sans lien avec CardDAV/Nextcloud | | Groupes/statuts | Route groupes ou champs presents dans la liste/export | A confirmer | | Authentification | Session, jeton, basic auth ou autre mecanisme Galette | A confirmer | | Filtrage incremental | Date de modification, ETag ou pagination | A confirmer | Decision : ne pas coupler RelaisSMS a une route Galette non confirmee. Le connecteur sera concu avec un adaptateur remplacable : API JSON si disponible, export CSV authentifie en solution de repli. Une eventuelle route vCard Galette reste un mecanisme interne au connecteur Galette, pas une source CardDAV. ## OVHcloud SMS ### Documentation consultee - Page produit API SMS OVHcloud : - Definition API OVH SMS Europe : ### Endpoints retenus pour le MVP Base API : `https://eu.api.ovh.com/1.0` | Usage | Methode | Endpoint | Notes | | --- | --- | --- | --- | | Lister les services SMS | `GET` | `/sms` | Permet de selectionner `serviceName`. | | Lire le compte SMS | `GET` | `/sms/{serviceName}` | Retourne notamment statut et credits restants. | | Estimer un message | `POST` | `/sms/estimate` | Parametres : `message`, `noStopClause`, `senderType`. | | Envoyer un job SMS | `POST` | `/sms/{serviceName}/jobs` | Parametres principaux : `message`, `receivers`, `sender`, `noStopClause`, `tag`. | | Lire jobs en attente | `GET` | `/sms/{serviceName}/jobs` | Utile pour debug et suivi. | | Lire un job | `GET` | `/sms/{serviceName}/jobs/{id}` | Suivi fin si necessaire. | | Annuler un job | `DELETE` | `/sms/{serviceName}/jobs/{id}` | A utiliser seulement tant que l'envoi est stoppable. | | Historique sortant | `GET` | `/sms/{serviceName}/outgoing` | Filtres disponibles : dates, destinataire, sender, tag, etc. | | Detail sortant | `GET` | `/sms/{serviceName}/outgoing/{id}` | Permet de recuperer les informations par SMS envoye. | | Expediteurs autorises | `GET` | `/sms/{serviceName}/senders` | Pour valider la configuration sender. | Les endpoints batch existent aussi (`/sms/{serviceName}/batches`) et supportent les envois differes via `deferred`. Decision MVP : demarrer avec `/jobs` pour l'envoi immediat et gerer la programmation cote RelaisSMS via queue/scheduler. Les batches restent une option a reevaluer au lot 7 si l'on veut deleguer le differe a OVH. ### Credentials attendus Configuration par variables d'environnement : ```dotenv OVH_ENDPOINT=ovh-eu OVH_APPLICATION_KEY= OVH_APPLICATION_SECRET= OVH_CONSUMER_KEY= OVH_SMS_SERVICE_NAME= OVH_SMS_SENDER= OVH_SMS_NO_STOP_CLAUSE=true ``` Les secrets ne sont pas stockes en clair en base. L'interface pourra afficher l'etat de configuration sans exposer les valeurs. ### Quota et garde-fous - Le compte OVH expose `creditsLeft` via `GET /sms/{serviceName}`. - RelaisSMS applique sa propre limite de 100 SMS par campagne par defaut. - Avant envoi, le connecteur doit refuser une campagne si : - aucun `serviceName` n'est configure ; - aucun expediteur valide n'est configure quand necessaire ; - la liste finale depasse la limite applicative ; - OVH retourne des destinataires invalides. ## Numeros de telephone ### Echantillon Galette Fichier analyse : `doc/tmp/liste_adherents_filtree.csv` - 179 lignes adherents. - 86 numeros presents dans `Telephone` ou `GSM`. - Exemples fictifs de formats : `06 11 11 11 11`, `0622222222`, `(+33) 6 33 33 33 33`, `+33 (0)6 44 44 44 44`, fixes francais, numeros internationaux. ### Regex portable par defaut La regex par defaut detecte les mobiles francais bruts avant normalisation : ```regex ^\s*(?:(?:\(\s*\+33\s*\)|\+33|0033)\s*(?:\(0\)\s*)?|0)\s*[67](?:[\s.-]*\d{2}){4}\s*$ ``` Elle accepte notamment : - `06 11 11 11 11` - `0622222222` - `07 55 55 55 55` - `(+33) 6 33 33 33 33` - `+33 (0)6 44 44 44 44` - `0033 6 11 11 11 11` Elle exclut les fixes francais (`01`, `02`, `03`, `04`, `05`, `09`) et les numeros mobiles non francais. Le pays par defaut est `FR`. Decision importante : cette regex sert a classifier les portables. La normalisation E.164 reste separee et devra etre faite avec `giggsey/libphonenumber-for-php` au lot 2. ## Parametres applicatifs par defaut | Cle | Valeur par defaut | Source | | --- | --- | --- | | `default_country` | `FR` | Cadrage projet | | `mobile_phone_regex` | Regex ci-dessus | Echantillon Galette | | `max_sms_per_campaign` | `100` | Cahier des charges | | `history_retention_years` | `3` | Cahier des charges | | `scheduled_sends_enabled` | `true` | MVP | | `active_sms_provider` | `ovh` | Fournisseur cible | | `galette_sync_enabled` | `false` | Upgrade Galette non fait | | `carddav_sync_enabled` | `false` | Active a partir du lot 3 | ## Impacts pour les lots suivants ### Lot 1 - Initialiser Laravel sans dependance Galette active. - Authentification simple. - Tous les utilisateurs authentifies sont administrateurs. - Prevoir `app_settings` ou configuration equivalente pour les parametres non secrets. ### Lot 2 - Stocker `raw_number`, `normalized_number`, `is_valid`, `is_mobile`. - Utiliser la regex configurable pour `is_mobile`. - Ajouter `sms_opt_out` sur les contacts. - Prevoir `deleted_at` sur contacts synchronises. - Prevoir des cles de source distinctes pour `local`, `carddav` et `galette`. ### Lot 3 - Implementer `CardDavSourceConnector`. - Synchroniser les vCards depuis Nextcloud/CardDAV avec UID et ETag si disponibles. - Mapper les categories vCard en groupes importes CardDAV. ### Lot 5 - Implementer `OvhSmsProvider` via client HTTP signe. - Journaliser reponse OVH brute et IDs retournes. - Utiliser le champ `tag` OVH pour relier les SMS a une campagne RelaisSMS. ### Lot 9 - Revalider Galette 1.2.1 apres upgrade. - Choisir entre API JSON, export CSV authentifie ou route Galette dediee. - Mapper les statuts Galette en groupes applicatifs importes. - Garder un nommage Galette explicite, separe de CardDAV/Nextcloud. ## Points ouverts | Sujet | Question | A trancher avant | | --- | --- | --- | | Galette auth | Quel mecanisme d'authentification sera disponible apres upgrade ? | Lot 9 | | Galette groupes | Les groupes sont-ils exposes directement ou seulement les statuts ? | Lot 9 | | CardDAV categories | Les categories Nextcloud sont-elles renseignees et suffisantes comme groupes ? | Lot 3 | | OVH sender | Expediteur alphanumerique valide ou numero reponse ? | Lot 5 | | STOP OVH | `noStopClause=true` est-il conforme a l'usage AFPMA non publicitaire ? | Lot 5 | | Numeros internationaux | Faut-il envoyer aux mobiles non francais presents dans les sources synchronisees ? | Lot 4 |