Skip to content

Commit

Permalink
✨ feat: support remote csv monitors
Browse files Browse the repository at this point in the history
closes #2
  • Loading branch information
yunsii committed Mar 21, 2024
1 parent fad95d1 commit 82e1ae3
Show file tree
Hide file tree
Showing 10 changed files with 132 additions and 17 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Monitor your websites, showcase status including daily history, and get Slack no
## Features

- 🦄 Written in TypeScript
- ✨ Support remote csv monitors
- 🚀 No max monitors limit, even with workers KV free tier
- 💎 More DX/UX detail you want

Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@floating-ui/react": "^0.26.9",
"@vitejs/plugin-react": "^4.2.1",
"node-fetch": "^2.6.9",
"papaparse": "^5.4.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-streaming": "^0.3.9",
Expand All @@ -40,6 +41,7 @@
"@types/express": "^4.17.21",
"@types/node": "^20.11.25",
"@types/node-fetch": "^2.6.11",
"@types/papaparse": "^5.3.14",
"@types/react": "^18.2.63",
"@types/react-dom": "^18.2.19",
"autoprefixer": "^10.4.18",
Expand Down
16 changes: 16 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,5 @@ export const config: Config = {
url: 'https://github.com/',
},
],
monitorsCsvUrl: 'https://docs.google.com/spreadsheets/d/e/2PACX-1vSnewwW9OuXgtuutyYSfFJ_AZdI-UpkUjP2wWi-zZWM3MKa8IzBceWCe9qB_-Lmk-S7mSFgqKVnokam/pub?gid=0&single=true&output=csv',
}
29 changes: 29 additions & 0 deletions src/helpers/monitors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import Papa from 'papaparse'

import type { Monitor } from '#src/types'

import { config } from '#src/config'

export default async function getRemoteMonitors() {
if (!config.monitorsCsvUrl) {
return []
}
const response = await fetch(config.monitorsCsvUrl, {
cf: {
cacheTtlByStatus: {
// Cache 10 minutes
'200-299': 600,
'404': 1,
'500-599': 0,
},
},
})
const csvString = await response.text()
const parsedResult = Papa.parse(csvString, { header: true, transform: (value, field) => {
if (field === 'followRedirect') {
return !!value
}
return value
} })
return parsedResult.data as Monitor[]
}
5 changes: 3 additions & 2 deletions src/pages/index/+Page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { usePageContext } from '#src/renderer/usePageContext'
import { config } from '#src/config'

