Last active
June 8, 2024 13:44
-
-
Save borispoehland/2738d2edd62e83f332260a80eb5a9335 to your computer and use it in GitHub Desktop.
Vercel Webhook to call to pause the project after the spending limit is reached
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import crypto from 'crypto' | |
const { INTEGRATION_SECRET, VERCEL_TEAM_ID, VERCEL_TOKEN } = process.env | |
function sha1(data: Buffer, secret: string): string { | |
return crypto.createHmac('sha1', secret).update(data).digest('hex') | |
} | |
export async function POST(request: Request) { | |
if (typeof INTEGRATION_SECRET != 'string') { | |
throw new Error('No integration secret found') | |
} | |
const rawBody = await request.text() | |
const rawBodyBuffer = Buffer.from(rawBody, 'utf-8') | |
const bodySignature = sha1(rawBodyBuffer, INTEGRATION_SECRET) | |
if (bodySignature !== request.headers.get('x-vercel-signature')) { | |
return Response.json({ | |
code: 'invalid_signature', | |
error: "signature didn't match", | |
}) | |
} | |
const { projects } = (await fetch( | |
`https://api.vercel.com/v9/projects?teamId=${VERCEL_TEAM_ID}`, | |
{ | |
headers: { | |
Authorization: `Bearer ${VERCEL_TOKEN}`, | |
}, | |
} | |
).then((res) => res.json())) as { projects: { id: string }[] } | |
let isSuccess = true | |
for (const project of projects) { | |
const pauseProject = await fetch( | |
`https://api.vercel.com/v1/projects/${project.id}/pause?teamId=${VERCEL_TEAM_ID}`, | |
{ | |
headers: { | |
Authorization: `Bearer ${VERCEL_TOKEN}`, | |
}, | |
method: 'POST', | |
} | |
) | |
if (!pauseProject.ok) { | |
// send me a backup notification to my email | |
console.error(pauseProject.statusText) | |
isSuccess = false | |
} | |
} | |
return Response.json({ code: 'paused_all', data: isSuccess }) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// /api/project/pause/route.ts | |
import crypto from 'crypto' | |
const { INTEGRATION_SECRET, VERCEL_PROJECT_ID, VERCEL_TEAM_ID, VERCEL_TOKEN } = | |
process.env | |
function sha1(data: Buffer, secret: string): string { | |
return crypto.createHmac('sha1', secret).update(data).digest('hex') | |
} | |
export async function POST(request: Request) { | |
if (typeof INTEGRATION_SECRET != 'string') { | |
throw new Error('No integration secret found') | |
} | |
const rawBody = await request.text() | |
const rawBodyBuffer = Buffer.from(rawBody, 'utf-8') | |
const bodySignature = sha1(rawBodyBuffer, INTEGRATION_SECRET) | |
if (bodySignature !== request.headers.get('x-vercel-signature')) { | |
return Response.json({ | |
code: 'invalid_signature', | |
error: "signature didn't match", | |
}) | |
} | |
const pauseProject = await fetch( | |
`https://api.vercel.com/v1/projects/${VERCEL_PROJECT_ID}/pause?teamId=${VERCEL_TEAM_ID}`, | |
{ | |
headers: { | |
Authorization: `Bearer ${VERCEL_TOKEN}`, | |
'Content-Type': 'application/json', | |
}, | |
method: 'POST', | |
} | |
) | |
if (!pauseProject.ok) { | |
// send me a backup notification to my email | |
console.error(pauseProject.statusText) | |
} | |
return Response.json({ code: 'paused', data: pauseProject.ok }) | |
} |
Thanks for this - was paranoid after the twitter drama of runaway spend and found this right away :)
Thank you! Do you know if there is any "easy" way to test it works?
Nice, thanks! Used this to write a small repo that can be directly deployed separately on Vercel.
Do we still need to manually pause/resume projects or is it automatically handled now??
I'm told its automatic now by some YouTuber.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Create a new POST API route with above code
Set the following 2 environment variables in your project:
If you use the
pause_all
snippet, all projects of yourVERCEL_TEAM_ID
will be paused. If you want to hardcode the project to pause only a single project, use thepause_single
snippet and additionally setNow that your project is paused, you can resume it at any time from the dashboard. Note that this is a hard switch, if you don't want to pause the project you can run more graceful logic