From 1b53e04b5d184a04e12c466fde8d858cdcfb582d Mon Sep 17 00:00:00 2001 From: ordinarthur <@arthurbarre.js@gmail.com> Date: Sat, 4 Apr 2026 11:41:34 +0200 Subject: [PATCH] feat: switch to SSR for live Sanity updates Migrate from SSG to SSR with @astrojs/node adapter so Sanity CMS changes are reflected immediately without rebuild. Separate ports for Astro SSR (4321) and Fastify API (3000) in production. Co-Authored-By: Claude Opus 4.6 --- CLAUDE.md | 44 ++++++---- astro.config.mjs | 4 +- nginx.conf | 30 +++---- package.json | 2 + pnpm-lock.yaml | 133 ++++++++++++++++++++++++++++++ server.mjs | 2 +- src/lib/sanity.mjs | 2 +- src/pages/collection/[slug].astro | 33 ++++---- 8 files changed, 191 insertions(+), 59 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index ba9c8cb..b9f15d4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -37,7 +37,7 @@ rebours/ | Couche | Techno | |--------|--------| -| Front (SSG) | Astro + HTML/CSS/JS vanilla + GSAP | +| Front (SSR) | Astro + HTML/CSS/JS vanilla + GSAP | | CMS | Sanity (headless, hébergé) | | API | Fastify (Node.js) | | Paiement | Stripe Checkout (price_data inline) | @@ -65,20 +65,21 @@ STRIPE_SECRET_KEY=sk_test_... STRIPE_WEBHOOK_SECRET=whsec_... DOMAIN=http://localhost:4321 -PORT=8888 +FASTIFY_PORT=3000 # Port Fastify API (prod) +ASTRO_PORT=4321 # Port Astro SSR (prod) ``` ### Lancer le projet ```bash -npm install -npm run dev +pnpm install +pnpm dev ``` Cela lance en parallèle (via `concurrently`) : - `astro dev` sur http://localhost:4321 -- `node --watch server.mjs` (mode dev, PORT=8888) +- `node --watch server.mjs` (mode dev) -Le proxy Vite dans `astro.config.mjs` redirige `/api/*` vers `http://127.0.0.1:8888`. +Le proxy Vite dans `astro.config.mjs` redirige `/api/*` vers le serveur Fastify. ### Sanity Studio ```bash @@ -90,8 +91,8 @@ Accessible sur http://localhost:3333 ### Build ```bash -npm run build -# Génère ./dist/ (fichiers statiques Astro) +pnpm build +# Génère ./dist/ (serveur Astro SSR + assets client) ``` --- @@ -116,8 +117,7 @@ Champs principaux : 1. Ouvrir Sanity Studio 2. Créer un nouveau document "Produit" 3. Remplir les champs, uploader l'image -4. Publier -5. Rebuild le site : `npm run build` + déployer +4. Publier → visible immédiatement sur le site (SSR, pas de rebuild nécessaire) ### Images Les images sont servies via le CDN Sanity avec transformations automatiques. @@ -154,12 +154,19 @@ Quand un client clique "Commander" : ### Serveur : ordinarthur@10.10.0.13 -### Architecture prod +### Architecture prod (SSR) ``` -Internet -> Nginx (port 80) -> /var/www/html/rebours/dist/ (statiques) - -> /api/* -> proxy -> Fastify :3000 +Internet -> Nginx (port 80) -> / -> proxy -> Astro SSR :4321 + -> /_astro/* -> fichiers statiques (dist/client/) + -> /api/* -> proxy -> Fastify :3000 ``` +### Services systemd +| Service | Port | Rôle | +|---------|------|------| +| `rebours-ssr` | 4321 | Astro SSR (pages dynamiques) | +| `rebours` | 3000 | Fastify API (Stripe, checkout) | + ### Variables d'environnement en prod ```env SANITY_PROJECT_ID=... @@ -167,14 +174,15 @@ SANITY_DATASET=production STRIPE_SECRET_KEY=sk_live_... STRIPE_WEBHOOK_SECRET=whsec_... DOMAIN=https://rebours.studio -PORT=3000 +FASTIFY_PORT=3000 +ASTRO_PORT=4321 ``` ### Déploiement ```bash -npm run build +pnpm build 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/" +ssh ordinarthur@10.10.0.13 "sudo rm -rf /var/www/html/rebours/dist && sudo mkdir -p /var/www/html/rebours/dist && sudo cp -r /tmp/rebours-dist/* /var/www/html/rebours/dist/ && sudo chown -R ordinarthur:ordinarthur /var/www/html/rebours/dist && sudo systemctl restart rebours-ssr" ``` Si server.mjs a changé : @@ -189,8 +197,8 @@ ssh ordinarthur@10.10.0.13 "sudo cp /tmp/server.mjs /var/www/html/rebours/server | URL | Comportement | |-----|-------------| -| `/` | Page principale Astro (SSG) | -| `/collection/{slug}` | Page produit (SSG), auto-open panel via `window.__OPEN_PANEL__` | +| `/` | Page principale Astro (SSR, données Sanity live) | +| `/collection/{slug}` | Page produit (SSR), auto-open panel via `window.__OPEN_PANEL__` | | `/success?session_id=...` | Page de confirmation Stripe | | `/robots.txt` | Généré au build | | `/sitemap.xml` | Généré au build depuis Sanity | diff --git a/astro.config.mjs b/astro.config.mjs index ccd7b93..53aba44 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -1,8 +1,10 @@ // @ts-check import { defineConfig } from 'astro/config'; +import node from '@astrojs/node'; export default defineConfig({ - output: 'static', + output: 'server', + adapter: node({ mode: 'standalone' }), outDir: './dist', server: { port: 4321 }, vite: { diff --git a/nginx.conf b/nginx.conf index faf17a0..e19113f 100644 --- a/nginx.conf +++ b/nginx.conf @@ -2,10 +2,7 @@ server { listen 80; server_name rebours.studio; - root /var/www/html/rebours/dist; - index index.html; - - # ── API proxy → Fastify ────────────────────────────────────────────────── + # ── API proxy → Fastify ────────────────���───────────────────────────────── location /api/ { proxy_pass http://127.0.0.1:3000; proxy_set_header Host $host; @@ -14,28 +11,23 @@ server { proxy_set_header X-Forwarded-Proto https; } - # ── Cache : Astro hashed → immutable ───────────────────────────────────── + # ── Static assets from Astro client build ──────────────────���───────────── location /_astro/ { + root /var/www/html/rebours/dist/client; add_header Cache-Control "public, max-age=31536000, immutable"; } - # ── Cache : CSS/JS sans hash → revalidation ───────────────────────────── - location ~* \.(css|js)$ { - add_header Cache-Control "no-cache"; - } - - # ── Cache : assets → 7 jours ──────────────────────────────────────────── - location ~* \.(jpg|jpeg|png|gif|webp|svg|woff2|woff|ttf|ico|mp3)$ { + location ~* \.(css|js|jpg|jpeg|png|gif|webp|svg|woff2|woff|ttf|ico|mp3)$ { + root /var/www/html/rebours/dist/client; add_header Cache-Control "public, max-age=604800"; } - # ── HTML : jamais caché ────────────────────────────────────────────────── - location ~* \.html$ { - add_header Cache-Control "no-store"; - } - - # ── SPA fallback ───────────────────────────────────────────────────────── + # ── SSR → Astro Node server ─────────────────────���──────────────────────── location / { - try_files $uri $uri/ $uri.html /index.html; + proxy_pass http://127.0.0.1:4321; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto https; } } diff --git a/package.json b/package.json index dbca50e..5e5aba4 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,11 @@ "build": "astro build", "preview": "astro preview", "server": "NODE_ENV=production node server.mjs", + "start": "NODE_ENV=production node dist/server/entry.mjs", "astro": "astro" }, "dependencies": { + "@astrojs/node": "^9.5.5", "@fastify/cors": "^10.0.2", "@sanity/client": "^7", "@sanity/image-url": "^1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c1b4e01..b5d90e1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + '@astrojs/node': + specifier: ^9.5.5 + version: 9.5.5(astro@5.18.1(@types/node@25.4.0)(jiti@2.6.1)(rollup@4.59.0)(typescript@5.9.3)) '@fastify/cors': specifier: ^10.0.2 version: 10.1.0 @@ -47,6 +50,11 @@ packages: '@astrojs/markdown-remark@6.3.11': resolution: {integrity: sha512-hcaxX/5aC6lQgHeGh1i+aauvSwIT6cfyFjKWvExYSxUhZZBBdvCliOtu06gbQyhbe0pGJNoNmqNlQZ5zYUuIyQ==} + '@astrojs/node@9.5.5': + resolution: {integrity: sha512-rtU2BGU5u3SfGURpANfMxVzCIoR86MkaN05ncza9rbtuMKJ/XnRJt/BbyVknDbOJ71hoci0SIsJwKcJR8vvi/A==} + peerDependencies: + astro: ^5.17.3 + '@astrojs/prism@3.3.0': resolution: {integrity: sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ==} engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} @@ -989,6 +997,10 @@ packages: defu@6.1.4: resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -1038,12 +1050,19 @@ packages: resolution: {integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==} engines: {node: '>=4'} + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -1069,6 +1088,9 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + escape-string-regexp@5.0.0: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} @@ -1079,6 +1101,10 @@ packages: estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + event-source-polyfill@1.0.31: resolution: {integrity: sha512-4IJSItgS/41IxN5UVAVuAyczwZF7ZIEsM1XAoUzIHA6A+xzusEZUutdXz2Nr+MQPLxfTiCvqE79/C8HT8fKFvA==} @@ -1149,6 +1175,10 @@ packages: resolution: {integrity: sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==} engines: {node: '>=20'} + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1218,6 +1248,10 @@ packages: http-cache-semantics@4.2.0: resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} + http-errors@2.0.1: + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} + import-meta-resolve@4.2.0: resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} @@ -1423,6 +1457,14 @@ packages: micromark@4.0.2: resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@3.0.2: + resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} + engines: {node: '>=18'} + mimic-response@3.1.0: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} @@ -1475,6 +1517,10 @@ packages: resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} engines: {node: '>=14.0.0'} + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + oniguruma-parser@0.12.1: resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==} @@ -1553,6 +1599,10 @@ packages: radix3@1.1.2: resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} @@ -1663,9 +1713,19 @@ packages: engines: {node: '>=10'} hasBin: true + send@1.2.1: + resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} + engines: {node: '>= 18'} + + server-destroy@1.0.1: + resolution: {integrity: sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==} + set-cookie-parser@2.7.2: resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + sharp@0.34.5: resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -1698,6 +1758,10 @@ packages: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -1764,6 +1828,10 @@ packages: resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==} engines: {node: '>=12'} + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + tree-kill@1.2.2: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true @@ -2061,6 +2129,15 @@ snapshots: transitivePeerDependencies: - supports-color + '@astrojs/node@9.5.5(astro@5.18.1(@types/node@25.4.0)(jiti@2.6.1)(rollup@4.59.0)(typescript@5.9.3))': + dependencies: + '@astrojs/internal-helpers': 0.7.6 + astro: 5.18.1(@types/node@25.4.0)(jiti@2.6.1)(rollup@4.59.0)(typescript@5.9.3) + send: 1.2.1 + server-destroy: 1.0.1 + transitivePeerDependencies: + - supports-color + '@astrojs/prism@3.3.0': dependencies: prismjs: 1.30.0 @@ -2826,6 +2903,8 @@ snapshots: defu@6.1.4: {} + depd@2.0.0: {} + dequal@2.0.3: {} destr@2.0.5: {} @@ -2869,10 +2948,14 @@ snapshots: dset@3.1.4: {} + ee-first@1.1.1: {} + emoji-regex@10.6.0: {} emoji-regex@8.0.0: {} + encodeurl@2.0.0: {} + entities@4.5.0: {} entities@6.0.1: {} @@ -2939,6 +3022,8 @@ snapshots: escalade@3.2.0: {} + escape-html@1.0.3: {} + escape-string-regexp@5.0.0: {} estree-walker@2.0.2: {} @@ -2947,6 +3032,8 @@ snapshots: dependencies: '@types/estree': 1.0.8 + etag@1.8.1: {} + event-source-polyfill@1.0.31: {} eventemitter3@5.0.4: {} @@ -3020,6 +3107,8 @@ snapshots: dependencies: tiny-inflate: 1.0.3 + fresh@2.0.0: {} + fsevents@2.3.3: optional: true @@ -3149,6 +3238,14 @@ snapshots: http-cache-semantics@4.2.0: {} + http-errors@2.0.1: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.2 + toidentifier: 1.0.1 + import-meta-resolve@4.2.0: {} inherits@2.0.4: {} @@ -3525,6 +3622,12 @@ snapshots: transitivePeerDependencies: - supports-color + mime-db@1.54.0: {} + + mime-types@3.0.2: + dependencies: + mime-db: 1.54.0 + mimic-response@3.1.0: {} mnemonist@0.40.0: @@ -3565,6 +3668,10 @@ snapshots: on-exit-leak-free@2.1.2: {} + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + oniguruma-parser@0.12.1: {} oniguruma-to-es@4.3.4: @@ -3650,6 +3757,8 @@ snapshots: radix3@1.1.2: {} + range-parser@1.2.1: {} + readable-stream@3.6.2: dependencies: inherits: 2.0.4 @@ -3820,8 +3929,28 @@ snapshots: semver@7.7.4: {} + send@1.2.1: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.1 + mime-types: 3.0.2 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + server-destroy@1.0.1: {} + set-cookie-parser@2.7.2: {} + setprototypeof@1.2.0: {} + sharp@0.34.5: dependencies: '@img/colour': 1.1.0 @@ -3881,6 +4010,8 @@ snapshots: split2@4.2.0: {} + statuses@2.0.2: {} + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -3951,6 +4082,8 @@ snapshots: toad-cache@3.7.0: {} + toidentifier@1.0.1: {} + tree-kill@1.2.2: {} trim-lines@3.0.1: {} diff --git a/server.mjs b/server.mjs index be8a6bd..4fd5522 100644 --- a/server.mjs +++ b/server.mjs @@ -131,7 +131,7 @@ app.get('/api/session/:id', async (request) => { // ── Start ─────────────────────────────────────────────────────────────────── try { - await app.listen({ port: process.env.PORT ?? 3000, host: '0.0.0.0' }) + await app.listen({ port: process.env.FASTIFY_PORT ?? process.env.PORT ?? 3000, host: '0.0.0.0' }) } catch (err) { app.log.error(err) process.exit(1) diff --git a/src/lib/sanity.mjs b/src/lib/sanity.mjs index fa04d2f..ee4a0bf 100644 --- a/src/lib/sanity.mjs +++ b/src/lib/sanity.mjs @@ -5,7 +5,7 @@ export const sanity = createClient({ projectId: import.meta.env.SANITY_PROJECT_ID, dataset: import.meta.env.SANITY_DATASET || 'production', apiVersion: '2024-01-01', - useCdn: true, + useCdn: false, }) const builder = imageUrlBuilder(sanity) diff --git a/src/pages/collection/[slug].astro b/src/pages/collection/[slug].astro index f5e14b3..5f97675 100644 --- a/src/pages/collection/[slug].astro +++ b/src/pages/collection/[slug].astro @@ -1,26 +1,21 @@ --- import Base from '../../layouts/Base.astro'; -import { getPublishedProducts, urlFor } from '../../lib/sanity.mjs'; +import { getPublishedProducts, getProductBySlug, urlFor } from '../../lib/sanity.mjs'; -export async function getStaticPaths() { - const products = await getPublishedProducts(); +const { slug } = Astro.params; +const product = await getProductBySlug(slug); - return products.map(p => ({ - params: { slug: p.slug }, - props: { - slug: p.slug, - name: p.name, - title: p.seoTitle || `REBOURS — ${p.productDisplayName} | Collection 001`, - description: p.seoDescription || p.description?.substring(0, 155) || '', - ogImage: p.image ? urlFor(p.image).width(1200).url() : '', - productName: p.productDisplayName, - price: p.price ? String(p.price / 100) : null, - availability: p.availability || 'https://schema.org/PreOrder', - }, - })); +if (!product) { + return Astro.redirect('/'); } -const { slug, title, description, ogImage, name, productName, price, availability } = Astro.props; +const name = product.name; +const title = product.seoTitle || `REBOURS — ${product.productDisplayName} | Collection 001`; +const description = product.seoDescription || product.description?.substring(0, 155) || ''; +const ogImage = product.image ? urlFor(product.image).width(1200).url() : ''; +const productName = product.productDisplayName; +const price = product.price ? String(product.price / 100) : null; +const availability = product.availability || 'https://schema.org/PreOrder'; const allProducts = await getPublishedProducts(); @@ -109,11 +104,11 @@ const schemaBreadcrumb = {


-
+
SPÉCIFICATIONS TECHNIQUES
-
+
NOTES DE CONCEPTION