export default function Page() {
const { data } = usePageContext<IndexPageData>()
const { data: { allMonitors, kvData } } = usePageContext<IndexPageData>()
const inputRef = useRef<HTMLInputElement>(null)
const [inputFocused, setInputFocused] = useState(false)
const [searchValue, setSearchValue] = useState('')
Expand Down Expand Up @@ -67,7 +67,8 @@ export default function Page() {
</header>
<main>
<MonitorPanel
data={data}
allMonitors={allMonitors}
data={kvData}
className={cls`mt-4`}
search={searchValue}
/>
Expand Down
48 changes: 43 additions & 5 deletions src/pages/index/+data.ts

Large diffs are not rendered by default.

21 changes: 15 additions & 6 deletions src/pages/index/components/MonitorPanel/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { cls } from 'tagged-classnames-free'

import type { DataV1 } from '#src/worker/_helpers/store'
import type { Monitor } from '#src/types'

import { config } from '#src/config'
import { getHistoryDates } from '#src/worker/_helpers/datetime'
Expand All @@ -9,15 +10,23 @@ import { Tooltip, TooltipContent, TooltipTrigger } from '#src/components/Tooltip
import { getChecksItemRenderStatus, getTargetDateChecksItem } from '#src/helpers/checks'

export interface IMonitorPanelProps extends React.HTMLAttributes<HTMLDivElement> {
allMonitors: Monitor[]
data?: DataV1 | null
search?: string
}

const MonitorPanel: React.FC<IMonitorPanelProps> = (props) => {
const { data, search, ...restDivProps } = props
const { allMonitors, data, search, ...restDivProps } = props

if (!data || !data.monitorHistoryData || Object.keys(data).length === 0) {
return <div>No Data</div>
return (
<div>
No Data (
{allMonitors.length}
{' '}
monitor(s))
</div>
)
}

const monitorIds = (Object.keys(data.monitorHistoryData) || [])
Expand All @@ -36,7 +45,7 @@ const MonitorPanel: React.FC<IMonitorPanelProps> = (props) => {
{allOperational ? 'All Systems Operational' : 'Not All Systems Operational'}
</div>
{!!data.lastUpdate && (
<div className='text-xs font-light' title={new Date(data.lastUpdate.time).toLocaleString()}>
<div className='text-xs font-light' suppressHydrationWarning title={new Date(data.lastUpdate.time).toLocaleString()}>
checked
{' '}
{Math.round((Date.now() - data.lastUpdate.time) / 1000)}
Expand All @@ -54,7 +63,7 @@ const MonitorPanel: React.FC<IMonitorPanelProps> = (props) => {
`}
>
{monitorIds.filter((item) => {
const targetMonitor = config.monitors.find((monitorItem) => monitorItem.id === item)
const targetMonitor = allMonitors.find((monitorItem) => monitorItem.id === item)
const title = targetMonitor?.name || item
const keyword = search?.trim().toLowerCase()
if (!keyword) {
Expand All @@ -66,8 +75,8 @@ const MonitorPanel: React.FC<IMonitorPanelProps> = (props) => {
return searchFields.filter(Boolean).some((item) => item!.includes(keyword))
}).map((item) => {
const monitorData = data.monitorHistoryData![item]
const monitorConfig = config.monitors.find((monitorItem) => monitorItem.id === item)
const targetMonitor = config.monitors.find((monitorItem) => monitorItem.id === item)
const monitorConfig = allMonitors.find((monitorItem) => monitorItem.id === item)
const targetMonitor = allMonitors.find((monitorItem) => monitorItem.id === item)
const title = targetMonitor?.name || item

const lastCheckInfo = [{
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export interface Config {
settings: Settings
monitors: Monitor[]
monitorsCsvUrl?: string
}

export interface Monitor {
Expand Down
25 changes: 21 additions & 4 deletions src/worker/_helpers/store.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { config } from '#src/config'
import getRemoteMonitors from '#src/helpers/monitors'
import { ensureWorkerEnv } from '#src/worker/_helpers'

export const WORKER_SUBREQUESTS_LIMIT = 50
Expand Down Expand Up @@ -76,19 +77,35 @@ export async function getStore() {
return { kvData }
}

export async function getAllMonitors(useRemoteMonitors = true) {
if (useRemoteMonitors) {
const remoteMonitors = await getRemoteMonitors()
return [...config.monitors, ...remoteMonitors]
}
return config.monitors
}

export async function getCoreData(useRemoteMonitors?: boolean) {
const [allMonitors, { kvData }] = await Promise.all([getAllMonitors(useRemoteMonitors), getStore()])
return {
allMonitors,
kvData,
}
}

export async function prepareMonitors() {
const { kvData } = await getStore()
const { allMonitors, kvData } = await getCoreData()

const lastCheckedMonitorIds = kvData.lastUpdate?.checks.ids || []
const uncheckMonitors = lastCheckedMonitorIds.length === 0
? config.monitors
: config.monitors.filter((item) => {
? allMonitors
: allMonitors.filter((item) => {
return !lastCheckedMonitorIds.includes(item.id)
})

if (uncheckMonitors.length === 0) {
return {
uncheckMonitors: config.monitors,
uncheckMonitors: allMonitors,
lastCheckedMonitorIds: [],
}
}
Expand Down

0 comments on commit 82e1ae3

Please sign in to comment.