From 2acff525e9266daaf90c954918e9b964cd347cb1 Mon Sep 17 00:00:00 2001 From: lyc8503 Date: Sun, 27 Apr 2025 01:12:13 +0800 Subject: [PATCH] alert config/style changes, worker code improve --- components/DetailBar.tsx | 2 +- components/DetailChart.tsx | 2 +- components/MaintenanceAlert.tsx | 63 +++++++++++++------------------ components/MonitorDetail.tsx | 6 +-- components/MonitorList.tsx | 2 +- components/OverallStatus.tsx | 31 +++++++++------- pages/api/data.ts | 2 +- pages/index.tsx | 2 +- types/config.ts | 66 ++++++++++++++++++++++----------- types/uptime.types.ts | 37 ------------------ worker/src/index.ts | 29 +++++++++------ 11 files changed, 113 insertions(+), 129 deletions(-) delete mode 100644 types/uptime.types.ts diff --git a/components/DetailBar.tsx b/components/DetailBar.tsx index f33d730..d336fcc 100644 --- a/components/DetailBar.tsx +++ b/components/DetailBar.tsx @@ -1,4 +1,4 @@ -import { MonitorState, MonitorTarget } from '@/types/uptime.types' +import { MonitorState, MonitorTarget } from '@/types/config' import { getColor } from '@/util/color' import { Box, Tooltip, Modal } from '@mantine/core' import { useResizeObserver } from '@mantine/hooks' diff --git a/components/DetailChart.tsx b/components/DetailChart.tsx index 531f7af..76a6536 100644 --- a/components/DetailChart.tsx +++ b/components/DetailChart.tsx @@ -11,7 +11,7 @@ import { TimeScale, } from 'chart.js' import 'chartjs-adapter-moment' -import { MonitorState, MonitorTarget } from '@/types/uptime.types' +import { MonitorState, MonitorTarget } from '@/types/config' import { iataToCountry } from '@/util/iata' ChartJS.register( diff --git a/components/MaintenanceAlert.tsx b/components/MaintenanceAlert.tsx index 1beaf95..2c514e3 100644 --- a/components/MaintenanceAlert.tsx +++ b/components/MaintenanceAlert.tsx @@ -1,55 +1,42 @@ import { Alert, List, Text } from '@mantine/core' import { IconAlertTriangle } from '@tabler/icons-react' -import { Maintenances, Monitor } from '@/types/config' -import { pageConfig } from '@/uptime.config' +import { MaintenanceConfig, MonitorTarget } from '@/types/config' export default function MaintenanceAlert({ maintenance, - groupedMonitor, + style, }: { - maintenance: Omit & { monitors: Monitor[] | undefined } - groupedMonitor: boolean + maintenance: Omit & { monitors: MonitorTarget[] } + style?: React.CSSProperties }) { return ( } - title={maintenance.title} - color={maintenance.color || 'primary'} - mt="md" - ml="md" - mr="md" + title={maintenance.title || 'Scheduled Maintenance'} + color={maintenance.color || 'yellow'} withCloseButton={false} - style={{ maxWidth: groupedMonitor ? '897px' : '865px', position: 'relative' }} + style={{ position: 'relative', margin: '16px auto 0 auto', ...style }} > {/* Date range in top right */} - {(maintenance.start || maintenance.end) && ( -
- {maintenance.start && ( - <> - From: {maintenance.start.toLocaleString()} -
- - )} - {maintenance.end && ( - <> - To: {maintenance.end.toLocaleString()} - - )} -
- )} +
+ From: {new Date(maintenance.start).toLocaleString()} +
+ To:{' '} + {maintenance.end ? new Date(maintenance.end).toLocaleString() : 'Until further notice'} +
- {maintenance.body && {maintenance.body}} - {maintenance.monitors && maintenance.monitors.length > 0 && ( + {maintenance.body} + {maintenance.monitors.length > 0 && ( <> Affected components: diff --git a/components/MonitorDetail.tsx b/components/MonitorDetail.tsx index 26957e0..66f95c6 100644 --- a/components/MonitorDetail.tsx +++ b/components/MonitorDetail.tsx @@ -1,5 +1,5 @@ import { Text, Tooltip } from '@mantine/core' -import { MonitorState, MonitorTarget } from '@/types/uptime.types' +import { MonitorState, MonitorTarget } from '@/types/config' import { IconAlertCircle, IconAlertTriangle, IconCircleCheck } from '@tabler/icons-react' import DetailChart from './DetailChart' import DetailBar from './DetailBar' @@ -39,8 +39,8 @@ export default function MonitorDetail({ // Hide real status icon if monitor is in maintenance const now = new Date() - const hasMaintenance = (maintenances || []) - .filter((m) => (!m.start && !m.end) || (m.start && m.end && now >= m.start && now <= m.end)) + const hasMaintenance = maintenances + .filter((m) => now >= new Date(m.start) && (!m.end || now <= new Date(m.end))) .find((maintenance) => maintenance.monitors?.includes(monitor.id)) if (hasMaintenance) statusIcon = ( diff --git a/components/MonitorList.tsx b/components/MonitorList.tsx index 3710496..f1f9d2f 100644 --- a/components/MonitorList.tsx +++ b/components/MonitorList.tsx @@ -1,4 +1,4 @@ -import { MonitorState, MonitorTarget } from '@/types/uptime.types' +import { MonitorState, MonitorTarget } from '@/types/config' import { Accordion, Card, Center, Text } from '@mantine/core' import MonitorDetail from './MonitorDetail' import { pageConfig } from '@/uptime.config' diff --git a/components/OverallStatus.tsx b/components/OverallStatus.tsx index d30174e..2c7d5d0 100644 --- a/components/OverallStatus.tsx +++ b/components/OverallStatus.tsx @@ -1,4 +1,4 @@ -import { Maintenances, Monitor } from '@/types/config' +import { MaintenanceConfig, MonitorTarget } from '@/types/config' import { Center, Container, Title } from '@mantine/core' import { IconCircleCheck, IconAlertCircle } from '@tabler/icons-react' import { useEffect, useState } from 'react' @@ -21,8 +21,8 @@ export default function OverallStatus({ monitors, }: { state: { overallUp: number; overallDown: number; lastUpdate: number } - maintenances: Maintenances[] - monitors: Monitor[] + maintenances: MaintenanceConfig[] + monitors: MonitorTarget[] }) { let group = pageConfig.group let groupedMonitor = (group && Object.keys(group).length > 0) || false @@ -58,16 +58,15 @@ export default function OverallStatus({ }) const now = new Date() - let filteredMaintenances: (Omit & { - monitors: Monitor[] | undefined - })[] = (maintenances || []) - .filter((m) => (!m.start && !m.end) || (m.start && m.end && now >= m.start && now <= m.end)) - .map((maintenance) => ({ - ...maintenance, - monitors: maintenance.monitors?.map( - (monitorId) => monitors.find((mon) => monitorId === mon.id)! - ), - })) + let filteredMaintenances: (Omit & { monitors: MonitorTarget[] })[] = + maintenances + .filter((m) => now >= new Date(m.start) && (!m.end || now <= new Date(m.end))) + .map((maintenance) => ({ + ...maintenance, + monitors: maintenance.monitors?.map( + (monitorId) => monitors.find((mon) => monitorId === mon.id)! + ), + })) return ( @@ -83,7 +82,11 @@ export default function OverallStatus({ {filteredMaintenances.map((maintenance, idx) => ( - + ))} ) diff --git a/pages/api/data.ts b/pages/api/data.ts index b2d29b5..ba52661 100644 --- a/pages/api/data.ts +++ b/pages/api/data.ts @@ -1,5 +1,5 @@ import { workerConfig } from '@/uptime.config' -import { MonitorState } from '@/types/uptime.types' +import { MonitorState } from '@/types/config' import { NextRequest } from 'next/server' export const runtime = 'edge' diff --git a/pages/index.tsx b/pages/index.tsx index c0133f5..384d204 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -1,7 +1,7 @@ import Head from 'next/head' import { Inter } from 'next/font/google' -import { MonitorState, MonitorTarget } from '@/types/uptime.types' +import { MonitorState, MonitorTarget } from '@/types/config' import { KVNamespace } from '@cloudflare/workers-types' import { maintenances, pageConfig, workerConfig } from '@/uptime.config' import OverallStatus from '@/components/OverallStatus' diff --git a/types/config.ts b/types/config.ts index 9893628..53fa176 100644 --- a/types/config.ts +++ b/types/config.ts @@ -4,18 +4,12 @@ export type PageConfig = { group?: PageConfigGroup } -export type Maintenances = { - // title to display - title: string - // array of monitor ids - monitors?: string[] - // body message - body?: string - // start date - start?: Date - // end Date - end?: Date - // display color +export type MaintenanceConfig = { + monitors: string[] + title?: string + body: string + start: number | string + end?: number | string color?: string } @@ -27,17 +21,10 @@ export type PageConfigLink = { highlight?: boolean } -export type WorkerConfig = { - kvWriteCooldownMinutes: number - monitors: Monitor[] - notification?: Notification - callbacks?: Callbacks -} - -export type Monitor = { +export type MonitorTarget = { id: string name: string - method: 'GET' | 'POST' | 'PATCH' | 'DELETE' | 'TCP_PING' + method: string target: string tooltip?: string statusPageLink?: string @@ -52,6 +39,13 @@ export type Monitor = { checkProxyFallback?: boolean } +export type WorkerConfig = { + kvWriteCooldownMinutes: number + monitors: MonitorTarget[] + notification?: Notification + callbacks?: Callbacks +} + export type Notification = { appriseApiServer?: string recipientUrl?: string @@ -77,3 +71,33 @@ export type Callbacks = { reason: string ) => Promise } + +export type MonitorState = { + lastUpdate: number + overallUp: number + overallDown: number + incident: Record< + string, + { + start: number[] + end: number | undefined // undefined if it's still open + error: string[] + }[] + > + + latency: Record< + string, + { + recent: { + loc: string + ping: number + time: number + }[] // recent 12 hour data, 2 min interval + all: { + loc: string + ping: number + time: number + }[] // all data in 90 days, 1 hour interval + } + > +} diff --git a/types/uptime.types.ts b/types/uptime.types.ts deleted file mode 100644 index dcb2594..0000000 --- a/types/uptime.types.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Monitor } from './config' - -type MonitorState = { - lastUpdate: number - overallUp: number - overallDown: number - incident: Record< - string, - { - start: number[] - end: number | undefined // undefined if it's still open - error: string[] - }[] - > - - latency: Record< - string, - { - recent: { - loc: string - ping: number - time: number - }[] // recent 12 hour data, 2 min interval - all: { - loc: string - ping: number - time: number - }[] // all data in 90 days, 1 hour interval - } - > -} - -type MonitorTarget = Monitor & { - body?: BodyInit -} - -export type { MonitorState, MonitorTarget } diff --git a/worker/src/index.ts b/worker/src/index.ts index 25d9b58..204f16b 100644 --- a/worker/src/index.ts +++ b/worker/src/index.ts @@ -1,6 +1,6 @@ import { workerConfig, maintenances } from '../../uptime.config' import { formatStatusChangeNotification, getWorkerLocation, notifyWithApprise } from './util' -import { MonitorState, MonitorTarget } from '../../types/uptime.types' +import { MonitorState, MonitorTarget } from '../../types/config' import { getStatus } from './monitor' import { DurableObject } from 'cloudflare:workers' @@ -22,23 +22,30 @@ const Worker = { timeNow: number, reason: string ) => { - const skipList: Set = new Set(workerConfig.notification?.skipNotificationIds) - const now = new Date() - // build list of ids that are currently in maintenance (based on start & end OR if they are missing) and merge with the skipList setting - maintenances - .filter((m) => (!m.start && !m.end) || (m.start && m.end && now >= m.start && now <= m.end)) - .map((e) => e.monitors || []) - .flat() - .map((monitor) => skipList.add(monitor)) - // Skip notification if monitor is in the skip list - if (skipList && skipList.has(monitor.id)) { + const skipList = workerConfig.notification?.skipNotificationIds + if (skipList && skipList.includes(monitor.id)) { console.log( `Skipping notification for ${monitor.name} (${monitor.id} in skipNotificationIds)` ) return } + // Skip notification if monitor is in maintenance + const maintenanceList = maintenances + .filter( + (m) => + new Date(timeNow * 1000) >= new Date(m.start) && + (!m.end || new Date(timeNow * 1000) <= new Date(m.end)) + ) + .map((e) => e.monitors) + .flat() + + if (maintenanceList.includes(monitor.id)) { + console.log(`Skipping notification for ${monitor.name} (in maintenance)`) + return + } + if (workerConfig.notification?.appriseApiServer && workerConfig.notification?.recipientUrl) { const notification = formatStatusChangeNotification( monitor,