diff --git a/.env.example b/.env.example index 78a54fc..dfa7f14 100644 --- a/.env.example +++ b/.env.example @@ -6,6 +6,8 @@ PUBLIC_STORYBLOK_TOKEN= # PUBLIC_STORYBLOK_IS_PREVIEW=true # Webhook rebuild — uniquement sur l'instance preview +# Secret partagé avec StoryBlok (Settings > Webhooks > Secret du Webhook) +# STORYBLOK_WEBHOOK_SECRET= # Token OAuth Clever Cloud (généré via clever login puis ~/.config/clever-cloud/clever-tools.json) # CLEVER_TOKEN= # ID de l'application Clever Cloud de production (app_xxxxxxxx) diff --git a/README.md b/README.md index 3965abb..758bf94 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ Le serveur démarre sur le port `8080` par défaut (configurable via `PORT`). |---|---|---| | `PUBLIC_STORYBLOK_TOKEN` | Public Access Token | Preview Access Token | | `PUBLIC_STORYBLOK_IS_PREVIEW` | *(non défini)* | `true` | +| `STORYBLOK_WEBHOOK_SECRET` | - | Secret du webhook StoryBlok | | `CLEVER_TOKEN` | - | Token OAuth Clever Cloud | | `CLEVER_APP_ID_PRODUCTION` | - | ID de l'app production (app_xxx) | | `CC_POST_BUILD_HOOK` | `npm run build` | `npm run build` | diff --git a/src/pages/api/rebuild.ts b/src/pages/api/rebuild.ts index 5f6f770..78de4ea 100644 --- a/src/pages/api/rebuild.ts +++ b/src/pages/api/rebuild.ts @@ -1,11 +1,21 @@ import type { APIRoute } from 'astro'; +import { createHmac, timingSafeEqual } from 'node:crypto'; -export const POST: APIRoute = async () => { +export const POST: APIRoute = async ({ request }) => { + const webhookSecret = import.meta.env.STORYBLOK_WEBHOOK_SECRET; const token = import.meta.env.CLEVER_TOKEN; const appId = import.meta.env.CLEVER_APP_ID_PRODUCTION; - if (!token || !appId) { - return new Response('Missing CLEVER_TOKEN or CLEVER_APP_ID_PRODUCTION', { status: 500 }); + if (!webhookSecret || !token || !appId) { + return new Response('Missing server configuration', { status: 500 }); + } + + const body = await request.text(); + const signature = request.headers.get('webhook-signature') ?? ''; + const expected = createHmac('sha1', webhookSecret).update(body).digest('hex'); + + if (!timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) { + return new Response('Invalid signature', { status: 401 }); } const response = await fetch( @@ -17,8 +27,8 @@ export const POST: APIRoute = async () => { ); if (!response.ok) { - const body = await response.text(); - return new Response(`Clever Cloud API error: ${response.status} ${body}`, { status: 502 }); + const error = await response.text(); + return new Response(`Clever Cloud API error: ${response.status} ${error}`, { status: 502 }); } return new Response('Rebuild triggered', { status: 200 });