SQLite / backend/server.js
Structure Relationnelle (Table Annonces)
- Traçabilité : Le champ
auteur_id enregistre de manière immuable quel enseignant a publié l'annonce, empêchant toute usurpation d'identité.
- Ciblage : Le champ
classe_cible permet de restreindre la visibilité du message. Si un enseignant choisit "MMI-A1", seuls les étudiants de cette classe verront l'alerte sur leur dashboard.
- Contextualisation : Le champ optionnel
sae_id permet d'attacher une annonce à une projet spécifique (SAE), créant un raccourci direct et cliquable pour l'étudiant.
/* Initialisation de la table des annonces */
CREATE TABLE IF NOT EXISTS Annonces (
id INTEGER PRIMARY KEY AUTOINCREMENT,
auteur_id INTEGER NOT NULL, -- Lien vers l'enseignant (Table Comptes)
message TEXT NOT NULL, -- Contenu du communiqué
classe_cible TEXT NOT NULL, -- Filtre de visibilité (ex: 'MMI-A1')
sae_id INTEGER, -- Lien optionnel vers une SAE
date_creation TEXT NOT NULL, -- Horodatage
FOREIGN KEY (auteur_id) REFERENCES Comptes(id) ON DELETE CASCADE,
FOREIGN KEY (sae_id) REFERENCES SAE(id) ON DELETE CASCADE
);
Node.js / backend/server.js
Insertion Sécurisée (Route POST)
- Contrôle d'accès (RBAC) : Le serveur vérifie que l'utilisateur tentant de publier l'annonce possède bien le rôle
enseignant ou admin. Les étudiants reçoivent une erreur 403 (Forbidden).
- Extraction d'identité : L'
auteur_id n'est pas fourni par le formulaire client (ce qui serait vulnérable), mais extrait directement du Token JWT sécurisé (req.user.id).
- Horodatage Serveur : La date de création est générée par le backend (
new Date().toISOString()) pour garantir une chronologie exacte, indépendante de l'horloge locale de l'ordinateur de l'enseignant.
app.post('/api/annonces', verifierToken, async (req, res) => {
// 1. Barrière de sécurité sur les rôles
if (req.user.role !== 'enseignant' && req.user.role !== 'admin') {
return res.status(403).json({ message: "Refusé" });
}
// 2. Extraction des données du corps de la requête
const { message, classe_cible, sae_id } = req.body;
// 3. Horodatage côté serveur
const date_creation = new Date().toISOString();
try {
// 4. Insertion en base de données
await db.run(
'INSERT INTO Annonces (auteur_id, message, classe_cible, sae_id, date_creation) VALUES (?, ?, ?, ?, ?)',
[req.user.id, message, classe_cible || 'Toutes', sae_id || null, date_creation]
);
res.status(201).json({ message: "Annonce publiée !" });
} catch (error) {
res.status(500).json({ message: "Erreur serveur" });
}
});
React / frontend/src/App.jsx
Gestion de l'État du Formulaire
- State Management : Le contenu du message est lié à la variable d'état
messageAnnonce via un événement onChange sur le composant Textarea, permettant un contrôle absolu du formulaire en temps réel.
- Transmission API : À la soumission, la fonction
handleCreateAnnonce compile ces états et les transmet au service réseau.
- Rafraîchissement automatique : Une fois le succès confirmé par le serveur, l'application recharge la liste des annonces (
setAnnonces(data)), vide le formulaire, et redirige l'enseignant vers son tableau de bord avec une notification de succès.
/* 1. Fonction de soumission asynchrone (App.jsx) */
const handleCreateAnnonce = async (e) => {
e.preventDefault();
setErreur(null);
try {
// Envoi au service API
await saeService.createAnnonce({
message: messageAnnonce,
classe_cible: classeCibleAnnonce,
sae_id: saeLieeAnnonce || null
}, token);
// Rafraîchissement des données locales
const data = await saeService.getAnnonces(token);
setAnnonces(data);
// Nettoyage et redirection
setMessageAnnonce('');
setSaeLieeAnnonce('');
setVueActuelle('dashboard');
setSucces("Annonce envoyée !");
} catch (err) {
setErreur(err.message);
}
};
/* 2. Liaison dans l'interface (JSX) */