
# Photos de profil

Une personne connectée gère sa propre photo de profil. Aucun identifiant d'utilisateur n'apparaît dans les chemins : le compte est toujours celui de l'appelant, si bien qu'on ne peut jamais écrire l'avatar de quelqu'un d'autre.

## Les deux points de terminaison

| Point de terminaison         | Comportement                                                                                       |
| ---------------------------- | -------------------------------------------------------------------------------------------------- |
| `POST /api/account/avatar`   | Téléversement multipart sous le nom de champ `file`. Renvoie `201` avec l'URL `image` enregistrée. |
| `DELETE /api/account/avatar` | Efface la photo et supprime l'objet stocké. Renvoie `200`, et l'opération est idempotente.         |

```bash
curl -X POST https://app.example.com/api/account/avatar \
  -H "Cookie: $SESSION" \
  -F "file=@portrait.png"
```

```json
{
  "success": true,
  "image": "/api/buckets/avatars/files/9f3c1e2a-7b44-4d10-9e21-8a6f0c1d2e3b-avatar.png"
}
```

L'URL se pose sur le champ `image` de la personne, celui que projette l'[annuaire des utilisateurs](/fr/docs/user-management) et que rapporte `GET /api/account/export`.

## Déclarez un bucket `avatars`

C'est une exigence stricte, pas une convention :

```yaml
buckets:
  - name: avatars
    public: true
    maxFileSize: 2097152
```

:::callout
**Sans bucket nommé `avatars`, le téléversement répond `404`.** L'URL forgée par ce point de terminaison nomme `avatars` littéralement : il n'y a donc délibérément aucun repli vers le bucket `default` implicite — un repli enregistrerait le fichier, renverrait une URL, et laisserait la personne avec un avatar qui ne se charge jamais. Le nom est vérifié avant toute lecture.

`public: true` est le choix habituel. Un avatar s'affiche par définition dans le navigateur des autres : un bucket privé ferait répondre `404` à chaque balise `<img>` pour tout le monde sauf son propriétaire.
:::

Le `maxFileSize` de ce bucket plafonne le téléversement ; à défaut, la limite est de 2 Mo. Au-delà, la réponse est `413`.

## Ce qui est accepté

Sovrium ignore le nom de fichier que vous envoyez et ignore le `Content-Type` que vous déclarez. Il décode les octets, lit l'en-tête de conteneur réel, puis en déduit l'extension.

| Format | Enregistré en |
| ------ | ------------- |
| PNG    | `.png`        |
| JPEG   | `.jpg`        |
| WebP   | `.webp`       |

Tout le reste — y compris un fichier qui se contente de prétendre être l'un des trois — répond `400` et laisse l'avatar existant intact.

:::callout
**Écarter le nom de fichier supprime toute une classe de problèmes au lieu de la vérifier.** Le nom de fichier est la seule partie d'un téléversement entièrement choisie par l'expéditeur : la traversée de chemin, les octets nuls et les incohérences entre extension et contenu cessent d'être des contrôles qui pourraient être faux et deviennent des formes qui ne peuvent pas arriver. Lire l'en-tête de conteneur plutôt que le type déclaré signifie qu'un fichier HTML se disant `image/png` est refusé ici, au lieu d'être stocké puis servi depuis votre propre origine.
:::

## L'URL de l'image n'est pas modifiable

`image` ne se définit pas à la main. En envoyer une à `POST /api/auth/update-user` — ou à l'inscription, ou aux points de terminaison d'administration des utilisateurs — répond `400` :

```json
{
  "message": "A profile image cannot be set directly. Upload one through the account avatar endpoint, or send `image: null` to clear it."
}
```

Toute valeur non nulle est refusée, y compris une URL bien formée pointant vers votre propre bucket `avatars`. La forme prouverait que l'URL désigne cette instance ; elle ne prouverait pas que l'objet appartient à l'appelant, et c'est cela qui mérite d'être prouvé. Envoyer `image: null` est autorisé et efface la photo ; omettre le champ le laisse tel quel, si bien qu'un simple changement de nom fonctionne toujours.

## Issues

| Issue                                    | Statut                   |
| ---------------------------------------- | ------------------------ |
| Téléversé                                | `201` avec l'URL `image` |
| Supprimé, ou déjà absent                 | `200`                    |
| Aucune session                           | `401`                    |
| Aucun bucket `avatars` déclaré           | `404`                    |
| Aucun champ `file` dans le corps         | `400`                    |
| Ni PNG, ni JPEG, ni WebP décodable       | `400`                    |
| Au-delà du plafond du bucket, ou de 2 Mo | `413`                    |

Supprimer un compte via l'[effacement RGPD](/fr/docs/gdpr-privacy) retire l'objet avatar stocké en même temps que la ligne.

## Pages connexes

- [Gestion des utilisateurs](/fr/docs/user-management) — l'annuaire qui affiche ces images.
- [Vue d'ensemble des buckets](/fr/docs/buckets-overview) — déclarer le bucket `avatars`.
- [Opérations sur les fichiers](/fr/docs/file-operations) — la route de téléchargement visée par l'URL forgée.
- [RGPD et vie privée](/fr/docs/gdpr-privacy) — ce que l'effacement retire.
