UptimeFlare/uptime.config.ts

202 lines
6.4 KiB
TypeScript

const pageConfig = {
// Title for your status page
title: "lyc8503's Status Page",
// Links shown at the header of your status page, could set `highlight` to `true`
links: [
{ link: 'https://github.com/lyc8503', label: 'GitHub' },
{ link: 'https://blog.lyc8503.site/', label: 'Blog' },
{ link: 'mailto:me@lyc8503.site', label: 'Email Me', highlight: true },
],
}
const workerConfig = {
// Write KV at most every 3 minutes unless the status changed.
kvWriteCooldownMinutes: 3,
// Define all your monitors here
monitors: [
// Example HTTP Monitor
{
// `id` should be unique, history will be kept if the `id` remains constant
id: 'foo_monitor',
// `name` is used at status page and callback message
name: 'My API Monitor',
// `url` for clickable link at status page
url: 'https://example.com',
// `method` should be a valid HTTP Method
method: 'POST',
// `target` is a valid URL
target: 'https://example.com',
// [OPTIONAL] `tooltip` is only used at status page to show a tooltip
tooltip: 'This is a tooltip for this monitor',
// [OPTIONAL] `expectedCodes` is an array of acceptable HTTP response codes, if not specified, default to 2xx
expectedCodes: [200],
// [OPTIONAL] `timeout` in millisecond, if not specified, default to 10000
timeout: 10000,
// [OPTIONAL] headers to be sent
headers: {
'User-Agent': 'Uptimeflare',
Authorization: 'Bearer YOUR_TOKEN_HERE',
},
// [OPTIONAL] body to be sent
body: 'Hello, world!',
// [OPTIONAL] if specified, the response must contains the keyword to be considered as operational.
responseKeyword: 'success',
// [OPTIONAL] if specified, the check will run in your specified region,
// refer to docs https://github.com/lyc8503/UptimeFlare/wiki/Geo-specific-checks-setup before setting this value
checkLocationWorkerRoute: 'https://xxx.example.com',
},
// Example TCP Monitor
{
id: 'test_tcp_monitor',
name: 'Example TCP Monitor',
// `method` should be `TCP_PING` for tcp monitors
method: 'TCP_PING',
// `target` should be `host:port` for tcp monitors
target: '1.2.3.4:22',
tooltip: 'My production server SSH',
timeout: 5000,
},
],
callbacks: {
onStatusChange: async (
env: Env,
monitor: any,
isUp: boolean,
timeIncidentStart: number,
timeNow: number,
reason: string
) => {
// This callback will be called when there's a status change for any monitor
// Write any Typescript code here
// By default, this sends Bark and Telegram notification on every status change if you setup Env correctly.
await notify(env, monitor, isUp, timeIncidentStart, timeNow, reason)
},
onIncident: async (
env: Env,
monitor: any,
isUp: boolean,
timeIncidentStart: number,
timeNow: number,
reason: string
) => {
// This callback will be called EVERY 1 MINTUE if there's an on-going incident for any monitor
// Write any Typescript code here
},
},
}
const escapeMarkdown = (text: string) => {
return text.replace(/[_*[\](){}~`>#+\-=|.!\\]/g, '\\$&');
};
async function notify(
env: Env,
monitor: any,
isUp: boolean,
timeIncidentStart: number,
timeNow: number,
reason: string,
) {
const dateFormatter = new Intl.DateTimeFormat('en-US', {
month: 'numeric',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
hour12: false,
timeZone: 'Asia/Shanghai',
});
let downtimeDuration = Math.round((timeNow - timeIncidentStart) / 60);
const timeIncidentStartFormatted = dateFormatter.format(new Date(timeIncidentStart * 1000));
let statusText = isUp
? `The service is up again after being down for ${downtimeDuration} minutes.`
: `Service became unavailable at ${timeIncidentStartFormatted}. Issue: ${reason || 'unspecified'}`;
console.log('Notifying: ', monitor.name, statusText);
if (env.BARK_SERVER && env.BARK_DEVICE_KEY) {
try {
let title = isUp ? `${monitor.name} is up again!` : `🔴 ${monitor.name} is currently down.`;
await sendBarkNotification(env, monitor, title, statusText);
} catch (error) {
console.error('Error sending Bark notification:', error);
}
}
if (env.SECRET_TELEGRAM_CHAT_ID && env.SECRET_TELEGRAM_API_TOKEN) {
try {
let operationalLabel = isUp ? 'Up' : 'Down';
let statusEmoji = isUp ? '✅' : '🔴';
let telegramText = `*${escapeMarkdown(
monitor.name,
)}* is currently *${operationalLabel}*\n${statusEmoji} ${escapeMarkdown(statusText)}`;
await notifyTelegram(env, monitor, isUp, telegramText);
} catch (error) {
console.error('Error sending Telegram notification:', error);
}
}
}
export async function notifyTelegram(env: Env, monitor: any, operational: boolean, text: string) {
const chatId = env.SECRET_TELEGRAM_CHAT_ID;
const apiToken = env.SECRET_TELEGRAM_API_TOKEN;
const payload = new URLSearchParams({
chat_id: chatId,
parse_mode: 'MarkdownV2',
text: text,
});
try {
const response = await fetch(`https://api.telegram.org/bot${apiToken}/sendMessage`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: payload.toString(),
});
if (!response.ok) {
console.error(
`Failed to send Telegram notification "${text}", ${response.status} ${response.statusText
} ${await response.text()}`,
);
}
} catch (error) {
console.error('Error sending Telegram notification:', error);
}
}
async function sendBarkNotification(env: Env, monitor: any, title: string, body: string, group: string = '') {
const barkServer = env.BARK_SERVER;
const barkDeviceKey = env.BARK_DEVICE_KEY;
const barkUrl = `${barkServer}/push`;
const data = {
title: title,
body: body,
group: group,
url: monitor.url,
device_key: barkDeviceKey,
};
const response = await fetch(barkUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
if (response.ok) {
console.log('Bark notification sent successfully.');
} else {
const respText = await response.text();
console.error('Failed to send Bark notification:', response.status, response.statusText, respText);
}
}
// Don't forget this, otherwise compilation fails.
export { pageConfig, workerConfig }