Skip to content

How-to : API de file de notifications

Ce guide démontre une file de notifications où les admins envoient des notifications ciblées aux utilisateurs, qui peuvent les lister, les lire et les supprimer.

Vue d'ensemble du pattern

  • Les admins envoient des notifications à des utilisateurs spécifiques via POST /notifications (admin uniquement).
  • Les utilisateurs reçoivent et gèrent leurs propres notifications via GET, POST /read, DELETE.
  • unread_count est retourné avec chaque réponse de liste.
  • ?unread=1 filtre sur les notifications non lues uniquement.
  • Le marquage comme lu est idempotent (les notifications déjà lues retournent 200, pas d'erreur).

Schéma

sql
CREATE TABLE IF NOT EXISTS notifications (
    id         INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id    INTEGER NOT NULL,
    type       TEXT    NOT NULL DEFAULT 'info',
    title      TEXT    NOT NULL,
    body       TEXT    NOT NULL DEFAULT '',
    is_read    INTEGER NOT NULL DEFAULT 0,
    created_at TEXT    NOT NULL,
    read_at    TEXT
);

Liste blanche de types

php
private const array ALLOWED_TYPES = ['info', 'warning', 'error', 'success'];

Les types inconnus retournent 422. Ne jamais utiliser un champ texte libre pour le type sans validation.

Marquage comme lu idempotent

php
public function markRead(int $id, int $userId): bool
{
    $notif = $this->findById($id);
    if ($notif === null || (int) $notif['user_id'] !== $userId) {
        return false;
    }
    if ((int) $notif['is_read'] === 1) {
        return true;  // Déjà lu — idempotent, retourner succès
    }
    $this->pdo->prepare(
        'UPDATE notifications SET is_read = 1, read_at = :now WHERE id = :id'
    )->execute([':now' => $this->now(), ':id' => $id]);
    return true;
}

Filtre de non-lus

php
if ($unreadOnly === true) {
    $stmt = $this->pdo->prepare(
        'SELECT * FROM notifications WHERE user_id = :uid AND is_read = 0 ORDER BY id DESC'
    );
}

Le paramètre de requête ?unread=1 active ce chemin ; toute autre valeur liste tout.

IDOR : Scoping par utilisateur

Toutes les opérations de lecture/suppression/liste vérifient user_id :

php
if (!$isAdmin && (int) $notif['user_id'] !== $userId) {
    return false;  // → 404
}

Les utilisateurs non-admin ne peuvent pas lire, marquer ou supprimer les notifications des autres utilisateurs.

Envoi réservé aux admins

php
private function send(ServerRequestInterface $req): ResponseInterface
{
    if (!$this->isAdmin($req)) {
        return $this->problem(403, 'forbidden', 'Admin access required.');
    }
    ...
}

Le user_id cible est spécifié dans le corps de la requête, validé comme is_int() && >= 1.

Résumé des validations

ChampRègle
user_id corpsEntier >= 1 (pas string/float)
type corpsUn de : info, warning, error, success
title corpsNon-vide, max 200 caractères
En-tête X-User-IdRequis pour lecture/suppression ; ctype_digit, >0
En-tête X-Admin-KeyRequis pour l'envoi ; à défaut fermé quand vide

Routes

POST   /notifications                  Envoyer une notification (admin uniquement)
GET    /users/{userId}/notifications   Lister les notifications (propriétaire ou admin)
POST   /notifications/{id}/read        Marquer comme lu (propriétaire uniquement)
DELETE /notifications/{id}             Supprimer la notification (propriétaire ou admin)

Voir aussi

  • Source FT214 : ../NENE2-FT/notiflog/
  • Connexe : docs/howto/session-token-management.md (FT208, pattern de clé admin)

Publié sous licence MIT.