diff --git a/scripts/cron-minute.js b/scripts/cron-minute.js index 5d5cf7b..96ed0d3 100644 --- a/scripts/cron-minute.js +++ b/scripts/cron-minute.js @@ -121,9 +121,15 @@ const OneMinuteFetch = async (envSecrets, url, method, headers, body, timeout, c 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(originalData, null, 2)); + fs.writeFileSync(out, JSON.stringify(sortedDay0, null, 2)); } catch (error) { console.error(error); } diff --git a/scripts/startup.js b/scripts/startup.js index 43e4bee..1f62809 100644 --- a/scripts/startup.js +++ b/scripts/startup.js @@ -78,12 +78,13 @@ const Startup = async () => { let name = monitor.name; let url = monitor.url; let method = monitor.method; + let tag = monitor.tag; let hasAPI = false; let folderName = name.replace(/[^a-z0-9]/gi, "-").toLowerCase(); monitors[i].folderName = folderName; - if (!name) { - console.log("name, url, method are required"); + if (!name || !tag) { + console.log("name, tag are required"); process.exit(1); } @@ -141,6 +142,10 @@ const Startup = async () => { 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); diff --git a/src/kener.css b/src/kener.css index 618ac5b..a3f0748 100644 --- a/src/kener.css +++ b/src/kener.css @@ -70,7 +70,7 @@ section { cursor: pointer; } .oneline:hover { - transform: scaleY(1.1); + transform: scaleY(1.2); } .oneline.hover { diff --git a/src/lib/server/webhook.js b/src/lib/server/webhook.js new file mode 100644 index 0000000..18e9350 --- /dev/null +++ b/src/lib/server/webhook.js @@ -0,0 +1,98 @@ +// @ts-nocheck +import fs from "fs-extra" +import { env } from "$env/dynamic/public"; +import moment from "moment"; +const WEBHOOK_TOKEN = process.env.WEBHOOK_TOKEN; +const WEBHOOK_IP = process.env.WEBHOOK_IP; + +const store = function(data, authHeader, ip){ + const tag = data.tag; + //remove Bearer from start in authHeader + const authToken = authHeader.replace("Bearer ", ""); + if (authToken !== WEBHOOK_TOKEN) { + return { error: "invalid token", status: 401 }; + } + + if (WEBHOOK_IP !== undefined && ip != "" && ip !== WEBHOOK_IP) { + return { error: "invalid ip", status: 401 }; + } + const resp = {}; + if (data.status === undefined || ["UP", "DOWN", "DEGRADED"].indexOf(data.status) === -1) { + return { error: "status missing", status: 400 }; + } + if (data.latency === undefined || isNaN(data.latency)) { + return { error: "latency missing or not a number", status: 400 }; + } + if (data.timestampInSeconds !== undefined && isNaN(data.timestampInSeconds)) { + return { error: "timestampInSeconds not a number", status: 400 }; + } + if (data.timestampInSeconds === undefined) { + data.timestampInSeconds = Math.floor(Date.now() / 1000); + } + resp.status = data.status; + resp.latency = data.latency; + resp.type = "webhook"; + let timestampISO = moment().toISOString(); + try { + timestampISO = moment.unix(data.timestampInSeconds).toISOString(); + //throw error if timestampISO is future or older than 90days + if (moment(timestampISO).isAfter(moment().add(1, "minute"))) { + throw new Error("timestampInSeconds is in future"); + } + if (moment(timestampISO).isBefore(moment().subtract(90, "days"))) { + throw new Error("timestampInSeconds is older than 90days"); + } + } catch (err) { + return { error: err.message, status: 400 }; + } + //check if tag is valid + let tags = []; + let monitors = []; + try { + monitors = JSON.parse(fs.readFileSync(env.PUBLIC_KENER_FOLDER + "/monitors.json", "utf8")); + tags = monitors.map((monitor) => monitor.tag); + if (tags.indexOf(tag) == -1) { + throw new Error("not a valid tag"); + } + } catch (err) { + return { error: err.message, status: 400 }; + } + + //get the monitor object matching the tag + const monitor = monitors.find((monitor) => monitor.tag === tag); + + //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 }; + } + + + + let timeStampISOMinute = moment(timestampISO).startOf('minute').toISOString(); + + 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]; + }); + + + //write the monitor.path0Day file + fs.writeFileSync(monitor.path0Day, JSON.stringify(sortedDay0, null, 2)); + + + return { status: 200, message: "success at " + timeStampISOMinute }; +} +export { store }; diff --git a/src/routes/api/webhook/+server.js b/src/routes/api/webhook/+server.js new file mode 100644 index 0000000..d0b90aa --- /dev/null +++ b/src/routes/api/webhook/+server.js @@ -0,0 +1,19 @@ +// @ts-nocheck +// @ts-ignore +import { json } from "@sveltejs/kit"; +import { store } from "$lib/server/webhook"; +export async function POST({ request }) { + const payload = await request.json(); + // const headers = await request.headers(); + const authorization = request.headers.get('authorization'); + let ip = "" + try { + ip = request.headers.get("x-forwarded-for") || request.socket.remoteAddress || request.headers.get("x-real-ip") + } catch(err){ + console.log("IP Not Found " + err.message) + } + let resp = store(payload, authorization, ip); + return json(resp, { + status: resp.status, + }); +} \ No newline at end of file