Plateforme e-commerce full-stack pour libraires de mangas â catalogue filtrĂ©, panier hybride (localStorage invitĂ© / MySQL connectĂ©), tunnel dâachat complet, annonces seconde main, avis, i18n FR/EN. Vanilla JS frontend (no framework), Node/Express/MySQL backend, JWT auth.
â https://abdoulrazack1.github.io/Kinka/page_accueil.html

â ïž La version live sur GitHub Pages affiche lâUI complĂšte (CSS, navigation, mode sombre, i18n) mais sans donnĂ©es dynamiques â le backend Node/Express/MySQL nâest pas dĂ©ployĂ©. Pour la version complĂšte avec catalogue, panier et auth, suis les instructions dâinstallation ci-dessous.
| Use case | Comment Kinka le résout |
|---|---|
| Visiteur ajoute des articles | Panier en localStorage â pas de friction dâinscription |
| Visiteur sâinscrit aprĂšs | Migration auto du panier localStorage â MySQL au login (sans perte) |
| Vendre du neuf et de lâoccasion | Catalogue principal + section âAnnoncesâ avec CRUD utilisateur |
| i18n FR/EN | SystĂšme de traduction custom, 1800+ chaĂźnes |
| Auth solide pour DWWM | JWT + bcrypt (12 rounds) + rate-limit + auth-guard cÎté client |
Pas une dĂ©mo â 41 pages HTML, 38 feuilles CSS, 18 scripts JS, 32 endpoints API, mode sombre + responsive complet.
Front : HTML5 · CSS3 · JavaScript ES6+ (sans framework) · Figma Back : Node.js (Express) · MySQL · JWT (jsonwebtoken) · bcryptjs Outils : Git · Live Server (VS Code, port 5503)
Catalogue
Panier & commande
Compte utilisateur
Favoris : ajout/retrait synchronisé entre localStorage (visiteur) et BDD (connecté)
Annonces seconde main : création/édition/suppression par les utilisateurs
Avis : note + commentaire (une fois par produit)
LĂ©gal & support : CGU, CGV, politique de retour, FAQ, contact (formulaire branchĂ© Ă lâAPI)
Mode sombre : toggle persistant + respect du prefers-color-scheme systĂšme
i18n : systĂšme de traduction FR/EN (1800+ chaĂźnes)
Kinka/
âââ README.md â ce fichier
âââ .gitignore â exclut node_modules, .env, builds
âââ .vscode/ â config Live Server (port 5503)
â
âââ client/ â Front
â âââ pages/ â 42 pages HTML
â â âââ page_accueil.html â homepage avec carrousel
â â âââ page_catalogue.html â catalogue filtrĂ©
â â âââ page_detail_produit.htmlâ fiche produit (+ onglet avis)
â â âââ page_annonces.html â annonces entre membres
â â âââ page_admin.html â back-office
â â âââ âŠ
â âââ assets/
â âââ css/ â feuilles de style (1 par page + partagĂ©es)
â âââ images/ â logos, banniĂšres, visuels
â âââ js/ â scripts frontend
â âââ kinka-api-client.js â client API + auth + cookies + toast
â âââ kinka-cards.js â rendu des cartes produit
â âââ admin.js â logique du back-office
â âââ avis.js â avis clients
â âââ âŠ
â
âââ server/ â Backend Node.js (MVC)
â âââ src/
â â âââ server.js â point d'entrĂ©e : middlewares + montage des routes
â â âââ routes/ â dĂ©claration des URL (chemin + middlewares)
â â âââ controllers/ â logique applicative
â â âââ models/ â accĂšs aux donnĂ©es : tout le SQL vit ici
â â âââ views/emails/ â gabarits des emails envoyĂ©s par le serveur
â â âââ services/ â dĂ©pendances externes (SMTP, API Jikan)
â â âââ middleware/ â auth JWT, rĂŽle admin, validation
â â âââ config/db.js â pool MySQL (mysql2/promise)
â âââ scripts/
â â âââ seed_big.js â jeu de donnĂ©es de dĂ©monstration
â â âââ sync_mangas_jikan.js â import depuis MyAnimeList
â â âââ sync_covers_mangadex.js â une couverture par tome
â â âââ make_admin.js â promotion d'un compte en administrateur
â
âââ database/
â âââ schema.sql â structure seule
â âââ seed.sql â donnĂ©es de dĂ©monstration
â âââ migrations/ â Ă©volutions du schĂ©ma
â
âââ docs/ â documentation et captures d'Ă©cran
âââ .env.example â copier en .env
fetch natif)git clone https://github.com/Abdoulrazack1/Kinka.git
cd Kinka
npm install
Dans MySQL :
CREATE DATABASE kinka_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
Puis importer le schéma :
mysql -u root -p kinka_db < kinka_db.sql
cp .env.example .env
Ăditer .env avec vos valeurs :
PORT=3000
DB_HOST=127.0.0.1
DB_PORT=3306
DB_NAME=kinka_db
DB_USER=root
DB_PASS=ton_mot_de_passe
# Générer une clé robuste avec :
# node -e "console.log(require('crypto').randomBytes(64).toString('base64'))"
JWT_SECRET=remplacer_par_une_cle_aleatoire_longue
JWT_EXPIRES_IN=7d
CLIENT_URL=http://127.0.0.1:5503
npm run seed:big # Jeu de données de démonstration
node server/scripts/create_demo_user.js # Compte démo : demo@kinka.fr / demo1234
(Optionnel : npm run sync pour ajouter ~100 mangas depuis MyAnimeList via Jikan)
npm start # Production
npm run dev # Développement (rechargement auto avec nodemon)
Vérifier :
GET http://localhost:3000/api/health
â { "success": true, "message": "Kinka API en ligne đ", "version": "2.0.0" }
Ouvrir le projet dans VS Code et clic-droit sur page_accueil.html â Open with Live Server.
Le front sera servi sur http://127.0.0.1:5503.
Démarrage :
npm run devsert Ă la fois lâAPI et le site sur http://localhost:3000 â front et API sur la mĂȘme origine, sans configuration dâhĂŽte virtuel. Le site reste servable par Apache : le.htaccessĂ la racine redirige versclient/et bloque lâaccĂšs web Ăserver/,database/etdocs/.
AprĂšs node server/scripts/create_demo_user.js :
| Mot de passe | Plan | |
|---|---|---|
| demo@kinka.fr | demo1234 | Premium |
| Méthode | Route | Auth | Description |
|---|---|---|---|
| GET | /api/health | â | VĂ©rification du serveur |
| POST | /api/auth/register | â | Inscription |
| POST | /api/auth/login | â | Connexion |
| POST | /api/auth/forgot | â | Demande de rĂ©initialisation |
| POST | /api/auth/reset | â | Enregistrer le nouveau mot de passe |
| GET | /api/auth/me | â | Profil courant |
| PUT | /api/auth/me | â | Modifier le profil |
| PUT | /api/auth/password | â | Changer le mot de passe |
| DELETE | /api/auth/me | â | Supprimer le compte |
| GET | /api/produits | â | Liste filtrĂ©e + paginĂ©e |
| GET | /api/produits/search?q= | â | Recherche (titre, sĂ©rie, auteurâŠ) |
| GET | /api/produits/:id | â | DĂ©tail dâun produit |
| GET | /api/editeurs | â | Maisons dâĂ©dition + nb de titres |
| GET | /api/editeurs/:slug | â | Fiche dâune maison dâĂ©dition |
| GET | /api/panier | â | Mon panier |
| POST | /api/panier | â | Ajouter un article |
| PUT | /api/panier/:id | â | Modifier la quantitĂ© |
| DELETE | /api/panier/:id | â | Retirer un article |
| DELETE | /api/panier | â | Vider le panier |
| GET | /api/favoris | â | Mes favoris |
| POST | /api/favoris | â | Ajouter un favori |
| DELETE | /api/favoris/:id | â | Retirer un favori |
| DELETE | /api/favoris | â | Vider les favoris |
| GET | /api/commandes | â | Historique commandes |
| GET | /api/commandes/:id | â | DĂ©tail dâune commande |
| POST | /api/commandes | â | Passer une commande (transaction) |
| GET | /api/annonces | â | Liste des annonces (occasion) |
| GET | /api/annonces/mes-annonces | â | Mes annonces publiĂ©es |
| GET | /api/annonces/:id | â | DĂ©tail dâune annonce |
| POST | /api/annonces | â | Publier une annonce |
| PUT | /api/annonces/:id | â | Modifier une annonce |
| DELETE | /api/annonces/:id | â | Supprimer une annonce |
| GET | /api/avis?produit_id= | â | Avis dâun produit |
| POST | /api/avis | â | Publier ou modifier un avis |
| DELETE | /api/avis/:produit_id | â | Supprimer mon avis |
| GET | /api/mangas/search?q= | â | Recherche Jikan (sans BDD) |
| GET | /api/mangas/:mal_id | â | Fiche Jikan par identifiant MAL |
| POST | /api/mangas/sync | đ | Sync mangas populaires |
| POST | /api/mangas/sync-one | đ | Importer un manga par mal_id |
| POST | /api/newsletter | â | Inscription newsletter |
| POST | /api/contact | â | Envoyer un message de contact |
LĂ©gende : â public · â connectĂ© · đ administrateur
Toutes ces routes exigent un compte administrateur.
| Méthode | Route | Description |
|---|---|---|
| GET | /api/admin/stats | Chiffres du tableau de bord |
| GET | /api/admin/produits | Catalogue (recherche, filtres) |
| POST | /api/admin/produits | Créer une fiche produit |
| PUT | /api/admin/produits/:id | Modifier une fiche produit |
| DELETE | /api/admin/produits/:id | Supprimer une fiche produit |
| GET | /api/admin/commandes | Toutes les commandes |
| GET | /api/admin/commandes/:id | Détail + historique des statuts |
| PATCH | /api/admin/commandes/:id | Statut, transporteur, numéro de suivi |
| GET | /api/admin/avis | File de modération des avis |
| PATCH | /api/admin/avis/:id | Publier ou retirer un avis |
| DELETE | /api/admin/avis/:id | Supprimer un avis |
| GET | /api/admin/annonces | Annonces entre membres |
| PATCH | /api/admin/annonces/:id | Changer le statut dâune annonce |
| DELETE | /api/admin/annonces/:id | Supprimer une annonce |
| GET | /api/admin/contact | Messages reçus |
| PATCH | /api/admin/contact/:id | Marquer traité / rouvrir |
| DELETE | /api/admin/contact/:id | Supprimer un message (spam) |
| GET | /api/admin/newsletter | Inscrits Ă la newsletter |
| GET | /api/admin/newsletter/export | Export CSV des inscrits |
| GET | /api/admin/utilisateurs | Comptes clients |
| PATCH | /api/admin/utilisateurs/:id | Changer le rĂŽle dâun compte |
| GET | /api/admin/logs | Journal des actions dâadministration |
Toutes les réponses suivent le format :
{ "success": true, "data": { ⊠} }
{ "success": false, "error": "Message" }
{ "success": false, "errors": { "champ": "Message" } }
Les routes protégées attendent un header :
Authorization: Bearer <token_jwt>
Toute page HTML inclut les scripts dans cet ordre (gérés par auth-guard et panier hybride) :
<script src="../assets/js/kinka-api-client.js"></script>
<script src="../assets/js/kinka-auth-guard.js"></script>
<script src="../assets/js/kinka-cards.js"></script>
<script src="../assets/js/authentification.js"></script>
<script src="../assets/js/panier.js"></script>
<script src="../assets/js/favoris.js"></script>
<script src="../assets/js/darkmode.js"></script>
<script src="../assets/js/recherche.js"></script>
<script src="../assets/js/translate.js"></script>
Exemples dâutilisation :
// Connexion (avec « se souvenir de moi »)
const user = await KinkaAPI.auth.login('demo@kinka.fr', 'demo1234', true);
// Catalogue filtré
const mangas = await KinkaAPI.produits.getAll({ categorie: 'ShĂŽnen', promo: '1' });
// Panier
await KinkaAPI.panier.add('one-piece-105', 1);
// Toast (XSS-safe, défini globalement)
showToast('Ajouté au panier !', 'success');
Pour pointer vers une autre URL dâAPI, dĂ©finir avant le chargement du client :
<script>window.KINKA_API_URL = 'https://api.kinka.fr/api';</script>
<script src="../assets/js/kinka-api-client.js"></script>
/api, 10 req/15 min sur /api/auth,
600/min sur les couvertures. Le limiteur ne couvre volontairement pas les pages
et les assets : un affichage demande une trentaine de fichiers, et un plafond
global rendait le site inaccessible au bout de trois pages visitĂ©es.kinkaFaiblesseMotDePasse) nâest quâun confort, le serveur tranche.site_web) sur lâinscription et le contact :
invisible et hors du parcours clavier, il fait refuser toute requĂȘte qui le
remplit. Le message de contact suspect reçoit une réponse de succÚs ordinaire
mais nâest pas enregistrĂ©.middleware/validate.js)_esc, _e)CLIENT_URLkinka-auth-guard.js) :
redirige vers la connexion en mémorisant la destination. Le panier et la page
Premium restent ouverts aux visiteurs ; la connexion nâest exigĂ©e quâau paiement.Les deux thĂšmes respectent le niveau WCAG AA (contraste 4,5:1 pour le texte courant, 3:1 pour les grands titres), vĂ©rifiĂ© par mesure sur 16 pages Ă 2 thĂšmes.
Le rose de marque #e03b8b ne satisfait ce seuil dans aucun de ses deux rĂŽles :
4,14:1 en texte sur fond sombre, 4,06:1 en aplat sous du texte blanc. Aucune
valeur unique ne convient â assombrir amĂ©liore lâaplat et dĂ©grade le texte.
Deux déclinaisons sont donc appliquées, générées depuis les feuilles du projet :
| RĂŽle | Couleur | Ratio |
|---|---|---|
| le rose est le texte | #ef5a9f (sombre) / #c62372 (clair) |
5,31:1 / 4,90:1 |
| le rose porte du texte blanc | #d12d7d |
4,79:1 |
Le jeton --pink lui-mĂȘme est inchangĂ© : bordures, icĂŽnes et usages dĂ©coratifs
gardent la teinte dâorigine. Les surcharges vivent dans darkmode.css et
contraste-clair.css.
La mise en page ne déborde plus horizontalement de 320 à 1280 px (18 pages
vĂ©rifiĂ©es). La cause Ă©tait presque toujours la mĂȘme : un Ă©lĂ©ment de grille garde
min-width: auto et impose la largeur minimale de son contenu Ă sa piste.
npm run lint # ESLint + contrĂŽle des scripts inline
npm run lint:inline # uniquement le JS écrit dans les pages HTML
npm run format # Prettier
lint:inline existe parce quâESLint ne lit que les fichiers .js : les ~1600
lignes de JavaScript écrites directement dans les <script> des pages
nâĂ©taient vĂ©rifiĂ©es par rien. Câest prĂ©cisĂ©ment lĂ que se trouvaient les deux
pannes les plus visibles de lâaudit (une IIFE et un setTimeout non refermĂ©s,
qui rendaient muet tout le script de la page Contact et de la page Paiement).
Le script échoue avec un code de sortie non nul, il est donc utilisable en CI.
JWT_SECRET par une clĂ© forte gĂ©nĂ©rĂ©e alĂ©atoirement (64 octets)CLIENT_URL Ă votre domaine de production (au lieu de *)SameSite=Lax ; passer en Strict + Secure derriĂšre HTTPS)/auth/forgot (le code actuel logge en console)categorie, promo, nouveaute, bestseller, commandes.user_id)Abdoulrazack Abdillahi Mahamoud â abdoul.abdillahi@gmail.com
Projet réalisé dans le cadre de la formation Développeur Web et Web Mobile (DWWM).