better callbacks, fix config leak
parent
dfd78b9ca4
commit
9bdeec90f2
|
|
@ -1,6 +1,6 @@
|
|||
import { Container, Group, Text } from '@mantine/core'
|
||||
import classes from '@/styles/Header.module.css'
|
||||
import config from '@/uptime.config'
|
||||
import { pageConfig } from '@/uptime.config'
|
||||
|
||||
export default function Header() {
|
||||
const linkToElement = (link: { label: string; link: string; highlight?: boolean }) => {
|
||||
|
|
@ -38,11 +38,11 @@ export default function Header() {
|
|||
</div>
|
||||
|
||||
<Group gap={5} visibleFrom="sm">
|
||||
{config.page.links.map(linkToElement)}
|
||||
{pageConfig.links.map(linkToElement)}
|
||||
</Group>
|
||||
|
||||
<Group gap={5} hiddenFrom="sm">
|
||||
{config.page.links.filter((link) => (link as any).highlight).map(linkToElement)}
|
||||
{pageConfig.links.filter((link) => (link as any).highlight).map(linkToElement)}
|
||||
</Group>
|
||||
</Container>
|
||||
</header>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { MonitorState, MonitorTarget } from '@/uptime.types'
|
|||
import { Card, Center, Divider } from '@mantine/core'
|
||||
import MonitorDetail from './MonitorDetail'
|
||||
|
||||
export default function MonitorList({ config, state }: { config: any; state: MonitorState }) {
|
||||
export default function MonitorList({ monitors, state }: { monitors: any; state: MonitorState }) {
|
||||
return (
|
||||
<Center>
|
||||
<Card
|
||||
|
|
@ -15,7 +15,7 @@ export default function MonitorList({ config, state }: { config: any; state: Mon
|
|||
withBorder
|
||||
style={{ width: '865px' }}
|
||||
>
|
||||
{config.monitors.map((monitor: MonitorTarget) => (
|
||||
{monitors.map((monitor: MonitorTarget) => (
|
||||
<div key={monitor.id}>
|
||||
<Card.Section ml="xs" mr="xs">
|
||||
<MonitorDetail monitor={monitor} state={state} />
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import Head from 'next/head'
|
||||
|
||||
import { Inter } from 'next/font/google'
|
||||
import { MonitorState } from '@/uptime.types'
|
||||
import { MonitorState, MonitorTarget } from '@/uptime.types'
|
||||
import { KVNamespace } from '@cloudflare/workers-types'
|
||||
import config from '@/uptime.config'
|
||||
import { pageConfig, workerConfig } from '@/uptime.config'
|
||||
import OverallStatus from '@/components/OverallStatus'
|
||||
import Header from '@/components/Header'
|
||||
import MonitorList from '@/components/MonitorList'
|
||||
|
|
@ -12,11 +12,17 @@ import { Center, Divider, Text } from '@mantine/core'
|
|||
export const runtime = 'experimental-edge'
|
||||
const inter = Inter({ subsets: ['latin'] })
|
||||
|
||||
export default function Home({ state }: { state: MonitorState }) {
|
||||
export default function Home({
|
||||
state,
|
||||
monitors,
|
||||
}: {
|
||||
state: MonitorState
|
||||
monitors: MonitorTarget[]
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{config.page.title}</title>
|
||||
<title>{pageConfig.title}</title>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
|
||||
|
|
@ -33,7 +39,7 @@ export default function Home({ state }: { state: MonitorState }) {
|
|||
) : (
|
||||
<div>
|
||||
<OverallStatus state={state} />
|
||||
<MonitorList config={config} state={state} />
|
||||
<MonitorList monitors={monitors} state={state} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
@ -66,5 +72,14 @@ export async function getServerSideProps() {
|
|||
}
|
||||
const state = (await UPTIMEFLARE_STATE?.get('state', 'json')) as unknown as MonitorState
|
||||
|
||||
return { props: { state } }
|
||||
// Only present these values to client
|
||||
const monitors = workerConfig.monitors.map((monitor) => {
|
||||
return {
|
||||
id: monitor.id,
|
||||
name: monitor.name,
|
||||
tooltip: monitor.tooltip,
|
||||
}
|
||||
})
|
||||
|
||||
return { props: { state, monitors } }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,26 +1,15 @@
|
|||
const config = {
|
||||
// Your locale for server-side callback. (client-side will always follow browser settings)
|
||||
dateLocale: 'zh-CN',
|
||||
// Your timezone for server-side callback. (client-side will always follow browser settings)
|
||||
timezone: 'Asia/Shanghai',
|
||||
page: {
|
||||
// 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 },
|
||||
],
|
||||
},
|
||||
callback: async (statusChangeMsg: string) => {
|
||||
// Write any typescript here
|
||||
// Example `statusChangeMsg` string:
|
||||
// "❌My Blog went down at 2023/11/18 14:08:59 with error Timeout after 10000ms"
|
||||
// "✔️My Blog came back up at 2023/11/18 14:10:48 after 2 minutes of downtime"
|
||||
// Example:
|
||||
// await fetch('https://api.example.com/callback?msg=' + statusChangeMsg)
|
||||
},
|
||||
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 = {
|
||||
// Define all your monitors here
|
||||
monitors: [
|
||||
// Example HTTP Monitor
|
||||
|
|
@ -50,7 +39,7 @@ const config = {
|
|||
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'
|
||||
checkLocationWorkerRoute: 'https://xxx.example.com',
|
||||
},
|
||||
// Example TCP Monitor
|
||||
{
|
||||
|
|
@ -64,7 +53,43 @@ const config = {
|
|||
timeout: 5000,
|
||||
},
|
||||
],
|
||||
callbacks: {
|
||||
onStatusChange: async (
|
||||
id: string,
|
||||
name: string,
|
||||
isUp: boolean,
|
||||
timeIncidentStart: number,
|
||||
timeNow: number,
|
||||
reason: string
|
||||
) => {
|
||||
// This callback will be called when any monitor's status changed
|
||||
// Write any Typescript code here
|
||||
// Example implementation:
|
||||
// const timeString = new Date(timeNow * 1000).toLocaleString('zh-CN', {
|
||||
// timeZone: 'Asia/Shanghai',
|
||||
// })
|
||||
// let statusChangeMsg
|
||||
// if (isUp) {
|
||||
// statusChangeMsg = `✔️${name} came back up at ${timeString} after ${Math.round(
|
||||
// (timeNow - timeIncidentStart) / 60
|
||||
// )} minutes of downtime`
|
||||
// } else {
|
||||
// statusChangeMsg = `❌${name} was down at ${timeString} with error ${reason}`
|
||||
// }
|
||||
// await fetch('https://api.example.com/callback?msg=' + statusChangeMsg)
|
||||
},
|
||||
onIncident: async (
|
||||
id: string,
|
||||
name: string,
|
||||
timeIncidentStart: number,
|
||||
timeNow: number,
|
||||
currentError: string
|
||||
) => {
|
||||
// This callback will be called EVERY 2 MINTUES if there's an on-going incident for any monitor
|
||||
// Write any Typescript code here
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Don't forget this, otherwise compilation fails.
|
||||
export default config
|
||||
export { pageConfig, workerConfig }
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import config from '../../uptime.config'
|
||||
import { workerConfig } from '../../uptime.config'
|
||||
import { getWorkerLocation } from './util'
|
||||
import { MonitorState } from '../../uptime.types'
|
||||
import { getStatus } from './monitor'
|
||||
|
|
@ -17,7 +17,7 @@ export default {
|
|||
}
|
||||
|
||||
const targetId = (await request.json<{ target: string }>())['target']
|
||||
const target = config.monitors.find((m) => m.id === targetId)
|
||||
const target = workerConfig.monitors.find((m) => m.id === targetId)
|
||||
|
||||
if (target === undefined) {
|
||||
return new Response('Target Not Found', { status: 404 })
|
||||
|
|
@ -59,9 +59,8 @@ export default {
|
|||
state.overallUp = 0
|
||||
|
||||
// Check each monitor
|
||||
// TODO: callback exception handler
|
||||
// TODO: concurrent status check
|
||||
for (const monitor of config.monitors) {
|
||||
for (const monitor of workerConfig.monitors) {
|
||||
console.log(`[${workerLocation}] Checking ${monitor.name}...`)
|
||||
|
||||
let checkLocation = workerLocation
|
||||
|
|
@ -97,7 +96,6 @@ export default {
|
|||
status.up ? state.overallUp++ : state.overallDown++
|
||||
|
||||
// Update incidents
|
||||
|
||||
// Create a dummy incident to store the start time of the monitoring and simplify logic
|
||||
state.incident[monitor.id] = state.incident[monitor.id] || [
|
||||
{
|
||||
|
|
@ -108,20 +106,26 @@ export default {
|
|||
]
|
||||
// Then lastIncident here must not be undefined
|
||||
const lastIncident = state.incident[monitor.id].slice(-1)[0]
|
||||
const timeString = new Date().toLocaleString(config.dateLocale, {
|
||||
timeZone: config.timezone,
|
||||
})
|
||||
|
||||
if (status.up) {
|
||||
// Current status is up
|
||||
// close existing incident if any
|
||||
if (lastIncident.end === undefined) {
|
||||
lastIncident.end = currentTimeSecond
|
||||
await config.callback(
|
||||
`✔️${monitor.name} came back up at ${timeString} after ${Math.round(
|
||||
(lastIncident.end - lastIncident.start[0]) / 60
|
||||
)} minutes of downtime`
|
||||
)
|
||||
|
||||
try {
|
||||
await workerConfig.callbacks.onStatusChange(
|
||||
monitor.id,
|
||||
monitor.name,
|
||||
true,
|
||||
lastIncident.start[0],
|
||||
currentTimeSecond,
|
||||
'OK'
|
||||
)
|
||||
} catch (e) {
|
||||
console.log('Error calling callback: ')
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Current status is down
|
||||
|
|
@ -132,9 +136,20 @@ export default {
|
|||
end: undefined,
|
||||
error: [status.err],
|
||||
})
|
||||
await config.callback(
|
||||
`❌${monitor.name} went down at ${timeString} with error ${status.err}`
|
||||
)
|
||||
|
||||
try {
|
||||
await workerConfig.callbacks.onStatusChange(
|
||||
monitor.id,
|
||||
monitor.name,
|
||||
false,
|
||||
currentTimeSecond,
|
||||
currentTimeSecond,
|
||||
status.err
|
||||
)
|
||||
} catch (e) {
|
||||
console.log('Error calling callback: ')
|
||||
console.log(e)
|
||||
}
|
||||
} else if (
|
||||
lastIncident.end === undefined &&
|
||||
lastIncident.error.slice(-1)[0] !== status.err
|
||||
|
|
@ -142,9 +157,33 @@ export default {
|
|||
// append if the error message changes
|
||||
lastIncident.start.push(currentTimeSecond)
|
||||
lastIncident.error.push(status.err)
|
||||
await config.callback(
|
||||
`❌${monitor.name} is still down at ${timeString} with error ${status.err}`
|
||||
|
||||
try {
|
||||
await workerConfig.callbacks.onStatusChange(
|
||||
monitor.id,
|
||||
monitor.name,
|
||||
false,
|
||||
lastIncident.start[0],
|
||||
currentTimeSecond,
|
||||
status.err
|
||||
)
|
||||
} catch (e) {
|
||||
console.log('Error calling callback: ')
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await workerConfig.callbacks.onIncident(
|
||||
monitor.id,
|
||||
monitor.name,
|
||||
lastIncident.start[0],
|
||||
currentTimeSecond,
|
||||
status.err
|
||||
)
|
||||
} catch (e) {
|
||||
console.log('Error calling callback: ')
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue