mirror of
https://github.com/fosrl/pangolin.git
synced 2026-05-30 12:42:22 +00:00
Support the all types in the schema and engine
This commit is contained in:
@@ -488,6 +488,9 @@ export const alertRules = pgTable("alertRules", {
|
|||||||
// Nullable depending on eventType
|
// Nullable depending on eventType
|
||||||
enabled: boolean("enabled").notNull().default(true),
|
enabled: boolean("enabled").notNull().default(true),
|
||||||
cooldownSeconds: integer("cooldownSeconds").notNull().default(300),
|
cooldownSeconds: integer("cooldownSeconds").notNull().default(300),
|
||||||
|
allSites: boolean("allSites").notNull().default(false),
|
||||||
|
allHealthChecks: boolean("allHealthChecks").notNull().default(false),
|
||||||
|
allResources: boolean("allResources").notNull().default(false),
|
||||||
lastTriggeredAt: bigint("lastTriggeredAt", { mode: "number" }), // nullable
|
lastTriggeredAt: bigint("lastTriggeredAt", { mode: "number" }), // nullable
|
||||||
createdAt: bigint("createdAt", { mode: "number" }).notNull(),
|
createdAt: bigint("createdAt", { mode: "number" }).notNull(),
|
||||||
updatedAt: bigint("updatedAt", { mode: "number" }).notNull()
|
updatedAt: bigint("updatedAt", { mode: "number" }).notNull()
|
||||||
|
|||||||
@@ -479,6 +479,9 @@ export const alertRules = sqliteTable("alertRules", {
|
|||||||
.notNull(),
|
.notNull(),
|
||||||
enabled: integer("enabled", { mode: "boolean" }).notNull().default(true),
|
enabled: integer("enabled", { mode: "boolean" }).notNull().default(true),
|
||||||
cooldownSeconds: integer("cooldownSeconds").notNull().default(300),
|
cooldownSeconds: integer("cooldownSeconds").notNull().default(300),
|
||||||
|
allSites: integer("allSites", { mode: "boolean" }).notNull().default(false),
|
||||||
|
allHealthChecks: integer("allHealthChecks", { mode: "boolean" }).notNull().default(false),
|
||||||
|
allResources: integer("allResources", { mode: "boolean" }).notNull().default(false),
|
||||||
lastTriggeredAt: integer("lastTriggeredAt"),
|
lastTriggeredAt: integer("lastTriggeredAt"),
|
||||||
createdAt: integer("createdAt").notNull(),
|
createdAt: integer("createdAt").notNull(),
|
||||||
updatedAt: integer("updatedAt").notNull()
|
updatedAt: integer("updatedAt").notNull()
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
* This file is not licensed under the AGPLv3.
|
* This file is not licensed under the AGPLv3.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { and, eq, isNull, or } from "drizzle-orm";
|
import { and, eq, or } from "drizzle-orm";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import {
|
import {
|
||||||
alertRules,
|
alertRules,
|
||||||
@@ -49,11 +49,9 @@ export async function processAlerts(context: AlertContext): Promise<void> {
|
|||||||
// ------------------------------------------------------------------
|
// ------------------------------------------------------------------
|
||||||
// 1. Find matching alert rules
|
// 1. Find matching alert rules
|
||||||
// ------------------------------------------------------------------
|
// ------------------------------------------------------------------
|
||||||
// Rules with no junction-table entries match ALL sites / health checks.
|
// Rules with allSites / allHealthChecks / allResources set to true match
|
||||||
// Rules with junction entries match only those specific IDs.
|
// ANY event of that type. Rules without these flags set match only the
|
||||||
// We implement this with a LEFT JOIN: a NULL join result means the rule
|
// specific IDs listed in the junction tables.
|
||||||
// has no scope restrictions (match all); a non-NULL result that satisfies
|
|
||||||
// the id equality filter means an explicit match.
|
|
||||||
const baseConditions = and(
|
const baseConditions = and(
|
||||||
eq(alertRules.orgId, context.orgId),
|
eq(alertRules.orgId, context.orgId),
|
||||||
eq(alertRules.eventType, context.eventType),
|
eq(alertRules.eventType, context.eventType),
|
||||||
@@ -74,12 +72,20 @@ export async function processAlerts(context: AlertContext): Promise<void> {
|
|||||||
and(
|
and(
|
||||||
baseConditions,
|
baseConditions,
|
||||||
or(
|
or(
|
||||||
eq(alertSites.siteId, context.siteId),
|
eq(alertRules.allSites, true),
|
||||||
isNull(alertSites.alertRuleId)
|
eq(alertSites.siteId, context.siteId)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
rules = rows.map((r) => r.alertRules);
|
// Deduplicate in case a rule matched on multiple junction rows
|
||||||
|
const seen = new Set<number>();
|
||||||
|
rules = rows
|
||||||
|
.map((r) => r.alertRules)
|
||||||
|
.filter((r) => {
|
||||||
|
if (seen.has(r.alertRuleId)) return false;
|
||||||
|
seen.add(r.alertRuleId);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
} else if (context.healthCheckId != null) {
|
} else if (context.healthCheckId != null) {
|
||||||
const rows = await db
|
const rows = await db
|
||||||
.select()
|
.select()
|
||||||
@@ -92,12 +98,19 @@ export async function processAlerts(context: AlertContext): Promise<void> {
|
|||||||
and(
|
and(
|
||||||
baseConditions,
|
baseConditions,
|
||||||
or(
|
or(
|
||||||
eq(alertHealthChecks.healthCheckId, context.healthCheckId),
|
eq(alertRules.allHealthChecks, true),
|
||||||
isNull(alertHealthChecks.alertRuleId)
|
eq(alertHealthChecks.healthCheckId, context.healthCheckId)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
rules = rows.map((r) => r.alertRules);
|
const seen = new Set<number>();
|
||||||
|
rules = rows
|
||||||
|
.map((r) => r.alertRules)
|
||||||
|
.filter((r) => {
|
||||||
|
if (seen.has(r.alertRuleId)) return false;
|
||||||
|
seen.add(r.alertRuleId);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
} else if (context.resourceId != null) {
|
} else if (context.resourceId != null) {
|
||||||
const rows = await db
|
const rows = await db
|
||||||
.select()
|
.select()
|
||||||
@@ -110,12 +123,19 @@ export async function processAlerts(context: AlertContext): Promise<void> {
|
|||||||
and(
|
and(
|
||||||
baseConditions,
|
baseConditions,
|
||||||
or(
|
or(
|
||||||
eq(alertResources.resourceId, context.resourceId),
|
eq(alertRules.allResources, true),
|
||||||
isNull(alertResources.alertRuleId)
|
eq(alertResources.resourceId, context.resourceId)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
rules = rows.map((r) => r.alertRules);
|
const seen = new Set<number>();
|
||||||
|
rules = rows
|
||||||
|
.map((r) => r.alertRules)
|
||||||
|
.filter((r) => {
|
||||||
|
if (seen.has(r.alertRuleId)) return false;
|
||||||
|
seen.add(r.alertRuleId);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
rules = [];
|
rules = [];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -246,6 +246,9 @@ export async function createAlertRule(
|
|||||||
eventType,
|
eventType,
|
||||||
enabled,
|
enabled,
|
||||||
cooldownSeconds,
|
cooldownSeconds,
|
||||||
|
allSites,
|
||||||
|
allHealthChecks,
|
||||||
|
allResources,
|
||||||
createdAt: now,
|
createdAt: now,
|
||||||
updatedAt: now
|
updatedAt: now
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -217,8 +217,11 @@ export async function updateAlertRule(
|
|||||||
enabled,
|
enabled,
|
||||||
cooldownSeconds,
|
cooldownSeconds,
|
||||||
siteIds,
|
siteIds,
|
||||||
|
allSites,
|
||||||
healthCheckIds,
|
healthCheckIds,
|
||||||
|
allHealthChecks,
|
||||||
resourceIds,
|
resourceIds,
|
||||||
|
allResources,
|
||||||
userIds,
|
userIds,
|
||||||
roleIds,
|
roleIds,
|
||||||
emails,
|
emails,
|
||||||
@@ -233,8 +236,10 @@ export async function updateAlertRule(
|
|||||||
if (name !== undefined) updateData.name = name;
|
if (name !== undefined) updateData.name = name;
|
||||||
if (eventType !== undefined) updateData.eventType = eventType;
|
if (eventType !== undefined) updateData.eventType = eventType;
|
||||||
if (enabled !== undefined) updateData.enabled = enabled;
|
if (enabled !== undefined) updateData.enabled = enabled;
|
||||||
if (cooldownSeconds !== undefined)
|
if (cooldownSeconds !== undefined) updateData.cooldownSeconds = cooldownSeconds;
|
||||||
updateData.cooldownSeconds = cooldownSeconds;
|
if (allSites !== undefined) updateData.allSites = allSites;
|
||||||
|
if (allHealthChecks !== undefined) updateData.allHealthChecks = allHealthChecks;
|
||||||
|
if (allResources !== undefined) updateData.allResources = allResources;
|
||||||
|
|
||||||
await db
|
await db
|
||||||
.update(alertRules)
|
.update(alertRules)
|
||||||
@@ -247,12 +252,14 @@ export async function updateAlertRule(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// --- Full-replace site associations if siteIds was provided ---
|
// --- Full-replace site associations if siteIds was provided ---
|
||||||
if (siteIds !== undefined) {
|
if (siteIds !== undefined || allSites !== undefined) {
|
||||||
await db
|
await db
|
||||||
.delete(alertSites)
|
.delete(alertSites)
|
||||||
.where(eq(alertSites.alertRuleId, alertRuleId));
|
.where(eq(alertSites.alertRuleId, alertRuleId));
|
||||||
|
|
||||||
if (siteIds.length > 0) {
|
// Only insert junction rows when allSites is not true
|
||||||
|
const effectiveAllSites = allSites ?? false;
|
||||||
|
if (!effectiveAllSites && siteIds !== undefined && siteIds.length > 0) {
|
||||||
await db.insert(alertSites).values(
|
await db.insert(alertSites).values(
|
||||||
siteIds.map((siteId) => ({
|
siteIds.map((siteId) => ({
|
||||||
alertRuleId,
|
alertRuleId,
|
||||||
@@ -263,12 +270,13 @@ export async function updateAlertRule(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- Full-replace health check associations if healthCheckIds was provided ---
|
// --- Full-replace health check associations if healthCheckIds was provided ---
|
||||||
if (healthCheckIds !== undefined) {
|
if (healthCheckIds !== undefined || allHealthChecks !== undefined) {
|
||||||
await db
|
await db
|
||||||
.delete(alertHealthChecks)
|
.delete(alertHealthChecks)
|
||||||
.where(eq(alertHealthChecks.alertRuleId, alertRuleId));
|
.where(eq(alertHealthChecks.alertRuleId, alertRuleId));
|
||||||
|
|
||||||
if (healthCheckIds.length > 0) {
|
const effectiveAllHealthChecks = allHealthChecks ?? false;
|
||||||
|
if (!effectiveAllHealthChecks && healthCheckIds !== undefined && healthCheckIds.length > 0) {
|
||||||
await db.insert(alertHealthChecks).values(
|
await db.insert(alertHealthChecks).values(
|
||||||
healthCheckIds.map((healthCheckId) => ({
|
healthCheckIds.map((healthCheckId) => ({
|
||||||
alertRuleId,
|
alertRuleId,
|
||||||
@@ -279,12 +287,13 @@ export async function updateAlertRule(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- Full-replace resource associations if resourceIds was provided ---
|
// --- Full-replace resource associations if resourceIds was provided ---
|
||||||
if (resourceIds !== undefined) {
|
if (resourceIds !== undefined || allResources !== undefined) {
|
||||||
await db
|
await db
|
||||||
.delete(alertResources)
|
.delete(alertResources)
|
||||||
.where(eq(alertResources.alertRuleId, alertRuleId));
|
.where(eq(alertResources.alertRuleId, alertRuleId));
|
||||||
|
|
||||||
if (resourceIds.length > 0) {
|
const effectiveAllResources = allResources ?? false;
|
||||||
|
if (!effectiveAllResources && resourceIds !== undefined && resourceIds.length > 0) {
|
||||||
await db.insert(alertResources).values(
|
await db.insert(alertResources).values(
|
||||||
resourceIds.map((resourceId) => ({
|
resourceIds.map((resourceId) => ({
|
||||||
alertRuleId,
|
alertRuleId,
|
||||||
|
|||||||
@@ -230,6 +230,7 @@ export async function createTarget(
|
|||||||
.values({
|
.values({
|
||||||
orgId: resource.orgId,
|
orgId: resource.orgId,
|
||||||
targetId: newTarget[0].targetId,
|
targetId: newTarget[0].targetId,
|
||||||
|
name: `Resource ${resource.name} - ${targetData.ip}:${targetData.port}`,
|
||||||
hcEnabled: targetData.hcEnabled ?? false,
|
hcEnabled: targetData.hcEnabled ?? false,
|
||||||
hcPath: targetData.hcPath ?? null,
|
hcPath: targetData.hcPath ?? null,
|
||||||
hcScheme: targetData.hcScheme ?? null,
|
hcScheme: targetData.hcScheme ?? null,
|
||||||
|
|||||||
Reference in New Issue
Block a user