diff --git a/src/App.tsx b/src/App.tsx index 465b937..c09d495 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,9 +1,8 @@ -import useSWR from 'swr' import './App.css' -import { sentryFetcher } from './utils' import WATcloudLogo from './assets/watcloud-logo' import { HealthchecksioStatus } from './healthchecksio' import { useState } from 'react' +import { SentryStatus } from './sentry' function App() { // Parse options from the URL @@ -22,10 +21,6 @@ function App() { } const [showInternal, setShowInternal] = useState(globalOptions.showInternal); - const { data: sentryDataRaw, error: sentryError, isLoading: sentryIsLoading } = useSWR('/api/0/organizations/watonomous/monitors/', sentryFetcher, { refreshInterval: 5000 }); - - console.log("==================") - console.log(sentryDataRaw, sentryError, sentryIsLoading); return ( <> @@ -53,8 +48,7 @@ function App() {

Sentry

- {sentryIsLoading &&

Loading...

} - {sentryError &&

Error: {sentryError.message}. Is your adblocker blocking the request?

} +
) diff --git a/src/constants.ts b/src/constants.ts index ee21d7b..3370ee0 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -13,8 +13,14 @@ export const HEALTHCHECKSIO_API_KEY = 'tCsst0GSKpfvslmpmlsmivRrUCRuv6Iv' // https://github.com/getsentry/sentry/issues/15310#issuecomment-650443556 export const SENTRY_API_KEY = '53ab13b4337a937bc66eec3a4b3f836b14dd51890fde15fd51b9523eb598382c' +export enum Status { + Good = 'good', + Neutral = 'neutral', + Bad = 'bad', +} + export const STATUS_SYMBOLS = { - good: '🟒', - neutral: '🟑', - bad: 'πŸ”΄', + [Status.Good]: '🟒', + [Status.Neutral]: '🟑', + [Status.Bad]: 'πŸ”΄', } as const; diff --git a/src/healthchecksio.tsx b/src/healthchecksio.tsx index f82c233..a548ba6 100644 --- a/src/healthchecksio.tsx +++ b/src/healthchecksio.tsx @@ -1,7 +1,7 @@ import { useState } from "react"; import useSWR from "swr"; import { groupBy, healthchecksioFetcher, removePrefix, removeSuffix, timeSinceShort } from "./utils"; -import { STATUS_SYMBOLS } from "./constants"; +import { Status, STATUS_SYMBOLS } from "./constants"; import { StatusSummary } from "./summary"; import { OptionGroup } from "./option-group"; @@ -45,22 +45,24 @@ function processHCData(data: any): HealthchecksioCheck[] | undefined { }); } -function getStatusSymbol(status: HealthchecksioCheckStatus) { +function getStatus(status: HealthchecksioCheckStatus) { return { - new: STATUS_SYMBOLS.neutral, - up: STATUS_SYMBOLS.good, - grace: STATUS_SYMBOLS.good, - down: STATUS_SYMBOLS.bad, - paused: '⏸️', + new: Status.Neutral, + up: Status.Good, + grace: Status.Good, + down: Status.Bad, + paused: 'paused', }[status]; } -function CheckStatus({ status }: { status: HealthchecksioCheckStatus }) { - return ( - - {getStatusSymbol(status)} - - ); +function getStatusSymbol(status: HealthchecksioCheckStatus) { + return { + new: STATUS_SYMBOLS[Status.Neutral], + up: STATUS_SYMBOLS[Status.Good], + grace: STATUS_SYMBOLS[Status.Good], + down: STATUS_SYMBOLS[Status.Bad], + paused: '⏸️', + }[status]; } const GROUP_KEYS = ['host', 'check'] as const; @@ -78,7 +80,7 @@ export function HealthchecksioStatus({ showInternal, initialGroupKey = "" }: { s const [groupKey, setGroupKey] = useState((GROUP_KEYS.includes(initialGroupKey as any) ? initialGroupKey : GROUP_KEYS[0]) as typeof GROUP_KEYS[number]); const { data: dataRaw, error, isLoading } = useSWR('/api/v3/checks/', healthchecksioFetcher, { refreshInterval: 5000 }); - const checks = (processHCData(dataRaw) || []).filter(check => check.tags_dict.public !== 'False' || showInternal); + const checks = (processHCData(dataRaw) || []).filter(check => check.tags_dict.public !== 'False' || showInternal).sort((a, b) => a.name.localeCompare(b.name)); const groupedChecks = groupBy(checks, c => c.tags_dict[groupKey]); return ( @@ -87,7 +89,7 @@ export function HealthchecksioStatus({ showInternal, initialGroupKey = "" }: { s {error &&

