1
0
mirror of https://github.com/tormachris/cf-workers-status-page.git synced 2025-07-04 11:02:48 +02:00
This commit is contained in:
Adam Janis
2020-11-08 13:56:02 +01:00
commit e85c5766a7
20 changed files with 1013 additions and 0 deletions

View 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>
)
}
}

View 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
View 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;
}

View 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
View 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' },
})
}