From 616fa2783449987f8bcda7e0554738c787f22982 Mon Sep 17 00:00:00 2001 From: Raj Nandan Sharma Date: Mon, 11 Dec 2023 20:02:53 +0530 Subject: [PATCH] changes --- .gitignore | 1 + package-lock.json | 202 +++----------------------- package.json | 1 + scripts/cron-minute.js | 155 ++++++++++++++------ scripts/startup.js | 7 +- src/app.html | 2 +- src/lib/components/monitor.svelte | 32 +++- src/lib/docs/docs.md | 24 ++- src/lib/server/webhook.js | 27 +--- src/routes/api/today/+server.js | 61 ++++---- src/routes/docs/+page.svelte | 2 +- src/routes/incident/[id]/+page.svelte | 6 +- static/issue.png | Bin 0 -> 6940 bytes 13 files changed, 228 insertions(+), 292 deletions(-) create mode 100644 static/issue.png diff --git a/.gitignore b/.gitignore index 4efb5e8..6cf8b47 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ config/site.yaml !.env.example vite.config.js.timestamp-* vite.config.ts.timestamp-* +nodemon.json diff --git a/package-lock.json b/package-lock.json index 4760939..a4532f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,6 @@ "@markdoc/markdoc": "^0.3.5", "axios": "^1.6.2", "bits-ui": "^0.9.8", - "cheerio": "^1.0.0-rc.12", "clsx": "^2.0.0", "croner": "^7.0.5", "express": "^4.18.2", @@ -23,6 +22,7 @@ "moment": "^2.29.4", "moment-timezone": "^0.5.43", "node-cache": "^5.1.2", + "randomstring": "^1.3.0", "tailwind-merge": "^2.0.0", "tailwind-variants": "^0.1.18" }, @@ -1084,11 +1084,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" - }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -1248,42 +1243,6 @@ "node": ">=8" } }, - "node_modules/cheerio": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", - "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", - "dependencies": { - "cheerio-select": "^2.1.0", - "dom-serializer": "^2.0.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "htmlparser2": "^8.0.1", - "parse5": "^7.0.0", - "parse5-htmlparser2-tree-adapter": "^7.0.0" - }, - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/cheeriojs/cheerio?sponsor=1" - } - }, - "node_modules/cheerio-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", - "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", - "dependencies": { - "boolbase": "^1.0.0", - "css-select": "^5.1.0", - "css-what": "^6.1.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -1507,21 +1466,6 @@ "node": ">= 8" } }, - "node_modules/css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, "node_modules/css-tree": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", @@ -1534,17 +1478,6 @@ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" } }, - "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -1669,57 +1602,6 @@ "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -1745,17 +1627,6 @@ "node": ">= 0.8" } }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, "node_modules/es6-promise": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", @@ -2193,24 +2064,6 @@ "node": ">= 0.4" } }, - "node_modules/htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" - } - }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -2717,17 +2570,6 @@ "node": ">=0.10.0" } }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -2783,29 +2625,6 @@ "node": ">=6" } }, - "node_modules/parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "dependencies": { - "entities": "^4.4.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", - "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", - "dependencies": { - "domhandler": "^5.0.2", - "parse5": "^7.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -3096,6 +2915,25 @@ } ] }, + "node_modules/randombytes": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.3.tgz", + "integrity": "sha512-lDVjxQQFoCG1jcrP06LNo2lbWp4QTShEXnhActFBwYuHprllQV6VUpwreApsYqCgD+N1mHoqJ/BI/4eV4R2GYg==" + }, + "node_modules/randomstring": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/randomstring/-/randomstring-1.3.0.tgz", + "integrity": "sha512-gY7aQ4i1BgwZ8I1Op4YseITAyiDiajeZOPQUbIq9TPGPhUm5FX59izIaOpmKbME1nmnEiABf28d9K2VSii6BBg==", + "dependencies": { + "randombytes": "2.0.3" + }, + "bin": { + "randomstring": "bin/randomstring" + }, + "engines": { + "node": "*" + } + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", diff --git a/package.json b/package.json index 176400f..48f1a18 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,7 @@ "moment": "^2.29.4", "moment-timezone": "^0.5.43", "node-cache": "^5.1.2", + "randomstring": "^1.3.0", "tailwind-merge": "^2.0.0", "tailwind-variants": "^0.1.18" } diff --git a/scripts/cron-minute.js b/scripts/cron-minute.js index 96ed0d3..a1a2af2 100644 --- a/scripts/cron-minute.js +++ b/scripts/cron-minute.js @@ -3,20 +3,18 @@ import fs from "fs-extra"; import { UP, DOWN, DEGRADED } from "./constants.js"; import moment from "moment"; - function replaceAllOccurrences(originalString, searchString, replacement) { const regex = new RegExp(`\\${searchString}`, "g"); const replacedString = originalString.replace(regex, replacement); return replacedString; } - -const OneMinuteFetch = async (envSecrets, url, method, headers, body, timeout, cb, out) => { - - let axiosHeaders = {}; +const Kener_folder = process.env.PUBLIC_KENER_FOLDER; +const OneMinuteFetch = async (envSecrets, folderName, url, method, headers, body, timeout, cb, out, out90) => { + let axiosHeaders = {}; axiosHeaders["User-Agent"] = "Kener/0.0.1"; axiosHeaders["Accept"] = "*/*"; const start = Date.now(); - const startOfMinute = moment(start).startOf("minute").toISOString(); + const startOfMinute = moment(start).startOf("minute").toISOString(); //replace all secrets for (let i = 0; i < envSecrets.length; i++) { const secret = envSecrets[i]; @@ -28,13 +26,12 @@ const OneMinuteFetch = async (envSecrets, url, method, headers, body, timeout, c } if (!!headers) { headers = replaceAllOccurrences(headers, secret.find, secret.replace); - } + } + } + if (!!headers) { + headers = JSON.parse(headers); + axiosHeaders = { ...axiosHeaders, ...headers }; } - if (!!headers){ - headers = JSON.parse(headers); - axiosHeaders = {...axiosHeaders, ...headers}; - } - const options = { method: method, @@ -57,7 +54,7 @@ const OneMinuteFetch = async (envSecrets, url, method, headers, body, timeout, c statusCode = data.status; resp = data.data; } catch (err) { - console.error("API call error: " + err.message); + console.error("API call error: " + err.message); if (err.response !== undefined && err.response.status !== undefined) { statusCode = err.response.status; } @@ -83,25 +80,25 @@ const OneMinuteFetch = async (envSecrets, url, method, headers, body, timeout, c type: "error", }; } else { - evalResp.type = "realtime"; - } + evalResp.type = "realtime"; + } - let toWrite = { + let toWrite = { status: DOWN, latency: latency, type: "error", }; - if(evalResp.status !== undefined && evalResp.status !== null){ - toWrite.status = evalResp.status; - } - if(evalResp.latency !== undefined && evalResp.latency !== null){ - toWrite.latency = evalResp.latency; - } - if(evalResp.type !== undefined && evalResp.type !== null){ - toWrite.type = evalResp.type; - } - let objectToWrite = {}; - objectToWrite[startOfMinute] = toWrite; + if (evalResp.status !== undefined && evalResp.status !== null) { + toWrite.status = evalResp.status; + } + if (evalResp.latency !== undefined && evalResp.latency !== null) { + toWrite.latency = evalResp.latency; + } + if (evalResp.type !== undefined && evalResp.type !== null) { + toWrite.type = evalResp.type; + } + let objectToWrite = {}; + objectToWrite[startOfMinute] = toWrite; let originalData = {}; @@ -114,26 +111,96 @@ const OneMinuteFetch = async (envSecrets, url, method, headers, body, timeout, c fs.writeFileSync(out, JSON.stringify({})); } - originalData[startOfMinute] = toWrite; + originalData[startOfMinute] = toWrite; + + //get data from webhook + //read all files that end with .webhook + let files = fs.readdirSync(Kener_folder); + files = files.filter((file) => file.startsWith(folderName +".webhook")); + for (let i = 0; i < files.length; i++) { + const file = files[i]; + let webhookData = {}; + try { + let fd = fs.readFileSync(Kener_folder + "/" + file, "utf8"); + webhookData = JSON.parse(fd); + for (const timestampISO in webhookData) { + originalData[timestampISO] = webhookData[timestampISO]; + } + //delete the file + fs.unlinkSync(Kener_folder + "/" + file); + } catch (error) { + console.error(error); + } + + } + + //read originaldata, create a new file with 90days data + let _90Day = {}; + let _90File = out90; + try { + let fd = fs.readFileSync(_90File, "utf8"); + _90Day = JSON.parse(fd); + } catch (err) { + fs.ensureFileSync(_90File); + fs.writeFileSync(_90File, JSON.stringify({})); + } + let dayISO = moment(startOfMinute).startOf('day').toISOString(); - //sort the keys - let keys = Object.keys(originalData); - keys.sort((a,b) => { - return moment(a).isBefore(moment(b)) ? -1 : 1; - }); - let sortedDay0 = {}; - keys.reverse() //reverse to keep 90days data - .slice(0, 129600) //90days data - .reverse() //reverse to keep 0day data - .forEach((key) => { - sortedDay0[key] = originalData[key]; - }); - try { - fs.writeFileSync(out, JSON.stringify(sortedDay0, null, 2)); - } catch (error) { - console.error(error); + if (_90Day[dayISO] === undefined) { + _90Day[dayISO] = { + timestamp: dayISO, + UP: toWrite.status == UP ? 1 : 0, + DEGRADED: toWrite.status == DEGRADED ? 1 : 0, + DOWN: toWrite.status == DOWN ? 1 : 0, + avgLatency: toWrite.latency, + latency: toWrite.latency, + }; + } else { + let d = _90Day[dayISO]; + let up = toWrite.status == UP ? d.UP + 1 : d.UP; + let degraded = toWrite.status == DEGRADED ? d.DEGRADED + 1 : d.DEGRADED; + let down = toWrite.status == DOWN ? d.DOWN + 1 : d.DOWN; + + _90Day[dayISO] = { + timestamp: dayISO, + UP: up, + DEGRADED: degraded, + DOWN: down, + avgLatency: ((d.latency + toWrite.latency) / (d.UP + d.DEGRADED + d.DOWN + 1)).toFixed(0), + latency: d.latency + toWrite.latency, + }; + } + + + fs.writeFileSync(_90File, JSON.stringify(_90Day, null, 2)); + + //from originaldata, delete all values older than today + let today = moment().startOf('day').toISOString(); + let _0Day = {}; + for (const ts in originalData) { + const element = originalData[ts]; + if (moment(ts).isAfter(moment(today))) { + _0Day[ts] = element; + } } + //sort the keys + let keys = Object.keys(_0Day); + keys.sort((a, b) => { + return moment(a).isBefore(moment(b)) ? -1 : 1; + }); + let sortedDay0 = {}; + keys.reverse() //reverse to keep 90days data + .slice(0, 1440) //90days data + .reverse() //reverse to keep 0day data + .forEach((key) => { + sortedDay0[key] = _0Day[key]; + }); + try { + fs.writeFileSync(out, JSON.stringify(sortedDay0, null, 2)); + } catch (error) { + console.error(error); + } }; export { OneMinuteFetch }; diff --git a/scripts/startup.js b/scripts/startup.js index 1f62809..6cf8ca7 100644 --- a/scripts/startup.js +++ b/scripts/startup.js @@ -117,9 +117,10 @@ const Startup = async () => { } if (monitor.timeout === undefined || monitor.timeout === null) { monitors[i].timeout = API_TIMEOUT; - } + } monitors[i].path0Day = `${FOLDER}/${folderName}-day-json.json`; + monitors[i].path90Day = `${FOLDER}/${folderName}.90day.json`; monitors[i].hasAPI = hasAPI; //secrets can be in url/body/headers @@ -165,7 +166,7 @@ const Startup = async () => { continue; } console.log("Staring One Minute Cron for ", monitor.path0Day); - await OneMinuteFetch(envSecrets, monitor.url, monitor.method, JSON.stringify(monitor.headers), monitor.body, monitor.timeout, monitor.eval, monitor.path0Day); + await OneMinuteFetch(envSecrets, monitor.folderName, monitor.url, monitor.method, JSON.stringify(monitor.headers), monitor.body, monitor.timeout, monitor.eval, monitor.path0Day, monitor.path90Day); } //trigger minute cron @@ -181,7 +182,7 @@ const Startup = async () => { } console.log("Staring " + cronExpession + " Cron for ", monitor.name); Cron(cronExpession, async () => { - OneMinuteFetch(envSecrets, monitor.url, monitor.method, JSON.stringify(monitor.headers), monitor.body, monitor.timeout, monitor.eval, monitor.path0Day); + OneMinuteFetch(envSecrets, monitor.folderName, monitor.url, monitor.method, JSON.stringify(monitor.headers), monitor.body, monitor.timeout, monitor.eval, monitor.path0Day, monitor.path90Day); }); } }; diff --git a/src/app.html b/src/app.html index 6f46c1f..59519f1 100644 --- a/src/app.html +++ b/src/app.html @@ -1,5 +1,5 @@ - + diff --git a/src/lib/components/monitor.svelte b/src/lib/components/monitor.svelte index 1f8cdcb..1736746 100644 --- a/src/lib/components/monitor.svelte +++ b/src/lib/components/monitor.svelte @@ -67,6 +67,7 @@ }, data: { day0: day0File, + day90: day90File, tz: Intl.DateTimeFormat().resolvedOptions().timeZone }, }; @@ -91,7 +92,7 @@ }); -
+
@@ -115,15 +116,30 @@
{#if !loading90} -
+
+ + {#if view == "90day"}

- Uptime for 90 Day is {uptime90Day}% and avg latency is {avgLatency90Day} ms + 90 Day

+ + + {uptime90Day}% Uptime + + + {avgLatency90Day}ms Latency + {:else} -

- Uptime for Today is {uptime0Day}% and avg latency is {avgLatency0Day} ms +

+ Today

+ + {uptime0Day}% Uptime + + + {avgLatency0Day}ms Latency + {/if}
{/if} @@ -171,9 +187,9 @@
{#if bar.message != "No Data"} - {bar.timestamp} / {bar.uptimePercentage}% up / {bar.avgLatency} ms AVG latency + {new Date(bar.timestamp).toLocaleDateString()} ● {bar.message} ● {bar.avgLatency} ms AVG latency {:else} - {bar.timestamp} / {bar.message} + {bar.timestamp} ● {bar.message} {/if}
@@ -189,7 +205,7 @@
{/each} diff --git a/src/lib/docs/docs.md b/src/lib/docs/docs.md index c575e87..e2fca90 100644 --- a/src/lib/docs/docs.md +++ b/src/lib/docs/docs.md @@ -46,7 +46,16 @@ export PUBLIC_KENER_FOLDER=./build/client/kener export tz=UTC node prod.js ``` -- +## Github Setup +- Create a Github Repositiory +- Go to [Personal Access Token](https://github.com/settings/personal-access-tokens/new) +- Create a Fine-grained token +- If your repository is private then give Read-Write access to issues +- Add the access token as an environment variable +```shell +export GH_TOKEN=github_pat_11AD3ZA3Y0 +``` + --- # Modify Site @@ -132,11 +141,12 @@ Example add a png called `logo.png` file in `static/` and then ## github -For incident kener uses github comments. Create an empty [github](https://github.com) public repo and add them to `site.yaml` +For incident kener uses github comments. Create an empty [github](https://github.com) repo and add them to `site.yaml` ```yaml github: owner: "username" repo: "your-reponame" + refer: true ``` ## metaTags Meta tags are nothing but html ``. You can use them for SEO purposes @@ -346,4 +356,12 @@ Assuming `ORDER_ID` is present in env latency: responseTime, } }) -``` \ No newline at end of file +``` +# Incident Management +Kener uses Github to power incident management. We encourage you to create public repositores so that others can subscribe to updates to issues +## How to create +Create an issue with two labels `your-monitor-tag` and `status` +![alt text](issue.png "issue") + +- Open issues are considered as live incidents. +- Add comments and it will showup in kener. \ No newline at end of file diff --git a/src/lib/server/webhook.js b/src/lib/server/webhook.js index 18e9350..9082bc6 100644 --- a/src/lib/server/webhook.js +++ b/src/lib/server/webhook.js @@ -2,6 +2,7 @@ import fs from "fs-extra" import { env } from "$env/dynamic/public"; import moment from "moment"; +import Randomstring from "randomstring"; const WEBHOOK_TOKEN = process.env.WEBHOOK_TOKEN; const WEBHOOK_IP = process.env.WEBHOOK_IP; @@ -63,11 +64,7 @@ const store = function(data, authHeader, ip){ //read the monitor.path0Day file let day0 = {}; - try { - day0 = JSON.parse(fs.readFileSync(monitor.path0Day, "utf8")); - } catch (error) { - return { error: "something went wrong", status: 400 }; - } + @@ -75,22 +72,12 @@ const store = function(data, authHeader, ip){ day0[timeStampISOMinute] = resp; //sort the keys - let keys = Object.keys(day0); - keys.sort((a,b) => { - return moment(a).isBefore(moment(b)) ? -1 : 1; - }); - let sortedDay0 = {}; - //oppsite of sort is required and only first 1440 keys are required - keys.reverse() - .slice(0, 129600) - .reverse() - .forEach((key) => { - sortedDay0[key] = day0[key]; - }); - - + + //create a random string with high cardinlity + //to avoid cache + //write the monitor.path0Day file - fs.writeFileSync(monitor.path0Day, JSON.stringify(sortedDay0, null, 2)); + fs.writeFileSync(env.PUBLIC_KENER_FOLDER + `/${monitor.folderName}.webhook.${Randomstring.generate()}.json`, JSON.stringify(day0, null, 2)); return { status: 200, message: "success at " + timeStampISOMinute }; diff --git a/src/routes/api/today/+server.js b/src/routes/api/today/+server.js index 8ab9f3d..71b0b23 100644 --- a/src/routes/api/today/+server.js +++ b/src/routes/api/today/+server.js @@ -67,43 +67,50 @@ export async function POST({ request }) { } let day0 = JSON.parse(fs.readFileSync(payload.day0, "utf8")); + let _90DayFileData = JSON.parse(fs.readFileSync(payload.day90, "utf8")); + //_90Day = {..._90Day, ..._90DayFileData}; + + for (const timestampISO in _90DayFileData) { + let cssClass = statusObj.UP; + let message = "OK"; + const element = _90DayFileData[timestampISO]; + + + let currentDay = moment.tz(timestampISO, tz).format("YYYY-MM-DD"); + + if(_90Day[currentDay] === undefined) continue; + + _90Day[currentDay].UP = element.UP; + _90Day[currentDay].DEGRADED = element.DEGRADED; + _90Day[currentDay].DOWN = element.DOWN; + _90Day[currentDay].avgLatency = element.avgLatency; + _90Day[currentDay].latency = element.latency; + + _90Day[currentDay].uptimePercentage = parseUptime(element.UP + element.DEGRADED, element.UP + element.DEGRADED + element.DOWN); + + if (element.DEGRADED > 0) { + cssClass = statusObj.DEGRADED; + message = "Degraded for " + element.DEGRADED + " minutes"; + } + + if (element.DOWN > 0) { + cssClass = statusObj.DOWN; + message = "Down for " + element.DOWN + " minutes"; + } + + _90Day[currentDay].cssClass = cssClass; + _90Day[currentDay].message = message; + } //loop day0 as object for (const timestampISO in day0) { if (Object.hasOwnProperty.call(day0, timestampISO)) { const element = day0[timestampISO]; let min = moment.tz(timestampISO, tz).format("YYYY-MM-DD HH:mm:00"); - let day = moment.tz(timestampISO, tz).format("YYYY-MM-DD"); let status = element.status; let latency = element.latency; - //90 Day data - let d = _90Day[day]; - _90Day[day] = { - timestamp: day, - UP: status == "UP" ? d.UP + 1 : d.UP, - DEGRADED: status == "DEGRADED" ? d.DEGRADED + 1 : d.DEGRADED, - DOWN: status == "DOWN" ? d.DOWN + 1 : d.DOWN, - latency: d.latency + latency, - avgLatency: ((d.latency + latency) / (d.UP + d.DEGRADED + d.DOWN + 1)).toFixed(0), - }; - _90Day[day].uptimePercentage = parseUptime(_90Day[day].UP + _90Day[day].DEGRADED, _90Day[day].UP + _90Day[day].DEGRADED + _90Day[day].DOWN); - let cssClass = statusObj.UP; - let message = "OK"; - - if (_90Day[day].DEGRADED > 0) { - cssClass = statusObj.DEGRADED; - message = "Degraded for " + _90Day[day].DEGRADED + " minutes"; - } - - if (_90Day[day].DOWN > 0) { - cssClass = statusObj.DOWN; - message = "Down for " + _90Day[day].DOWN + " minutes"; - } - - _90Day[day].cssClass = cssClass; - _90Day[day].message = message; //0 Day data if (_0Day[min] !== undefined) { diff --git a/src/routes/docs/+page.svelte b/src/routes/docs/+page.svelte index 3ec4830..205f353 100644 --- a/src/routes/docs/+page.svelte +++ b/src/routes/docs/+page.svelte @@ -41,7 +41,7 @@