Projet de boutique en ligne « Benn59 ». Deux parties cohabitent dans ce dépôt :
backend/: API REST Node.js (Express) + base de données SQLite.frontend/: boutiques publiques en HTML / CSS / JavaScript pur (sans framework), plus un back-office d'administration connecté à l'API.
| Couche | Technologie |
|---|---|
| Langage | JavaScript (CommonJS côté serveur, modules ES6 côté navigateur) |
| Serveur HTTP | Express ^5 |
| Base de données | SQLite via better-sqlite3 (base fichier backend/database.sqlite) |
| Front public | HTML + CSS + JS vanilla (aucun framework) |
| Carrousels | Glide.js (CDN) |
| Icones | Bootstrap Icons (CDN) |
| Back-office | admin.html + js/admin.js (pure JS, appels fetch à l'API) |
Registre d'accès : GitHub classique (node_modules est versionné dans le repo).
ben-56/
├── README.md ← ce fichier
├── backend/
│ ├── index.js ← point d'entrée réel (le serveur complet)
│ ├── server.js ← fichier vide (inutilisé, à supprimer)
│ ├── package.json ← scripts/dépendances (start = node index.js)
│ ├── database.sqlite ← base de données (créée + seed au démarrage)
│ └── node_modules/
└── frontend/
├── index.html, shop.html, single-product.html, cart.html,
│ checkout.html, account.html, admin.html, blog.html,
│ blog-detail.html, single-blog.html, contact.html,
│ about.html, coupons.html, forgotpassword.html,
│ product-detail.html
├── css/
│ ├── main.css, base.css, admin.css
│ ├── layout/ (header.css, footer.css)
│ ├── components/ (slider, product-item, modal-search, …)
│ ├── pages/ (home, contact, cart, blog, account)
│ └── single-products/ (breadcrumb, gallery, info, tabs)
├── js/
│ ├── main.js, header.js, product.js, search.js, cart.js,
│ │ admin.js, slider.js, glide.js, single-product.js
│ ├── data.json ← <-- produits affichés côté public (source de vérité du front)
│ └── single-product/ (tabs, colors, comments, zoom, thumbsActive, values)
└── img/ (produits, catégories, marques, blogs, slider, avatars…)
Le serveur Express sert aussi les pages front (statique + route racine). Une seule commande suffit :
cd backend
npm install # la 1ère fois seulement
npm start # = node index.jsPuis ouvrir : http://localhost:3000
- Boutique publique :
http://localhost:3000/ - Back-office :
http://localhost:3000/admin.html
Variables d'environnement optionnelles (il n'y a aucun fichier .env actuellement) :
| Variable | Rôle | Défaut |
|---|---|---|
PORT |
Port du serveur | 3000 |
ADMIN_EMAIL |
Email de l'admin principal | `` |
ADMIN_PASSWORD |
Mot de passe de l'admin principal | `` |
FRONTEND_ORIGIN |
En-tête CORS Access-Control-Allow-Origin |
* |
Express + SQLite. Au démarrage le serveur :
- crée les tables si elles n'existent pas (
CREATE TABLE IF NOT EXISTS …), - ajoute des colonnes manquantes (
ensureColumn) pour lesusers, - exécute
seedDatabase(): insère les données de démonstration et le compte admin par défaut.
| Table | Contenu |
|---|---|
users |
Clients (nom, email, statut, date de création) |
admins |
Comptes administrateurs (mot de passe haché) |
admin_sessions |
Sessions admin (token haché, expiration 8 h) |
suppliers |
Fournisseurs |
products |
Produits (prix, stock, seuil d'alerte low_stock_threshold, view_count, fournisseur) |
orders |
Commandes (total, statut, client) |
coupons |
Codes promo (réduction %, expiration, limite d'usage) |
announcements |
Annonce en bandeau (une seule ligne, id = 1) |
blog_posts |
Articles de blog |
reviews |
Avis clients (statuts : pending, approved, rejected) |
cart_sessions |
Paniers abandonnés / convertis (statistiques) |
newsletter_campaigns |
Campagnes email (brouillon / programmée) |
store_settings |
Paramètres boutique (une seule ligne, id = 1) |
page_seo |
Title / description / keywords par page |
- Mots de passe hachés avec
scrypt(sel aléatoire 16 octets + hash 64 octets, formatsel:hash). - Tokens de session (48 octets
base64url) stockés hachés en SHA-256, expiration 8 h. - Sessions vérifiées via
Authorization: Bearer <token>. requirePermission(scope)interdit l'accès selon le rôle (403) et contrôle la session (401).
| Rôle | Permissions |
|---|---|
super_admin |
tout (['*']) |
catalog_manager |
tableau de bord, produits, inventaire |
orders_manager |
tableau de bord, commandes |
marketing_manager |
tableau de bord, marketing, contenu, SEO |
support_manager |
tableau de bord, clients, avis |
Email :
Mot de passe :
⚠️ À changer avant toute mise en production !
Endpoints publics (vitrine, sans authentification)
GET /api/products→ produits formatés pour la vitrine (id,name,price.oldPrice/newPrice,discount,stock,img.singleImage/thumbs)GET /api/products/:id→ un produitPOST /api/products/:id/view→ incrémente le compteur de vuesGET /api/blog-posts(+/api/blog-posts/:id) → articles publiésGET /api/announcement→ l'annonce active (bandeau haut de page)GET /api/settings→ paramètres boutiqueGET /api/reviews?product_id=→ avis approuvés
Authentification
POST /api/auth/login→ renvoie{ token, expiresAt, admin }POST /api/auth/logoutGET /api/admin/session→ admin actuel
Administration (toutes protégées par rôle)
- Tableau de bord :
GET /api/admin/dashboard,GET /api/admin/analytics - Clients :
GET /api/admin/users,PATCH /api/admin/users/:id - Produits :
GET|POST /api/admin/products,PATCH|DELETE /api/admin/products/:id - Commandes :
GET /api/admin/orders,PATCH /api/admin/orders/:id - Codes promo :
GET|POST /api/admin/coupons,DELETE /api/admin/coupons/:id - Annonce :
GET /api/admin/announcement,PUT /api/admin/announcement - Articles :
GET|POST /api/admin/blog-posts,DELETE /api/admin/blog-posts/:id - Avis :
GET /api/admin/reviews,PATCH /api/admin/reviews/:id,DELETE /api/admin/reviews/:id - Fournisseurs :
GET|POST /api/admin/suppliers,DELETE /api/admin/suppliers/:id - Paramètres :
GET /api/admin/settings,PUT /api/admin/settings - Marketing :
GET|POST /api/admin/campaigns,DELETE /api/admin/campaigns/:id - Équipe :
GET|POST /api/admin/admins,PATCH /api/admin/admins/:id(réservésuper_admin) - SEO :
GET /api/admin/seo,PUT /api/admin/seo/:pageKey
Depuis le branchement sur l'API, main.js :
fetch("/api/products")→ les produits viennent de la base SQLite gérée par l'admin,- s'il échoue (serveur arrêté), repli automatique sur
frontend/js/data.json, - stocke le tableau dans
localStorage["products"], - lance le rendu (
product.js), la recherche (search.js) et le header (header.js).
main.js charge aussi et affiche :
- le bandeau d'annonce (
.global-notification) depuis/api/announcement, - la liste des articles de blog (
.blog-list) depuis/api/blog-posts(si des articles existent).
Le panier reste en localStorage (voir 5.2) ; la fiche produit (single-product.js) cherche d'abord dans localStorage["products"], sinon interroge /api/products/:id, et incrémente le compteur de vues.
Le format attendu par la vitrine est imposé par les modèles (data.json / réponse API) :
{
"id": 1,
"name": "Bracelet en résine analogique",
"price": { "oldPrice": 165, "newPrice": 108 },
"discount": 22,
"stock": 45,
"img": { "singleImage": "img/products/product1/1.png", "thumbs": [".../1.png", ".../2.png", ".../3.png"] }
}Dans l'admin, l'ancien prix et la galerie d'images (séparées par des virgules) sont modifiables : s'ils sont absents, l'API calcule pas de réduction et utilise l'image principale.
Le panier n'est pas géré côté serveur pour la boutique publique :
- Stocké dans
localStorage["cart"](tableau de produits avecquantity). - Le compteur
.header-cart-countaffiche le nombre d'articles. product.js(addToCart) câble les boutons.add-to-cart, met le bouton endisabledsi le produit est déjà au panier.cart.jsaffiche le panier, permet de supprimer, calcule sous-total / total, et une option « expédition rapide » à 15 $.single-product.jsaffiche la fiche produit à partir delocalStorage["productId"]etlocalStorage["products"].
single-product.html est une page unique générique : tout dépend de l'id stocké.
Le module single-product.js alimente : titre, prix, image principale, vignettes (glide + gestion de l'image active), et le bouton « ajouter au panier » avec quantité.
Modules annexes : tabs.js (onglets description/avis), comments.js (envoyer un avis), colors.js, values.js, zoom.js (zoom image), thumbsActive.js.
search.js : filtre les produits par nom au fil de la saisie dans la modale de recherche, et redirige vers la fiche produit (single-product.html).
slider.js: slider d'accueil (bannières).glide.js: 3 configurations Glide.js (product1: 4 vues,product2: 3 vues,product3: vignettes 5 vues).
Organisation par couches :
main.css(regroupe les imports),base.css,layout/(header, footer),components/(produits, sliders, modales, catégories, campagnes, marques, blogs…),pages/(home, contact, cart, blog, account),single-products/(breadcrumb, galerie, infos, onglets),admin.css(back-office).
Écran unique en pur JavaScript (pas de framework) qui gère tout :
- Connexion/déconnexion via l'API (
/api/auth/login,logout), token conservé ensessionStorage["adminToken"]. - Un menu latéral bascule entre 13 panneaux : Tableau de bord (stats + graphique de ventes sur 6 mois, top produits, paniers abandonnés), Clients, Produits, Inventaire (alerte stock faible), Commandes, Avis, Codes promo, Marketing (campagnes + abonnés), Annonces, Articles, SEO, Paramètres, Équipe & droits.
- Toutes les mutations passent par l'API d'administration (CRUD produit, statut commande, modération des avis, création de codes promo, article de blog, campagne, fournisseur, admin, réglages boutique, SEO).
- HTML échappé (
escapeHtml) avant injection, dates et montants formatés enfr-FR.
J'ai relevé pendant l'analyse quelques incohérences à traiter quand on travaillera dessus :
- Sources de données alignées. La vitrine lit désormais l'API (
/api/products) donc les produits créés/modifiés dans l'admin apparaissent sur le site.data.jsonne sert plus que de secours hors serveur. Les noms de produits seedés sont encore en anglais (« Analogue Resin Strap »…) : à renommer en français quand tu veux. - Le panier vitrine ne crée pas encore de commande en base. On peut brancher
checkout.htmloucart.htmlsur un futur endpointPOST /api/orders. - Pages ou feuilles CSS potentiellement manquantes/incorrectes :
account.html→css/account.css(le fichier estcss/pages/account.css),coupons.html→css/coupons.css(introuvable),contact.html→/frontend/css/main.css(chemin absolu, peut casser si servi depuis un autre sous-chemin).
blog-detail.htmlest vide ; la liste du blog pointe verssingle-blog.html?id=Nqui reste un template statique (à brancher si on veut afficher le contenu des articles).product-detail.html,coupons.html,forgotpassword.htmlsont aussi des templates non câblés.server.jsest le point d'entrée (démarre le serveur HTTP) ;index.jsconstruit l'app + la base et l'exporte. OK, rien à faire.data.json→search.js: message turc « Aradığınız Ürün Bulunamadı » (produit introuvable) dans la recherche ;discountdouteux dans les données de secours.node_modulesest commité dans git (initial commit) — en général on l'exclut (.gitignore).
Ce README est notre référence commune (moi = l'assistant de dev, toi = développeur). On s'appuiera dessus pour :
- retrouver rapidement l'architecture et les responsabilités de chaque fichier,
- décider ensemble des corrections (section 7),
- rendre le projet cohérent : une seule base de données pour la vitrine et l'admin, un panier/commande réellement branché sur l'API, etc.
Quand on modifie le projet, on mettra ce fichier à jour au fil de l'eau.