mirror of
https://github.com/rajnandan1/kener.git
synced 2026-01-08 02:19:53 -06:00
251 lines
8.8 KiB
JavaScript
251 lines
8.8 KiB
JavaScript
/*
|
|
The startup js script will
|
|
check if monitors.yaml exists
|
|
if it does, it will read the file and parse it into a json array of objects
|
|
each objects will have a name, url, method: required
|
|
name of each of these objects need to be unique
|
|
*/
|
|
import fs from "fs-extra";
|
|
import yaml from "js-yaml";
|
|
import { Cron } from "croner";
|
|
import { FOLDER, FOLDER_MONITOR, FOLDER_SITE, API_TIMEOUT } from "./constants.js";
|
|
import { IsValidURL, IsValidHTTPMethod, LoadMonitorsPath, LoadSitePath } from "./tool.js";
|
|
import { GetAllGHLabels, CreateGHLabel } from "./github.js";
|
|
import { Minuter } from "./cron-minute.js";
|
|
let monitors = [];
|
|
let site = {};
|
|
const envSecrets = [];
|
|
const defaultEval = `(function (statusCode, responseTime, responseData) {
|
|
let statusCodeShort = Math.floor(statusCode/100);
|
|
if(statusCode == 429 || (statusCodeShort >=2 && statusCodeShort <= 3)) {
|
|
return {
|
|
status: 'UP',
|
|
latency: responseTime,
|
|
}
|
|
}
|
|
return {
|
|
status: 'DOWN',
|
|
latency: responseTime,
|
|
}
|
|
})`;
|
|
|
|
function checkIfDuplicateExists(arr) {
|
|
return new Set(arr).size !== arr.length;
|
|
}
|
|
function getWordsStartingWithDollar(text) {
|
|
const regex = /\$\w+/g;
|
|
const wordsArray = text.match(regex);
|
|
return wordsArray || [];
|
|
}
|
|
if (!fs.existsSync(FOLDER)) {
|
|
fs.mkdirSync(FOLDER);
|
|
console.log(".kener folder created successfully!");
|
|
}
|
|
|
|
const Startup = async () => {
|
|
try {
|
|
const fileContent = fs.readFileSync(LoadMonitorsPath(), "utf8");
|
|
site = yaml.load(fs.readFileSync(LoadSitePath(), "utf8"));
|
|
monitors = yaml.load(fileContent);
|
|
} catch (error) {
|
|
console.log(error);
|
|
process.exit(1);
|
|
}
|
|
|
|
// Use the 'monitors' array of JSON objects as needed
|
|
//check if each object has name, url, method
|
|
//if not, exit with error
|
|
//if yes, check if name is unique
|
|
|
|
for (let i = 0; i < monitors.length; i++) {
|
|
const monitor = monitors[i];
|
|
let name = monitor.name;
|
|
let tag = monitor.tag;
|
|
let hasAPI = monitor.api !== undefined && monitor.api !== null;
|
|
let folderName = name.replace(/[^a-z0-9]/gi, "-").toLowerCase();
|
|
monitors[i].folderName = folderName;
|
|
|
|
if (!name || !tag) {
|
|
console.log("name, tag are required");
|
|
process.exit(1);
|
|
}
|
|
|
|
if(hasAPI) {
|
|
let url = monitor.api.url;
|
|
let method = monitor.api.method;
|
|
let headers = monitor.api.headers;
|
|
let evaluator = monitor.api.eval;
|
|
let body = monitor.api.body;
|
|
let timeout = monitor.api.timeout;
|
|
//url
|
|
if (!!url) {
|
|
if (!IsValidURL(url)) {
|
|
console.log("url is not valid");
|
|
process.exit(1);
|
|
}
|
|
}
|
|
if (!!method) {
|
|
if (!IsValidHTTPMethod(method)) {
|
|
console.log("method is not valid");
|
|
process.exit(1);
|
|
}
|
|
method = method.toUpperCase();
|
|
} else {
|
|
method = "GET";
|
|
}
|
|
monitors[i].api.method = method;
|
|
//headers
|
|
if (headers === undefined || headers === null) {
|
|
monitors[i].api.headers = undefined;
|
|
} else {
|
|
//check if headers is a valid json
|
|
try {
|
|
JSON.parse(headers);
|
|
} catch (error) {
|
|
console.log("headers is not valid ");
|
|
process.exit(1);
|
|
}
|
|
}
|
|
//eval
|
|
if (evaluator === undefined || evaluator === null) {
|
|
monitors[i].api.eval = defaultEval;
|
|
} else {
|
|
let evalResp = eval(evaluator + `(200, 1000, "")`);
|
|
if (evalResp === undefined || evalResp === null || evalResp.status === undefined || evalResp.status === null || evalResp.latency === undefined || evalResp.latency === null) {
|
|
console.log("eval is not valid ");
|
|
process.exit(1);
|
|
}
|
|
}
|
|
//body
|
|
if (body === undefined || body === null) {
|
|
monitors[i].api.body = undefined;
|
|
} else {
|
|
//check if body is a valid string
|
|
if (typeof body !== "string") {
|
|
console.log("body is not valid should be a string");
|
|
process.exit(1);
|
|
}
|
|
}
|
|
//timeout
|
|
if (timeout === undefined || timeout === null) {
|
|
monitors[i].api.timeout = API_TIMEOUT;
|
|
} else {
|
|
//check if timeout is a valid number
|
|
if (isNaN(timeout) || timeout < 0) {
|
|
console.log("timeout is not valid ");
|
|
process.exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
monitors[i].path0Day = `${FOLDER}/${folderName}.0day.utc.json`;
|
|
monitors[i].hasAPI = hasAPI;
|
|
|
|
//secrets can be in url/body/headers
|
|
//match in monitor.url if a words starts with $, get the word
|
|
const requiredSecrets = getWordsStartingWithDollar(`${monitor.url} ${monitor.body} ${JSON.stringify(monitor.headers)}`).map((x) => x.substr(1));
|
|
|
|
//iterate over process.env
|
|
for (const [key, value] of Object.entries(process.env)) {
|
|
if (requiredSecrets.indexOf(key) !== -1) {
|
|
envSecrets.push({
|
|
find: `$${key}`,
|
|
replace: value,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
if (site.github === undefined || site.github.owner === undefined || site.github.repo === undefined) {
|
|
console.log("github owner and repo are required");
|
|
process.exit(1);
|
|
}
|
|
if (site.github.incidentSince === undefined || site.github.incidentSince === null) {
|
|
site.github.incidentSince = 48;
|
|
}
|
|
if (checkIfDuplicateExists(monitors.map((monitor) => monitor.folderName)) === true) {
|
|
console.log("duplicate monitor detected");
|
|
process.exit(1);
|
|
}
|
|
if (checkIfDuplicateExists(monitors.map((monitor) => monitor.tag)) === true) {
|
|
console.log("duplicate tag detected");
|
|
process.exit(1);
|
|
}
|
|
|
|
fs.ensureFileSync(FOLDER_MONITOR);
|
|
fs.ensureFileSync(FOLDER_SITE);
|
|
|
|
try {
|
|
fs.writeFileSync(FOLDER_MONITOR, JSON.stringify(monitors, null, 4));
|
|
fs.writeFileSync(FOLDER_SITE, JSON.stringify(site, null, 4));
|
|
} catch (error) {
|
|
console.log(error);
|
|
process.exit(1);
|
|
}
|
|
|
|
if (!!site.github && !!site.github.owner && !!site.github.repo) {
|
|
const ghowner = site.github.owner;
|
|
const ghrepo = site.github.repo;
|
|
const ghlabels = await GetAllGHLabels(ghowner, ghrepo);
|
|
const tagsAndDescription = monitors.map((monitor) => {
|
|
return { tag: monitor.tag, description: monitor.name };
|
|
});
|
|
//add incident label if does not exist
|
|
|
|
if (ghlabels.indexOf("incident") === -1) {
|
|
await CreateGHLabel(ghowner, ghrepo, "incident", "Status of the site");
|
|
}
|
|
if (ghlabels.indexOf("resolved") === -1) {
|
|
await CreateGHLabel(ghowner, ghrepo, "resolved", "Incident is resolved", "65dba6");
|
|
}
|
|
if (ghlabels.indexOf("identified") === -1) {
|
|
await CreateGHLabel(ghowner, ghrepo, "identified", "Incident is Identified", "EBE3D5");
|
|
}
|
|
if (ghlabels.indexOf("investigating") === -1) {
|
|
await CreateGHLabel(ghowner, ghrepo, "investigating", "Incident is investigated", "D4E2D4");
|
|
}
|
|
if (ghlabels.indexOf("incident-degraded") === -1) {
|
|
await CreateGHLabel(ghowner, ghrepo, "incident-degraded", "Status is degraded of the site", "f5ba60");
|
|
}
|
|
if (ghlabels.indexOf("incident-down") === -1) {
|
|
await CreateGHLabel(ghowner, ghrepo, "incident-down", "Status is down of the site", "ea3462");
|
|
}
|
|
//add tags if does not exist
|
|
for (let i = 0; i < tagsAndDescription.length; i++) {
|
|
const tag = tagsAndDescription[i].tag;
|
|
const description = tagsAndDescription[i].description;
|
|
if (ghlabels.indexOf(tag) === -1) {
|
|
await CreateGHLabel(ghowner, ghrepo, tag, description);
|
|
}
|
|
}
|
|
}
|
|
|
|
// init monitors
|
|
for (let i = 0; i < monitors.length; i++) {
|
|
const monitor = monitors[i];
|
|
if (!fs.existsSync(monitor.path0Day)) {
|
|
fs.ensureFileSync(monitor.path0Day);
|
|
fs.writeFileSync(monitor.path0Day, JSON.stringify({}));
|
|
}
|
|
|
|
console.log("Staring One Minute Cron for ", monitor.path0Day);
|
|
await Minuter(envSecrets, monitor, site.github);
|
|
}
|
|
|
|
//trigger minute cron
|
|
|
|
for (let i = 0; i < monitors.length; i++) {
|
|
const monitor = monitors[i];
|
|
|
|
let cronExpession = "* * * * *";
|
|
if (monitor.cron !== undefined && monitor.cron !== null) {
|
|
cronExpession = monitor.cron;
|
|
}
|
|
console.log("Staring " + cronExpession + " Cron for ", monitor.name);
|
|
Cron(cronExpession, async () => {
|
|
await Minuter(envSecrets, monitor, site.github);
|
|
});
|
|
}
|
|
};
|
|
|
|
export { Startup };
|