mirror of
https://github.com/rajnandan1/kener.git
synced 2026-08-07 07:14:56 +00:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1970cd8891 | |||
| 8ae9b2dec1 | |||
| 1662608984 | |||
| 42693983f1 | |||
| f43048d783 | |||
| b5ec332b4a | |||
| dab1c44b18 | |||
| d9c0bff780 | |||
| beceace2c2 |
@@ -23,6 +23,9 @@ Which version of kener you are using.
|
||||
**Environment**
|
||||
Which environment you are using or where is it deployed. `docker`, `kubernetes`, `bare-metal`, `development`, `pm2` etc
|
||||
|
||||
**Database**
|
||||
Which database you are using. `sqlite`, `mysql`, `postgres`
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
|
||||
+3
-3
@@ -31,7 +31,7 @@ RUN apk add --no-cache --update \
|
||||
g++=14.2.0-r4 \
|
||||
sqlite=3.48.0-r0 \
|
||||
sqlite-dev=3.48.0-r0 \
|
||||
tzdata=2024b-r1 \
|
||||
tzdata \
|
||||
iputils=20240905-r0
|
||||
|
||||
FROM builder-${VARIANT} AS builder
|
||||
@@ -58,7 +58,7 @@ COPY . .
|
||||
# TODO: Reevaluate permissions (possibly reduce?)...
|
||||
# Remove docs directory and ensure required directories exist
|
||||
RUN rm -rf src/routes/\(docs\) && \
|
||||
rm -rf src/static/documentation && \
|
||||
rm -rf static/documentation && \
|
||||
mkdir -p uploads database && \
|
||||
# TODO: Consider changing below to `chmod -R u-rwX,g=rX,o= uploads database`
|
||||
chmod -R 750 uploads database
|
||||
@@ -85,7 +85,7 @@ FROM node:${ALPINE_VERSION} AS final-alpine
|
||||
RUN apk add --no-cache --update \
|
||||
iputils=20240905-r0 \
|
||||
sqlite=3.48.0-r0 \
|
||||
tzdata=2024b-r1
|
||||
tzdata
|
||||
|
||||
FROM final-${VARIANT} AS final
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ npm run dev
|
||||
|
||||
Official Docker images for **Kener** are available on [Docker Hub](https://hub.docker.com/r/rajnandan1/kener). Multiple versions are maintained to support different use cases.
|
||||
|
||||
<a href="https://hub.docker.com/r/rajnandan1/kener/tags?page=1&ordering=last_updated&name=3.1.1"><img src="https://img.shields.io/badge/Latest_Stable_Release-3.1.1-blue" alt="Kener latest stable version: 3.1.1" /></a>
|
||||
<a href="https://hub.docker.com/r/rajnandan1/kener/tags?page=1&ordering=last_updated&name=3.1.4"><img src="https://img.shields.io/badge/Latest_Stable_Release-3.1.4-blue" alt="Kener latest stable version: 3.1.4" /></a>
|
||||
|
||||
#### Available Tags
|
||||
|
||||
@@ -93,38 +93,38 @@ Official Docker images for **Kener** are available on [Docker Hub](https://hub.d
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://hub.docker.com/r/rajnandan1/kener/tags?page=1&ordering=last_updated&name=latest" target="_blank"><code>latest</code></td>
|
||||
<td>Latest stable release (aka 3.1.1)</td>
|
||||
<td>Latest stable release (aka 3.1.4)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://hub.docker.com/r/rajnandan1/kener/tags?page=1&ordering=last_updated&name=3.1.1" target="_blank"><code>3.1.1</code></a></td>
|
||||
<td><a href="https://hub.docker.com/r/rajnandan1/kener/tags?page=1&ordering=last_updated&name=3.1.4" target="_blank"><code>3.1.4</code></a></td>
|
||||
<td>Specific release version</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://hub.docker.com/r/rajnandan1/kener/tags?page=1&ordering=last_updated&name=3.1" target="_blank"><code>3.1</code></a></td>
|
||||
<td>Major-minor version tag pointing to the latest patch (3.1.1) release within that minor version (3.1.x)</td>
|
||||
<td>Major-minor version tag pointing to the latest patch (3.1.4) release within that minor version (3.1.x)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://hub.docker.com/r/rajnandan1/kener/tags?page=1&ordering=last_updated&name=3" target="_blank"><code>3</code></a></td>
|
||||
<td>Major version tag pointing to the latest stable (3.1.1) release within that major version (3.x.x)</td>
|
||||
<td>Major version tag pointing to the latest stable (3.1.4) release within that major version (3.x.x)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left" colspan="2" style="color:#0D597F;text-align:left;">Alpine Linux 3.21 w/ Node.js v23.7.0 <strong><em>(smallest image size)</em></strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://hub.docker.com/r/rajnandan1/kener/tags?page=1&ordering=last_updated&name=alpine" target="_blank"><code>alpine</code></td>
|
||||
<td>Latest stable release (aka 3.1.1)</td>
|
||||
<td>Latest stable release (aka 3.1.4)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://hub.docker.com/r/rajnandan1/kener/tags?page=1&ordering=last_updated&name=3.1.1-alpine" target="_blank"><code>3.1.1-alpine</code></a></td>
|
||||
<td><a href="https://hub.docker.com/r/rajnandan1/kener/tags?page=1&ordering=last_updated&name=3.1.4-alpine" target="_blank"><code>3.1.4-alpine</code></a></td>
|
||||
<td>Specific release version</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://hub.docker.com/r/rajnandan1/kener/tags?page=1&ordering=last_updated&name=3.1-alpine" target="_blank"><code>3.1-alpine</code></a></td>
|
||||
<td>Major-minor version tag pointing to the latest patch (3.1.1) release within that minor version (3.1.x)</td>
|
||||
<td>Major-minor version tag pointing to the latest patch (3.1.4) release within that minor version (3.1.x)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://hub.docker.com/r/rajnandan1/kener/tags?page=1&ordering=last_updated&name=3-alpine" target="_blank"><code>3-alpine</code></a></td>
|
||||
<td>Major version tag pointing to the latest stable (3.1.1) release within that major version (3.x.x)</td>
|
||||
<td>Major version tag pointing to the latest stable (3.1.4) release within that major version (3.x.x)</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "kener",
|
||||
"version": "3.1.2",
|
||||
"version": "3.1.5",
|
||||
"private": false,
|
||||
"license": "MIT",
|
||||
"description": "Kener: An open-source Node.js status page application for real-time service monitoring, incident management, and customizable reporting. Simplify service outage tracking, enhance incident communication, and ensure a seamless user experience.",
|
||||
|
||||
@@ -8,13 +8,12 @@ const SITE = "./config/site.yaml";
|
||||
const UP = "UP";
|
||||
const DOWN = "DOWN";
|
||||
const DEGRADED = "DEGRADED";
|
||||
const NO_DATA = "NO_DATA";
|
||||
const API_TIMEOUT = 10 * 1000; // 10 seconds
|
||||
const AnalyticsProviders = {
|
||||
GA: "https://unpkg.com/@analytics/google-analytics@1.0.7/dist/@analytics/google-analytics.min.js",
|
||||
AMPLITUDE:
|
||||
"https://unpkg.com/@analytics/amplitude@0.1.3/dist/@analytics/amplitude.min.js",
|
||||
MIXPANEL:
|
||||
"https://unpkg.com/@analytics/mixpanel@0.4.0/dist/@analytics/mixpanel.min.js",
|
||||
AMPLITUDE: "https://unpkg.com/@analytics/amplitude@0.1.3/dist/@analytics/amplitude.min.js",
|
||||
MIXPANEL: "https://unpkg.com/@analytics/mixpanel@0.4.0/dist/@analytics/mixpanel.min.js",
|
||||
};
|
||||
const AllRecordTypes = {
|
||||
A: 1,
|
||||
@@ -117,4 +116,5 @@ export {
|
||||
TIMEOUT,
|
||||
ERROR,
|
||||
MANUAL,
|
||||
NO_DATA,
|
||||
};
|
||||
|
||||
@@ -17,6 +17,7 @@ import bcrypt from "bcrypt";
|
||||
import jwt from "jsonwebtoken";
|
||||
import crypto from "crypto";
|
||||
import { format, subMonths, addMonths, startOfMonth } from "date-fns";
|
||||
import { UP, DOWN, DEGRADED, NO_DATA } from "../constants.js";
|
||||
|
||||
const saltRounds = 10;
|
||||
const DUMMY_SECRET = "DUMMY_SECRET";
|
||||
@@ -199,17 +200,28 @@ export const CreateUpdateMonitor = async (monitor) => {
|
||||
export const GetMonitors = async (data) => {
|
||||
return await db.getMonitors(data);
|
||||
};
|
||||
export const GetMonitorsParsed = async (data) => {
|
||||
let monitors = await db.getMonitors(data);
|
||||
let parsedMonitors = monitors.map((m) => {
|
||||
let parsedMonitor = { ...m };
|
||||
if (!!parsedMonitor.type_data) {
|
||||
parsedMonitor.type_data = JSON.parse(parsedMonitor.type_data);
|
||||
export const GetMonitorsParsed = async (query) => {
|
||||
// Retrieve monitors from the database based on the provided query
|
||||
const rawMonitors = await db.getMonitors(query);
|
||||
|
||||
// Parse type_data if available for each monitor
|
||||
const parsedMonitors = rawMonitors.map((monitor) => {
|
||||
const monitorData = { ...monitor };
|
||||
|
||||
if (monitorData.type_data) {
|
||||
try {
|
||||
monitorData.type_data = JSON.parse(monitorData.type_data);
|
||||
} catch (error) {
|
||||
// Fallback to an empty object if JSON parsing fails
|
||||
monitorData.type_data = {};
|
||||
}
|
||||
} else {
|
||||
parsedMonitor.type_data = {};
|
||||
monitorData.type_data = {};
|
||||
}
|
||||
return parsedMonitor;
|
||||
|
||||
return monitorData;
|
||||
});
|
||||
|
||||
return parsedMonitors;
|
||||
};
|
||||
|
||||
@@ -386,31 +398,32 @@ export const HashString = (str) => {
|
||||
return hash.digest("hex");
|
||||
};
|
||||
|
||||
export const InterpolateData = (data, start, anchorStatus, e) => {
|
||||
let finalData = [];
|
||||
let status = anchorStatus || "UP";
|
||||
let end = start;
|
||||
if (!!data && data.length > 0) {
|
||||
end = data[data.length - 1].timestamp;
|
||||
export const InterpolateData = (rawData, startTimestamp, initialStatus, overrideEndTimestamp) => {
|
||||
const interpolatedData = [];
|
||||
let currentStatus = initialStatus || "UP";
|
||||
let endTimestamp = startTimestamp;
|
||||
|
||||
if (rawData && rawData.length > 0) {
|
||||
endTimestamp = rawData[rawData.length - 1].timestamp;
|
||||
}
|
||||
if (overrideEndTimestamp) {
|
||||
endTimestamp = overrideEndTimestamp;
|
||||
}
|
||||
|
||||
if (e) {
|
||||
end = e;
|
||||
}
|
||||
let dataMap = data.reduce((acc, d) => {
|
||||
acc[d.timestamp] = d;
|
||||
return acc;
|
||||
const dataByTimestamp = rawData.reduce((accumulator, entry) => {
|
||||
accumulator[entry.timestamp] = entry;
|
||||
return accumulator;
|
||||
}, {});
|
||||
|
||||
for (let i = start; i <= end; i += 60) {
|
||||
let nowData = dataMap[i];
|
||||
if (!!nowData) {
|
||||
status = nowData.status;
|
||||
for (let timestamp = startTimestamp; timestamp <= endTimestamp; timestamp += 60) {
|
||||
const currentEntry = dataByTimestamp[timestamp];
|
||||
if (currentEntry) {
|
||||
currentStatus = currentEntry.status;
|
||||
}
|
||||
|
||||
finalData.push({ timestamp: i, status: status });
|
||||
interpolatedData.push({ timestamp, status: currentStatus });
|
||||
}
|
||||
return finalData;
|
||||
|
||||
return interpolatedData;
|
||||
};
|
||||
|
||||
export const GetLastStatusBefore = async (monitor_tag, timestamp) => {
|
||||
@@ -418,7 +431,7 @@ export const GetLastStatusBefore = async (monitor_tag, timestamp) => {
|
||||
if (data) {
|
||||
return data.status;
|
||||
}
|
||||
return "UP";
|
||||
return NO_DATA;
|
||||
};
|
||||
|
||||
export const GetDataGroupByDayAlternative = async (monitor_tag, start, end, timezoneOffsetMinutes = 0) => {
|
||||
@@ -443,16 +456,13 @@ export const GetDataGroupByDayAlternative = async (monitor_tag, start, end, time
|
||||
UP: 0,
|
||||
DOWN: 0,
|
||||
DEGRADED: 0,
|
||||
latencySum: 0,
|
||||
latencies: [],
|
||||
NO_DATA: 0,
|
||||
};
|
||||
}
|
||||
|
||||
const group = acc[dayGroup];
|
||||
group.total++;
|
||||
group[row.status]++;
|
||||
group.latencySum += row.latency;
|
||||
group.latencies.push(row.latency);
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
@@ -464,9 +474,7 @@ export const GetDataGroupByDayAlternative = async (monitor_tag, start, end, time
|
||||
UP: group.UP,
|
||||
DOWN: group.DOWN,
|
||||
DEGRADED: group.DEGRADED,
|
||||
avgLatency: group.total > 0 ? Number((group.latencySum / group.total).toFixed(3)) : null,
|
||||
maxLatency: group.latencies.length > 0 ? Number(Math.max(...group.latencies).toFixed(3)) : null,
|
||||
minLatency: group.latencies.length > 0 ? Number(Math.min(...group.latencies).toFixed(3)) : null,
|
||||
NO_DATA: group.NO_DATA,
|
||||
}));
|
||||
};
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ class DbImpl {
|
||||
.whereIn("monitor_tag", monitor_tags_arr)
|
||||
.where("timestamp", "=", timestamp)
|
||||
.groupBy("timestamp")
|
||||
.having("total_entries", "=", monitor_tags_arr.length)
|
||||
.havingRaw("COUNT(*) = ?", [monitor_tags_arr.length])
|
||||
.orderBy("timestamp", "desc")
|
||||
.limit(1)
|
||||
.first();
|
||||
|
||||
+156
-171
@@ -2,211 +2,196 @@
|
||||
// @ts-ignore
|
||||
import db from "$lib/server/db/db.js";
|
||||
import {
|
||||
GetMinuteStartNowTimestampUTC,
|
||||
BeginningOfDay,
|
||||
StatusObj,
|
||||
ParseUptime,
|
||||
GetDayStartTimestampUTC
|
||||
GetMinuteStartNowTimestampUTC,
|
||||
BeginningOfDay,
|
||||
StatusObj,
|
||||
ParseUptime,
|
||||
GetDayStartTimestampUTC,
|
||||
} from "$lib/server/tool.js";
|
||||
import { formatDuration, intervalToDuration } from "date-fns";
|
||||
import { fdm, l, summaryTime } from "$lib/i18n/client";
|
||||
import {
|
||||
GetDataGroupByDayAlternative,
|
||||
InterpolateData,
|
||||
GetLastStatusBefore
|
||||
GetDataGroupByDayAlternative,
|
||||
InterpolateData,
|
||||
GetLastStatusBefore,
|
||||
} from "$lib/server/controllers/controller.js";
|
||||
import { UP, DOWN, DEGRADED, NO_DATA } from "$lib/server/constants.js";
|
||||
|
||||
function getSummaryDuration(numOfMinute, selectedLang) {
|
||||
// Convert minutes to milliseconds and create duration object
|
||||
const duration = intervalToDuration({
|
||||
start: new Date(0),
|
||||
end: new Date(numOfMinute * 60 * 1000)
|
||||
});
|
||||
// Convert minutes to milliseconds and create duration object
|
||||
const duration = intervalToDuration({
|
||||
start: new Date(0),
|
||||
end: new Date(numOfMinute * 60 * 1000),
|
||||
});
|
||||
|
||||
return fdm(duration, selectedLang);
|
||||
return fdm(duration, selectedLang);
|
||||
}
|
||||
function getTimezoneOffset(timeZone) {
|
||||
const formatter = new Intl.DateTimeFormat("en-US", { timeZone, timeZoneName: "short" });
|
||||
const parts = formatter.formatToParts(new Date());
|
||||
const timeZoneOffset = parts.find((part) => part.type === "timeZoneName")?.value;
|
||||
const formatter = new Intl.DateTimeFormat("en-US", { timeZone, timeZoneName: "short" });
|
||||
const parts = formatter.formatToParts(new Date());
|
||||
const timeZoneOffset = parts.find((part) => part.type === "timeZoneName")?.value;
|
||||
|
||||
const match = timeZoneOffset.match(/([+-]\d{2}):?(\d{2})/);
|
||||
if (!match) return 0;
|
||||
const match = timeZoneOffset.match(/([+-]\d{2}):?(\d{2})/);
|
||||
if (!match) return 0;
|
||||
|
||||
const [, hours, minutes] = match;
|
||||
return parseInt(hours) * 60 + parseInt(minutes);
|
||||
const [, hours, minutes] = match;
|
||||
return parseInt(hours) * 60 + parseInt(minutes);
|
||||
}
|
||||
|
||||
function returnStatusClass(val, total, c, barStyle) {
|
||||
// return "api-degraded-10";
|
||||
if (barStyle === undefined || barStyle == "FULL") {
|
||||
return c;
|
||||
} else if (barStyle == "PARTIAL") {
|
||||
let totalHeight = total;
|
||||
let cl = `api-up`;
|
||||
if (val > 0 && val <= 0.1 * totalHeight) {
|
||||
cl = c + "-10";
|
||||
} else if (val > 0.1 * totalHeight && val <= 0.2 * totalHeight) {
|
||||
cl = c + "-20";
|
||||
} else if (val > 0.2 * totalHeight && val <= 0.4 * totalHeight) {
|
||||
cl = c + "-40";
|
||||
} else if (val > 0.4 * totalHeight && val <= 0.6 * totalHeight) {
|
||||
cl = c + "-60";
|
||||
} else if (val > 0.6 * totalHeight && val <= 0.8 * totalHeight) {
|
||||
cl = c + "-80";
|
||||
} else if (val > 0.8 * totalHeight && val < totalHeight) {
|
||||
cl = c + "-90";
|
||||
} else if (val == totalHeight) {
|
||||
cl = c;
|
||||
}
|
||||
return cl;
|
||||
}
|
||||
return c;
|
||||
// return "api-degraded-10";
|
||||
if (barStyle === undefined || barStyle == "FULL") {
|
||||
return c;
|
||||
} else if (barStyle == "PARTIAL") {
|
||||
let totalHeight = total;
|
||||
let cl = `api-up`;
|
||||
if (val > 0 && val <= 0.1 * totalHeight) {
|
||||
cl = c + "-10";
|
||||
} else if (val > 0.1 * totalHeight && val <= 0.2 * totalHeight) {
|
||||
cl = c + "-20";
|
||||
} else if (val > 0.2 * totalHeight && val <= 0.4 * totalHeight) {
|
||||
cl = c + "-40";
|
||||
} else if (val > 0.4 * totalHeight && val <= 0.6 * totalHeight) {
|
||||
cl = c + "-60";
|
||||
} else if (val > 0.6 * totalHeight && val <= 0.8 * totalHeight) {
|
||||
cl = c + "-80";
|
||||
} else if (val > 0.8 * totalHeight && val < totalHeight) {
|
||||
cl = c + "-90";
|
||||
} else if (val == totalHeight) {
|
||||
cl = c;
|
||||
}
|
||||
return cl;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
function getCountOfSimilarStatuesEnd(arr, statusType) {
|
||||
let count = 0;
|
||||
for (let i = arr.length - 1; i >= 0; i--) {
|
||||
if (arr[i].status === statusType) {
|
||||
count++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
let count = 0;
|
||||
for (let i = arr.length - 1; i >= 0; i--) {
|
||||
if (arr[i].status === statusType) {
|
||||
count++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
const FetchData = async function (site, monitor, localTz, selectedLang, lang) {
|
||||
const secondsInDay = 24 * 60 * 60;
|
||||
//get offset from utc in minutes
|
||||
const now = GetMinuteStartNowTimestampUTC() + 60;
|
||||
const midnight = BeginningOfDay({ timeZone: localTz });
|
||||
const midnight90DaysAgo = midnight - 90 * 24 * 60 * 60;
|
||||
const NO_DATA = "No Data";
|
||||
const midnightTomorrow = midnight + secondsInDay;
|
||||
let offsetInMinutes = parseInt((GetDayStartTimestampUTC(now) - midnight) / 60);
|
||||
const _90Day = {};
|
||||
let latestTimestamp = 0;
|
||||
const secondsInDay = 24 * 60 * 60;
|
||||
//get offset from utc in minutes
|
||||
const now = GetMinuteStartNowTimestampUTC() + 60;
|
||||
const midnight = BeginningOfDay({ timeZone: localTz });
|
||||
const midnight90DaysAgo = midnight - 90 * 24 * 60 * 60;
|
||||
const NO_DATA = "No Data";
|
||||
const midnightTomorrow = midnight + secondsInDay;
|
||||
let offsetInMinutes = parseInt((GetDayStartTimestampUTC(now) - midnight) / 60);
|
||||
const _90Day = {};
|
||||
let latestTimestamp = 0;
|
||||
|
||||
let dbData = await GetDataGroupByDayAlternative(
|
||||
monitor.tag,
|
||||
midnight90DaysAgo,
|
||||
midnightTomorrow,
|
||||
offsetInMinutes
|
||||
);
|
||||
let totalDegradedCount = 0;
|
||||
let totalDownCount = 0;
|
||||
let totalUpCount = 0;
|
||||
let dbData = await GetDataGroupByDayAlternative(monitor.tag, midnight90DaysAgo, midnightTomorrow, offsetInMinutes);
|
||||
let totalDegradedCount = 0;
|
||||
let totalDownCount = 0;
|
||||
let totalUpCount = 0;
|
||||
|
||||
for (let i = 0; i < dbData.length; i++) {
|
||||
let dayData = dbData[i];
|
||||
let ts = dayData.timestamp;
|
||||
let cssClass = StatusObj.UP;
|
||||
let summaryDuration = 0;
|
||||
let summaryStatus = "UP";
|
||||
latestTimestamp = ts;
|
||||
totalDegradedCount += dayData.DEGRADED;
|
||||
totalDownCount += dayData.DOWN;
|
||||
totalUpCount += dayData.UP;
|
||||
for (let i = 0; i < dbData.length; i++) {
|
||||
let dayData = dbData[i];
|
||||
let ts = dayData.timestamp;
|
||||
let cssClass = StatusObj.UP;
|
||||
let summaryDuration = 0;
|
||||
let summaryStatus = UP;
|
||||
latestTimestamp = ts;
|
||||
totalDegradedCount += dayData.DEGRADED;
|
||||
totalDownCount += dayData.DOWN;
|
||||
totalUpCount += dayData.UP;
|
||||
|
||||
if (dayData.DEGRADED >= monitor.day_degraded_minimum_count) {
|
||||
cssClass = returnStatusClass(
|
||||
dayData.DEGRADED,
|
||||
dayData.total,
|
||||
StatusObj.DEGRADED,
|
||||
site.barStyle
|
||||
);
|
||||
summaryDuration = getSummaryDuration(dayData.DEGRADED, selectedLang);
|
||||
summaryStatus = "DEGRADED";
|
||||
}
|
||||
if (dayData.DOWN >= monitor.day_down_minimum_count) {
|
||||
cssClass = returnStatusClass(
|
||||
dayData.DOWN,
|
||||
dayData.total,
|
||||
StatusObj.DOWN,
|
||||
site.barStyle
|
||||
);
|
||||
if (dayData.DEGRADED >= monitor.day_degraded_minimum_count) {
|
||||
cssClass = returnStatusClass(dayData.DEGRADED, dayData.total, StatusObj.DEGRADED, site.barStyle);
|
||||
summaryDuration = getSummaryDuration(dayData.DEGRADED, selectedLang);
|
||||
summaryStatus = "DEGRADED";
|
||||
}
|
||||
if (dayData.DOWN >= monitor.day_down_minimum_count) {
|
||||
cssClass = returnStatusClass(dayData.DOWN, dayData.total, StatusObj.DOWN, site.barStyle);
|
||||
|
||||
summaryDuration = getSummaryDuration(dayData.DOWN, selectedLang);
|
||||
summaryStatus = "DOWN";
|
||||
}
|
||||
summaryDuration = getSummaryDuration(dayData.DOWN, selectedLang);
|
||||
summaryStatus = "DOWN";
|
||||
}
|
||||
if (dayData.NO_DATA === dayData.total) {
|
||||
cssClass = StatusObj.NO_DATA;
|
||||
summaryStatus = NO_DATA;
|
||||
}
|
||||
|
||||
_90Day[ts] = {
|
||||
border: true
|
||||
};
|
||||
_90Day[ts] = {
|
||||
border: true,
|
||||
};
|
||||
|
||||
_90Day[ts].timestamp = ts;
|
||||
_90Day[ts].cssClass = cssClass;
|
||||
_90Day[ts].summaryStatus = l(lang, summaryTime(summaryStatus), {
|
||||
status: l(lang, summaryStatus),
|
||||
duration: summaryDuration
|
||||
});
|
||||
_90Day[ts].timestamp = ts;
|
||||
_90Day[ts].cssClass = cssClass;
|
||||
_90Day[ts].summaryStatus = l(lang, summaryTime(summaryStatus), {
|
||||
status: l(lang, summaryStatus),
|
||||
duration: summaryDuration,
|
||||
});
|
||||
|
||||
_90Day[ts].textClass = cssClass.replace(/-\d+$/, "");
|
||||
}
|
||||
let uptime90DayNumerator = totalUpCount + totalDegradedCount;
|
||||
let uptime90DayDenominator = totalUpCount + totalDownCount + totalDegradedCount;
|
||||
_90Day[ts].textClass = cssClass.replace(/-\d+$/, "");
|
||||
}
|
||||
|
||||
//remove degraded from uptime
|
||||
if (monitor.include_degraded_in_downtime === "YES") {
|
||||
uptime90DayNumerator = totalUpCount;
|
||||
}
|
||||
// return _90Day;
|
||||
let uptime90Day = ParseUptime(uptime90DayNumerator, uptime90DayDenominator);
|
||||
let uptime90DayNumerator = totalUpCount + totalDegradedCount;
|
||||
let uptime90DayDenominator = totalUpCount + totalDownCount + totalDegradedCount;
|
||||
|
||||
let summaryDuration = 0;
|
||||
let summaryStatus = "UP";
|
||||
//remove degraded from uptime
|
||||
if (monitor.include_degraded_in_downtime === "YES") {
|
||||
uptime90DayNumerator = totalUpCount;
|
||||
}
|
||||
// return _90Day;
|
||||
let uptime90Day = ParseUptime(uptime90DayNumerator, uptime90DayDenominator);
|
||||
|
||||
let summaryColorClass = "api-nodata";
|
||||
let summaryDuration = 0;
|
||||
let summaryStatus = "UP";
|
||||
|
||||
let todayDataDb = await db.getMonitoringData(monitor.tag, midnight, midnight + secondsInDay);
|
||||
let anchorStatus = await GetLastStatusBefore(monitor.tag, midnight);
|
||||
todayDataDb = InterpolateData(
|
||||
todayDataDb,
|
||||
midnight,
|
||||
anchorStatus,
|
||||
todayDataDb.length == 0 ? midnight + secondsInDay : null
|
||||
);
|
||||
let summaryColorClass = "api-nodata";
|
||||
|
||||
if (site.summaryStyle === "CURRENT") {
|
||||
summaryColorClass = "api-up";
|
||||
let todayDataDb = await db.getMonitoringData(monitor.tag, midnight, midnight + secondsInDay);
|
||||
let anchorStatus = await GetLastStatusBefore(monitor.tag, midnight);
|
||||
todayDataDb = InterpolateData(
|
||||
todayDataDb,
|
||||
midnight,
|
||||
anchorStatus,
|
||||
todayDataDb.length == 0 ? midnight + secondsInDay : null,
|
||||
);
|
||||
|
||||
let lastRow = todayDataDb[todayDataDb.length - 1];
|
||||
if (site.summaryStyle === "CURRENT") {
|
||||
summaryColorClass = "api-up";
|
||||
|
||||
if (!!lastRow && lastRow.status == "DEGRADED") {
|
||||
summaryDuration = getSummaryDuration(
|
||||
getCountOfSimilarStatuesEnd(todayDataDb, "DEGRADED"),
|
||||
selectedLang
|
||||
);
|
||||
summaryStatus = "DEGRADED";
|
||||
summaryColorClass = "api-degraded";
|
||||
}
|
||||
if (!!lastRow && lastRow.status == "DOWN") {
|
||||
summaryDuration = getSummaryDuration(
|
||||
getCountOfSimilarStatuesEnd(todayDataDb, "DOWN"),
|
||||
selectedLang
|
||||
);
|
||||
summaryStatus = "DOWN";
|
||||
summaryColorClass = "api-down";
|
||||
}
|
||||
summaryStatus = l(lang, summaryTime(summaryStatus), {
|
||||
status: l(lang, summaryStatus),
|
||||
duration: summaryDuration
|
||||
});
|
||||
} else {
|
||||
let lastData = _90Day[latestTimestamp];
|
||||
summaryColorClass = lastData.cssClass.replace(/-\d+$/, "");
|
||||
summaryDuration = lastData.summaryDuration;
|
||||
summaryStatus = lastData.summaryStatus;
|
||||
}
|
||||
return {
|
||||
_90Day: _90Day,
|
||||
uptime90Day: uptime90Day,
|
||||
summaryStatus: summaryStatus,
|
||||
summaryColorClass: summaryColorClass,
|
||||
barRoundness: site.barRoundness,
|
||||
midnight90DaysAgo: midnight90DaysAgo,
|
||||
midnightTomorrow: midnightTomorrow
|
||||
};
|
||||
let lastRow = todayDataDb[todayDataDb.length - 1];
|
||||
|
||||
if (!!lastRow && lastRow.status == "DEGRADED") {
|
||||
summaryDuration = getSummaryDuration(getCountOfSimilarStatuesEnd(todayDataDb, "DEGRADED"), selectedLang);
|
||||
summaryStatus = "DEGRADED";
|
||||
summaryColorClass = "api-degraded";
|
||||
}
|
||||
if (!!lastRow && lastRow.status == "DOWN") {
|
||||
summaryDuration = getSummaryDuration(getCountOfSimilarStatuesEnd(todayDataDb, "DOWN"), selectedLang);
|
||||
summaryStatus = "DOWN";
|
||||
summaryColorClass = "api-down";
|
||||
}
|
||||
summaryStatus = l(lang, summaryTime(summaryStatus), {
|
||||
status: l(lang, summaryStatus),
|
||||
duration: summaryDuration,
|
||||
});
|
||||
} else {
|
||||
let lastData = _90Day[latestTimestamp];
|
||||
summaryColorClass = lastData.cssClass.replace(/-\d+$/, "");
|
||||
summaryDuration = lastData.summaryDuration;
|
||||
summaryStatus = lastData.summaryStatus;
|
||||
}
|
||||
return {
|
||||
_90Day: _90Day,
|
||||
uptime90Day: uptime90Day,
|
||||
summaryStatus: summaryStatus,
|
||||
summaryColorClass: summaryColorClass,
|
||||
barRoundness: site.barRoundness,
|
||||
midnight90DaysAgo: midnight90DaysAgo,
|
||||
midnightTomorrow: midnightTomorrow,
|
||||
};
|
||||
};
|
||||
export { FetchData };
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
<!-- Document Icon - Replace with your own logo -->
|
||||
<img src="https://kener.ing/logo.png" class="h-8 w-8" alt="" />
|
||||
<span class="text-xl font-medium">Kener Documentation</span>
|
||||
<span class="me-2 rounded border px-2.5 py-0.5 text-xs font-medium"> 3.1.2 </span>
|
||||
<span class="me-2 rounded border px-2.5 py-0.5 text-xs font-medium"> 3.1.5 </span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
export let data;
|
||||
let subHeadings = [];
|
||||
let previousH2 = null;
|
||||
let subPath = data.docFilePath.split(".md")[0];
|
||||
|
||||
function fillSubHeadings() {
|
||||
subHeadings = [];
|
||||
const headings = document.querySelectorAll("#markdown h2, #markdown h3");
|
||||
@@ -48,7 +48,7 @@
|
||||
<svg class="check-btn absolute left-0 top-0" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="green" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check"><path d="M20 6 9 17l-5-5"/></svg>`;
|
||||
button.style = "margin-left: 10px;height:16px;width:16px background-color: transparent; cursor: pointer;";
|
||||
button.onclick = () => {
|
||||
navigator.clipboard.writeText(`https://kener.ing/docs${subPath}#` + heading.id);
|
||||
navigator.clipboard.writeText(`https://kener.ing/docs${data.docFilePath.split(".md")[0]}#` + heading.id);
|
||||
};
|
||||
heading.appendChild(button);
|
||||
});
|
||||
@@ -121,7 +121,7 @@
|
||||
<svelte:head>
|
||||
<title>{data.title}</title>
|
||||
<meta name="description" content={data.description} />
|
||||
<link rel="canonical" href="https://kener.ing/docs{subPath}" />
|
||||
<link rel="canonical" href="https://kener.ing/docs{data.docFilePath.split('.md')[0]}" />
|
||||
</svelte:head>
|
||||
|
||||
<div id="markdown">
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
<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.hero.image}
|
||||
<GMI src={data.site.hero.image} classList="m-auto mb-2 h-14 w-14" alt="" srcset="" />
|
||||
<GMI src={data.hero.image} classList="m-auto mb-2 h-14 w-14" alt="" srcset="" />
|
||||
{/if}
|
||||
{#if data.hero.title}
|
||||
<h1
|
||||
|
||||
Reference in New Issue
Block a user