#02 — rgz-web
PLANIFIÉ
Priorité: 🔴 CRITIQUE · Type: TYPE A · Conteneur: rgz-web · Code: web/src/
Dépendances: #01 rgz-api
Description
rgz-web est le dashboard d'administration de la plateforme RGZ. Il couvre deux profils d'utilisateurs : les opérateurs NOC (surveillance temps réel, incidents, infrastructure) et les revendeurs WiFi Zone (revenus, abonnés, statistiques de leur réseau). L'interface est une SPA (Single Page Application) React 18, compilée avec Vite et servie par nginx.
La stack React + Tailwind CSS assure une interface réactive et moderne. Les composants réutilisables (tableaux, cartes métriques, graphiques) sont centralisés dans web/src/components/. Les pages correspondent directement aux outils de la catégorie H : NocDashboard.tsx (#52), ResellerDashboard.tsx (#51), BannersAdmin.tsx (#53), RmaDashboard.tsx (#54), StatusPage.tsx (#55). Chaque page communique exclusivement avec rgz-api via le client Axios défini dans web/src/services/.
Le build est multi-stage : une image node:20-alpine compile le bundle React (Vite), puis l'artefact statique est copié dans une image nginx:alpine légère. La configuration nginx proxy les appels /api/ vers rgz-api:8000 — avec la règle LL#28 (resolver 127.0.0.11 + variable $backend, jamais de bloc upstream statique). Le domaine public est admin-rgz.duckdns.org, protégé par Traefik avec un certificat Let's Encrypt valide jusqu'au 2026-05-20.
La charte graphique ACCESS est implémentée via des variables CSS globales : --access-blue: #3f68ae pour les headers et sidebars, --access-yellow: #f5c445 pour les accents et boutons principaux, --access-red: #da3747 pour les alertes et CTAs urgents. La police Poppins (Google Fonts) est utilisée pour toute l'interface.
Architecture Interne
Build (CI/CD ou docker build)
│
node:20-alpine
├── npm install
├── vite build → dist/
│
▼
nginx:alpine (runtime)
│
├── / → dist/ (SPA React)
├── /api/ → proxy rgz-api:8000/api/ (LL#28: resolver 127.0.0.11)
│
▼
React SPA (navigateur)
│
├── React Router — routes protégées (AuthGuard)
├── Axios client → /api/v1/ (token JWT Authorization header)
└── Pages:
├── /login → Login.tsx
├── /noc → NocDashboard.tsx (#52)
├── /reseller/:id → ResellerDashboard.tsx (#51)
├── /banners → BannersAdmin.tsx (#53)
├── /rma → RmaDashboard.tsx (#54)
└── /status → StatusPage.tsx (#55, public)Configuration
Variables d'environnement (build-time Vite)
| Variable | Exemple | Description |
|---|---|---|
VITE_API_URL | https://api-rgz.duckdns.org | URL base de l'API REST |
VITE_APP_NAME | RGZ NOC Dashboard | Nom affiché dans l'interface |
VITE_GRAFANA_URL | https://grafana-rgz.duckdns.org | URL Grafana pour les iframes |
VITE_APP_ENV | production | Contrôle les comportements dev/prod |
Fichiers de configuration
web/
├── package.json # Dépendances: react, react-router, axios, tailwind, chart.js, leaflet
├── vite.config.ts # Build config, proxy dev, alias
├── tailwind.config.js # Extension thème ACCESS colors
├── index.html # Point d'entrée HTML
└── src/
├── main.tsx # ReactDOM.render + providers
├── App.tsx # BrowserRouter + AuthGuard + routes
├── types/index.ts # TypeScript types (mirror schemas Python)
├── services/
│ ├── api.ts # Axios instance + intercepteurs JWT
│ └── auth.ts # Login, refresh token, logout
├── hooks/
│ ├── useAuth.ts # Context auth
│ ├── useWebSocket.ts # WS temps réel NOC
│ └── usePagination.ts # Pagination {items, total, page, pages}
└── components/
├── MetricCard.tsx # Carte KPI réutilisable
├── DataTable.tsx # Tableau paginé avec tri
├── AlertBanner.tsx # Bandeau alerte P0/P1/P2
└── Sidebar.tsx # Navigation latérale ACCESS blueTailwind ACCESS Theme
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
'access-yellow': '#f5c445',
'access-blue': '#3f68ae',
'access-red': '#da3747',
'access-dark': '#34383c',
},
fontFamily: {
sans: ['Poppins', 'sans-serif'],
},
},
},
}Endpoints API Consommés
| Endpoint | Page | Description |
|---|---|---|
POST /api/v1/auth/login | Login | Authentification JWT |
GET /api/v1/resellers/{id}/stats | ResellerDashboard | Stats revenus/abonnés |
GET /api/v1/monitoring/sla | NocDashboard | Métriques SLA temps réel |
GET /api/v1/monitoring/alerts | NocDashboard | Alertes actives Prometheus |
GET /api/v1/rma/tickets | RmaDashboard | File RMA en cours |
GET /api/v1/banners | BannersAdmin | Liste bannières publicitaires |
POST /api/v1/banners | BannersAdmin | Upload nouvelle bannière |
GET /api/v1/status/incidents | StatusPage | Incidents actifs (public) |
Healthcheck
# LL#43 : utiliser 127.0.0.1 (jamais localhost — IPv6 ::1 dans Docker)
# Healthcheck Docker dans docker-compose.core.yml
wget -qO- http://127.0.0.1:80/ | grep -q "RGZ" && echo "OK"
# Depuis l'hôte
curl -s -o /dev/null -w "%{http_code}" https://admin-rgz.duckdns.org/
# Attendu: 200
# Vérifier le proxy /api/
curl -s https://admin-rgz.duckdns.org/api/v1/health | jq .Sécurité
| Règle | Implémentation |
|---|---|
| SEC-10 JWT | Token stocké en mémoire (pas localStorage), refresh via cookie HttpOnly |
| SEC-11 CORS | nginx proxy uniquement /api/, pas de direct cross-origin |
| SEC-13 XSS | textContent uniquement pour les données utilisateur, jamais innerHTML |
| LL#28 nginx | set $backend rgz-api:8000; resolver 127.0.0.11; — jamais de bloc upstream |
| LL#2 CORS | Les requêtes API passent via le proxy nginx, pas de CORS navigateur côté admin |
# docker/web/nginx.conf — proxy correct (LL#28)
location /api/ {
resolver 127.0.0.11 valid=30s;
set $backend "http://rgz-api:8000";
proxy_pass $backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}Commandes Utiles
# Build et démarrage
docker compose -f /home/claude-dev/RGZ/docker-compose.core.yml up -d --build rgz-web
# Logs nginx
docker logs rgz-web -f --tail=100
# Status
docker inspect rgz-web --format='{}'
# Accéder au shell
docker exec -it rgz-web sh
# Rebuild sans cache (après modif tailwind.config.js)
docker compose -f /home/claude-dev/RGZ/docker-compose.core.yml build --no-cache rgz-web
# Développement local (hot-reload)
cd /home/claude-dev/RGZ/web && npm run dev
# Build de production local
cd /home/claude-dev/RGZ/web && npm run build && ls -lh dist/
# Vérifier taille du bundle (objectif < 500KB gzipped)
cd /home/claude-dev/RGZ/web && npm run build 2>&1 | grep -E "dist|gzip"Implémentation TODO
- [x] Dockerfile créé (
docker/web/Dockerfile) - [x] nginx.conf créé (
docker/web/nginx.conf) - [x] Service défini dans
docker-compose.core.yml - [x] Variables d'env dans
.env.example - [ ]
web/package.json— dépendances React 18 + Tailwind + Vite - [ ]
web/vite.config.ts— config build + proxy dev - [ ]
web/tailwind.config.js— thème ACCESS colors - [ ]
web/src/main.tsx— point d'entrée - [ ]
web/src/App.tsx— router + auth guard - [ ]
web/src/services/api.ts— client Axios + intercepteurs - [ ]
web/src/types/index.ts— types TypeScript (mirror Python schemas) - [ ]
web/src/pages/Login.tsx - [ ]
web/src/pages/NocDashboard.tsx(#52) - [ ]
web/src/pages/ResellerDashboard.tsx(#51) - [ ]
web/src/pages/BannersAdmin.tsx(#53) - [ ]
web/src/pages/RmaDashboard.tsx(#54) - [ ]
web/src/pages/StatusPage.tsx(#55, accès public sans auth) - [ ] Tests composants (Vitest + React Testing Library)
Dernière mise à jour: 2026-02-21