mirror of
https://github.com/Portabase/portabase.git
synced 2026-07-14 11:16:13 +02:00
Revert "feat: add-healthcheck"
This commit is contained in:
+1
-3
@@ -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({
|
||||
|
||||
@@ -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;
|
||||
@@ -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 +0,0 @@
|
||||
ALTER TABLE "agents" ADD COLUMN "health_error_count" integer;
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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]}),
|
||||
}));
|
||||
|
||||
|
||||
|
||||
@@ -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,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"),
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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];
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user