mirror of
https://github.com/rajnandan1/kener.git
synced 2026-08-07 07:14:56 +00:00
fix: Centralize and validate the monitoring status-filter contract
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { isMonitoringStatus } from "./global-constants.js";
|
||||
|
||||
describe("isMonitoringStatus", () => {
|
||||
it("accepts only statuses supported by the monitoring filter", () => {
|
||||
expect(isMonitoringStatus("UP")).toBe(true);
|
||||
expect(isMonitoringStatus("DOWN")).toBe(true);
|
||||
expect(isMonitoringStatus("DEGRADED")).toBe(true);
|
||||
expect(isMonitoringStatus("MAINTENANCE")).toBe(false);
|
||||
expect(isMonitoringStatus("invalid")).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -3,7 +3,13 @@ export const BADGE_STYLES = ["flat", "plastic", "flat-square", "for-the-badge",
|
||||
export type BadgeStyle = (typeof BADGE_STYLES)[number];
|
||||
|
||||
// Status types
|
||||
export type StatusType = "UP" | "DOWN" | "DEGRADED" | "MAINTENANCE" | "NO_DATA";
|
||||
export const MONITORING_STATUSES = ["UP", "DOWN", "DEGRADED"] as const;
|
||||
export type MonitoringStatus = (typeof MONITORING_STATUSES)[number];
|
||||
export type StatusType = MonitoringStatus | "MAINTENANCE" | "NO_DATA";
|
||||
|
||||
export function isMonitoringStatus(value: unknown): value is MonitoringStatus {
|
||||
return typeof value === "string" && MONITORING_STATUSES.includes(value as MonitoringStatus);
|
||||
}
|
||||
|
||||
// Incident states
|
||||
export type IncidentState = "INVESTIGATING" | "IDENTIFIED" | "MONITORING" | "RESOLVED";
|
||||
|
||||
@@ -21,7 +21,7 @@ import type {
|
||||
import type { MonitorFilter } from "../db/repositories/base.js";
|
||||
import db from "../db/db.js";
|
||||
import type { PaginationInput } from "../../types/common.js";
|
||||
import GC, { getBadgeStyle, type BadgeStyle } from "../../global-constants.js";
|
||||
import GC, { getBadgeStyle, type BadgeStyle, type MonitoringStatus } from "../../global-constants.js";
|
||||
import { makeBadge } from "badge-maker";
|
||||
import { ErrorSvg } from "../../anywhere.js";
|
||||
import { GetLastMonitoringValue, SetLastHeartbeat, DeleteMonitorCaches } from "../cache/setGet.js";
|
||||
@@ -490,7 +490,7 @@ export const GetStatusCountsByInterval = async (
|
||||
export const GetMonitoringDataPaginated = async (
|
||||
page: number,
|
||||
limit: number,
|
||||
filter?: { monitor_tag?: string; status?: string; start_time?: number; end_time?: number },
|
||||
filter?: { monitor_tag?: string; status?: MonitoringStatus; start_time?: number; end_time?: number },
|
||||
): Promise<{ data: MonitoringData[]; total: number }> => {
|
||||
const data = await db.getMonitoringDataPaginated(page, limit, filter);
|
||||
const countResult = await db.getMonitoringDataCount(filter);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Knex as KnexType } from "knex";
|
||||
import { BaseRepository } from "./base.js";
|
||||
import GC from "../../../global-constants.js";
|
||||
import GC, { type MonitoringStatus } from "../../../global-constants.js";
|
||||
import { GetMinuteStartNowTimestampUTC } from "../../tool.js";
|
||||
import type {
|
||||
MonitoringData,
|
||||
@@ -82,7 +82,7 @@ export class MonitoringRepository extends BaseRepository {
|
||||
async getMonitoringDataPaginated(
|
||||
page: number,
|
||||
limit: number,
|
||||
filter?: { monitor_tag?: string; status?: string; start_time?: number; end_time?: number },
|
||||
filter?: { monitor_tag?: string; status?: MonitoringStatus; start_time?: number; end_time?: number },
|
||||
): Promise<MonitoringData[]> {
|
||||
let query = this.knex("monitoring_data").select("*");
|
||||
|
||||
@@ -110,7 +110,7 @@ export class MonitoringRepository extends BaseRepository {
|
||||
|
||||
async getMonitoringDataCount(filter?: {
|
||||
monitor_tag?: string;
|
||||
status?: string;
|
||||
status?: MonitoringStatus;
|
||||
start_time?: number;
|
||||
end_time?: number;
|
||||
}): Promise<{ count: number }> {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { format } from "date-fns";
|
||||
import sharp from "sharp";
|
||||
import { nanoid } from "nanoid";
|
||||
import db from "$lib/server/db/db";
|
||||
import GC from "$lib/global-constants.js";
|
||||
import GC, { isMonitoringStatus, type MonitoringStatus } from "$lib/global-constants.js";
|
||||
import {
|
||||
CreateUpdateMonitor,
|
||||
UpdateMonitoringData,
|
||||
@@ -245,11 +245,14 @@ export async function POST({ request, cookies }) {
|
||||
} else if (action == "getMonitoringDataPaginated") {
|
||||
const page = parseInt(String(data.page)) || 1;
|
||||
const limit = parseInt(String(data.limit)) || 50;
|
||||
const filter: { monitor_tag?: string; status?: string; start_time?: number; end_time?: number } = {};
|
||||
const filter: { monitor_tag?: string; status?: MonitoringStatus; start_time?: number; end_time?: number } = {};
|
||||
if (data.monitor_tag && data.monitor_tag !== "ALL") {
|
||||
filter.monitor_tag = data.monitor_tag;
|
||||
}
|
||||
if (data.status && data.status !== "ALL") {
|
||||
if (!isMonitoringStatus(data.status)) {
|
||||
return json({ error: "Invalid monitoring status" }, { status: 400 });
|
||||
}
|
||||
filter.status = data.status;
|
||||
}
|
||||
if (data.start_time) {
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import { toast } from "svelte-sonner";
|
||||
import { resolve } from "$app/paths";
|
||||
import clientResolver from "$lib/client/resolver.js";
|
||||
import GC, { isMonitoringStatus, type MonitoringStatus } from "$lib/global-constants.js";
|
||||
|
||||
// Types
|
||||
interface MonitoringData {
|
||||
@@ -35,7 +36,7 @@
|
||||
name: string;
|
||||
}
|
||||
|
||||
type StatusFilter = "ALL" | "UP" | "DOWN" | "DEGRADED";
|
||||
type StatusFilter = "ALL" | MonitoringStatus;
|
||||
|
||||
// Helper to format datetime as YYYY-MM-DDTHH:mm for datetime-local input
|
||||
function formatDateTimeForInput(date: Date): string {
|
||||
@@ -249,8 +250,8 @@
|
||||
}
|
||||
|
||||
function handleStatusChange(value: string | undefined) {
|
||||
if (value) {
|
||||
statusFilter = value as StatusFilter;
|
||||
if (value === "ALL" || isMonitoringStatus(value)) {
|
||||
statusFilter = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -325,9 +326,9 @@
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
<Select.Item value="ALL">All Statuses</Select.Item>
|
||||
<Select.Item value="UP">UP</Select.Item>
|
||||
<Select.Item value="DOWN">DOWN</Select.Item>
|
||||
<Select.Item value="DEGRADED">DEGRADED</Select.Item>
|
||||
<Select.Item value={GC.UP}>UP</Select.Item>
|
||||
<Select.Item value={GC.DOWN}>DOWN</Select.Item>
|
||||
<Select.Item value={GC.DEGRADED}>DEGRADED</Select.Item>
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user