mirror of
https://github.com/Portabase/portabase.git
synced 2026-07-14 11:16:13 +02:00
Working on implementation of alert policies.
This commit is contained in:
+5
-1
@@ -10,6 +10,8 @@ import * as agent from "./schema/08_agent";
|
||||
import * as database from "./schema/07_database";
|
||||
import * as notificationChannel from "./schema/09_notification-channel";
|
||||
import * as organizationNotificationChannel from "./schema/09_notification-channel";
|
||||
import * as alertPolicy from "./schema/10_alert-policy";
|
||||
import * as notificationLog from "./schema/11_notification-log";
|
||||
|
||||
|
||||
import {Pool} from "pg";
|
||||
@@ -36,7 +38,9 @@ export const schemas = {
|
||||
...agent,
|
||||
...database,
|
||||
...notificationChannel,
|
||||
...organizationNotificationChannel
|
||||
...organizationNotificationChannel,
|
||||
...alertPolicy,
|
||||
...notificationLog
|
||||
};
|
||||
|
||||
export const db = drizzle({
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
CREATE TYPE "public"."event_kind" AS ENUM('error_backup', 'error_restore', 'success_restore', 'success_backup', 'weekly_report');--> statement-breakpoint
|
||||
CREATE TYPE "public"."level" AS ENUM('critical', 'warning', 'info');--> statement-breakpoint
|
||||
CREATE TABLE "alert_policy" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"notification_channel_id" uuid NOT NULL,
|
||||
"event_kind" "event_kind"[] NOT NULL,
|
||||
"enabled" boolean DEFAULT true NOT NULL,
|
||||
"database_id" uuid NOT NULL,
|
||||
"updated_at" timestamp,
|
||||
"created_at" timestamp DEFAULT now() NOT NULL,
|
||||
"deleted_at" timestamp
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "notification_log" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"channel_id" uuid NOT NULL,
|
||||
"policy_id" uuid,
|
||||
"organization_id" uuid,
|
||||
"title" varchar(255) NOT NULL,
|
||||
"message" text NOT NULL,
|
||||
"level" "level" NOT NULL,
|
||||
"payload" jsonb,
|
||||
"success" boolean NOT NULL,
|
||||
"error" text,
|
||||
"provider_response" jsonb,
|
||||
"sent_at" timestamp DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp,
|
||||
"created_at" timestamp DEFAULT now() NOT NULL,
|
||||
"deleted_at" timestamp
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "alert_policy" ADD CONSTRAINT "alert_policy_notification_channel_id_notification_channel_id_fk" FOREIGN KEY ("notification_channel_id") REFERENCES "public"."notification_channel"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "alert_policy" ADD CONSTRAINT "alert_policy_database_id_databases_id_fk" FOREIGN KEY ("database_id") REFERENCES "public"."databases"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "notification_log" ADD CONSTRAINT "notification_log_channel_id_notification_channel_id_fk" FOREIGN KEY ("channel_id") REFERENCES "public"."notification_channel"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "notification_log" ADD CONSTRAINT "notification_log_policy_id_alert_policy_id_fk" FOREIGN KEY ("policy_id") REFERENCES "public"."alert_policy"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "notification_log" ADD CONSTRAINT "notification_log_organization_id_organization_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE set null ON UPDATE no action;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -57,6 +57,13 @@
|
||||
"when": 1762685571404,
|
||||
"tag": "0007_last_umar",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 8,
|
||||
"version": "7",
|
||||
"when": 1763914207236,
|
||||
"tag": "0008_aberrant_scorpion",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,13 +1,16 @@
|
||||
import { pgTable, text, uuid } from "drizzle-orm/pg-core";
|
||||
import { relations } from "drizzle-orm";
|
||||
import { project } from "./06_project";
|
||||
import { createSelectSchema } from "drizzle-zod";
|
||||
import { z } from "zod";
|
||||
import {pgTable, text, uuid} from "drizzle-orm/pg-core";
|
||||
import {relations} from "drizzle-orm";
|
||||
import {Project, project} from "./06_project";
|
||||
import {createSelectSchema} from "drizzle-zod";
|
||||
import {z} from "zod";
|
||||
import {invitation, OrganizationInvitation} from "@/db/schema/05_invitation";
|
||||
import {member, OrganizationMember} from "@/db/schema/04_member";
|
||||
import {User} from "@/db/schema/02_user";
|
||||
import {timestamps} from "@/db/schema/00_common";
|
||||
import {organizationNotificationChannel} from "@/db/schema/09_notification-channel";
|
||||
import {NotificationChannel, organizationNotificationChannel} from "@/db/schema/09_notification-channel";
|
||||
import {Agent} from "@/db/schema/08_agent";
|
||||
import {AlertPolicy} from "@/db/schema/10_alert-policy";
|
||||
import {Backup, Database, Restoration, RetentionPolicy} from "@/db/schema/07_database";
|
||||
|
||||
export const organization = pgTable("organization", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
@@ -19,7 +22,7 @@ export const organization = pgTable("organization", {
|
||||
});
|
||||
|
||||
|
||||
export const organizationRelations = relations(organization, ({ many }) => ({
|
||||
export const organizationRelations = relations(organization, ({many}) => ({
|
||||
members: many(member),
|
||||
invitations: many(invitation),
|
||||
projects: many(project),
|
||||
@@ -27,7 +30,6 @@ export const organizationRelations = relations(organization, ({ many }) => ({
|
||||
}));
|
||||
|
||||
|
||||
|
||||
export const organizationSchema = createSelectSchema(organization);
|
||||
export type Organization = z.infer<typeof organizationSchema>;
|
||||
|
||||
@@ -42,4 +44,12 @@ export type MemberWithUser = OrganizationMember & {
|
||||
export type OrganizationWithMembersAndUsers = Organization & {
|
||||
members: MemberWithUser[];
|
||||
invitations: OrganizationInvitation[];
|
||||
};
|
||||
};
|
||||
|
||||
export type OrganizationWith = Organization & {
|
||||
user?: User | null;
|
||||
members?: MemberWithUser[] | null;
|
||||
invitations?: OrganizationInvitation[] | null;
|
||||
notificationChannels?: NotificationChannel[] | null;
|
||||
projects?: Project[] | null;
|
||||
};
|
||||
|
||||
@@ -6,6 +6,11 @@ import {dbmsEnum, statusEnum} from "./types";
|
||||
import {createSelectSchema} from "drizzle-zod";
|
||||
import {z} from "zod";
|
||||
import {timestamps} from "@/db/schema/00_common";
|
||||
import {member} from "@/db/schema/04_member";
|
||||
import {invitation} from "@/db/schema/05_invitation";
|
||||
import {organizationNotificationChannel} from "@/db/schema/09_notification-channel";
|
||||
import {organization} from "@/db/schema/03_organization";
|
||||
import {AlertPolicy, alertPolicy} from "@/db/schema/10_alert-policy";
|
||||
|
||||
export const database = pgTable("databases", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
@@ -20,13 +25,13 @@ export const database = pgTable("databases", {
|
||||
.notNull()
|
||||
.references(() => agent.id, {onDelete: "cascade"}),
|
||||
lastContact: timestamp("last_contact"),
|
||||
|
||||
projectId: uuid("project_id")
|
||||
.references(() => project.id),
|
||||
...timestamps
|
||||
|
||||
});
|
||||
|
||||
|
||||
export const backup = pgTable(
|
||||
"backups",
|
||||
{
|
||||
@@ -77,6 +82,7 @@ export const databaseRelations = relations(database, ({one, many}) => ({
|
||||
project: one(project, {fields: [database.projectId], references: [project.id]}),
|
||||
backups: many(backup),
|
||||
restorations: many(restoration),
|
||||
alertPolicies: many(alertPolicy),
|
||||
}));
|
||||
|
||||
export const backupRelations = relations(backup, ({one, many}) => ({
|
||||
@@ -116,5 +122,6 @@ export type DatabaseWith = Database & {
|
||||
backups?: Backup[] | null;
|
||||
restorations?: Restoration[] | null;
|
||||
retentionPolicy?: RetentionPolicy | null;
|
||||
alertPolicies?: AlertPolicy[] | null;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import {boolean, pgEnum, pgTable, uuid} from "drizzle-orm/pg-core";
|
||||
import {notificationChannel} from "@/db/schema/09_notification-channel";
|
||||
import {timestamps} from "@/db/schema/00_common";
|
||||
import {relations} from "drizzle-orm";
|
||||
import {database, retentionPolicy} 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']);
|
||||
|
||||
export const alertPolicy = pgTable('alert_policy', {
|
||||
id: uuid('id').defaultRandom().primaryKey(),
|
||||
notificationChannelId: uuid('notification_channel_id')
|
||||
.notNull()
|
||||
.references(() => notificationChannel.id, {onDelete: 'restrict'}),
|
||||
eventKinds: eventKindEnum("event_kind").array().notNull(),
|
||||
enabled: boolean('enabled').default(true).notNull(),
|
||||
databaseId: uuid('database_id')
|
||||
.notNull()
|
||||
.references(() => database.id, { onDelete: 'cascade' }),
|
||||
...timestamps
|
||||
});
|
||||
|
||||
export const alertPolicyRelations = relations(alertPolicy, ({one}) => ({
|
||||
notificationChannel: one(notificationChannel, {
|
||||
fields: [alertPolicy.notificationChannelId],
|
||||
references: [notificationChannel.id],
|
||||
}),
|
||||
database: one(database, {
|
||||
fields: [alertPolicy.databaseId],
|
||||
references: [database.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const notificationChannelsToAlertPoliciesRelations = relations(notificationChannel, ({many}) => ({
|
||||
alertPolicies: many(alertPolicy),
|
||||
}));
|
||||
|
||||
|
||||
export const alertPolicySchema = createSelectSchema(alertPolicy);
|
||||
export type AlertPolicy = z.infer<typeof alertPolicySchema>;
|
||||
@@ -0,0 +1,32 @@
|
||||
import {pgTable, uuid, timestamp, jsonb, varchar, boolean, text, pgEnum} from 'drizzle-orm/pg-core';
|
||||
import {notificationChannel, providerKindEnum} from "@/db/schema/09_notification-channel";
|
||||
import {alertPolicy} from "@/db/schema/10_alert-policy";
|
||||
import {organization} from "@/db/schema/03_organization";
|
||||
import {timestamps} from "@/db/schema/00_common";
|
||||
|
||||
export const levelEnum = pgEnum('level', ['critical', 'warning', 'info']);
|
||||
|
||||
|
||||
export const notificationLog = pgTable('notification_log', {
|
||||
id: uuid('id').defaultRandom().primaryKey(),
|
||||
|
||||
channelId: uuid('channel_id')
|
||||
.notNull()
|
||||
.references(() => notificationChannel.id, { onDelete: 'restrict' }),
|
||||
policyId: uuid('policy_id')
|
||||
.references(() => alertPolicy.id, { onDelete: 'restrict' }),
|
||||
organizationId: uuid('organization_id')
|
||||
.references(() => organization.id, { onDelete: 'set null' }),
|
||||
|
||||
title: varchar('title', { length: 255 }).notNull(),
|
||||
message: text('message').notNull(),
|
||||
level: levelEnum('level').notNull(),
|
||||
payload: jsonb('payload'),
|
||||
|
||||
success: boolean('success').notNull(),
|
||||
error: text('error'),
|
||||
providerResponse: jsonb('provider_response'),
|
||||
sentAt: timestamp('sent_at').defaultNow().notNull(),
|
||||
|
||||
...timestamps
|
||||
});
|
||||
@@ -0,0 +1,55 @@
|
||||
import {desc, eq, and, gte, lte} from 'drizzle-orm';
|
||||
import {
|
||||
notificationLog
|
||||
} from "@/db/schema/11_notification-log";
|
||||
import {
|
||||
notificationChannel,
|
||||
} from "@/db/schema/09_notification-channel";
|
||||
import {db} from "@/db";
|
||||
import {alertPolicy} from "@/db/schema/10_alert-policy";
|
||||
|
||||
|
||||
export async function getNotificationHistory(filters?: {
|
||||
channelId?: string;
|
||||
policyId?: string;
|
||||
organizationId?: string;
|
||||
level?: string;
|
||||
success?: boolean;
|
||||
from?: Date;
|
||||
to?: Date;
|
||||
limit?: number;
|
||||
}) {
|
||||
const where = [];
|
||||
if (filters?.channelId) where.push(eq(notificationLog.channelId, filters.channelId));
|
||||
if (filters?.policyId) where.push(eq(notificationLog.policyId, filters.policyId));
|
||||
if (filters?.organizationId) where.push(eq(notificationLog.organizationId, filters.organizationId));
|
||||
if (filters?.level) where.push(eq(notificationLog.level, filters.level));
|
||||
if (typeof filters?.success === 'boolean') where.push(eq(notificationLog.success, filters.success));
|
||||
if (filters?.from) where.push(gte(notificationLog.sentAt, filters.from));
|
||||
if (filters?.to) where.push(lte(notificationLog.sentAt, filters.to));
|
||||
|
||||
return await db
|
||||
.select({
|
||||
id: notificationLog.id,
|
||||
title: notificationLog.title,
|
||||
level: notificationLog.level,
|
||||
success: notificationLog.success,
|
||||
error: notificationLog.error,
|
||||
sentAt: notificationLog.sentAt,
|
||||
channel: {
|
||||
id: notificationLog.id,
|
||||
name: notificationChannel.name,
|
||||
provider: notificationChannel.provider,
|
||||
},
|
||||
policy: {
|
||||
id: alertPolicy.id,
|
||||
eventKind: alertPolicy.eventKind,
|
||||
},
|
||||
})
|
||||
.from(notificationLog)
|
||||
.leftJoin(notificationChannel, eq(notificationLog.channelId, notificationChannel.id))
|
||||
.leftJoin(alertPolicy, eq(notificationLog.policyId, alertPolicy.id))
|
||||
.where(and(...where))
|
||||
.orderBy(desc(notificationLog.sentAt))
|
||||
.limit(filters?.limit || 100);
|
||||
}
|
||||
Reference in New Issue
Block a user