UptimeFlare/worker/src/util.ts

120 lines
3.1 KiB
TypeScript

async function getWorkerLocation() {
const res = await fetch('https://cloudflare.com/cdn-cgi/trace')
const text = await res.text()
const colo = /^colo=(.*)$/m.exec(text)?.[1]
return colo
}
const fetchTimeout = (
url: string,
ms: number,
{ signal, ...options }: RequestInit<RequestInitCfProperties> | undefined = {}
): Promise<Response> => {
const controller = new AbortController()
const promise = fetch(url, { signal: controller.signal, ...options })
if (signal) signal.addEventListener('abort', () => controller.abort())
const timeout = setTimeout(() => controller.abort(), ms)
return promise.finally(() => clearTimeout(timeout))
}
function withTimeout<T>(millis: number, promise: Promise<T>): Promise<T> {
const timeout = new Promise<T>((resolve, reject) =>
setTimeout(() => reject(new Error(`Promise timed out after ${millis}ms`)), millis)
)
return Promise.race([promise, timeout])
}
function formatStatusChangeNotification(
monitor: any,
isUp: boolean,
timeIncidentStart: number,
timeNow: number,
reason: string,
timeZone: string
) {
const dateFormatter = new Intl.DateTimeFormat('en-US', {
month: 'numeric',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
hour12: false,
timeZone: timeZone,
})
let downtimeDuration = Math.round((timeNow - timeIncidentStart) / 60)
const timeNowFormatted = dateFormatter.format(new Date(timeNow * 1000))
const timeIncidentStartFormatted = dateFormatter.format(new Date(timeIncidentStart * 1000))
if (isUp) {
return {
title: `${monitor.name} is up!`,
body: `The service is up again after being down for ${downtimeDuration} minutes.`,
}
} else if (timeNow == timeIncidentStart) {
return {
title: `🔴 ${monitor.name} is currently down.`,
body: `Service is unavailable at ${timeNowFormatted}. Issue: ${reason || 'unspecified'}`,
}
} else {
return {
title: `🔴 ${monitor.name} is still down.`,
body: `Service is unavailable since ${timeIncidentStartFormatted} (${downtimeDuration} minutes). Issue: ${
reason || 'unspecified'
}`,
}
}
}
async function notifyWithApprise(
appriseApiServer: string,
recipientUrl: string,
title: string,
body: string
) {
console.log(
'Sending Apprise notification: ' +
title +
'-' +
body +
' to ' +
recipientUrl +
' via ' +
appriseApiServer
)
try {
const resp = await fetchTimeout(appriseApiServer, 5000, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
urls: recipientUrl,
title,
body,
type: 'warning',
format: 'text',
}),
})
if (!resp.ok) {
console.log(
'Error calling apprise server, code: ' + resp.status + ', response: ' + (await resp.text())
)
} else {
console.log('Apprise notification sent successfully, code: ' + resp.status)
}
} catch (e) {
console.log('Error calling apprise server: ' + e)
}
}
export {
getWorkerLocation,
fetchTimeout,
withTimeout,
notifyWithApprise,
formatStatusChangeNotification,
}