Error: {error.message}

} {checks && checks.length > 0 && ( <> - getStatusSymbol(check.status))} symbolClassName="text-xl" className="mb-4" /> + getStatus(check.status))} symbolClassName="text-xl" className="mb-4" />
Group by: {shortenCheckName(groupKey, group, check.name)} {timeSinceShort(new Date(check.last_ping))} ago - + {getStatusSymbol(check.status)} ))} diff --git a/src/sentry.tsx b/src/sentry.tsx new file mode 100644 index 0000000..28b3212 --- /dev/null +++ b/src/sentry.tsx @@ -0,0 +1,59 @@ +import useSWR from "swr"; +import { sentryFetcher, timeSinceShort } from "./utils"; +import { STATUS_SYMBOLS, Status } from "./constants"; +import { StatusSummary } from "./summary"; + +// The API docs just says "string" lol. We can add to this list if we find more +export type SentryStatus = "ok" | "error"; + +function getStatus(status: SentryStatus) { + return { + ok: Status.Good, + error: Status.Bad, + }[status] || Status.Neutral; +} + +function getStatusSymbol(status: SentryStatus) { + return STATUS_SYMBOLS[getStatus(status)]; +} + +export function SentryStatus() { + const { data: monitors, error, isLoading } = useSWR('/api/0/organizations/watonomous/monitors/', sentryFetcher, { refreshInterval: 5000 }); + + // process data + if (monitors) { + for (const monitor of monitors) { + // Remove checks for the dev environment + monitor.environments = monitor.environments?.filter((environment: any) => environment.name != 'dev').sort((a: any, b: any) => a.name.localeCompare(b.name)); + } + monitors.sort((a: any, b: any) => a.name.localeCompare(b.name)); + } + + return ( +
+ {isLoading &&

Loading...

} + {error &&

Error: {error.message}

} + {monitors && monitors.length > 0 && ( + <> + monitor.environments?.map((environment: any) => getStatus(environment.status)))} symbolClassName="text-xl" className="mb-4" /> +
+ {monitors.map((monitor: any) => ( +
+

{monitor.name}

+
    + {monitor.environments?.map((environment: any) => ( +
  • + {environment.name} {timeSinceShort(new Date(environment.lastCheckIn))} ago + {getStatusSymbol(environment.status)} +
  • + ))} +
+
+ ))} +
+ + )} +
+ ) +} \ No newline at end of file diff --git a/src/summary.tsx b/src/summary.tsx index d5192a0..3f12d3b 100644 --- a/src/summary.tsx +++ b/src/summary.tsx @@ -1,21 +1,21 @@ -import { STATUS_SYMBOLS } from "./constants"; +import { Status, STATUS_SYMBOLS } from "./constants"; import { cn } from "./utils"; -export function StatusSummary({ symbols, className, symbolClassName }: { - symbols: string[], className?: string, symbolClassName?: string +export function StatusSummary({ statuses, className, symbolClassName }: { + statuses: string[], className?: string, symbolClassName?: string }) { - const summary = Object.fromEntries(Object.values(STATUS_SYMBOLS).map(symbol => [symbol, 0])); - for (const symbol of symbols) { - if (symbol in summary) { - summary[symbol] += 1; + const summary = statuses.reduce((acc, status) => { + if (Object.values(Status).includes(status as any)) { + acc[status as Status]++; } - } + return acc; + }, Object.fromEntries(Object.values(Status).map(status => [status, 0])) as Record); return (
- {Object.entries(summary).map(([symbol, count]) => ( -
- {symbol} + {Object.entries(summary).map(([status, count]) => ( +
+ {STATUS_SYMBOLS[status as Status]} {count}
))}