feat: Working on storage modal in database page in order to add multiple backend storages. Adding a system to manage independently channels from organizations and from system admin.

This commit is contained in:
charlesgauthereau
2026-01-14 21:54:13 +01:00
parent d8e9e2dac3
commit 91f25bc25d
32 changed files with 7663 additions and 461 deletions
+3 -1
View File
@@ -13,6 +13,7 @@ import * as organizationNotificationChannel from "./schema/09_notification-chann
import * as alertPolicy from "./schema/10_alert-policy";
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 {Pool} from "pg";
@@ -42,7 +43,8 @@ export const schemas = {
...organizationNotificationChannel,
...alertPolicy,
...notificationLog,
...storageChannel
...storageChannel,
...storagePolicy
};
export const db = drizzle({
@@ -0,0 +1,16 @@
CREATE TYPE "public"."backup_storage_status" AS ENUM('pending', 'success', 'failed');--> statement-breakpoint
CREATE TABLE "backup_storage" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"backup_id" uuid NOT NULL,
"storage_channel_id" uuid NOT NULL,
"status" "backup_storage_status" DEFAULT 'pending' NOT NULL,
"path" text,
"size" integer,
"checksum" text,
"updated_at" timestamp,
"created_at" timestamp DEFAULT now() NOT NULL,
"deleted_at" timestamp
);
--> statement-breakpoint
ALTER TABLE "backup_storage" ADD CONSTRAINT "backup_storage_backup_id_backups_id_fk" FOREIGN KEY ("backup_id") REFERENCES "public"."backups"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "backup_storage" ADD CONSTRAINT "backup_storage_storage_channel_id_storage_channel_id_fk" FOREIGN KEY ("storage_channel_id") REFERENCES "public"."storage_channel"("id") ON DELETE cascade ON UPDATE no action;
@@ -0,0 +1,2 @@
ALTER TABLE "storage_channel" ADD COLUMN "organization_id" uuid;--> statement-breakpoint
ALTER TABLE "storage_channel" ADD CONSTRAINT "storage_channel_organization_id_organization_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE cascade ON UPDATE no action;
@@ -0,0 +1,2 @@
ALTER TABLE "notification_channel" ADD COLUMN "organization_id" uuid;--> statement-breakpoint
ALTER TABLE "notification_channel" ADD CONSTRAINT "notification_channel_organization_id_organization_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE cascade ON UPDATE no action;
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
+21
View File
@@ -155,6 +155,27 @@
"when": 1768337434929,
"tag": "0021_soft_blockbuster",
"breakpoints": true
},
{
"idx": 22,
"version": "7",
"when": 1768379186747,
"tag": "0022_purple_retro_girl",
"breakpoints": true
},
{
"idx": 23,
"version": "7",
"when": 1768412730102,
"tag": "0023_common_the_captain",
"breakpoints": true
},
{
"idx": 24,
"version": "7",
"when": 1768412750113,
"tag": "0024_lush_blindfold",
"breakpoints": true
}
]
}
+2 -1
View File
@@ -8,6 +8,7 @@ import {z} from "zod";
import {timestamps} from "@/db/schema/00_common";
import {AlertPolicy, alertPolicy} from "@/db/schema/10_alert-policy";
import {StoragePolicy, storagePolicy} from "@/db/schema/13_storage-policy";
import {backupStorage} from "@/db/schema/14_storage-backup";
export const database = pgTable("databases", {
id: uuid("id").primaryKey().defaultRandom(),
@@ -41,7 +42,6 @@ export const backup = pgTable(
.references(() => database.id, {onDelete: "cascade"}),
...timestamps
},
// (table) => [uniqueIndex("database_id_status_unique").on(table.databaseId, table.status)]
);
export const retentionPolicyType = pgEnum("retention_policy_type", ["count", "days", "gfs"]);
@@ -87,6 +87,7 @@ export const databaseRelations = relations(database, ({one, many}) => ({
export const backupRelations = relations(backup, ({one, many}) => ({
database: one(database, {fields: [backup.databaseId], references: [database.id]}),
restorations: many(restoration),
storages: many(backupStorage),
}));
export const restorationRelations = relations(restoration, ({one}) => ({
+1
View File
@@ -12,6 +12,7 @@ export const providerKindEnum = pgEnum('provider_kind', ['slack', 'smtp', 'disco
export const notificationChannel = pgTable('notification_channel', {
id: uuid("id").defaultRandom().primaryKey(),
provider: providerKindEnum('provider').notNull(),
organizationId: uuid("organization_id").references(() => organization.id, {onDelete: "cascade"}),
name: varchar('name', {length: 255}).notNull(),
config: jsonb('config').notNull(),
enabled: boolean('enabled').default(false).notNull(),
+2
View File
@@ -4,12 +4,14 @@ import {organization} from "@/db/schema/03_organization";
import {relations} from "drizzle-orm";
import {createSelectSchema} from "drizzle-zod";
import {z} from "zod";
import {database} from "@/db/schema/07_database";
export const providerStorageKindEnum = pgEnum('provider_storage_kind', ['local', 's3']);
export const storageChannel = pgTable('storage_channel', {
id: uuid("id").defaultRandom().primaryKey(),
organizationId: uuid("organization_id").references(() => organization.id, {onDelete: "cascade"}),
provider: providerStorageKindEnum('provider').notNull(),
name: varchar('name', {length: 255}).notNull(),
config: jsonb('config').notNull(),
+38
View File
@@ -0,0 +1,38 @@
import { pgTable, uuid, text, integer, pgEnum } from "drizzle-orm/pg-core";
import { timestamps } from "@/db/schema/00_common";
import { storageChannel } from "@/db/schema/12_storage-channel";
import {backup} from "@/db/schema/07_database";
import {relations} from "drizzle-orm";
export const backupStorageStatusEnum = pgEnum("backup_storage_status", [
"pending",
"success",
"failed",
]);
export const backupStorage = pgTable("backup_storage", {
id: uuid("id").primaryKey().defaultRandom(),
backupId: uuid("backup_id")
.notNull()
.references(() => backup.id, { onDelete: "cascade" }),
storageChannelId: uuid("storage_channel_id")
.notNull()
.references(() => storageChannel.id, { onDelete: "cascade" }),
status: backupStorageStatusEnum("status").notNull().default("pending"),
path: text("path"),
size: integer("size"),
checksum: text("checksum"),
...timestamps,
});
export const backupStorageRelations = relations(backupStorage, ({ one }) => ({
backup: one(backup, {
fields: [backupStorage.backupId],
references: [backup.id],
}),
storageChannel: one(storageChannel, {
fields: [backupStorage.storageChannelId],
references: [storageChannel.id],
}),
}));
+2
View File
@@ -5,6 +5,7 @@ import {
notificationChannel,
organizationNotificationChannel
} from "@/db/schema/09_notification-channel";
import {storageChannel} from "@/db/schema/12_storage-channel";
export async function getOrganizationChannels(organizationId: string) {
return await db
@@ -17,6 +18,7 @@ export async function getOrganizationChannels(organizationId: string) {
updatedAt: notificationChannel.updatedAt,
createdAt: notificationChannel.createdAt,
deletedAt: notificationChannel.deletedAt,
organizationId: notificationChannel.organizationId
})
.from(organizationNotificationChannel)
.innerJoin(
+25
View File
@@ -0,0 +1,25 @@
import {desc, eq} from "drizzle-orm";
import {db} from "@/db";
import {organizationStorageChannel, StorageChannel, storageChannel} from "@/db/schema/12_storage-channel";
export async function getOrganizationStorageChannels(organizationId: string) {
return await db
.select({
id: storageChannel.id,
name: storageChannel.name,
provider: storageChannel.provider,
organizationId: storageChannel.organizationId,
config: storageChannel.config,
enabled: storageChannel.enabled,
updatedAt: storageChannel.updatedAt,
createdAt: storageChannel.createdAt,
deletedAt: storageChannel.deletedAt,
})
.from(organizationStorageChannel)
.innerJoin(
storageChannel,
eq(organizationStorageChannel.storageChannelId, storageChannel.id)
)
.orderBy(desc(storageChannel.createdAt))
.where(eq(organizationStorageChannel.organizationId, organizationId)) as unknown as StorageChannel[];
}