added feature requested in Issue #45

This commit is contained in:
Raj Nandan Sharma
2024-01-27 22:51:38 +05:30
parent a85820ed2b
commit 1b4fa02efe
4 changed files with 173 additions and 103 deletions

View File

@@ -143,7 +143,8 @@ const apiCall = async (envSecrets, url, method, headers, body, timeout, monitorE
statusCode = data.status;
resp = data.data;
} catch (err) {
if (err.message.startsWith("timeout of") && err.message.endsWith("exceeded")) {
if (err.message.startsWith("timeout of") && err.message.endsWith("exceeded")) {
timeoutError = true;
}

View File

@@ -12,6 +12,7 @@ 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";
import axios from "axios";
let monitors = [];
let site = {};
const envSecrets = [];
@@ -71,20 +72,20 @@ const Startup = async () => {
}
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) {
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 (!!method) {
if (!IsValidHTTPMethod(method)) {
console.log("method is not valid");
process.exit(1);
@@ -94,20 +95,20 @@ const Startup = async () => {
method = "GET";
}
monitors[i].api.method = method;
//headers
if (headers === undefined || headers === null) {
//headers
if (headers === undefined || headers === null) {
monitors[i].api.headers = undefined;
} else {
//check if headers is a valid json
try {
JSON.parse(headers);
JSON.parse(JSON.stringify(headers));
} catch (error) {
console.log("headers is not valid ");
console.log("headers are not valid. Quiting");
process.exit(1);
}
}
//eval
if (evaluator === undefined || evaluator === null) {
//eval
if (evaluator === undefined || evaluator === null) {
monitors[i].api.eval = defaultEval;
} else {
let evalResp = eval(evaluator + `(200, 1000, "")`);
@@ -116,27 +117,52 @@ const Startup = async () => {
process.exit(1);
}
}
//body
if (body === undefined || body === null) {
//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);
}
if (typeof body !== "string") {
console.log("body is not valid should be a string");
process.exit(1);
}
}
//timeout
if (timeout === undefined || timeout === null) {
//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) {
//check if timeout is a valid number
if (isNaN(timeout) || timeout < 0) {
console.log("timeout is not valid ");
process.exit(1);
}
}
}
}
//add a description to the monitor if it is website using api.url and method = GET and headers == undefined
//call the it to see if recevied content-type is text/html
//if yes, append to description
if ((headers === undefined || headers === null) && url !== undefined && method === "GET") {
try {
const response = await axios({
method: "GET",
url: url,
timeout: API_TIMEOUT,
});
if (response.headers["content-type"].includes("text/html")) {
let link = `<a href="${url}" class="font-medium underline underline-offset-4" target="_blank">${url}</a>`;
if(monitors[i].description === undefined) {
monitors[i].description = link;
} else {
monitors[i].description = monitors[i].description?.trim() + " " + link;
}
}
} catch (error) {
console.log(error);
}
}
}
monitors[i].path0Day = `${FOLDER}/${folderName}.0day.utc.json`;
monitors[i].hasAPI = hasAPI;

View File

@@ -345,7 +345,7 @@
transition: transform 0.1s ease-in;
cursor: pointer;
}
.oneline:hover {
.oneline:hover {
transform: scaleY(1.2);
}
@@ -356,7 +356,7 @@
.show-hover {
display: none;
top: 30px;
top: 40px;
padding: 0px;
text-align: left;
}

View File

@@ -2,85 +2,128 @@
import Monitor from "$lib/components/monitor.svelte";
import * as Card from "$lib/components/ui/card";
import { Button, buttonVariants } from "$lib/components/ui/button";
import Incident from "$lib/components/incident.svelte";
import { Badge } from "$lib/components/ui/badge";
export let data;
let hasActiveIncidents = data.openIncidents.length > 0;
const imports = {
incident: () => import("$lib/components/incident.svelte"),
};
</script>
<div class="mt-32"></div>
{#if data.site.hero}
<section class="mx-auto mb-8 flex w-full max-w-4xl flex-1 flex-col items-start justify-center">
<div class="mx-auto max-w-screen-xl px-4 lg:flex lg:items-center">
<div class="blurry-bg mx-auto max-w-3xl text-center">
{#if data.site.hero.image}
<img src="{data.site.hero.image}" class="m-auto h-16 w-16" alt="" srcset="" />
{/if} {#if data.site.hero.title}
<h1 class="bg-gradient-to-r from-green-300 via-blue-500 to-purple-600 bg-clip-text text-5xl font-extrabold leading-snug text-transparent">{data.site.hero.title}</h1>
{/if} {#if data.site.hero.subtitle}
<p class="mx-auto mt-4 max-w-xl sm:text-xl">{data.site.hero.subtitle}</p>
{/if}
<section
class="mx-auto mb-8 flex w-full max-w-4xl flex-1 flex-col items-start justify-center"
>
<div class="mx-auto max-w-screen-xl px-4 lg:flex lg:items-center">
<div class="blurry-bg mx-auto max-w-3xl text-center">
{#if data.site.hero.image}
<img
src={data.site.hero.image}
class="m-auto h-16 w-16"
alt=""
srcset=""
/>
{/if}
{#if data.site.hero.title}
<h1
class="bg-gradient-to-r from-green-300 via-blue-500 to-purple-600 bg-clip-text text-5xl font-extrabold leading-snug text-transparent"
>
{data.site.hero.title}
</h1>
{/if}
{#if data.site.hero.subtitle}
<p class="mx-auto mt-4 max-w-xl sm:text-xl">
{data.site.hero.subtitle}
</p>
{/if}
</div>
</div>
</div>
</section>
{/if} {#if hasActiveIncidents}
<section class="mx-auto mb-4 flex w-full max-w-[890px] flex-1 flex-col items-start justify-center bg-transparent" id="">
<div class="grid w-full grid-cols-2 gap-4">
<div class="col-span-2 text-center md:col-span-1 md:text-left">
<Badge variant="outline">Ongoing Incidents</Badge>
</div>
</div>
</section>
<section class="mx-auto mb-8 flex w-full max-w-[890px] flex-1 flex-col items-start justify-center backdrop-blur-[2px]" id="">
{#each data.openIncidents as incident, i} {#await imports["incident"]() then module}
<svelte:component this="{module.default}" {incident} state="close" variant="title+body+comments+monitor" monitor="{incident.monitor}" />
{/await} {/each}
</section>
{/if} {#if data.monitors.length > 0}
<section class="mx-auto mb-4 flex w-full max-w-[890px] flex-1 flex-col items-start justify-center bg-transparent" id="">
<div class="grid w-full grid-cols-2 gap-4">
<div class="col-span-2 text-center md:col-span-1 md:text-left">
<Badge class="" variant="outline"> Availability per Component </Badge>
</div>
<div class="col-span-2 text-center md:col-span-1 md:text-right">
<Badge variant="outline">
<span class="bg-api-up mr-1 inline-flex h-[8px] w-[8px] rounded-full opacity-75"></span>
<span class="mr-3">UP</span>
<span class="bg-api-degraded mr-1 inline-flex h-[8px] w-[8px] rounded-full opacity-75"></span>
<span class="mr-3">DEGRADED</span>
<span class="bg-api-down mr-1 inline-flex h-[8px] w-[8px] rounded-full opacity-75"></span>
<span class="mr-3">DOWN</span>
</Badge>
</div>
</div>
</section>
<section class="mx-auto mb-8 flex w-full max-w-[890px] flex-1 flex-col items-start justify-center backdrop-blur-[2px]">
<Card.Root>
<Card.Content class="monitors-card p-0">
{#each data.monitors as monitor}
<Monitor {monitor} localTz="{data.localTz}" />
{/each}
</Card.Content>
</Card.Root>
</section>
{/if} {#if data.site.categories}
<section class="mx-auto mb-8 w-full max-w-[890px] backdrop-blur-[2px]">
<h2 class="mb-2 mt-2 px-2 text-xl font-semibold">Other Monitors</h2>
{#each data.site.categories as category}
<Card.Root class="mb-2 w-full">
<Card.Header>
<Card.Title>{category.name}</Card.Title>
<Card.Description class="relative pr-[100px]">
{#if category.description} {category.description} {/if}
<a href="/category-{category.name}" class="{buttonVariants({ variant: 'secondary', })} absolute -top-4 right-2"> View </a>
</Card.Description>
</Card.Header>
</Card.Root>
{/each}
</section>
</section>
{/if}
{#if hasActiveIncidents}
<section
class="mx-auto mb-4 flex w-full max-w-[890px] flex-1 flex-col items-start justify-center bg-transparent"
id=""
>
<div class="grid w-full grid-cols-2 gap-4">
<div class="col-span-2 text-center md:col-span-1 md:text-left">
<Badge variant="outline">Ongoing Incidents</Badge>
</div>
</div>
</section>
<section
class="mx-auto mb-8 flex w-full max-w-[890px] flex-1 flex-col items-start justify-center backdrop-blur-[2px]"
id=""
>
{#each data.openIncidents as incident, i}
<Incident {incident} state="close" variant="title+body+comments+monitor" monitor="{incident.monitor}" />
{/each}
</section>
{/if}
{#if data.monitors.length > 0}
<section
class="mx-auto mb-4 flex w-full max-w-[890px] flex-1 flex-col items-start justify-center bg-transparent"
id=""
>
<div class="grid w-full grid-cols-2 gap-4">
<div class="col-span-2 text-center md:col-span-1 md:text-left">
<Badge class="" variant="outline">
Availability per Component
</Badge>
</div>
<div class="col-span-2 text-center md:col-span-1 md:text-right">
<Badge variant="outline">
<span
class="bg-api-up mr-1 inline-flex h-[8px] w-[8px] rounded-full opacity-75"
></span>
<span class="mr-3">UP</span>
<span
class="bg-api-degraded mr-1 inline-flex h-[8px] w-[8px] rounded-full opacity-75"
></span>
<span class="mr-3">DEGRADED</span>
<span
class="bg-api-down mr-1 inline-flex h-[8px] w-[8px] rounded-full opacity-75"
></span>
<span class="mr-3">DOWN</span>
</Badge>
</div>
</div>
</section>
<section
class="mx-auto mb-8 flex w-full max-w-[890px] flex-1 flex-col items-start justify-center backdrop-blur-[2px]"
>
<Card.Root>
<Card.Content class="monitors-card p-0">
{#each data.monitors as monitor}
<Monitor {monitor} localTz={data.localTz} />
{/each}
</Card.Content>
</Card.Root>
</section>
{/if}
{#if data.site.categories}
<section class="mx-auto mb-8 w-full max-w-[890px] backdrop-blur-[2px]">
<h2 class="mb-2 mt-2 px-2 text-xl font-semibold">Other Monitors</h2>
{#each data.site.categories as category}
<Card.Root class="mb-2 w-full">
<Card.Header>
<Card.Title>{category.name}</Card.Title>
<Card.Description class="relative pr-[100px]">
{#if category.description}
{category.description}
{/if}
<a
href="/category-{category.name}"
class="{buttonVariants({
variant: 'secondary',
})} absolute -top-4 right-2"
>
View
</a>
</Card.Description>
</Card.Header>
</Card.Root>
{/each}
</section>
{/if}