mirror of
https://github.com/tormachris/cf-workers-status-page.git
synced 2025-07-04 11:02:48 +02:00
init
This commit is contained in:
49
src/components/monitorHistogram.js
Normal file
49
src/components/monitorHistogram.js
Normal file
@ -0,0 +1,49 @@
|
||||
import config from '../../config.yaml'
|
||||
|
||||
export default function MonitorHistogram({ kvMonitorsDaysMap, monitor }) {
|
||||
let date = new Date()
|
||||
date.setDate(date.getDate() - config.settings.daysInHistory)
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
return (
|
||||
<div
|
||||
key={`${monitor.id}-histogram`}
|
||||
className="horizontal flex histogram"
|
||||
>
|
||||
{Array.from(Array(config.settings.daysInHistory).keys()).map(key => {
|
||||
date.setDate(date.getDate() + 1)
|
||||
const dayInHistory = date.toISOString().split('T')[0]
|
||||
const dayInHistoryKey = 'h_' + monitor.id + '_' + dayInHistory
|
||||
|
||||
let bg = ''
|
||||
let dayInHistoryStatus = 'No data'
|
||||
|
||||
if (typeof kvMonitorsDaysMap[dayInHistoryKey] !== 'undefined') {
|
||||
bg = kvMonitorsDaysMap[dayInHistoryKey] ? 'green' : 'orange'
|
||||
dayInHistoryStatus = kvMonitorsDaysMap[dayInHistoryKey]
|
||||
? 'No outages'
|
||||
: 'Some outages'
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={key} className="hitbox">
|
||||
<div
|
||||
className={`${bg} bar`}
|
||||
data-tooltip={`${dayInHistory} - ${dayInHistoryStatus}`}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<div
|
||||
key={`${monitor.id}-histogram`}
|
||||
className="horizontal flex histogram"
|
||||
>
|
||||
<div className="grey-text">Loading histogram ...</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
16
src/components/monitorStatusLabel.js
Normal file
16
src/components/monitorStatusLabel.js
Normal file
@ -0,0 +1,16 @@
|
||||
export default function MonitorStatusLabel({ kvMonitorsMap, monitor }) {
|
||||
let labelColor = 'grey'
|
||||
let labelText = 'No data'
|
||||
|
||||
if (typeof kvMonitorsMap[monitor.id] !== 'undefined') {
|
||||
if (kvMonitorsMap[monitor.id].operational) {
|
||||
labelColor = 'green'
|
||||
labelText = 'Operational'
|
||||
} else {
|
||||
labelColor = 'orange'
|
||||
labelText = 'Not great not terrible'
|
||||
}
|
||||
}
|
||||
|
||||
return <div className={`ui ${labelColor} horizontal label`}>{labelText}</div>
|
||||
}
|
66
src/css/index.css
Normal file
66
src/css/index.css
Normal file
@ -0,0 +1,66 @@
|
||||
body {
|
||||
background: #eeeeee;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.flex.horizontal {
|
||||
flex-direction: row;
|
||||
}
|
||||
.flex.vertical {
|
||||
flex-direction: column;
|
||||
}
|
||||
.flex.between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
.marginless {
|
||||
margin: 0 !important;
|
||||
}
|
||||
.paddingless {
|
||||
padding: 0 !important;
|
||||
}
|
||||
.black-text {
|
||||
color: #000 !important;
|
||||
}
|
||||
.grey-text {
|
||||
color: #a0a0a0 !important;
|
||||
}
|
||||
.white-text {
|
||||
color: #fff !important;
|
||||
}
|
||||
.histogram {
|
||||
height: 24px;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.hitbox {
|
||||
align-items: flex-end;
|
||||
box-sizing: border-box;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 1px;
|
||||
border-radius: 3.75px;
|
||||
}
|
||||
.bar {
|
||||
background: #dcddde;
|
||||
padding-bottom: 1px;
|
||||
height: 100%;
|
||||
width: 85%;
|
||||
border-radius: 100px;
|
||||
}
|
||||
.bar.green {
|
||||
background: #21ba45;
|
||||
}
|
||||
.bar.red {
|
||||
background: #db2828;
|
||||
}
|
||||
.bar.orange {
|
||||
background: #f2711c;
|
||||
}
|
||||
span i.icon {
|
||||
margin: 0 !important;
|
||||
}
|
57
src/functions/cronTrigger.js
Normal file
57
src/functions/cronTrigger.js
Normal file
@ -0,0 +1,57 @@
|
||||
import config from '../../config.yaml'
|
||||
|
||||
import { setKV, getKV, getKVWithMetadata, gcMonitors } from './helpers'
|
||||
|
||||
export async function processCronTrigger(event) {
|
||||
for (const monitor of config.monitors) {
|
||||
console.log(`Checking ${monitor.name} ...`)
|
||||
|
||||
const init = {
|
||||
method: monitor.method || 'GET',
|
||||
redirect: monitor.followRedirect ? 'follow' : 'manual',
|
||||
headers: {
|
||||
'User-Agent': 'cf-worker-status-page',
|
||||
},
|
||||
}
|
||||
|
||||
const response = await fetch(monitor.url, init)
|
||||
const monitorOperational = response.status === (monitor.expectStatus || 200)
|
||||
const kvMonitor = await getKVWithMetadata('s_' + monitor.id)
|
||||
|
||||
// metadata from monitor settings
|
||||
const metadata = {
|
||||
operational: monitorOperational,
|
||||
statusCode: response.status,
|
||||
id: monitor.id,
|
||||
}
|
||||
|
||||
// write current status if status changed or for first time
|
||||
if (
|
||||
!kvMonitor.metadata ||
|
||||
kvMonitor.metadata.operational !== monitorOperational
|
||||
) {
|
||||
console.log('saving new results..')
|
||||
|
||||
if (typeof SECRET_SLACK_WEBHOOK !== 'undefined') {
|
||||
await notifySlack(metadata)
|
||||
}
|
||||
|
||||
await setKV('s_' + monitor.id, null, metadata)
|
||||
}
|
||||
|
||||
// check day status, write only on not operational or for first time
|
||||
const kvDayStatusKey =
|
||||
'h_' + monitor.id + '_' + new Date().toISOString().split('T')[0]
|
||||
//console.log(kvDayStatusKey)
|
||||
const kvDayStatus = await getKV(kvDayStatusKey)
|
||||
|
||||
if (!kvDayStatus || (kvDayStatus && !monitorOperational)) {
|
||||
await setKV(kvDayStatusKey, null, metadata)
|
||||
}
|
||||
|
||||
await setKV('lastUpdate', Date.now())
|
||||
}
|
||||
await gcMonitors(config)
|
||||
|
||||
return new Response('OK')
|
||||
}
|
89
src/functions/helpers.js
Normal file
89
src/functions/helpers.js
Normal file
@ -0,0 +1,89 @@
|
||||
export async function getMonitors() {
|
||||
const monitors = await listKV('s_')
|
||||
return monitors.keys
|
||||
}
|
||||
|
||||
export async function getMonitorsHistory() {
|
||||
const monitorsHistory = await listKV('h_', 600)
|
||||
return monitorsHistory.keys
|
||||
}
|
||||
|
||||
export async function getLastUpdate() {
|
||||
return await getKV('lastUpdate')
|
||||
}
|
||||
|
||||
export async function listKV(prefix = '', cacheTtl = false) {
|
||||
const cacheKey = 'list_' + prefix + '_' + process.env.BUILD_ID
|
||||
const cachedResponse = await getKV(cacheKey)
|
||||
|
||||
if (cacheTtl && cachedResponse) {
|
||||
return JSON.parse(cachedResponse)
|
||||
}
|
||||
|
||||
let list = []
|
||||
let cursor = null
|
||||
let res = {}
|
||||
do {
|
||||
res = await KV_STATUS_PAGE.list({ prefix: prefix, cursor })
|
||||
list = list.concat(res.keys)
|
||||
cursor = res.cursor
|
||||
} while (!res.list_complete)
|
||||
|
||||
if (cacheTtl) {
|
||||
await setKV(cacheKey, JSON.stringify({ keys: list }), null, 600)
|
||||
}
|
||||
return { keys: list }
|
||||
}
|
||||
|
||||
export async function setKV(key, value, metadata, expirationTtl) {
|
||||
return KV_STATUS_PAGE.put(key, value, { metadata, expirationTtl })
|
||||
}
|
||||
|
||||
export async function getKV(key, type = 'text') {
|
||||
return KV_STATUS_PAGE.get(key, type)
|
||||
}
|
||||
|
||||
export async function getKVWithMetadata(key) {
|
||||
return KV_STATUS_PAGE.getWithMetadata(key)
|
||||
}
|
||||
|
||||
export async function deleteKV(key) {
|
||||
return KV_STATUS_PAGE.delete(key)
|
||||
}
|
||||
|
||||
export async function gcMonitors(config) {
|
||||
const checkKvPrefix = 's_'
|
||||
|
||||
const monitors = config.monitors.map(key => {
|
||||
return key.id
|
||||
})
|
||||
|
||||
const kvMonitors = await listKV(checkKvPrefix)
|
||||
const kvState = kvMonitors.keys.map(key => {
|
||||
return key.metadata.id
|
||||
})
|
||||
|
||||
const keysForRemoval = kvState.filter(x => !monitors.includes(x))
|
||||
|
||||
keysForRemoval.forEach(key => {
|
||||
console.log('gc: deleting ' + checkKvPrefix + key)
|
||||
deleteKV(checkKvPrefix + key)
|
||||
})
|
||||
}
|
||||
|
||||
async function notifySlack(monitor, metadata) {
|
||||
const blocks = [
|
||||
{
|
||||
type: 'section',
|
||||
text: {
|
||||
type: 'mrkdwn',
|
||||
text: `Some monitor is now in :this: status`,
|
||||
},
|
||||
},
|
||||
]
|
||||
return fetch(SECRET_SLACK_WEBHOOK_URL, {
|
||||
body: JSON.stringify({ blocks }),
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
})
|
||||
}
|
Reference in New Issue
Block a user