/nouvelles — Guide pour assistants IAPour le contexte général du site CJEAO, voir
CLAUDE.mdà la racine.
Le tableau de bord /nouvelles est la fonctionnalité la plus complexe du site :
~20 sources de données opérationnelles + ~15 sources d’articles de presse, le
tout agrégé en widgets sur une seule page. C’est de loin la zone où l’on passe
le plus de temps de développement.
┌──────────────┐ cron ┌────────────────────┐ REST ┌──────────┐
│ Source web │ ─────────→ │ Netlify Function │ ──────→ │ Supabase │
│ (RSS, HTML, │ │ nouvelles-*.js │ upsert │ table │
│ API, CSV) │ └────────────────────┘ └────┬─────┘
└──────────────┘ │
│ SELECT (anon)
▼
┌─────────────────┐
│ nouvelles.astro │
│ render widget │
└─────────────────┘
SUPABASE_SERVICE_KEY), le client lit en direct via
SUPABASE_ANON_KEY (RLS SELECT public)nouvelles-proxy.js qui agrège les tables côté serveur si
la lecture directe échoue{id:1, ...payload, mis_a_jour})| Function | Table | Source | Cron | Notes |
|---|---|---|---|---|
nouvelles-meteo.js | nouvelles_meteo | Open-Meteo API | 0 */1 * * * | La Sarre 48.8°N/-79.2°O |
nouvelles-essence.js | nouvelles_essence | Régie énergie QC | 2×/jour | 8 stations MRC + comparatif régional |
nouvelles-sopfeu.js | nouvelles_sopfeu | SOPFEU | 2×/jour | Zone La Sarre-Amos |
nouvelles-finances.js | nouvelles_finances | gold-api + frankfurter.dev | 1×/jour | Or USD/oz, USD-CAD, EUR-CAD + variation 7j |
nouvelles-hydro.js | nouvelles_hydro | Hydro-Québec Info-Pannes | aux 4h | |
nouvelles-routes.js | nouvelles_routes | Québec 511 | aux 3h | Routes 101, 111, 117, 393 — filtré aux municipalités d’A-T via netlify/lib/abitibi-temiscamingue.js |
nouvelles-alertes-meteo.js | nouvelles_alertes | Env. Canada MSC GeoMet | aux 3h | |
nouvelles-urgences.js | nouvelles_urgences | MSSS Québec | 0 */1 * * * | Achalandage urgences CIUSSS A-T |
nouvelles-emplois.js | nouvelles_emplois | cjeao.jobstat.ca | aux 6h | |
nouvelles-bornes.js | nouvelles_bornes_electriques | Circuit Électrique | aux 6h | 80 km autour La Sarre |
nouvelles-air.js | nouvelles_air | Open-Meteo Air Quality | aux 3h | |
nouvelles-aurores.js | nouvelles_aurores | NOAA SWPC + calcul lune + IMO météores | aux 6h | |
nouvelles-eau.js | nouvelles_eau | CEHQ + Env. Canada | 2×/jour | Lac Macamic, Abitibi, Kinojevis, Harricana |
nouvelles-internet.js | nouvelles_internet | M-Lab NDT via BigQuery | 1×/sem | Vitesses par FAI |
nouvelles-transport.js | nouvelles_transport | CSS Lac-Abitibi | 0 */1 * * * | Alerte annulation/retard transport scolaire |
| Function | Table | Source | Cron | Onglet widget |
|---|---|---|---|---|
nouvelles-loisirs-ao.js | nouvelles_activites_loisirs | loisirs.ao.ca | 0 8 * * * | « Loisirs » |
nouvelles-cciao.js | nouvelles_cciao | cciao.ca | 4×/jour | « CCIAO » |
nouvelles-agenda-lasarre.js | nouvelles_activites_ville_lasarre | lasarre.ca/calendrier | 15 8 * * * | « Ville LS » (max 3 — voir pièges) |
(table activites interne) | activites | Saisie manuelle CJEAO | — | « Carrefour » |
Le widget Agenda déduplique entre Loisirs A-O et Ville LS (match flou par date + similitude de titre). Ville LS gagne en cas de conflit (source officielle).
nouvelles_articles, schéma{source, titre, url, resume, date_publication, image_url, mis_a_jour})
Chaque function fait INSERT ... ON CONFLICT DO UPDATE (upsert) avec une
contrainte unique sur url.
| Function | Source | Type | Cron | Filtre |
|---|---|---|---|---|
nouvelles-radio-canada.js | Radio-Canada A-T RSS | RSS | aux 4h | Whitelist 60+ toponymes A-T (MOTS_REGION) |
nouvelles-tva-abitibi.js | TVA Abitibi RSS | RSS | aux 4h | Aucun (site mono-régional) |
nouvelles-mon-abitibi.js | Mon Abitibi RSS | RSS | aux 4h | Filtre GUID monabitibi.ca (rejette les sites jumeaux du réseau) |
nouvelles-le-citoyen.js | journallecitoyen.com | Scraping | 0 9 * * * | Aucun (hebdo régional) |
nouvelles-indice-bohemien.js | indicebohemien.org | RSS | aux 6h | |
nouvelles-ville-lasarre.js | lasarre.ca/actualites | Scraping | 0 9 * * * | À ne PAS confondre avec nouvelles-agenda-lasarre.js |
nouvelles-avis-lasarre.js | citoyen.lasarre.ca/avis | Scraping | 1×/jour | |
nouvelles-mrcao.js | mrcao.qc.ca | Scraping | 1×/jour | |
nouvelles-cssla.js | cssla.gouv.qc.ca | Scraping | 1×/jour | |
nouvelles-uqat.js | UQAT | API interne | 1×/jour | |
nouvelles-ecole-musique-ao.js | ecolemusiqueao.ca | Scraping mono-page | 0 10 * * * | |
nouvelles-cisss-at.js | CISSS-AT communiqués | Scraping | 1×/jour | |
nouvelles-orezone.js | orezone.com press releases | Scraping | 1×/jour | |
nouvelles-sadcao.js | sadcao.com | Scraping | 1×/jour | (récent, vérifier état) |
nouvelles-proxy.js — agrège toutes les tables en un seul JSON (filet de secours)nouvelles-archives.js — gestion des archivesnouvelles-commentaire.js — formulaire de suggestions (GET/POST)nouvelles-healthcheck.js — monitoring interne (anon: aucun accès)nouvelles-presence.js — compteur « en ligne » first-party (battement + table presence)push-subscribe.js / push-unsubscribe.js / push-envoyer.js — push notificationsfacebook-feed.js — publication automatique vers Facebookenvoyer-rappels.js — rappels d’inscription aux activitéscreate table if not exists public.nouvelles_xxx (
id smallint primary key default 1, -- toujours 1 ligne
...payload jsonb ou colonnes typées...,
mis_a_jour timestamptz not null default now()
);
alter table public.nouvelles_xxx enable row level security;
create policy "lecture publique" on public.nouvelles_xxx
for select to anon using (true);
grant select on public.nouvelles_xxx to anon;
nouvelles-xxx.jsimport { withSentry } from '../lib/sentry.js';
export const config = {
schedule: '0 8 * * *', // cron Netlify (en UTC)
};
const __sentryHandler = async () => {
const SUPABASE_URL = process.env.SUPABASE_URL;
const SUPABASE_SERVICE_KEY = process.env.SUPABASE_SERVICE_KEY;
if (!SUPABASE_URL || !SUPABASE_SERVICE_KEY) {
console.error('[xxx] Variables manquantes');
return new Response('Erreur de configuration', { status: 500 });
}
// 1. Fetch source
// 2. Parse
// 3. Upsert : POST /rest/v1/nouvelles_xxx avec Prefer: resolution=merge-duplicates
return new Response(JSON.stringify({ ok: true, ... }), { status: 200 });
};
export default withSentry(__sentryHandler);
supabase/migrations/AAAAMMJJ_nom.sql (appliquer
manuellement dans Supabase SQL Editor)netlify/functions/nouvelles-xxx.js selon le squelettenouvelles-proxy.js) — ajouter le fetchTable(...), le res, le
data, et la clé dans le payload finalsrc/pages/nouvelles.astro :
TABLES_NOUVELLES (mapping clé → nom de table)renderXxx(data) qui produit du HTML<div id="widget-xxx"> dans le markupif (wXxx && data.xxx) wXxx.innerHTML = renderXxx(data.xxx); dans
appliquerDonneessrc/pages/nouvelles/methodologie.astro) — ligne dans le
tableau des sourceslasarre.ca/calendrier-des-activites//wp-json/facetwp/v1/refresh qui exige un cookie + nonce
→ pas reproductible côté serveur.methodologie.astro.new URL(item.guid).hostname)
pour ne garder que l’host attendu.lasarre.ca/wp-json/wp/v2/eventdate du post est la date d’IMPORT en lot dans le CMS, pas la date
réelle de l’événement. Les dates réelles sont dans des post-meta non exposés.lasarre.ca/evenements/*netlify/lib/abitibi-temiscamingue.js expose la liste des
municipalités d’A-T + lieux-dits, on filtre sur la localisation de chaque
entrave.MOTS_REGION avec 60+
toponymes → l’article doit en mentionner au moins unnew URL(item.guid).hostname === 'monabitibi.ca'abitibi-temiscamingue.jsnouvelles.astro lit directement Supabase (clé anon) en priorité — la
function nouvelles-proxy.js n’est qu’un filet de secours. Cette stratégie
économise des invocations Netlify (les lectures direct Supabase ne coûtent rien
côté Netlify) et permet l’auto-refresh sans surcharger nos quotas.
Quand on ajoute une source au widget, il faut donc s’assurer que :
TABLES_NOUVELLES (pour la lecture directe)nouvelles-proxy.js (pour le filet de secours)SUPABASE_SERVICE_KEY côté client (jamais).methodologie.astro (transparence).Voir docs/notes-nouvelles-todo.md pour le carnet de travail.