Created
February 27, 2024 09:51
-
-
Save princefishthrower/4517ff44a9f4c5b2d205343feabe08b8 to your computer and use it in GitHub Desktop.
Netlify serverless function that sends a warning email / SMS if the bandwidth is close to the specified limits.
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
// This Netlify serverless function will send a warning email / SMS if the bandwidth is close to the specified limits. | |
// Many environment variables need to be set in order for this function to work: | |
// - MJ_API_KEY (Mailjet API key) | |
// - MJ_API_SECRET (Mailjet API secret) | |
// - SINCH_SERVICE_PLAN_ID (Sinch service plan ID) | |
// - SINCH_API_TOKEN (Sinch API token) | |
// - SINCH_NUMBER (Sinch phone number) | |
// - SINCH_TO_NUMBER (Sinch phone number to send the SMS to) | |
// - NETLIFY_ACCESS_TOKEN (Netlify access token) - generate one at https://app.netlify.com/sites/{your-site-name}/settings/general#access-control | |
// - NETLIFY_API_BANDWIDTH_URL (Netlify API bandwidth URL) - should have this pattern: https://api.netlify.com/access-control/bb-api/api/v1/accounts{your-account-id}/bandwidth | |
// It's recommended to set this up as an hourly scheduled function in your netlify.toml file: | |
// # ./netlify.toml | |
// # Hourly warn bandwidth function | |
// [functions."warn-bandwidth"] | |
// schedule = "@hourly" | |
import { Handler } from '@netlify/functions' | |
import Mailjet from 'node-mailjet' | |
// Note: Warning thresholds must be defined in ascending order! | |
const warningThresholds = [25, 50, 75, 99] | |
// The threshold for sending an SMS (greater than or equal to) | |
const smsThreshold = 75 | |
const sendWarningEmail = async (threshold: number, percentage: number, periodEndDate: string) => { | |
const mailjet = Mailjet.apiConnect(process.env.MJ_APIKEY_PUBLIC || '', process.env.MJ_APIKEY_PRIVATE || '') | |
try { | |
await mailjet.post('send', { version: 'v3.1' }).request({ | |
Messages: [ | |
{ | |
From: { | |
Email: 'yourtoemail@email.com', | |
Name: 'From Name' | |
}, | |
To: [ | |
{ | |
Email: 'yourtoemail@email.com', | |
Name: 'To Name' | |
} | |
], | |
Subject: `WARNING: Your Netlify bandwidth has exceeded ${threshold}%!`, | |
TextPart: `WARNING: Your Netlify bandwidth usage has exceeded ${threshold} and is currently at ${percentage}%! Your bandwidth allocation renews on ${periodEndDate}.`, | |
HTMLPart: `WARNING: Your Netlify bandwidth usage has exceeded ${threshold} and is currently at ${percentage}%! Your bandwidth allocation renews on ${periodEndDate}.` | |
} | |
] | |
}) | |
console.log(`Warning email successfully sent for exceeding ${threshold}% threshold.`) | |
} catch (err: any) { | |
console.log('Error sending warning email', err.statusCode) | |
} | |
} | |
const sendSMS = async (threshold: number, percentage: number, periodEndDate: string) => { | |
try { | |
const resp = await fetch('https://us.sms.api.sinch.com/xms/v1/' + process.env.SINCH_SERVICE_PLAN_ID + '/batches', { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json', | |
Authorization: 'Bearer ' + process.env.SINCH_API_TOKEN | |
}, | |
body: JSON.stringify({ | |
from: process.env.SINCH_NUMBER, | |
to: [process.env.SINCH_TO_NUMBER], | |
body: `WARNING: Your Netlify bandwidth usage has exceeded ${threshold} and is currently at ${percentage}%! Your bandwidth allocation renews on ${periodEndDate}.` | |
}) | |
}) | |
const data = await resp.json() | |
console.log(data) | |
} catch (err: any) { | |
console.log('Error sending SMS', err) | |
} | |
} | |
const warnIfBandwidthExceeded = async () => { | |
const netlifyAuth = `Bearer ${process.env.NETLIFY_ACCESS_TOKEN}` | |
const bandwidthUrl = `${process.env.NETLIFY_API_BANDWIDTH_URL}` | |
const response = await fetch(bandwidthUrl, { | |
method: 'GET', | |
headers: { | |
'User-Agent': 'YourApp (youremail@email.com)', | |
Authorization: netlifyAuth | |
} | |
}) | |
const data = await response.json() | |
if (data.used && data.included && data.period_end_date) { | |
const used = data.used | |
const included = data.included | |
const percentage = Math.round((used / included) * 100) | |
const periodEndDate = new Date(data.period_end_date).toLocaleDateString() | |
for (const threshold of warningThresholds) { | |
if (percentage > threshold) { | |
sendWarningEmail(threshold, percentage, periodEndDate) | |
if (smsThreshold >= 75) { | |
sendSMS(threshold, percentage, periodEndDate) | |
} | |
return | |
} | |
} | |
} | |
} | |
const handler: Handler = async () => { | |
try { | |
await warnIfBandwidthExceeded() | |
return { | |
statusCode: 200, | |
body: 'OK' | |
} | |
} catch (error) { | |
console.log(error) | |
return { | |
statusCode: 500, | |
body: 'Unknown error' | |
} | |
} | |
} | |
export { handler } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment