Aller au contenu principal

Change user status (Admin only)

POST 

/company/users/status/:id

Modifier l'état d'un utilisateur (activer/désactiver) via le champ status. Seuls les administrateurs peuvent modifier l'état des utilisateurs.

Objectif

Permettre aux administrateurs de contrôler l'accès des utilisateurs à la plateforme via l'activation/désactivation des comptes.

Cas d'utilisation

  • Désactiver temporairement un utilisateur
  • Activer un utilisateur précédemment désactivé
  • Suspendre l'accès pour des raisons de sécurité
  • Bloquer un utilisateur pour non-paiement ou manquement

Authentification & Autorisation

  • Requiert un JWT valide (middleware m.isLoged)
  • Requiert le rôle admin ou dev (middleware m.isAdmin)

Comportement

  • reason = NONE : Active l'utilisateur (status = true), efface reason, reasonDate, reasonMessage
  • reason ≠ NONE : Désactive l'utilisateur (status = false), définit reason et reasonMessage
  • Valide la raison avec model.isValidReason()
  • Met à jour reasonDate si la raison est valide (format ISO8601)

Raisons valides

  • NONE : Utilisateur normal actif sans incident (active l'utilisateur)
  • BAD_USER : Bloqué pour mauvaise conduite (signalements, fraude)
  • PENDING : Inscription terminée, en attente d'activation
  • ACTIVE : Utilisateur vérifié et opérationnel
  • BLOCKED : Bloqué administrativement (impayé, sécurité)

Flux de validation

flowchart TD
A[Recevoir POST /status/:id] --> B\{Utilisateur Admin?\}
B -->|Non| C[403 Forbidden]
B -->|Oui| D\{Utilisateur Existe?\}
D -->|Non| E[404 Not Found]
D -->|Oui| F\{Reason === 'NONE'?\}
F -->|Oui| G[Activer Utilisateur]
G --> H[status = true]
H

<Heading
id={"request"}
as={"h2"}
className={"openapi-tabs__heading"}
children={"Request"}
>
</Heading>

<ParamsDetails
parameters={[{"name":"id","in":"path","required":true,"description":"ID de l'utilisateur dont l'état sera modifié","schema":{"type":"string","pattern":"^[a-f0-9]{24}$","example":"63d7907cbe76403b35da63df"}}]}
>

</ParamsDetails>

<RequestSchema
title={"Body"}
body={{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","enum":["NONE","BAD_USER","PENDING","ACTIVE","BLOCKED"],"description":"Raison du changement d'état. Si c'est NONE, l'utilisateur est activé.\nToute autre valeur désactive l'utilisateur.","example":"NONE"},"reasonDate":{"type":"string","format":"date-time","description":"Date/heure du changement d'état (facultatif). Si non fournie,\nla date actuelle est utilisée. Format ISO8601.","example":"2025-02-12T14:30:00.000Z","nullable":true},"reasonMessage":{"type":"string","maxLength":500,"description":"Message explicatif du changement d'état (optionnel).\nVisible par l'utilisateur et les administrateurs.","example":"Usuario bloqueado temporalmente por verificación de documentación","nullable":true}}},"example":{"reason":"NONE","reasonDate":"2025-02-12T14:30:00.000Z","reasonMessage":"Usuario reactivado tras completar verificación"}}}}}
>

</RequestSchema>

<StatusCodes
id={undefined}
label={undefined}
responses={{"200":{"description":"État de l'utilisateur modifié avec succès.","content":{"application/json":{"schema":{"type":"object","description":"Représente un utilisateur d'entreprise (company_user) ayant accès à la plateforme Cargoffer. \n**Fonctionnalité** : \n- Utilisateur employé d'une entreprise de transport qui gère les enchères, les livraisons et la documentation \n- Authentification via JWT (jetons avec expiration configurable selon refresh_time) \n- Système de rôles hiérarchique : dev > admin > gestionnaire (permissions descendantes) \n- Associé à une entreprise (company.users array contient des références aux _id des utilisateurs) \n\n**Modèle** : `src/features/models/company_user.model.js` \n**Contrôleurs** : \n- `src/features/company/users/company_user.account.controller.js` (CRUD, mots de passe) \n- `src/features/company/users/company_user.profile.controller.js` (profil, préférences) \n- `src/features/company/auth/auth.account.controller.js` (connexion, jetons) \n\n**Middleware** : `src/features/common/middleware/company.middleware.js` \n- `m.isLoged` – Vérifie la validité du JWT \n- `m.isGestor` – Requiert le rôle 'gestionnaire' ou supérieur \n- `m.isAdmin` – Requiert le rôle 'admin' ou 'dev'","properties":{"_id":{"type":"string","description":"Identifiant unique MongoDB de l'utilisateur (24 caractères hexadécimaux). Généré automatiquement lors de l'enregistrement. Référencé dans : company.users[], auction.user, delivery.user, notifications.user. Inclus dans le payload JWT en tant que claim '_id'.","pattern":"^[a-f0-9]{24}$","example":"63d7907cbe76403b35da63df"},"email":{"type":"string","format":"email","description":"Adresse e-mail unique de l'utilisateur (utilisée pour la connexion). **Requis** - Unique dans la base de données (index unique en production). Normalisée automatiquement en minuscules (hook pre-save). Utilisée pour : l'authentification, la récupération de mot de passe, les notifications. Validée avec le format d'e-mail standard.","example":"usuario@cargoffer.com"},"name":{"type":"string","description":"Nom de l'utilisateur (firstName dans un contexte international). Utilisé dans : interface, signature des documents CMR, identification dans les notifications. Minimum 2 caractères (validation en frontend).","minLength":2,"maxLength":50,"example":"Juan"},"lastname":{"type":"string","description":"Nom de famille de l'utilisateur (lastName dans l'API, lastname dans le modèle). Utilisé conjointement avec 'name' pour une identification complète dans les documents officiels. Concaténé dans : contrats, CMR, factures.","minLength":2,"maxLength":100,"example":"García López"},"role":{"type":"string","enum":["dev","admin","gestor"],"description":"Rôle de l'utilisateur définissant les permissions sur la plateforme. \n**dev** : Accès total + débogage + gestion de toutes les entreprises (superadmin) \n**admin** : Gestion complète de son entreprise (créer des utilisateurs, voir la facturation, modifier les données) \n**gestor** : Opérations quotidiennes (créer des enchères/livraisons, voir le tableau de bord, éditer son profil) \n\n**Vérifications middleware** : \n- m.isGestor accepte : ['gestor', 'admin', 'dev'] \n- m.isAdmin accepte : ['admin', 'dev'] \n\nStocké dans le payload JWT (utilisé pour la validation des permissions).","default":"gestor","example":"admin"},"status":{"type":"boolean","description":"État actif/inactif de l'utilisateur. **true** : L'utilisateur peut se connecter et fonctionner normalement. **false** : Utilisateur bloqué (ne peut pas se connecter, JWT invalides).\nUtilisé dans : le middleware `m.isLoged` vérifie que `status=true` avant d'autoriser l'accès. Modifié via : POST `/company/users/status/:id` (admin uniquement). Différent de 'deleted' (suppression douce) - il s'agit ici d'un blocage réversible.","default":true,"example":true},"reason":{"type":"string","enum":["NONE","BAD_USER","PENDING","ACTIVE","BLOCKED"],"description":"Code de raison de l'état de l'utilisateur. **NONE** : Utilisateur normal actif sans incident **PENDING** : Inscription terminée, en attente d'activation par email **ACTIVE** : Utilisateur vérifié et opérationnel **BAD_USER** : Bloqué pour mauvais comportement (signalements, fraude) **BLOCKED** : Bloqué administrativement (impayé, sécurité)\nUtilisé conjointement avec 'status' et 'reasonMessage' pour l'audit. Stocké avec 'reasonDate' (horodatage du changement).","default":"NONE","example":"ACTIVE"},"reasonMessage":{"type":"string","description":"Message descriptif de la raison du blocage/état actuel. Optionnel - Rempli par l'administrateur lors du changement de statut à false. Visible par : les administrateurs dans le panneau des utilisateurs, l'utilisateur bloqué dans le message d'erreur de connexion. Exemple : « Compte suspendu pour défaut de paiement », « Utilisateur signalé pour pratiques incorrectes »","example":"Usuario bloqueado temporalmente por verificación de documentación"},"reasonDate":{"type":"string","format":"date-time","description":"Horodatage de la dernière modification du champ 'reason'. Automatique - Défini dans le hook pre-save lors de la détection d'un changement dans 'reason'. Utilisé dans : audit, calcul de la durée de blocage, historiques des modifications.","example":"2025-01-15T14:30:00.000Z"},"i18n":{"type":"string","enum":["es","en","fr","de"],"description":"Code de langue préféré de l'utilisateur (internationalisation). **es** : Espagnol (par défaut pour l'Espagne) **en** : Anglais (par défaut international)\nUtilisé dans : emails automatiques, interface utilisateur, messages d'erreur, documents CMR. Stocké dans le payload JWT pour la personnalisation depuis le backend. Modifié via : POST /company/users/lang","default":"es","example":"en"},"birthDate":{"type":"string","format":"date-time","description":"Date de naissance de l'utilisateur (optionnelle). Utilisée pour : les validations d'âge minimum (18 ans pour les conducteurs), les statistiques démographiques. Format ISO 8601. Nullable.","nullable":true,"example":"1990-05-15T00:00:00.000Z"},"emailVerified":{"type":"boolean","description":"Indique si l'utilisateur a vérifié son adresse e-mail. **false** : Utilisateur inscrit mais n'a pas cliqué sur le lien d'activation **true** : E-mail confirmé via le jeton d'activation\nFlux : inscription → e-mail avec jeton → GET /company/auth/activate/:token → emailVerified=true Les utilisateurs avec emailVerified=false peuvent avoir des fonctionnalités limitées (dépend de la configuration).","default":false,"example":true},"emailVerifiedDate":{"type":"string","format":"date-time","description":"Horodatage de la vérification de l'email (null si emailVerified=false). Défini automatiquement lors de la confirmation du jeton d'activation. Utilisé pour : l'audit, le calcul du temps écoulé depuis la vérification, les exigences de re-vérification.","nullable":true,"example":"2025-01-10T09:20:30.000Z"},"phone":{"type":"string","description":"Numéro de téléphone de l'utilisateur (optionnel mais recommandé). Format : international de préférence (+code pays), minimum 9 chiffres, maximum 15. Utilisé pour : coordination logistique, alertes SMS, contact d'urgence. Validation : `minlength: 9, maxlength: 15` dans le modèle.","pattern":"^\\+?[0-9]{9,15}$","example":"+34612345678"},"lastSignInAt":{"type":"string","format":"date-time","description":"Horodatage de la dernière connexion réussie de l'utilisateur. Mise à jour automatique dans le contrôleur de connexion (POST /company/auth/login). Utilisé dans : tableau de bord d'activité, détection des comptes inactifs, audit de sécurité.","example":"2025-10-23T08:15:45.000Z"},"lastSignInIp":{"type":"string","description":"Adresse IP depuis laquelle la dernière connexion a été effectuée. Extraite de req.ip ou req.headers['x-forwarded-for']. Utilisé dans : détection d'accès suspects, journaux de sécurité, géolocalisation des accès.","format":"ipv4","example":"192.168.1.100"},"country":{"type":"string","description":"Code pays ISO 3166-1 alpha-3 de l'utilisateur (3 lettres minuscules). Utilisé dans : filtres de fuseau horaire, formats de date, paramètres régionaux, validations de TaxID. Par défaut : esp (Espagne). Doit exister dans la collection 'countries'.","pattern":"^[a-z]{3}$","default":"esp","example":"esp"},"timezone":{"type":"string","description":"Fuseau horaire de l'utilisateur au format IANA Time Zone Database. Utilisé pour : conversion des horodatages UTC en heure locale, affichage des dates, calcul des plages horaires de chargement/déchargement. Doit correspondre aux zones valides de la bibliothèque moment-timezone. Par défaut : Europe/Madrid.","default":"Europe/Madrid","example":"Europe/Madrid"},"taxid":{"type":"string","description":"Numéro d'identification fiscale personnelle (NIF, DNI, NIE, etc.). **Unique en production** (index unique). Minimum 6 caractères, maximum 9. Normalisé automatiquement en majuscules (uppercase: true dans le modèle). Utilisé dans : facturation, contrats, documentation légale. Valeur par défaut temporaire : '---------' (9 tirets) pour permettre la création sans donnée.","minLength":6,"maxLength":9,"example":"12345678A"},"img":{"type":"string","format":"uri","description":"URL de l'image de profil de l'utilisateur (stockée dans AWS S3). Optionnel - Téléchargée via l'endpoint de documents avec multerS3. Utilisé dans : avatar dans l'interface, signature visuelle sur les documents. Format : URL complète du bucket S3.","nullable":true,"example":"https://cargoffer-storage.s3.amazonaws.com/users/63d7907cbe76403b35da63df.jpg"},"recovery_token":{"type":"string","description":"Jeton temporaire pour récupération de mot de passe (hash). Généré par : POST /company/auth/recovery (demande de réinitialisation) Utilisé par : GET /company/auth/recovery/:token (valider le jeton) Supprimé après : changement de mot de passe réussi ou expiration (48h) Par défaut : '' (chaîne vide lorsqu'aucun processus de récupération n'est actif)","example":"a7f5e8d3c2b9..."},"refresh_time":{"type":"integer","enum":[1,3,5,10],"description":"Durée d'expiration du JWT en jours. Utilisé dans : génération de token (exp claim = iat + refresh_time * 24h). **1** : Sécurité maximale (re-connexion quotidienne) - Pour les rôles sensibles **3** : Équilibre par défaut sécurité/commodité **5** : Usage fréquent **10** : Applications mobiles avec mode hors-ligne\nStocké dans le payload JWT, validé dans le middleware m.isLoged.","default":3,"example":3},"is_bot":{"type":"boolean","description":"Indique si l'utilisateur est un bot automatisé (pour les intégrations API). **true** : Utilisateur créé pour une intégration automatique (webhooks, scripts) **false** : Utilisateur humain réel\nUtilisé dans : statistiques, audit, exclusion des bots des métriques d'activité humaine. Les bots peuvent avoir des permissions spéciales et des limites de débit différentes.","default":false,"example":false},"deleted":{"type":"boolean","description":"Indicateur de suppression logique (plugin mongoose-delete). **false** : Utilisateur actif **true** : Utilisateur supprimé (n'apparaît pas dans les requêtes normales). \nLes requêtes incluent automatiquement deleted:false (plugin overrideMethods:true). Récupérable via : POST /company/users/disabled/reactivate/:id. Lié au timestamp 'deletedAt'.","default":false,"example":false},"deletedAt":{"type":"string","format":"date-time","description":"Horodatage de la suppression de l'utilisateur (soft delete). Null si deleted=false. Défini automatiquement par le plugin mongoose-delete. Utilisé pour : audit des suppressions, récupération d'utilisateurs, conformité RGPD.","nullable":true,"example":"2025-10-01T12:00:00.000Z"},"createdAt":{"type":"string","format":"date-time","description":"Horodatage de création de l'utilisateur (automatique par mongoose timestamps). Immuable - Ne peut pas être modifié après la création. Utilisé pour : tri, statistiques de croissance, audit.","example":"2024-12-01T10:30:00.000Z"},"updatedAt":{"type":"string","format":"date-time","description":"Horodatage de dernière modification de l'utilisateur (automatique par mongoose timestamps). Mis à jour à chaque save() - Reflète la dernière édition de n'importe quel champ. Utilisé dans : synchronisation, invalidation du cache, détection des changements.","example":"2025-10-20T15:45:30.000Z"},"lastAccess":{"type":"string","format":"date-time","description":"Horodatage de la dernière accès enregistré à n'importe quel point de terminaison authentifié. Mis à jour par : le middleware m.getAction à chaque requête authentifiée. Différent de lastSignInAt (celui-ci est mis à jour à chaque requête, pas seulement à la connexion). Utilisé dans : détection d'inactivité, métriques d'engagement.","example":"2025-10-23T10:22:15.000Z"}},"title":"User"},"example":{"_id":"63d7907cbe76403b35da63df","email":"usuario@empresa.com","name":"Juan","lastname":"Pérez García","status":true,"reason":"NONE","reasonDate":null,"reasonMessage":null,"updatedAt":"2025-02-12T14:30:00.000Z"}}}},"400":{"description":"Demande invalide. Causes possibles :\n- Motif non valide\n- Erreur lors de l'enregistrement","content":{"application/json":{"schema":{"type":"object","required":["status","message"],"properties":{"status":{"type":"integer","description":"Code de statut HTTP","minimum":400,"maximum":599,"example":400},"message":{"type":"string","description":"Code d'erreur du système.\n\nCodes d'erreur courants (voir listado_errores_http.txt pour la liste complète) :\n- NO_TOKEN (401) : Jeton JWT non fourni\n- TOKEN_NOT_VALID (401) : Le jeton JWT est invalide ou a expiré\n- NO_ADMIN_ROLE (401) : L'utilisateur ne dispose pas des privilèges d'administrateur\n- INVALID_PARAMETERS (400) : Les paramètres de la requête sont invalides\n- NOT_FOUND (404) : Ressource demandée introuvable\n- ALREADY_EXIST (401) : Une ressource avec le même identifiant existe déjà\n- UTC_VALIDATION_FAILED (400) : Échec de la validation de l'horodatage UTC\n- INTERNAL_ERROR (500) : Une erreur serveur inattendue s'est produite\n\nLe message est résolu par `handlerError.getErrorMessage(error)`.","example":"INVALID_PARAMETERS"}},"description":"Format de réponse d'erreur standard utilisé sur tous les points d'extrémité de l'API.\n\nToutes les erreurs suivent le modèle `{status: number, message: string}`.\nLe code de statut est répété à la fois dans la réponse HTTP et dans le corps.\n\nLes messages d'erreur sont des constantes définies dans la base de code et doivent être\ngérés côté client avec des messages appropriés destinés à l'utilisateur.","example":{"status":400,"message":"INVALID_PARAMETERS"},"title":"ErrorResponse"},"example":{"message":"FORM_DATA_NOT_VALID"}}}},"401":{"description":"Non autorisé (rôle admin ou dev requis)","content":{"application/json":{"schema":{"type":"object","required":["status","message"],"properties":{"status":{"type":"integer","description":"Code de statut HTTP","minimum":400,"maximum":599,"example":400},"message":{"type":"string","description":"Code d'erreur du système.\n\nCodes d'erreur courants (voir listado_errores_http.txt pour la liste complète) :\n- NO_TOKEN (401) : Jeton JWT non fourni\n- TOKEN_NOT_VALID (401) : Le jeton JWT est invalide ou a expiré\n- NO_ADMIN_ROLE (401) : L'utilisateur ne dispose pas des privilèges d'administrateur\n- INVALID_PARAMETERS (400) : Les paramètres de la requête sont invalides\n- NOT_FOUND (404) : Ressource demandée introuvable\n- ALREADY_EXIST (401) : Une ressource avec le même identifiant existe déjà\n- UTC_VALIDATION_FAILED (400) : Échec de la validation de l'horodatage UTC\n- INTERNAL_ERROR (500) : Une erreur serveur inattendue s'est produite\n\nLe message est résolu par `handlerError.getErrorMessage(error)`.","example":"INVALID_PARAMETERS"}},"description":"Format de réponse d'erreur standard utilisé sur tous les points d'extrémité de l'API.\n\nToutes les erreurs suivent le modèle `{status: number, message: string}`.\nLe code de statut est répété à la fois dans la réponse HTTP et dans le corps.\n\nLes messages d'erreur sont des constantes définies dans la base de code et doivent être\ngérés côté client avec des messages appropriés destinés à l'utilisateur.","example":{"status":400,"message":"INVALID_PARAMETERS"},"title":"ErrorResponse"},"example":{"message":"NO_TOKEN"}}}},"403":{"description":"Interdit (l'utilisateur n'est pas administrateur)","content":{"application/json":{"schema":{"type":"object","required":["status","message"],"properties":{"status":{"type":"integer","description":"Code de statut HTTP","minimum":400,"maximum":599,"example":400},"message":{"type":"string","description":"Code d'erreur du système.\n\nCodes d'erreur courants (voir listado_errores_http.txt pour la liste complète) :\n- NO_TOKEN (401) : Jeton JWT non fourni\n- TOKEN_NOT_VALID (401) : Le jeton JWT est invalide ou a expiré\n- NO_ADMIN_ROLE (401) : L'utilisateur ne dispose pas des privilèges d'administrateur\n- INVALID_PARAMETERS (400) : Les paramètres de la requête sont invalides\n- NOT_FOUND (404) : Ressource demandée introuvable\n- ALREADY_EXIST (401) : Une ressource avec le même identifiant existe déjà\n- UTC_VALIDATION_FAILED (400) : Échec de la validation de l'horodatage UTC\n- INTERNAL_ERROR (500) : Une erreur serveur inattendue s'est produite\n\nLe message est résolu par `handlerError.getErrorMessage(error)`.","example":"INVALID_PARAMETERS"}},"description":"Format de réponse d'erreur standard utilisé sur tous les points d'extrémité de l'API.\n\nToutes les erreurs suivent le modèle `{status: number, message: string}`.\nLe code de statut est répété à la fois dans la réponse HTTP et dans le corps.\n\nLes messages d'erreur sont des constantes définies dans la base de code et doivent être\ngérés côté client avec des messages appropriés destinés à l'utilisateur.","example":{"status":400,"message":"INVALID_PARAMETERS"},"title":"ErrorResponse"},"example":{"message":"USER_NOT_ALLOWED"}}}},"404":{"description":"Utilisateur non trouvé","content":{"application/json":{"schema":{"type":"object","required":["status","message"],"properties":{"status":{"type":"integer","description":"Code de statut HTTP","minimum":400,"maximum":599,"example":400},"message":{"type":"string","description":"Code d'erreur du système.\n\nCodes d'erreur courants (voir listado_errores_http.txt pour la liste complète) :\n- NO_TOKEN (401) : Jeton JWT non fourni\n- TOKEN_NOT_VALID (401) : Le jeton JWT est invalide ou a expiré\n- NO_ADMIN_ROLE (401) : L'utilisateur ne dispose pas des privilèges d'administrateur\n- INVALID_PARAMETERS (400) : Les paramètres de la requête sont invalides\n- NOT_FOUND (404) : Ressource demandée introuvable\n- ALREADY_EXIST (401) : Une ressource avec le même identifiant existe déjà\n- UTC_VALIDATION_FAILED (400) : Échec de la validation de l'horodatage UTC\n- INTERNAL_ERROR (500) : Une erreur serveur inattendue s'est produite\n\nLe message est résolu par `handlerError.getErrorMessage(error)`.","example":"INVALID_PARAMETERS"}},"description":"Format de réponse d'erreur standard utilisé sur tous les points d'extrémité de l'API.\n\nToutes les erreurs suivent le modèle `{status: number, message: string}`.\nLe code de statut est répété à la fois dans la réponse HTTP et dans le corps.\n\nLes messages d'erreur sont des constantes définies dans la base de code et doivent être\ngérés côté client avec des messages appropriés destinés à l'utilisateur.","example":{"status":400,"message":"INVALID_PARAMETERS"},"title":"ErrorResponse"},"example":{"message":"USER_NOT_FOUND"}}}}}}
>

</StatusCodes>