diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..3d23acb --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,241 @@ +# REBOURS — Documentation technique + +## Architecture du projet + +``` +rebours/ +├── src/ +│ ├── layouts/ +│ │ └── Base.astro # Layout HTML commun (SEO, fonts, CSS) +│ └── pages/ +│ ├── index.astro # Page principale (hero, collection, newsletter) +│ ├── collection/ +│ │ └── [slug].astro # Pages produits statiques (SSG) +│ └── success.astro # Page de confirmation Stripe +├── public/ +│ ├── style.css # CSS global +│ ├── main.js # JS client (cursor, grid, panel, routing) +│ ├── robots.txt # SEO +│ ├── sitemap.xml # SEO +│ └── assets/ # Images produits +├── server.mjs # Serveur API Fastify (Stripe) +├── astro.config.mjs # Config Astro (SSG, proxy dev) +├── nginx.conf # Config nginx de référence +└── .env # Variables d'environnement (non versionné) +``` + +## Stack + +| Couche | Techno | +|--------|--------| +| Front (SSG) | Astro + HTML/CSS/JS vanilla | +| API | Fastify (Node.js) | +| Paiement | Stripe Checkout | +| Reverse proxy | Nginx | +| Hébergement | VPS (Debian) | +| Fonts | Space Mono (Google Fonts) | + +--- + +## Développement local + +### Prérequis +- Node.js ≥ 18 +- Un fichier `.env` à la racine (voir `.env.example`) + +### Variables d'environnement (.env) +```env +STRIPE_SECRET_KEY=sk_test_... +STRIPE_WEBHOOK_SECRET=whsec_... +DOMAIN=http://localhost:4321 +PORT=8888 +``` + +> En dev, le serveur Fastify tourne sur le port **8888** (pour ne pas entrer en conflit avec d'autres services). +> Le proxy Vite dans `astro.config.mjs` redirige `/api/*` → `http://127.0.0.1:8888`. + +### Lancer le projet +```bash +npm install +npm run dev +``` + +Cela lance en parallèle (via `concurrently`) : +- `astro dev` → http://localhost:4321 +- `node --watch server.mjs` (mode dev, PORT=8888) + +### Build +```bash +npm run build +# Génère ./dist/ (fichiers statiques Astro) +``` + +--- + +## Production + +### Serveur : ordinarthur@10.10.0.13 + +### Architecture prod +``` +Internet → Nginx (port 80) → /var/www/html/rebours/dist/ (fichiers statiques) + → /api/* → proxy → Fastify :3000 +``` + +### Chemins importants sur le serveur +| Quoi | Où | +|------|----| +| Fichiers web | `/var/www/html/rebours/dist/` | +| Projet complet | `/var/www/html/rebours/` | +| Config nginx | `/etc/nginx/sites-available/rebours` | +| Service systemd | `rebours.service` | +| Logs | `journalctl -u rebours -f` | + +### Variables d'environnement en prod +Le fichier `.env` est dans `/var/www/html/rebours/.env` : +```env +STRIPE_SECRET_KEY=sk_live_... +STRIPE_WEBHOOK_SECRET=whsec_... +DOMAIN=https://rebours.studio +PORT=3000 +``` + +### Service systemd +Le serveur Fastify est géré par systemd : +```bash +sudo systemctl status rebours +sudo systemctl restart rebours +sudo systemctl stop rebours +journalctl -u rebours -f # logs en temps réel +``` + +--- + +## Déploiement (mise à jour du site) + +### 1. Build en local +```bash +npm run build +# Génère ./dist/ +``` + +### 2. Envoyer les fichiers sur le serveur + +Les fichiers statiques Astro : +```bash +scp -r dist/* ordinarthur@10.10.0.13:/tmp/rebours-dist/ +ssh ordinarthur@10.10.0.13 "sudo cp -r /tmp/rebours-dist/* /var/www/html/rebours/dist/" +``` + +Si le server.mjs a changé : +```bash +scp server.mjs ordinarthur@10.10.0.13:/tmp/server.mjs +ssh ordinarthur@10.10.0.13 "sudo cp /tmp/server.mjs /var/www/html/rebours/server.mjs && sudo systemctl restart rebours" +``` + +### 3. Vérifier +```bash +ssh ordinarthur@10.10.0.13 "sudo nginx -t && sudo systemctl status rebours" +``` + +### Permissions (si problème 403 nginx) +```bash +ssh ordinarthur@10.10.0.13 "sudo chown -R www-data:www-data /var/www/html/rebours/dist" +``` + +--- + +## Nginx — référence + +Le fichier `nginx.conf` à la racine du projet est la config de **référence**. +La config réelle sur le serveur est dans `/etc/nginx/sites-available/rebours`. + +Pour mettre à jour la config nginx : +```bash +scp nginx.conf ordinarthur@10.10.0.13:/tmp/nginx-rebours.conf +ssh ordinarthur@10.10.0.13 "sudo cp /tmp/nginx-rebours.conf /etc/nginx/sites-available/rebours && sudo nginx -t && sudo systemctl reload nginx" +``` + +Points clés de la config : +- `root /var/www/html/rebours/dist` → fichiers statiques Astro +- `try_files $uri $uri/ $uri.html /index.html` → SPA fallback pour les routes Astro +- `/api/` → proxy vers Fastify sur `127.0.0.1:3000` +- HTML : `no-store` (jamais caché) +- CSS/JS/assets : `immutable` (hash dans le nom de fichier, cache 1 an) + +--- + +## Routing + +Le routing est hybride : + +| URL | Comportement | +|-----|-------------| +| `/` | Page principale Astro | +| `/collection/lumiere-orbitale` | Page Astro SSG générée statiquement, ouvre le panel auto via `window.__OPEN_PANEL__` | +| `/collection/table-terrazzo` | idem | +| `/collection/module-serie` | idem | +| `/success?session_id=...` | Page de confirmation Stripe | + +Quand on clique sur une carte produit depuis le navigateur (sans refresh) : +- Le panel s'ouvre +- `history.pushState` change l'URL → `/collection/{slug}` +- Le bouton retour du navigateur ferme le panel et revient à `/` + +Quand on arrive directement sur `/collection/{slug}` (lien partagé, refresh) : +- Astro sert la page statique correspondante +- Un script inline lit `` et set `window.__OPEN_PANEL__` +- `main.js` lit `window.__OPEN_PANEL__` au DOMContentLoaded et ouvre le bon panel + +--- + +## Ajouter un produit + +### 1. Ajouter la carte dans `src/pages/index.astro` et `src/pages/collection/[slug].astro` +Copier un `
` existant et modifier les `data-*`. + +### 2. Ajouter le slug dans `[slug].astro` +Dans `getStaticPaths()`, ajouter une entrée dans le tableau `PRODUCTS` : +```js +{ + slug: 'mon-nouveau-produit', + name: 'MON_PRODUIT', + title: 'REBOURS — Mon Produit | Collection 001', + description: 'Description pour le SEO.', + ogImage: 'https://rebours.studio/assets/mon-produit.jpg', +}, +``` + +### 3. Ajouter l'image dans `public/assets/` +Format recommandé : JPG 1024×1024, < 300 Ko. + +### 4. Ajouter le prix Stripe dans `server.mjs` +```js +const PRODUCTS = { + lumiere_orbitale: { price_id: 'price_xxx' }, + mon_nouveau_produit: { price_id: 'price_yyy' }, // ← ajouter +} +``` + +### 5. Rebuild et déployer +```bash +npm run build +# puis déployer (voir section Déploiement) +``` + +--- + +## Stripe + +- **Test** : utiliser les clés `sk_test_...` dans `.env` +- **Prod** : utiliser les clés `sk_live_...` dans `.env` sur le serveur +- La redirection après paiement va vers `${DOMAIN}/success?session_id=...` +- Le webhook Stripe doit pointer vers `https://rebours.studio/api/webhook` +- Le `STRIPE_WEBHOOK_SECRET` correspond au secret généré dans le dashboard Stripe pour ce webhook + +--- + +## Fichiers à ne jamais versionner +- `.env` (clés Stripe, secrets) +- `node_modules/` +- `dist/` (généré par le build) diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..4140d77 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,5 @@ +User-agent: * +Allow: / +Disallow: /success + +Sitemap: https://rebours.studio/sitemap.xml diff --git a/public/sitemap.xml b/public/sitemap.xml new file mode 100644 index 0000000..2c28572 --- /dev/null +++ b/public/sitemap.xml @@ -0,0 +1,27 @@ + + + + https://rebours.studio/ + 2026-02-27 + weekly + 1.0 + + + https://rebours.studio/collection/lumiere-orbitale/ + 2026-02-27 + monthly + 0.9 + + + https://rebours.studio/collection/table-terrazzo/ + 2026-02-27 + monthly + 0.9 + + + https://rebours.studio/collection/module-serie/ + 2026-02-27 + monthly + 0.9 + + diff --git a/src/layouts/Base.astro b/src/layouts/Base.astro index 0b01bf7..0ff28df 100644 --- a/src/layouts/Base.astro +++ b/src/layouts/Base.astro @@ -10,7 +10,7 @@ const { title, description = 'REBOUR Studio crée du mobilier d\'art contemporain inspiré du Space Age et du mouvement Memphis. Pièces uniques fabriquées à Paris. Collection 001 en cours.', ogImage = 'https://rebours.studio/assets/lamp-violet.jpg', - canonical = 'https://rebour.studio/', + canonical = 'https://rebours.studio/', } = Astro.props; --- diff --git a/src/pages/collection/[slug].astro b/src/pages/collection/[slug].astro index 518a5ad..e12d20a 100644 --- a/src/pages/collection/[slug].astro +++ b/src/pages/collection/[slug].astro @@ -9,6 +9,9 @@ export function getStaticPaths() { title: 'REBOURS — Solar Altar | Collection 001', description: 'Lampe de table unique. Béton texturé coulé à la main + dôme céramique laqué. Pièce unique fabriquée à Paris.', ogImage: 'https://rebours.studio/assets/lamp-violet.jpg', + productName: 'Solar Altar', + price: '1800', + availability: 'https://schema.org/LimitedAvailability', }, { slug: 'table-terrazzo', @@ -16,6 +19,9 @@ export function getStaticPaths() { title: 'REBOURS — TABLE TERRAZZO | Collection 001', description: 'Table basse et étagère modulaire. Terrazzo fait main + acier tubulaire. Pièce unique fabriquée à Paris.', ogImage: 'https://rebours.studio/assets/table-terrazzo.jpg', + productName: 'Table Terrazzo', + price: null, + availability: 'https://schema.org/PreOrder', }, { slug: 'module-serie', @@ -23,20 +29,57 @@ export function getStaticPaths() { title: 'REBOURS — MODULE SÉRIE | Collection 001', description: 'Série de 7 lampes béton colorées, dôme laqué et néon. Édition limitée fabriquée à Paris.', ogImage: 'https://rebours.studio/assets/lampes-serie.jpg', + productName: 'Module Série', + price: null, + availability: 'https://schema.org/PreOrder', }, ]; return PRODUCTS.map(p => ({ params: { slug: p.slug }, props: p })); } -const { slug, title, description, ogImage, name } = Astro.props; +const { slug, title, description, ogImage, name, productName, price, availability } = Astro.props; + +const schemaProduct = { + "@context": "https://schema.org", + "@type": "Product", + "name": productName, + "description": description, + "image": ogImage, + "brand": { "@type": "Brand", "name": "REBOURS Studio" }, + "url": `https://rebours.studio/collection/${slug}/`, + ...(price ? { + "offers": { + "@type": "Offer", + "price": price, + "priceCurrency": "EUR", + "availability": availability, + "seller": { "@type": "Organization", "name": "REBOURS Studio" } + } + } : {}), +}; + +const schemaBreadcrumb = { + "@context": "https://schema.org", + "@type": "BreadcrumbList", + "itemListElement": [ + { "@type": "ListItem", "position": 1, "name": "Accueil", "item": "https://rebours.studio/" }, + { "@type": "ListItem", "position": 2, "name": "Collection 001", "item": "https://rebours.studio/#collection" }, + { "@type": "ListItem", "position": 3, "name": productName, "item": `https://rebours.studio/collection/${slug}/` }, + ] +}; --- + +