Revert "feat: add-healthcheck"

This commit is contained in:
Charles GTE
2026-03-28 16:27:57 +01:00
committed by GitHub
parent 9098012426
commit 941a9256f0
53 changed files with 1333 additions and 19938 deletions
+1 -3
View File
@@ -15,7 +15,6 @@ import * as notificationLog from "./schema/11_notification-log";
import * as storageChannel from "./schema/12_storage-channel";
import * as storagePolicy from "@/db/schema/13_storage-policy";
import * as backupStorage from "@/db/schema/14_storage-backup";
import * as healthcheckLog from "@/db/schema/15_healthcheck-log";
import {Pool} from "pg";
@@ -47,8 +46,7 @@ export const schemas = {
...notificationLog,
...storageChannel,
...storagePolicy,
...backupStorage,
...healthcheckLog
...backupStorage
};
export const db = drizzle({
-12
View File
@@ -1,12 +0,0 @@
CREATE TYPE "public"."healthcheck_status" AS ENUM('success', 'failed');--> statement-breakpoint
CREATE TYPE "public"."healthcheck_kind" AS ENUM('database', 'agent');--> statement-breakpoint
CREATE TABLE "healthcheck_log" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"kind" "healthcheck_kind" NOT NULL,
"date" timestamp,
"status" "healthcheck_status",
"object_id" uuid,
"updated_at" timestamp,
"created_at" timestamp DEFAULT now() NOT NULL,
"deleted_at" timestamp
);
@@ -1,3 +0,0 @@
ALTER TABLE "healthcheck_log" ALTER COLUMN "date" SET NOT NULL;--> statement-breakpoint
ALTER TABLE "healthcheck_log" ALTER COLUMN "status" SET NOT NULL;--> statement-breakpoint
ALTER TABLE "healthcheck_log" ALTER COLUMN "object_id" SET NOT NULL;
-2
View File
@@ -1,2 +0,0 @@
ALTER TABLE "settings" ADD COLUMN "default_notification_channel_id" uuid;--> statement-breakpoint
ALTER TABLE "settings" ADD CONSTRAINT "settings_default_notification_channel_id_notification_channel_id_fk" FOREIGN KEY ("default_notification_channel_id") REFERENCES "public"."notification_channel"("id") ON DELETE set null ON UPDATE no action;
-1
View File
@@ -1 +0,0 @@
ALTER TABLE "agents" ADD COLUMN "health_error_count" integer;
-1
View File
@@ -1 +0,0 @@
ALTER TYPE "public"."event_kind" ADD VALUE 'error_health_agent';
@@ -1 +0,0 @@
ALTER TYPE "public"."event_kind" ADD VALUE 'error_health_database';
@@ -1 +0,0 @@
ALTER TABLE "databases" ADD COLUMN "health_error_count" integer;
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-49
View File
@@ -281,55 +281,6 @@
"when": 1773659096498,
"tag": "0039_conscious_solo",
"breakpoints": true
},
{
"idx": 40,
"version": "7",
"when": 1774375027038,
"tag": "0040_quick_lester",
"breakpoints": true
},
{
"idx": 41,
"version": "7",
"when": 1774378412843,
"tag": "0041_spooky_radioactive_man",
"breakpoints": true
},
{
"idx": 42,
"version": "7",
"when": 1774472168308,
"tag": "0042_breezy_namora",
"breakpoints": true
},
{
"idx": 43,
"version": "7",
"when": 1774643615673,
"tag": "0043_peaceful_chat",
"breakpoints": true
},
{
"idx": 44,
"version": "7",
"when": 1774648872657,
"tag": "0044_steep_wiccan",
"breakpoints": true
},
{
"idx": 45,
"version": "7",
"when": 1774696680244,
"tag": "0045_needy_martin_li",
"breakpoints": true
},
{
"idx": 46,
"version": "7",
"when": 1774706071024,
"tag": "0046_mysterious_menace",
"breakpoints": true
}
]
}
+2 -5
View File
@@ -1,10 +1,10 @@
import {boolean, pgTable, uuid, varchar} from "drizzle-orm/pg-core";
import {boolean, pgTable, timestamp, uuid, varchar} from "drizzle-orm/pg-core";
import {createSelectSchema} from "drizzle-zod";
import {z} from "zod";
import {timestamps} from "@/db/schema/00_common";
import {storageChannel} from "@/db/schema/12_storage-channel";
import {relations} from "drizzle-orm";
import {notificationChannel} from "@/db/schema/09_notification-channel";
export const setting = pgTable("settings", {
id: uuid("id").primaryKey().defaultRandom(),
@@ -15,8 +15,6 @@ export const setting = pgTable("settings", {
smtpPort: varchar("smtp_port", {length: 255}),
smtpUser: varchar("smtp_user", {length: 255}),
smtpSecure: boolean("smtp_secure"),
defaultNotificationChannelId: uuid('default_notification_channel_id')
.references(() => notificationChannel.id, {onDelete: "set null"}),
defaultStorageChannelId: uuid('default_storage_channel_id')
.references(() => storageChannel.id, {onDelete: "set null"}),
encryption: boolean("encryption").default(false),
@@ -25,7 +23,6 @@ export const setting = pgTable("settings", {
export const settingRelations = relations(setting, ({one, many}) => ({
storageChannel: one(storageChannel, {fields: [setting.defaultStorageChannelId], references: [storageChannel.id]}),
notificationChannel: one(notificationChannel, {fields: [setting.defaultNotificationChannelId], references: [notificationChannel.id]}),
}));
-1
View File
@@ -19,7 +19,6 @@ export const database = pgTable("databases", {
backupPolicy: text("backup_policy"),
isWaitingForBackup: boolean("is_waiting_for_backup").default(false).notNull(),
backupToRestore: text("backup_to_restore"),
healthErrorCount: integer("health_error_count"),
agentId: uuid("agent_id")
.notNull()
.references(() => agent.id, {onDelete: "cascade"}),
+1 -2
View File
@@ -1,4 +1,4 @@
import {boolean, pgTable, text, timestamp, uuid, integer} from "drizzle-orm/pg-core";
import {boolean, pgTable, text, timestamp, uuid} from "drizzle-orm/pg-core";
import {createSelectSchema} from "drizzle-zod";
import {z} from "zod";
import {Database, database} from "@/db/schema/07_database";
@@ -10,7 +10,6 @@ export const agent = pgTable("agents", {
slug: text("slug").notNull().unique(),
version: text("version"),
name: text("name").notNull().notNull(),
healthErrorCount: integer("health_error_count"),
description: text("description").notNull(),
isArchived: boolean("is_archived").default(false),
lastContact: timestamp("last_contact"),
+1 -1
View File
@@ -6,7 +6,7 @@ import {database} from "@/db/schema/07_database";
import {createSelectSchema} from "drizzle-zod";
import {z} from "zod";
export const eventKindEnum = pgEnum('event_kind', ['error_backup', 'error_restore', 'success_restore', 'success_backup', 'weekly_report', 'error_health_agent', 'error_health_database']);
export const eventKindEnum = pgEnum('event_kind', ['error_backup', 'error_restore', 'success_restore', 'success_backup', 'weekly_report']);
export const alertPolicy = pgTable('alert_policy', {
id: uuid('id').defaultRandom().primaryKey(),
-23
View File
@@ -1,23 +0,0 @@
import {pgTable, uuid, timestamp, pgEnum} from 'drizzle-orm/pg-core';
import {timestamps} from "@/db/schema/00_common";
import {createSelectSchema} from "drizzle-zod";
import {z} from "zod";
export const healthcheckKindEnum = pgEnum('healthcheck_kind', ['database', 'agent']);
export const healthCheckStatusEnum = pgEnum('healthcheck_status', ['success', 'failed']);
export const healthcheckLog = pgTable('healthcheck_log', {
id: uuid('id').defaultRandom().primaryKey(),
kind: healthcheckKindEnum('kind').notNull(),
date: timestamp("date").notNull(),
status: healthCheckStatusEnum("status").notNull(),
objectId: uuid('object_id').notNull(),
...timestamps
});
export const healthcheckLogSchema = createSelectSchema(healthcheckLog);
export type HealthcheckLog = z.infer<typeof healthcheckLogSchema>;
export type HealthcheckKind = (typeof healthcheckKindEnum.enumValues)[number];
export type HealthcheckStatus = (typeof healthCheckStatusEnum.enumValues)[number];
-187
View File
@@ -1,187 +0,0 @@
import {db} from "@/db";
import * as drizzleDb from "@/db";
import {and, eq, gte, isNotNull, lt} from "drizzle-orm";
import {dispatchNotification} from "@/features/notifications/dispatch";
import {EventPayload} from "@/features/notifications/types";
export async function getHealthLast12hLogs({id}: { id: string }) {
const now = new Date()
const since = new Date(now.getTime() - 12 * 60 * 60 * 1000)
return db
.select()
.from(drizzleDb.schemas.healthcheckLog)
.where(
and(
eq(drizzleDb.schemas.healthcheckLog.objectId, id),
gte(drizzleDb.schemas.healthcheckLog.date, since)
)
)
}
export async function deleteHealthLogsOlderThan12h() {
const now = new Date()
const threshold = new Date(now.getTime() - 12 * 60 * 60 * 1000)
const logsToDelete = await db
.select()
.from(drizzleDb.schemas.healthcheckLog)
.where(
lt(drizzleDb.schemas.healthcheckLog.date, threshold)
)
console.log(`Number of logs found to delete: ${logsToDelete.length}`)
await db
.delete(drizzleDb.schemas.healthcheckLog)
.where(
lt(drizzleDb.schemas.healthcheckLog.date, threshold)
)
return logsToDelete.length
}
export async function checkAgentsHealthError() {
const agents = await db.query.agent.findMany({
where: isNotNull(drizzleDb.schemas.agent.lastContact),
});
const settings = await db.query.setting.findFirst({
where: (fields, {eq}) => eq(fields.name, "system"),
});
if (!settings) {
throw new Error("System settings not found");
}
if (!settings.defaultNotificationChannelId) {
console.error("No default notification channel id found.");
return
}
const now = new Date();
for (const agent of agents) {
if (!agent.lastContact) continue;
const lastContactDate = new Date(agent.lastContact);
const diffMinutes = (now.getTime() - lastContactDate.getTime()) / 1000 / 60;
if (diffMinutes > 10) {
if ((agent.healthErrorCount ?? 0) < 3) {
const newHealthErrorCount = (agent.healthErrorCount ?? 0) + 1
await db.update(drizzleDb.schemas.agent)
.set({
healthErrorCount: newHealthErrorCount,
})
.where(eq(drizzleDb.schemas.agent.id, agent.id));
const payload: EventPayload = {
title: "Agent down",
message: `Agent ${agent.name} is down, (notification number: ${newHealthErrorCount}/3)`,
level: "critical",
event: "error_health_agent",
data: {
agent: agent.name,
id: agent.id,
error: "Agent is down",
},
};
console.log("[Agent Healthcheck] :", payload);
await dispatchNotification(
payload,
undefined,
settings.defaultNotificationChannelId,
undefined
);
}
}
}
}
export async function checkDatabasesHealthError() {
const databases = await db.query.database.findMany({
where: isNotNull(drizzleDb.schemas.database.lastContact),
with: {
agent: true,
alertPolicies: true
}
})
const now = new Date();
for (const database of databases) {
if (!database.lastContact) continue;
const lastContactDate = new Date(database.lastContact);
const diffMinutes = (now.getTime() - lastContactDate.getTime()) / 1000 / 60;
if (diffMinutes > 10) {
if ((database.healthErrorCount ?? 0) < 3) {
const newHealthErrorCount = (database.healthErrorCount ?? 0) + 1
await db.update(drizzleDb.schemas.database)
.set({
healthErrorCount: newHealthErrorCount,
})
.where(eq(drizzleDb.schemas.database.id, database.id));
const settings = await db.query.setting.findFirst({
where: eq(drizzleDb.schemas.setting.name, "system"),
with: { notificationChannel: true },
});
const defaultPolicy = settings?.notificationChannel
? [{
id: null,
notificationChannelId: settings.notificationChannel.id,
enabled: settings.notificationChannel.enabled,
eventKinds: ["error_health_database"]
}]
: [];
const policiesToUse = (database.alertPolicies && database.alertPolicies.length > 0)
? database.alertPolicies.filter(policy => policy.enabled && policy.eventKinds.includes("error_health_database"))
: defaultPolicy;
if (!policiesToUse || policiesToUse.length === 0) {
continue
}
const promises = policiesToUse.map(alertPolicy => {
const payload: EventPayload = {
title: "Database down",
message: `Database ${database.name} is down, (notification number: ${newHealthErrorCount}/3)`,
level: "critical",
event: "error_health_database",
data: {
agent: database.name,
id: database.id,
error: "Database is down",
},
};
console.log("[Database Healthcheck] :", payload);
return dispatchNotification(payload, alertPolicy.id == null ? undefined : alertPolicy.id, alertPolicy.id ? undefined : alertPolicy.notificationChannelId, undefined);
});
await Promise.all(promises);
}
}
}
}