mirror of
https://github.com/tormachris/cf-workers-status-page.git
synced 2024-11-23 14:45:41 +01:00
init
This commit is contained in:
commit
e85c5766a7
21
.github/workflows/deploy.yml
vendored
Normal file
21
.github/workflows/deploy.yml
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
name: Deploy
|
||||||
|
on:
|
||||||
|
- repository_dispatch
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 10
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: 12
|
||||||
|
- run: yarn install
|
||||||
|
- run: yarn build
|
||||||
|
- name: Publish
|
||||||
|
uses: cloudflare/wrangler-action@1.2.0
|
||||||
|
with:
|
||||||
|
apiToken: ${{ secrets.CF_API_TOKEN }}
|
||||||
|
env:
|
||||||
|
CF_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }}
|
||||||
|
IS_WORKER: true
|
29
.gitignore
vendored
Normal file
29
.gitignore
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.js
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
out/*
|
||||||
|
!out/.gitkeep
|
||||||
|
|
||||||
|
# production
|
||||||
|
/dist/
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
*.pem
|
||||||
|
|
||||||
|
# debug
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# local env files
|
||||||
|
.env
|
||||||
|
|
||||||
|
/worker
|
7
.prettierrc
Normal file
7
.prettierrc
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"singleQuote": true,
|
||||||
|
"semi": false,
|
||||||
|
"trailingComma": "all",
|
||||||
|
"tabWidth": 2,
|
||||||
|
"printWidth": 80
|
||||||
|
}
|
378
config.yaml
Normal file
378
config.yaml
Normal file
@ -0,0 +1,378 @@
|
|||||||
|
settings:
|
||||||
|
title: "Status Page"
|
||||||
|
logo: logo-192x192.png
|
||||||
|
daysInHistory: 90
|
||||||
|
|
||||||
|
allmonitorsOperational: "All Systems Operational"
|
||||||
|
notAllmonitorsOperational: "Not All Systems Operational"
|
||||||
|
monitorLabelOperational: "Operational"
|
||||||
|
monitorLabelNotOperational: "Not great not terrible"
|
||||||
|
monitorLabelNoData: "No data"
|
||||||
|
|
||||||
|
|
||||||
|
monitors:
|
||||||
|
- id: kiwi-com-homepage
|
||||||
|
name: Kiwi.com homepage
|
||||||
|
description: Kiwi.com en homepage
|
||||||
|
url: 'https://www.kiwi.com/en/'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
followRedirect: false
|
||||||
|
|
||||||
|
- id: eidam-dev
|
||||||
|
name: Cheesy Status Page
|
||||||
|
description: 'status-page.eidam.dev'
|
||||||
|
url: 'https://status-page.eidam.dev/'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
- id: google-com
|
||||||
|
name: Google.com
|
||||||
|
description: Google homepage
|
||||||
|
url: 'https://www.google.com'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
- id: cf-workers-status-page
|
||||||
|
name: This Workers Status Page project made public
|
||||||
|
description: /shrug
|
||||||
|
url: 'https://github.com/adam-janis/cf-workers-status-page'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
- id: kiwicomapi-cn
|
||||||
|
name: Some other site
|
||||||
|
description: Is this done yet?
|
||||||
|
url: 'http://kiwicomapi.cn/'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
- id: testy-testy
|
||||||
|
name: Testy testy
|
||||||
|
description: Something /shrug
|
||||||
|
url: 'http://kiwicomapiiii.cn/'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
- id: hello-world
|
||||||
|
name: Hello World
|
||||||
|
url: 'http://cnn.cn/'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- id: eidam-dev-2
|
||||||
|
name: Eidam.dev
|
||||||
|
description: 'Eidam.dev homepage, there is none'
|
||||||
|
url: 'https://eidam.dev'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 403
|
||||||
|
|
||||||
|
- id: google-com-2
|
||||||
|
name: Google.com
|
||||||
|
description: Google homepage
|
||||||
|
url: 'https://www.google.com'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
- id: cf-workers-status-page-2
|
||||||
|
name: This Workers Status Page project made public
|
||||||
|
description: /shrug
|
||||||
|
url: 'https://github.com/adam-janis/cf-workers-status-page'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
- id: kiwicomapi-cn-2
|
||||||
|
name: Kiwi.com API CN
|
||||||
|
description: Is this done yet?
|
||||||
|
url: 'http://kiwicomapi.cn/'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
- id: testy-testy-2
|
||||||
|
name: Testy testy
|
||||||
|
description: Something /shrug
|
||||||
|
url: 'http://kiwicomapiiii.cn/'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
- id: hello-world-2
|
||||||
|
name: Hello World
|
||||||
|
url: 'http://cnn.cn/'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- id: eidam-dev-22
|
||||||
|
name: Eidam.dev
|
||||||
|
description: 'Eidam.dev homepage, there is none'
|
||||||
|
url: 'https://eidam.dev'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 403
|
||||||
|
|
||||||
|
- id: google-com-22
|
||||||
|
name: Google.com
|
||||||
|
description: Google homepage
|
||||||
|
url: 'https://www.google.com'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
- id: cf-workers-status-page-22
|
||||||
|
name: This Workers Status Page project made public
|
||||||
|
description: /shrug
|
||||||
|
url: 'https://github.com/adam-janis/cf-workers-status-page'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
- id: kiwicomapi-cn-22
|
||||||
|
name: Kiwi.com API CN
|
||||||
|
description: Is this done yet?
|
||||||
|
url: 'http://kiwicomapi.cn/'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
- id: testy-testy-22
|
||||||
|
name: Testy testy
|
||||||
|
description: Something /shrug
|
||||||
|
url: 'http://kiwicomapiiii.cn/'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
- id: hello-world-22
|
||||||
|
name: Hello World
|
||||||
|
url: 'http://cnn.cn/'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
|
||||||
|
- id: eidam-dev-333
|
||||||
|
name: Eidam.dev
|
||||||
|
description: 'Eidam.dev homepage, there is none'
|
||||||
|
url: 'https://eidam.dev'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 403
|
||||||
|
|
||||||
|
- id: google-com-333
|
||||||
|
name: Google.com
|
||||||
|
description: Google homepage
|
||||||
|
url: 'https://www.google.com'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
- id: cf-workers-status-page-333
|
||||||
|
name: This Workers Status Page project made public
|
||||||
|
description: /shrug
|
||||||
|
url: 'https://github.com/adam-janis/cf-workers-status-page'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
- id: kiwicomapi-cn-333
|
||||||
|
name: Kiwi.com API CN
|
||||||
|
description: Is this done yet?
|
||||||
|
url: 'http://kiwicomapi.cn/'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
- id: testy-testy-333
|
||||||
|
name: Testy testy
|
||||||
|
description: Something /shrug
|
||||||
|
url: 'http://kiwicomapiiii.cn/'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
- id: hello-world-333
|
||||||
|
name: Hello World
|
||||||
|
url: 'http://cnn.cn/'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- id: 25-eidam-dev
|
||||||
|
name: Eidam.dev
|
||||||
|
description: 'Eidam.dev homepage, there is none'
|
||||||
|
url: 'https://eidam.dev'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 403
|
||||||
|
|
||||||
|
- id: 25-google-com
|
||||||
|
name: Bing.com
|
||||||
|
description: Bing homepage
|
||||||
|
url: 'https://www.google.com'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
- id: 25-cf-workers-status-page
|
||||||
|
name: This Workers Status Page project made public
|
||||||
|
description: /shrug
|
||||||
|
url: 'https://github.com/adam-janis/cf-workers-status-page'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
- id: 25-kiwicomapi-cn
|
||||||
|
name: Kiwi.com API CN
|
||||||
|
description: Is this done yet?
|
||||||
|
url: 'http://kiwicomapi.cn/'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
- id: 25-testy-testy
|
||||||
|
name: Testy testy
|
||||||
|
description: Something /shrug
|
||||||
|
url: 'http://kiwicomapiiii.cn/'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
- id: 25-hello-world
|
||||||
|
name: Hello World
|
||||||
|
url: 'http://cnn.cn/'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- id: 25-eidam-dev-2
|
||||||
|
name: Seznam.cz
|
||||||
|
description: 'Just seznam'
|
||||||
|
url: 'https://eidam.dev'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 403
|
||||||
|
|
||||||
|
- id: 25-google-com-2
|
||||||
|
name: Google.com
|
||||||
|
description: Google homepage
|
||||||
|
url: 'https://www.google.com'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
- id: 25-cf-workers-status-page-2
|
||||||
|
name: This Workers Status Page project made public
|
||||||
|
description: /shrug
|
||||||
|
url: 'https://github.com/adam-janis/cf-workers-status-page'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
- id: 25-kiwicomapi-cn-2
|
||||||
|
name: Kiwi.com API CN
|
||||||
|
description: Is this done yet?
|
||||||
|
url: 'http://kiwicomapi.cn/'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
- id: 25-testy-testy-2
|
||||||
|
name: Testy testy
|
||||||
|
description: Something /shrug
|
||||||
|
url: 'http://kiwicomapiiii.cn/'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
- id: 25-hello-world-2
|
||||||
|
name: Hello World
|
||||||
|
url: 'http://cnn.cn/'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- id: 25-eidam-dev-22
|
||||||
|
name: Eidam.dev
|
||||||
|
description: 'Eidam.dev homepage, there is none'
|
||||||
|
url: 'https://eidam.dev'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 403
|
||||||
|
|
||||||
|
- id: 25-google-com-22
|
||||||
|
name: Google.com
|
||||||
|
description: Google homepage
|
||||||
|
url: 'https://www.google.com'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
- id: 25-cf-workers-status-page-22
|
||||||
|
name: This Workers Status Page project made public
|
||||||
|
description: /shrug
|
||||||
|
url: 'https://github.com/adam-janis/cf-workers-status-page'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
- id: 25-kiwicomapi-cn-22
|
||||||
|
name: Something totally different
|
||||||
|
description: Is this done yet?
|
||||||
|
url: 'http://kiwicomapi.cn/'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
- id: 25-testy-testy-22
|
||||||
|
name: Testy testy
|
||||||
|
description: Something /shrug
|
||||||
|
url: 'http://kiwicomapiiii.cn/'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
- id: 25-hello-world-22
|
||||||
|
name: Hello World
|
||||||
|
url: 'http://cnn.cn/'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
|
||||||
|
- id: 25-eidam-dev-333
|
||||||
|
name: Eidam.dev
|
||||||
|
description: 'Eidam.dev homepage, there is none'
|
||||||
|
url: 'https://eidam.dev'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 403
|
||||||
|
|
||||||
|
- id: 25-google-com-333
|
||||||
|
name: Google.com
|
||||||
|
description: Google homepage
|
||||||
|
url: 'https://www.google.com'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
- id: 25-cf-workers-status-page-333
|
||||||
|
name: This Workers Status Page project made public
|
||||||
|
description: /shrug
|
||||||
|
url: 'https://github.com/adam-janis/cf-workers-status-page'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
- id: 25-kiwicomapi-cn-333
|
||||||
|
name: Kiwi.com API CN
|
||||||
|
description: Is this done yet?
|
||||||
|
url: 'http://kiwicomapi.cn/'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
- id: 25-testy-testy-333
|
||||||
|
name: Testy testy
|
||||||
|
description: Something /shrug
|
||||||
|
url: 'http://kiwicomapiiii.cn/'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
- id: 25-hello-world-333
|
||||||
|
name: Hello World
|
||||||
|
url: 'http://cnn.cn/'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
||||||
|
- id: 25-hello-world-333
|
||||||
|
name: Hello World
|
||||||
|
url: 'http://cnn.cn/'
|
||||||
|
method: GET
|
||||||
|
expectStatus: 200
|
||||||
|
|
11
flareact.config.js
Normal file
11
flareact.config.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
module.exports = {
|
||||||
|
webpack: (config, options) => {
|
||||||
|
config.module.rules.push({
|
||||||
|
test: /\.ya?ml$/,
|
||||||
|
type: 'json',
|
||||||
|
use: 'yaml-loader',
|
||||||
|
})
|
||||||
|
|
||||||
|
return config
|
||||||
|
},
|
||||||
|
}
|
32
index.js
Normal file
32
index.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { handleEvent } from 'flareact'
|
||||||
|
import { processCronTrigger } from './src/functions/cronTrigger'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The DEBUG flag will do two things that help during development:
|
||||||
|
* 1. we will skip caching on the edge, which makes it easier to
|
||||||
|
* debug.
|
||||||
|
* 2. we will return an error message on exception in your Response rather
|
||||||
|
* than the default 404.html page.
|
||||||
|
*/
|
||||||
|
const DEBUG = false
|
||||||
|
|
||||||
|
addEventListener('fetch', event => {
|
||||||
|
try {
|
||||||
|
event.respondWith(
|
||||||
|
handleEvent(event, require.context('./pages/', true, /\.js$/), DEBUG),
|
||||||
|
)
|
||||||
|
} catch (e) {
|
||||||
|
if (DEBUG) {
|
||||||
|
return event.respondWith(
|
||||||
|
new Response(e.message || e.toString(), {
|
||||||
|
status: 500,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
event.respondWith(new Response('Internal Error', { status: 500 }))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
addEventListener('scheduled', event => {
|
||||||
|
event.waitUntil(processCronTrigger(event))
|
||||||
|
})
|
0
out/.gitkeep
Normal file
0
out/.gitkeep
Normal file
23
package.json
Normal file
23
package.json
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "cf-workers-status-page",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": "Adam Janiš <adam.janis@gmail.com>",
|
||||||
|
"license": "MIT",
|
||||||
|
"main": "index.js",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "flareact dev",
|
||||||
|
"build": "flareact build",
|
||||||
|
"deploy": "flareact publish",
|
||||||
|
"format": "prettier --write '**/*.{js,css,json,md}'"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"flareact": "^0.7.1",
|
||||||
|
"react": "^16.13.1",
|
||||||
|
"react-dom": "^16.13.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"prettier": "^1.18.2",
|
||||||
|
"yaml-loader": "^0.6.0"
|
||||||
|
}
|
||||||
|
}
|
5
pages/api/triggerCron.js
Normal file
5
pages/api/triggerCron.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { processCronTrigger } from '../../src/functions/cronTrigger'
|
||||||
|
|
||||||
|
export default async event => {
|
||||||
|
return processCronTrigger()
|
||||||
|
}
|
141
pages/index.js
Normal file
141
pages/index.js
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
import Head from 'flareact/head'
|
||||||
|
import MonitorHistogram from '../src/components/monitorHistogram'
|
||||||
|
|
||||||
|
import {
|
||||||
|
getLastUpdate,
|
||||||
|
getMonitors,
|
||||||
|
getMonitorsHistory,
|
||||||
|
} from '../src/functions/helpers'
|
||||||
|
|
||||||
|
import config from '../config.yaml'
|
||||||
|
import MonitorStatusLabel from '../src/components/monitorStatusLabel'
|
||||||
|
|
||||||
|
export async function getEdgeProps() {
|
||||||
|
// get KV data
|
||||||
|
const kvMonitors = await getMonitors()
|
||||||
|
const kvMonitorsDays = await getMonitorsHistory()
|
||||||
|
const kvLastUpdate = await getLastUpdate()
|
||||||
|
|
||||||
|
// prepare data maps for components
|
||||||
|
let monitorsOperational = true
|
||||||
|
let kvMonitorsMap = {}
|
||||||
|
kvMonitors.forEach(x => {
|
||||||
|
kvMonitorsMap[x.metadata.id] = x.metadata
|
||||||
|
if (x.metadata.operational === false) monitorsOperational = false
|
||||||
|
})
|
||||||
|
|
||||||
|
let kvMonitorsDaysMap = {}
|
||||||
|
kvMonitorsDays.forEach(x => {
|
||||||
|
kvMonitorsDaysMap[x.name] = x.metadata.operational
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
config,
|
||||||
|
kvMonitorsMap,
|
||||||
|
kvMonitorsDaysMap,
|
||||||
|
monitorsOperational,
|
||||||
|
kvLastUpdate,
|
||||||
|
},
|
||||||
|
// Revalidate these props once every x seconds
|
||||||
|
revalidate: 5,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Index({
|
||||||
|
config,
|
||||||
|
kvMonitorsMap,
|
||||||
|
kvMonitorsDaysMap,
|
||||||
|
monitorsOperational,
|
||||||
|
kvLastUpdate,
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Head>
|
||||||
|
<title>{config.settings.title}</title>
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.8.7/semantic.min.css"
|
||||||
|
crossOrigin="anonymous"
|
||||||
|
/>
|
||||||
|
<link rel="stylesheet" href="./main.css" />
|
||||||
|
</Head>
|
||||||
|
<div className="ui basic segment container">
|
||||||
|
<h1 className="ui huge header">
|
||||||
|
<img
|
||||||
|
className="ui middle aligned tiny image"
|
||||||
|
src={config.settings.logo}
|
||||||
|
/>
|
||||||
|
{config.settings.title}
|
||||||
|
</h1>
|
||||||
|
<div
|
||||||
|
className={`ui inverted segment ${
|
||||||
|
monitorsOperational ? 'green' : 'yellow'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="horizontal flex between">
|
||||||
|
<div className="ui marginless header black-text">
|
||||||
|
{monitorsOperational
|
||||||
|
? config.settings.allmonitorsOperational
|
||||||
|
: config.settings.notAllmonitorsOperational}
|
||||||
|
</div>
|
||||||
|
<div className="black-text">
|
||||||
|
checked {Math.round((Date.now() - kvLastUpdate) / 1000)} sec ago
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{config.monitors.map((monitor, key) => {
|
||||||
|
return (
|
||||||
|
<div key={key} className="ui segment">
|
||||||
|
<div
|
||||||
|
className="ui horizontal flex between"
|
||||||
|
style={{ marginBottom: '8px' }}
|
||||||
|
>
|
||||||
|
<div className="ui marginless header">
|
||||||
|
<span data-tooltip={monitor.description}>
|
||||||
|
<i className="blue small info circle icon" />
|
||||||
|
</span>
|
||||||
|
<div className="content">{monitor.name}</div>
|
||||||
|
</div>
|
||||||
|
<MonitorStatusLabel
|
||||||
|
kvMonitorsMap={kvMonitorsMap}
|
||||||
|
monitor={monitor}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<MonitorHistogram
|
||||||
|
kvMonitorsDaysMap={kvMonitorsDaysMap}
|
||||||
|
monitor={monitor}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="horizontal flex between grey-text">
|
||||||
|
<div>{config.settings.daysInHistory} days ago</div>
|
||||||
|
<div>Today</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
<div className="horizontal flex between grey-text">
|
||||||
|
<div>
|
||||||
|
Powered by{' '}
|
||||||
|
<a href="https://workers.cloudflare.com/" target="_blank">
|
||||||
|
Cloudflare Workers{' '}
|
||||||
|
</a>
|
||||||
|
&{' '}
|
||||||
|
<a href="https://flareact.com/" target="_blank">
|
||||||
|
Flareact{' '}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<a
|
||||||
|
href="https://github.com/adam-janis/cf-workers-status-page"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Get Your Status Page
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
BIN
public/logo-192x192.png
Normal file
BIN
public/logo-192x192.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
66
public/main.css
Normal file
66
public/main.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;
|
||||||
|
}
|
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' },
|
||||||
|
})
|
||||||
|
}
|
23
wrangler.toml
Normal file
23
wrangler.toml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
name = "cf-workers-status-page"
|
||||||
|
type = "webpack"
|
||||||
|
account_id = ""
|
||||||
|
workers_dev = true
|
||||||
|
route = ""
|
||||||
|
zone_id = ""
|
||||||
|
webpack_config = "node_modules/flareact/webpack"
|
||||||
|
|
||||||
|
# uncomment and adjust following if you are not using GitHub Actions
|
||||||
|
# kv_namespaces = [{binding="KV_GITHUB_RELEASES", id="xxxx"}]
|
||||||
|
# preview_id = "9581809385634861ae93b0e01677b44d"
|
||||||
|
|
||||||
|
# delete afterwards
|
||||||
|
kv-namespaces = [
|
||||||
|
{ binding = "KV_STATUS_PAGE", id = "c27344947ebb476880fa2ba0ef9bbd10", preview_id = "c27344947ebb476880fa2ba0ef9bbd10" }
|
||||||
|
]
|
||||||
|
|
||||||
|
[triggers]
|
||||||
|
crons = ["* * * * *"]
|
||||||
|
|
||||||
|
[site]
|
||||||
|
bucket = "out"
|
||||||
|
entry-point = "./"
|
Loading…
Reference in New Issue
Block a user