-
-
- {capitalizeFirstLetter(proj.name)}
-
- {!isMember && (
-
- )}
-
+ return (
+
+
+
+
+ {capitalizeFirstLetter(proj.name)}
+
+ {!isMember && (
+
-
- {proj.databases.length > 0 ? (
-
- new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
- )}
- organizationSlug={organization.slug}
- // @ts-ignore
- cardItem={ProjectDatabaseCard}
- cardsPerPage={20}
- numberOfColumns={3}
- pageSizeOptions={[10, 20, 50]}
- extendedProps={proj}
- />
- ) : (
-
-
No databases found
-
You haven’t added any databases to this project yet.
-
- )}
-
-
- );
-}
\ No newline at end of file
+ )}
+
+
+
+ {proj.databases.length > 0 ? (
+
+ new Date(b.createdAt).getTime() -
+ new Date(a.createdAt).getTime(),
+ )}
+ organizationSlug={organization.slug}
+ // @ts-ignore
+ cardItem={ProjectDatabaseCard}
+ cardsPerPage={20}
+ numberOfColumns={3}
+ pageSizeOptions={[10, 20, 50]}
+ extendedProps={proj}
+ />
+ ) : (
+
+
No databases found
+
+ You haven’t added any databases to this project yet.
+
+
+ )}
+
+
+ );
+}
diff --git a/app/(customer)/dashboard/layout.tsx b/app/(customer)/dashboard/layout.tsx
index 4773bf99..74a40963 100644
--- a/app/(customer)/dashboard/layout.tsx
+++ b/app/(customer)/dashboard/layout.tsx
@@ -5,12 +5,13 @@ import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar";
import { AppSidebar } from "@/features/layout/app-sidebar";
import { Header } from "@/features/layout/header";
import { currentUser } from "@/lib/auth/current-user";
-import { isOnboardingDone } from "@/features/onboarding/is-onboarding-done";
+import { isOnboardingDone } from "@/db/services/setting";
+import { env } from "@/env.mjs";
import { ModeToggle } from "@/features/theme/mode-toggle";
import { UpdateNotification } from "@/features/updates/update-notification";
export default async function Layout({ children }: { children: ReactNode }) {
- if (!(await isOnboardingDone())) {
+ if (env.SKIP_ONBOARDING !== "true" && !(await isOnboardingDone())) {
redirect("/welcome");
}
diff --git a/app/(landing)/page.tsx b/app/(landing)/page.tsx
index 1d453b2c..8d1da1a2 100644
--- a/app/(landing)/page.tsx
+++ b/app/(landing)/page.tsx
@@ -1,10 +1,11 @@
import { redirect } from "next/navigation";
import { getCurrentOrganizationSlug } from "@/features/organizations/organization-cookie";
import { currentUser } from "@/lib/auth/current-user";
-import { isOnboardingDone } from "@/features/onboarding/is-onboarding-done";
+import { isOnboardingDone } from "@/db/services/setting";
+import { env } from "@/env.mjs";
export default async function Index() {
- if (!(await isOnboardingDone())) {
+ if (env.SKIP_ONBOARDING !== "true" && !(await isOnboardingDone())) {
redirect("/welcome");
}
diff --git a/app/(welcome)/welcome/page.tsx b/app/(welcome)/welcome/page.tsx
index c15be355..fc50104a 100644
--- a/app/(welcome)/welcome/page.tsx
+++ b/app/(welcome)/welcome/page.tsx
@@ -11,6 +11,7 @@ export default async function WelcomePage() {
return (
diff --git a/app/api/agent/[agentId]/backup/upload/init/route.ts b/app/api/agent/[agentId]/backup/upload/init/route.ts
index 4f9deb47..67a6bb03 100644
--- a/app/api/agent/[agentId]/backup/upload/init/route.ts
+++ b/app/api/agent/[agentId]/backup/upload/init/route.ts
@@ -1,79 +1,86 @@
-import {NextResponse} from "next/server";
-import {and, eq} from "drizzle-orm";
+import { NextResponse } from "next/server";
+import { and, eq } from "drizzle-orm";
import * as drizzleDb from "@/db";
-import {db} from "@/db";
-import {getDatabaseOrThrow, withAgentCheck} from "../../helpers";
-import {isUuidv4} from "@/utils/verify-uuid";
-import {eventEmitter} from "@/lib/event";
-import {logger} from "@/lib/logger";
+import { db } from "@/db";
+import { getDatabaseOrThrow, withAgentCheck } from "../../helpers";
+import { eventEmitter } from "@/lib/event";
+import { logger } from "@/lib/logger";
+import { isUUID } from "@/utils/text";
-const log = logger.child({module: "api/agent/backup/upload/init"});
+const log = logger.child({ module: "api/agent/backup/upload/init" });
export type Body = {
- generatedId: string
- storageChannelId: string
- backupId: string
-}
-export const POST = withAgentCheck(async (request: Request, {params, agent}: {
- params: Promise<{ agentId: string }>,
- agent: any
-}) => {
+ generatedId: string;
+ storageChannelId: string;
+ backupId: string;
+};
+export const POST = withAgentCheck(
+ async (
+ request: Request,
+ {
+ params,
+ agent,
+ }: {
+ params: Promise<{ agentId: string }>;
+ agent: any;
+ },
+ ) => {
try {
- const body: Body = await request.json();
+ const body: Body = await request.json();
- log.info({data: body}, "Body for backup upload init");
+ log.info({ data: body }, "Body for backup upload init");
- const generatedId = body.generatedId;
- const storageChannelId = body.storageChannelId;
- const backupId = body.backupId;
-
- if (!generatedId || !isUuidv4(generatedId)) {
- return NextResponse.json(
- {error: "generatedId is not a valid UUID"},
- {status: 400}
- );
- }
-
- const database = await getDatabaseOrThrow(generatedId);
-
- const backup = await db.query.backup.findFirst({
- where: and(
- eq(drizzleDb.schemas.backup.id, backupId),
- eq(drizzleDb.schemas.backup.databaseId, database.id),
- ),
- });
-
- if (!backup) {
- return NextResponse.json(
- {error: "Unable to find the corresponding backup"},
- {status: 404}
- );
- }
-
- const [backupStorage] = await db
- .insert(drizzleDb.schemas.backupStorage)
- .values({
- backupId: backup.id,
- storageChannelId: storageChannelId,
- status: "pending",
- })
- .returning();
-
- eventEmitter.emit('modification', {update: true});
+ const generatedId = body.generatedId;
+ const storageChannelId = body.storageChannelId;
+ const backupId = body.backupId;
+ if (!generatedId || !isUUID(generatedId)) {
return NextResponse.json(
- {
- message: "Backup storage successfully created",
- backupStorage: backupStorage
- },
- {status: 200}
+ { error: "generatedId is not a valid UUID" },
+ { status: 400 },
);
+ }
+
+ const database = await getDatabaseOrThrow(generatedId);
+
+ const backup = await db.query.backup.findFirst({
+ where: and(
+ eq(drizzleDb.schemas.backup.id, backupId),
+ eq(drizzleDb.schemas.backup.databaseId, database.id),
+ ),
+ });
+
+ if (!backup) {
+ return NextResponse.json(
+ { error: "Unable to find the corresponding backup" },
+ { status: 404 },
+ );
+ }
+
+ const [backupStorage] = await db
+ .insert(drizzleDb.schemas.backupStorage)
+ .values({
+ backupId: backup.id,
+ storageChannelId: storageChannelId,
+ status: "pending",
+ })
+ .returning();
+
+ eventEmitter.emit("modification", { update: true });
+
+ return NextResponse.json(
+ {
+ message: "Backup storage successfully created",
+ backupStorage: backupStorage,
+ },
+ { status: 200 },
+ );
} catch (error) {
- log.error({error: error}, "Error in POST for INIT backup");
- return NextResponse.json(
- {error: "Internal server error"},
- {status: 500}
- );
+ log.error({ error: error }, "Error in POST for INIT backup");
+ return NextResponse.json(
+ { error: "Internal server error" },
+ { status: 500 },
+ );
}
-});
-
+ },
+);
diff --git a/app/api/agent/[agentId]/restore/route.ts b/app/api/agent/[agentId]/restore/route.ts
index 267a92e0..ad770025 100644
--- a/app/api/agent/[agentId]/restore/route.ts
+++ b/app/api/agent/[agentId]/restore/route.ts
@@ -1,110 +1,124 @@
-import {NextResponse} from "next/server";
-import {isUuidv4} from "@/utils/verify-uuid";
+import { NextResponse } from "next/server";
import * as drizzleDb from "@/db";
-import {db as dbClient, db} from "@/db";
-import {and, eq} from "drizzle-orm";
-import {sendNotificationsBackupRestore} from "@/features/notifications/notifications.helpers";
-import {logger} from "@/lib/logger";
-import {withUpdatedAt} from "@/db/utils";
-import {JobLogEntry} from "@/features/logs/types";
+import { db as dbClient, db } from "@/db";
+import { and, eq } from "drizzle-orm";
+import { sendNotificationsBackupRestore } from "@/features/notifications/notifications.helpers";
+import { logger } from "@/lib/logger";
+import { withUpdatedAt } from "@/db/utils";
+import { JobLogEntry } from "@/features/logs/types";
+import { isUUID } from "@/utils/text";
-const log = logger.child({module: "api/agent/restore"});
+const log = logger.child({ module: "api/agent/restore" });
export type BodyResultRestore = {
- generatedId: string
- status: string
- logs: JobLogEntry[]
- durationMs: number
-}
-type RestorationStatus = 'waiting' | 'ongoing' | 'failed' | 'success';
-
+ generatedId: string;
+ status: string;
+ logs: JobLogEntry[];
+ durationMs: number;
+};
+type RestorationStatus = "waiting" | "ongoing" | "failed" | "success";
export async function POST(
- request: Request,
- {params}: { params: Promise<{ agentId: string }> }
+ request: Request,
+ { params }: { params: Promise<{ agentId: string }> },
) {
+ try {
+ const agentId = (await params).agentId;
+ const body: BodyResultRestore = await request.json();
- try {
-
- const agentId = (await params).agentId
- const body: BodyResultRestore = await request.json();
-
-
- if (!isUuidv4(body.generatedId)) {
- return NextResponse.json(
- {error: "generatedId is not a valid uuid"},
- {status: 500}
- );
- }
-
- const agent = await db.query.agent.findFirst({
- where: and(eq(drizzleDb.schemas.agent.id, agentId), eq(drizzleDb.schemas.agent.isArchived, false)),
- })
- if (!agent) {
- return NextResponse.json({error: "Agent not found"}, {status: 404})
- }
-
- const database = await db.query.database.findFirst({
- where: eq(drizzleDb.schemas.database.agentDatabaseId, body.generatedId),
- with: {
- alertPolicies: true
- }
- })
-
- if (!database) {
- return NextResponse.json({error: "Database associated with generatedId provided not found"}, {status: 404})
- }
-
- const restoration = await db.query.restoration.findFirst({
- where: and(eq(drizzleDb.schemas.restoration.status, "ongoing"), eq(drizzleDb.schemas.restoration.databaseId, database.id),)
- })
-
- if (!restoration) {
- return NextResponse.json({error: "Unable to fin the corresponding restoration"}, {status: 404})
- }
-
- const [restorationUpdated] = await db
- .update(drizzleDb.schemas.restoration)
- .set(withUpdatedAt({status: body.status as RestorationStatus, durationMs: body.durationMs}))
- .where(eq(drizzleDb.schemas.restoration.id, restoration.id)).returning();
-
-
- const logsToInsert = body.logs.map((entry) => ({
- backupId: null,
- restorationId: restorationUpdated.id,
-
- loggedAt: new Date(entry.timestamp),
-
- entryType: entry.type,
- level: entry.level,
-
- message: entry.message,
- command: entry.command ?? null,
- output: entry.output ?? null,
-
- exitCode: entry.exit_code ?? null,
- durationMs: entry.duration_ms ?? null,
- }));
-
- if (logsToInsert.length > 0) {
- await dbClient
- .insert(drizzleDb.schemas.jobLog)
- .values(logsToInsert);
- }
-
- await sendNotificationsBackupRestore(database, body.status == "failed" ? "error_restore" : "success_restore");
-
- const response = {
- status: true,
- message: "Restoration successfully updated"
- }
-
- return Response.json(response, {status: 200})
- } catch (error) {
- log.error({error: error}, "Error in POST handler")
- return NextResponse.json(
- {error: 'Internal server error'},
- {status: 500}
- );
+ if (!isUUID(body.generatedId)) {
+ return NextResponse.json(
+ { error: "generatedId is not a valid uuid" },
+ { status: 500 },
+ );
}
-}
\ No newline at end of file
+
+ const agent = await db.query.agent.findFirst({
+ where: and(
+ eq(drizzleDb.schemas.agent.id, agentId),
+ eq(drizzleDb.schemas.agent.isArchived, false),
+ ),
+ });
+ if (!agent) {
+ return NextResponse.json({ error: "Agent not found" }, { status: 404 });
+ }
+
+ const database = await db.query.database.findFirst({
+ where: eq(drizzleDb.schemas.database.agentDatabaseId, body.generatedId),
+ with: {
+ alertPolicies: true,
+ },
+ });
+
+ if (!database) {
+ return NextResponse.json(
+ { error: "Database associated with generatedId provided not found" },
+ { status: 404 },
+ );
+ }
+
+ const restoration = await db.query.restoration.findFirst({
+ where: and(
+ eq(drizzleDb.schemas.restoration.status, "ongoing"),
+ eq(drizzleDb.schemas.restoration.databaseId, database.id),
+ ),
+ });
+
+ if (!restoration) {
+ return NextResponse.json(
+ { error: "Unable to fin the corresponding restoration" },
+ { status: 404 },
+ );
+ }
+
+ const [restorationUpdated] = await db
+ .update(drizzleDb.schemas.restoration)
+ .set(
+ withUpdatedAt({
+ status: body.status as RestorationStatus,
+ durationMs: body.durationMs,
+ }),
+ )
+ .where(eq(drizzleDb.schemas.restoration.id, restoration.id))
+ .returning();
+
+ const logsToInsert = body.logs.map((entry) => ({
+ backupId: null,
+ restorationId: restorationUpdated.id,
+
+ loggedAt: new Date(entry.timestamp),
+
+ entryType: entry.type,
+ level: entry.level,
+
+ message: entry.message,
+ command: entry.command ?? null,
+ output: entry.output ?? null,
+
+ exitCode: entry.exit_code ?? null,
+ durationMs: entry.duration_ms ?? null,
+ }));
+
+ if (logsToInsert.length > 0) {
+ await dbClient.insert(drizzleDb.schemas.jobLog).values(logsToInsert);
+ }
+
+ await sendNotificationsBackupRestore(
+ database,
+ body.status == "failed" ? "error_restore" : "success_restore",
+ );
+
+ const response = {
+ status: true,
+ message: "Restoration successfully updated",
+ };
+
+ return Response.json(response, { status: 200 });
+ } catch (error) {
+ log.error({ error: error }, "Error in POST handler");
+ return NextResponse.json(
+ { error: "Internal server error" },
+ { status: 500 },
+ );
+ }
+}
diff --git a/app/api/agent/[agentId]/status/helpers.ts b/app/api/agent/[agentId]/status/helpers.ts
index 9b32bf1c..ca8605fd 100644
--- a/app/api/agent/[agentId]/status/helpers.ts
+++ b/app/api/agent/[agentId]/status/helpers.ts
@@ -1,275 +1,328 @@
-import {NextResponse} from "next/server";
-import {Body} from "./route";
-import {isUuidv4} from "@/utils/verify-uuid";
-import {Agent} from "@/db/schema/08_agent";
-import {DatabaseWith} from "@/db/schema/07_database";
+import { NextResponse } from "next/server";
+import { Body } from "./route";
+import { Agent } from "@/db/schema/08_agent";
+import { DatabaseWith } from "@/db/schema/07_database";
import * as drizzleDb from "@/db";
-import {db, db as dbClient} from "@/db";
-import {and, eq, inArray} from "drizzle-orm";
-import {dbmsEnumSchema, EDbmsSchema} from "@/db/schema/types";
-import {withUpdatedAt} from "@/db/utils";
-import type {StorageInput} from "@/features/storages/storages.types";
-import {dispatchStorage} from "@/features/storages/storages.dispatch";
-import {Setting} from "@/db/schema/01_setting";
-import {logger} from "@/lib/logger";
+import { db, db as dbClient } from "@/db";
+import { and, eq, inArray } from "drizzle-orm";
+import { dbmsEnumSchema, EDbmsSchema } from "@/db/schema/types";
+import { withUpdatedAt } from "@/db/utils";
+import type { StorageInput } from "@/features/storages/storages.types";
+import { dispatchStorage } from "@/features/storages/storages.dispatch";
+import { Setting } from "@/db/schema/01_setting";
+import { logger } from "@/lib/logger";
+import { isUUID } from "@/utils/text";
-const log = logger.child({module: "api/agent/status/helpers"});
+const log = logger.child({ module: "api/agent/status/helpers" });
-export async function handleDatabases(body: Body, agent: Agent, lastContact: Date, settings: Setting) {
- const databasesResponse = [];
+export async function handleDatabases(
+ body: Body,
+ agent: Agent,
+ lastContact: Date,
+ settings: Setting,
+) {
+ const databasesResponse = [];
- const formatDatabase = (database: DatabaseWith, backupAction: boolean, restoreAction: boolean, UrlBackup: string | null, storages: PingDatabaseStorageChannels[], urlMeta: string | null) => ({
- generatedId: database.agentDatabaseId,
- dbms: database.dbms,
- storages: storages,
- encrypt: settings.encryption,
- data: {
- backup: {
- action: backupAction,
- cron: database.backupPolicy,
- },
- restore: {
- action: restoreAction,
- file: UrlBackup,
- metaFile: urlMeta
- },
- },
+ const formatDatabase = (
+ database: DatabaseWith,
+ backupAction: boolean,
+ restoreAction: boolean,
+ UrlBackup: string | null,
+ storages: PingDatabaseStorageChannels[],
+ urlMeta: string | null,
+ ) => ({
+ generatedId: database.agentDatabaseId,
+ dbms: database.dbms,
+ storages: storages,
+ encrypt: settings.encryption,
+ data: {
+ backup: {
+ action: backupAction,
+ cron: database.backupPolicy,
+ },
+ restore: {
+ action: restoreAction,
+ file: UrlBackup,
+ metaFile: urlMeta,
+ },
+ },
+ });
+
+ for (const db of body.databases) {
+ const existingDatabase = await dbClient.query.database.findFirst({
+ where: eq(drizzleDb.schemas.database.agentDatabaseId, db.generatedId),
+ with: {
+ project: true,
+ },
});
- for (const db of body.databases) {
+ let backupAction: boolean = false;
+ let restoreAction: boolean = false;
+ let urlBackup: string | null = null;
+ let urlMeta: string | null = null;
- const existingDatabase = await dbClient.query.database.findFirst({
- where: eq(drizzleDb.schemas.database.agentDatabaseId, db.generatedId),
- with: {
- project: true
- }
+ if (!existingDatabase) {
+ if (!isUUID(db.generatedId)) {
+ return NextResponse.json(
+ { error: "generatedId is not a valid uuid" },
+ { status: 500 },
+ );
+ }
+
+ if (!dbmsEnumSchema.safeParse(db.dbms).success) {
+ log.error(
+ { name: "handleDatabases" },
+ `Database type not available: ${db.dbms}`,
+ );
+ continue;
+ }
+
+ const [databaseCreated] = await dbClient
+ .insert(drizzleDb.schemas.database)
+ .values({
+ agentId: agent.id,
+ name: db.name,
+ dbms: db.dbms as EDbmsSchema,
+ agentDatabaseId: db.generatedId,
+ lastContact: db.pingStatus ? lastContact : null,
+ healthErrorCount: null,
+ })
+ .returning();
+
+ if (databaseCreated) {
+ await dbClient.insert(drizzleDb.schemas.healthcheckLog).values({
+ kind: "database",
+ status: db.pingStatus ? "success" : "failed",
+ objectId: databaseCreated.id,
+ date: lastContact,
});
- let backupAction: boolean = false
- let restoreAction: boolean = false
- let urlBackup: string | null = null;
- let urlMeta: string | null = null
+ const storages = await getDatabaseStorageChannels(databaseCreated.id);
- if (!existingDatabase) {
- if (!isUuidv4(db.generatedId)) {
- return NextResponse.json(
- {error: "generatedId is not a valid uuid"},
- {status: 500}
- );
- }
+ databasesResponse.push(
+ formatDatabase(
+ databaseCreated,
+ backupAction,
+ restoreAction,
+ urlBackup,
+ storages,
+ null,
+ ),
+ );
+ }
+ } else {
+ const [databaseUpdated] = await dbClient
+ .update(drizzleDb.schemas.database)
+ .set(
+ withUpdatedAt({
+ name: db.name,
+ agentId: agent.id,
+ dbms: db.dbms as EDbmsSchema,
+ lastContact: db.pingStatus
+ ? lastContact
+ : existingDatabase.lastContact,
+ healthErrorCount: db.pingStatus
+ ? null
+ : existingDatabase.healthErrorCount,
+ }),
+ )
+ .where(eq(drizzleDb.schemas.database.id, existingDatabase.id))
+ .returning();
- if (!dbmsEnumSchema.safeParse(db.dbms).success) {
- log.error({name: "handleDatabases"},`Database type not available: ${db.dbms}`);
- continue;
- }
+ await dbClient.insert(drizzleDb.schemas.healthcheckLog).values({
+ kind: "database",
+ status: db.pingStatus ? "success" : "failed",
+ objectId: databaseUpdated.id,
+ date: lastContact,
+ });
- const [databaseCreated] = await dbClient
- .insert(drizzleDb.schemas.database)
- .values({
- agentId: agent.id,
- name: db.name,
- dbms: db.dbms as EDbmsSchema,
- agentDatabaseId: db.generatedId,
- lastContact: db.pingStatus ? lastContact : null,
- healthErrorCount: null
- })
- .returning();
+ const activeBackup = await dbClient.query.backup.findFirst({
+ where: and(
+ eq(drizzleDb.schemas.backup.databaseId, databaseUpdated.id),
+ inArray(drizzleDb.schemas.backup.status, ["waiting", "ongoing"]),
+ ),
+ });
+ const restoration = await dbClient.query.restoration.findFirst({
+ where: and(
+ eq(drizzleDb.schemas.restoration.databaseId, databaseUpdated.id),
+ eq(drizzleDb.schemas.restoration.status, "waiting"),
+ ),
+ with: {
+ backupStorage: true,
+ },
+ });
- if (databaseCreated) {
+ if (activeBackup && activeBackup.status == "waiting") {
+ backupAction = true;
+ await dbClient
+ .update(drizzleDb.schemas.backup)
+ .set(withUpdatedAt({ status: "ongoing" }))
+ .where(eq(drizzleDb.schemas.backup.id, activeBackup.id));
+ }
- await dbClient
- .insert(drizzleDb.schemas.healthcheckLog)
- .values({
- kind: "database",
- status: db.pingStatus ? "success" : "failed",
- objectId: databaseCreated.id,
- date: lastContact
- })
+ if (restoration) {
+ restoreAction = true;
- const storages = await getDatabaseStorageChannels(databaseCreated.id)
-
- databasesResponse.push(formatDatabase(databaseCreated, backupAction, restoreAction, urlBackup, storages, null));
- }
- } else {
-
- const [databaseUpdated] = await dbClient
- .update(drizzleDb.schemas.database)
- .set(withUpdatedAt({
- name: db.name,
- agentId: agent.id,
- dbms: db.dbms as EDbmsSchema,
- lastContact: db.pingStatus ? lastContact : existingDatabase.lastContact,
- healthErrorCount: db.pingStatus ? null : existingDatabase.healthErrorCount,
- }))
- .where(eq(drizzleDb.schemas.database.id, existingDatabase.id))
- .returning();
-
-
- await dbClient
- .insert(drizzleDb.schemas.healthcheckLog)
- .values({
- kind: "database",
- status: db.pingStatus ? "success" : "failed",
- objectId: databaseUpdated.id,
- date: lastContact
- })
-
-
- const activeBackup = await dbClient.query.backup.findFirst({
- where: and(
- eq(drizzleDb.schemas.backup.databaseId, databaseUpdated.id),
- inArray(drizzleDb.schemas.backup.status, ["waiting", "ongoing"])
- )
- })
-
- const restoration = await dbClient.query.restoration.findFirst({
- where: and(eq(drizzleDb.schemas.restoration.databaseId, databaseUpdated.id), eq(drizzleDb.schemas.restoration.status, "waiting")),
- with: {
- backupStorage: true
- }
- })
-
- if (activeBackup && activeBackup.status == "waiting") {
- backupAction = true
-
- await dbClient
- .update(drizzleDb.schemas.backup)
- .set(withUpdatedAt({status: "ongoing"}))
- .where(eq(drizzleDb.schemas.backup.id, activeBackup.id));
- }
-
- if (restoration) {
- restoreAction = true
-
- if (!restoration.backupStorage || restoration.backupStorage.status != "success" || !restoration.backupStorage.path) {
- restoreAction = false
- continue;
- }
-
- const input: StorageInput = {
- action: "get",
- data: {
- path: restoration.backupStorage.path,
- signedUrl: true,
- },
- metadata: {
- storageId: restoration.backupStorage.storageChannelId,
- fileKind: "backups"
- }
- };
-
- const inputMeta: StorageInput = {
- action: "get",
- data: {
- path: `${restoration.backupStorage.path}.meta`,
- signedUrl: true,
- },
- metadata: {
- storageId: restoration.backupStorage.storageChannelId,
- fileKind: "backups"
- }
- };
-
-
- try {
- const result = await dispatchStorage(input, undefined, restoration.backupStorage.storageChannelId);
- const resultMeta = await dispatchStorage(inputMeta, undefined, restoration.backupStorage.storageChannelId);
-
- if (result.success) {
- urlBackup = result.url ?? null;
- urlMeta = resultMeta.url ?? null
- } else {
- await dbClient
- .update(drizzleDb.schemas.restoration)
- .set(withUpdatedAt({status: "failed"}))
- .where(eq(drizzleDb.schemas.restoration.id, restoration.id));
-
- const errorMessage = "Failed to get backup URL";
- log.error({error: errorMessage, name: "handleDatabases"}, "Restoration failed");
- continue;
- }
- } catch (err) {
- log.error({error: err, name: "handleDatabases"}, "Restoration crashed unexpectedly");
- await dbClient
- .update(drizzleDb.schemas.restoration)
- .set(withUpdatedAt({status: "failed"}))
- .where(eq(drizzleDb.schemas.restoration.id, restoration.id));
- continue;
- }
-
- await dbClient
- .update(drizzleDb.schemas.restoration)
- .set(withUpdatedAt({status: "ongoing"}))
- .where(eq(drizzleDb.schemas.restoration.id, restoration.id));
- }
- const storages = await getDatabaseStorageChannels(databaseUpdated.id)
- databasesResponse.push(formatDatabase(databaseUpdated, backupAction, restoreAction, urlBackup, storages, urlMeta));
+ if (
+ !restoration.backupStorage ||
+ restoration.backupStorage.status != "success" ||
+ !restoration.backupStorage.path
+ ) {
+ restoreAction = false;
+ continue;
}
- }
- return databasesResponse;
-}
+ const input: StorageInput = {
+ action: "get",
+ data: {
+ path: restoration.backupStorage.path,
+ signedUrl: true,
+ },
+ metadata: {
+ storageId: restoration.backupStorage.storageChannelId,
+ fileKind: "backups",
+ },
+ };
+
+ const inputMeta: StorageInput = {
+ action: "get",
+ data: {
+ path: `${restoration.backupStorage.path}.meta`,
+ signedUrl: true,
+ },
+ metadata: {
+ storageId: restoration.backupStorage.storageChannelId,
+ fileKind: "backups",
+ },
+ };
+
+ try {
+ const result = await dispatchStorage(
+ input,
+ undefined,
+ restoration.backupStorage.storageChannelId,
+ );
+ const resultMeta = await dispatchStorage(
+ inputMeta,
+ undefined,
+ restoration.backupStorage.storageChannelId,
+ );
+
+ if (result.success) {
+ urlBackup = result.url ?? null;
+ urlMeta = resultMeta.url ?? null;
+ } else {
+ await dbClient
+ .update(drizzleDb.schemas.restoration)
+ .set(withUpdatedAt({ status: "failed" }))
+ .where(eq(drizzleDb.schemas.restoration.id, restoration.id));
+
+ const errorMessage = "Failed to get backup URL";
+ log.error(
+ { error: errorMessage, name: "handleDatabases" },
+ "Restoration failed",
+ );
+ continue;
+ }
+ } catch (err) {
+ log.error(
+ { error: err, name: "handleDatabases" },
+ "Restoration crashed unexpectedly",
+ );
+ await dbClient
+ .update(drizzleDb.schemas.restoration)
+ .set(withUpdatedAt({ status: "failed" }))
+ .where(eq(drizzleDb.schemas.restoration.id, restoration.id));
+ continue;
+ }
+
+ await dbClient
+ .update(drizzleDb.schemas.restoration)
+ .set(withUpdatedAt({ status: "ongoing" }))
+ .where(eq(drizzleDb.schemas.restoration.id, restoration.id));
+ }
+ const storages = await getDatabaseStorageChannels(databaseUpdated.id);
+ databasesResponse.push(
+ formatDatabase(
+ databaseUpdated,
+ backupAction,
+ restoreAction,
+ urlBackup,
+ storages,
+ urlMeta,
+ ),
+ );
+ }
+ }
+ return databasesResponse;
+}
type PingDatabaseStorageChannels = {
- id: string;
- config: any
- provider: string
-}
+ id: string;
+ config: any;
+ provider: string;
+};
-async function getDatabaseStorageChannels(databaseId: string): Promise
{
+async function getDatabaseStorageChannels(
+ databaseId: string,
+): Promise {
+ const database = await db.query.database.findFirst({
+ where: eq(drizzleDb.schemas.database.id, databaseId),
+ with: {
+ project: true,
+ retentionPolicy: true,
+ alertPolicies: true,
+ storagePolicies: true,
+ },
+ });
- const database = await db.query.database.findFirst({
- where: eq(drizzleDb.schemas.database.id, databaseId),
- with: {
- project: true,
- retentionPolicy: true,
- alertPolicies: true,
- storagePolicies: true
- }
- });
+ if (!database) {
+ return [];
+ }
- if (!database) {
- return []
- }
+ const settings = await db.query.setting.findFirst({
+ where: eq(drizzleDb.schemas.setting.name, "system"),
+ with: { storageChannel: true },
+ });
- const settings = await db.query.setting.findFirst({
- where: eq(drizzleDb.schemas.setting.name, "system"),
- with: {storageChannel: true},
- });
-
- const defaultStorageChannel: PingDatabaseStorageChannels[] = settings?.storageChannel
- ? [{
+ const defaultStorageChannel: PingDatabaseStorageChannels[] =
+ settings?.storageChannel
+ ? [
+ {
id: settings.storageChannel.id,
provider: settings.storageChannel.provider,
config: settings.storageChannel.config,
- }]
- : [];
+ },
+ ]
+ : [];
+ const enabledDatabaseStorageChannels = await Promise.all(
+ (database.storagePolicies ?? [])
+ .filter((p) => p.enabled)
+ .map(async (policy) => {
+ const storageChannel = await db.query.storageChannel.findFirst({
+ where: eq(
+ drizzleDb.schemas.storageChannel.id,
+ policy.storageChannelId,
+ ),
+ });
- const enabledDatabaseStorageChannels = await Promise.all(
- (database.storagePolicies ?? [])
- .filter(p => p.enabled)
- .map(async policy => {
- const storageChannel = await db.query.storageChannel.findFirst({
- where: eq(drizzleDb.schemas.storageChannel.id, policy.storageChannelId),
- });
+ if (!storageChannel) return null;
- if (!storageChannel) return null;
+ return {
+ id: storageChannel.id,
+ config: storageChannel.config,
+ provider: storageChannel.provider,
+ } as PingDatabaseStorageChannels;
+ }),
+ );
- return {
- id: storageChannel.id,
- config: storageChannel.config,
- provider: storageChannel.provider,
- } as PingDatabaseStorageChannels;
- })
+ const filteredChannels: PingDatabaseStorageChannels[] =
+ enabledDatabaseStorageChannels.filter(
+ (c): c is PingDatabaseStorageChannels => c !== null,
);
- const filteredChannels: PingDatabaseStorageChannels[] = enabledDatabaseStorageChannels.filter(
- (c): c is PingDatabaseStorageChannels => c !== null
- );
-
- return filteredChannels.length > 0 ? filteredChannels : defaultStorageChannel;
+ return filteredChannels.length > 0 ? filteredChannels : defaultStorageChannel;
}
-
diff --git a/app/api/agent/[agentId]/status/route.ts b/app/api/agent/[agentId]/status/route.ts
index cf67b690..e5362d03 100644
--- a/app/api/agent/[agentId]/status/route.ts
+++ b/app/api/agent/[agentId]/status/route.ts
@@ -1,99 +1,107 @@
-import {NextResponse} from "next/server";
-import {handleDatabases} from "./helpers";
+import { NextResponse } from "next/server";
+import { handleDatabases } from "./helpers";
import * as drizzleDb from "@/db";
-import {db} from "@/db";
-import {EDbmsSchema} from "@/db/schema/types";
-import {and, eq} from "drizzle-orm";
-import {isUuidv4} from "@/utils/verify-uuid";
-import {withUpdatedAt} from "@/db/utils";
-import {logger} from "@/lib/logger";
-
-
-const log = logger.child({module: "api/agent/status/route"});
+import { db } from "@/db";
+import { EDbmsSchema } from "@/db/schema/types";
+import { and, eq } from "drizzle-orm";
+import { withUpdatedAt } from "@/db/utils";
+import { logger } from "@/lib/logger";
+import { isUUID } from "@/utils/text";
+const log = logger.child({ module: "api/agent/status/route" });
export type databaseAgent = {
- name: string,
- dbms: EDbmsSchema,
- generatedId: string
- pingStatus: boolean
-}
+ name: string;
+ dbms: EDbmsSchema;
+ generatedId: string;
+ pingStatus: boolean;
+};
export type Body = {
- version: string,
- databases: databaseAgent[]
-}
-
+ version: string;
+ databases: databaseAgent[];
+};
export async function POST(
- request: Request,
- {params}: { params: Promise<{ agentId: string }> }
+ request: Request,
+ { params }: { params: Promise<{ agentId: string }> },
) {
- try {
- const agentId = (await params).agentId
- log.debug(`Agent ID: ${agentId}`)
- const body: Body = await request.json();
- const lastContact = new Date();
- let message: string
+ try {
+ const agentId = (await params).agentId;
+ log.debug(`Agent ID: ${agentId}`);
+ const body: Body = await request.json();
+ const lastContact = new Date();
+ let message: string;
- if (!isUuidv4(agentId)) {
- message = "agentId is not a valid uuid"
- log.error({error: message}, "An error occurred")
- return NextResponse.json(
- {error: "agentId is not a valid uuid"},
- {status: 500}
- );
- }
-
- const agent = await db.query.agent.findFirst({
- where: and(eq(drizzleDb.schemas.agent.id, agentId), eq(drizzleDb.schemas.agent.isArchived, false)),
- })
-
- if (!agent) {
- message = "Agent not found"
- return NextResponse.json({error: message}, {status: 404})
- }
-
- const [settings] = await db.select().from(drizzleDb.schemas.setting).where(eq(drizzleDb.schemas.setting.name, "system")).limit(1);
- if (!settings) {
- return NextResponse.json({error: "An error occured"}, {status: 404})
- }
-
- const databasesResponse = await handleDatabases(body, agent, lastContact, settings)
-
- await db
- .update(drizzleDb.schemas.agent)
- .set(withUpdatedAt({
- version: body.version,
- lastContact: lastContact,
- healthErrorCount: null
- }))
- .where(eq(drizzleDb.schemas.agent.id, agentId));
-
- await db
- .insert(drizzleDb.schemas.healthcheckLog)
- .values({
- kind: "agent",
- status: "success",
- objectId: agentId,
- date: lastContact
- })
-
- const response = {
- agent: {
- id: agentId,
- lastContact: lastContact
- },
- databases: databasesResponse
- }
-
- return Response.json(response)
- } catch (error) {
- log.error({error: error}, "Error in POST handler")
- return NextResponse.json(
- {error: 'Internal server error'},
- {status: 500}
- );
+ if (!isUUID(agentId)) {
+ message = "agentId is not a valid uuid";
+ log.error({ error: message }, "An error occurred");
+ return NextResponse.json(
+ { error: "agentId is not a valid uuid" },
+ { status: 500 },
+ );
}
-}
+ const agent = await db.query.agent.findFirst({
+ where: and(
+ eq(drizzleDb.schemas.agent.id, agentId),
+ eq(drizzleDb.schemas.agent.isArchived, false),
+ ),
+ });
+
+ if (!agent) {
+ message = "Agent not found";
+ return NextResponse.json({ error: message }, { status: 404 });
+ }
+
+ const [settings] = await db
+ .select()
+ .from(drizzleDb.schemas.setting)
+ .where(eq(drizzleDb.schemas.setting.name, "system"))
+ .limit(1);
+ if (!settings) {
+ return NextResponse.json({ error: "An error occured" }, { status: 404 });
+ }
+
+ const databasesResponse = await handleDatabases(
+ body,
+ agent,
+ lastContact,
+ settings,
+ );
+
+ await db
+ .update(drizzleDb.schemas.agent)
+ .set(
+ withUpdatedAt({
+ version: body.version,
+ lastContact: lastContact,
+ healthErrorCount: null,
+ }),
+ )
+ .where(eq(drizzleDb.schemas.agent.id, agentId));
+
+ await db.insert(drizzleDb.schemas.healthcheckLog).values({
+ kind: "agent",
+ status: "success",
+ objectId: agentId,
+ date: lastContact,
+ });
+
+ const response = {
+ agent: {
+ id: agentId,
+ lastContact: lastContact,
+ },
+ databases: databasesResponse,
+ };
+
+ return Response.json(response);
+ } catch (error) {
+ log.error({ error: error }, "Error in POST handler");
+ return NextResponse.json(
+ { error: "Internal server error" },
+ { status: 500 },
+ );
+ }
+}
diff --git a/app/api/avatar/route.tsx b/app/api/avatar/route.tsx
index 6235a3ab..3c4fdee8 100644
--- a/app/api/avatar/route.tsx
+++ b/app/api/avatar/route.tsx
@@ -1,7 +1,8 @@
import { ImageResponse } from "next/og";
-import { NextRequest } from "next/server";
+import { NextRequest, NextResponse } from "next/server";
+import { getSettings } from "@/db/services/setting";
-export const runtime = "edge";
+export const runtime = "nodejs";
const AVATAR_COLORS = [
"#4f46e5",
@@ -15,6 +16,11 @@ const AVATAR_COLORS = [
];
export async function GET(request: NextRequest) {
+ const settings = await getSettings();
+ if (settings?.avatarMode && settings.avatarMode !== "internal") {
+ return new NextResponse(null, { status: 404 });
+ }
+
const { searchParams } = new URL(request.url);
const initials = (searchParams.get("initials") ?? "?")
.slice(0, 2)
diff --git a/portabase.config.ts b/portabase.config.ts
index 7b3b728a..4e756ada 100644
--- a/portabase.config.ts
+++ b/portabase.config.ts
@@ -46,7 +46,7 @@ export const PORTABASE_DEFAULT_SETTINGS = {
"https://api.iconify.design",
"https://code.iconify.design",
"https://api.github.com",
-
+ "https://api.dicebear.com",
],
OBJECT_SRC: ["'none'"],
BASE_URI: ["'self'"],
diff --git a/src/db/migrations/0067_adorable_jean_grey.sql b/src/db/migrations/0067_adorable_jean_grey.sql
new file mode 100644
index 00000000..455e0c92
--- /dev/null
+++ b/src/db/migrations/0067_adorable_jean_grey.sql
@@ -0,0 +1,2 @@
+CREATE TYPE "public"."avatar_mode" AS ENUM('internal', 'gravatar', 'dicebear');--> statement-breakpoint
+ALTER TABLE "settings" ADD COLUMN "avatar_mode" "avatar_mode" DEFAULT 'internal' NOT NULL;
\ No newline at end of file
diff --git a/src/db/migrations/0068_bitter_revanche.sql b/src/db/migrations/0068_bitter_revanche.sql
new file mode 100644
index 00000000..a801cd73
--- /dev/null
+++ b/src/db/migrations/0068_bitter_revanche.sql
@@ -0,0 +1 @@
+ALTER TABLE "settings" ADD COLUMN "dicebear_style" varchar(64) DEFAULT 'thumbs' NOT NULL;
\ No newline at end of file
diff --git a/src/db/migrations/meta/0067_snapshot.json b/src/db/migrations/meta/0067_snapshot.json
new file mode 100644
index 00000000..99b22bc1
--- /dev/null
+++ b/src/db/migrations/meta/0067_snapshot.json
@@ -0,0 +1,2994 @@
+{
+ "id": "c2dda0eb-6358-43d0-8d13-17c2b3641f21",
+ "prevId": "30cd506c-1b93-44d2-9b65-5c0987efeb1a",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.settings": {
+ "name": "settings",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "smtp_password": {
+ "name": "smtp_password",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "smtp_from": {
+ "name": "smtp_from",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "smtp_host": {
+ "name": "smtp_host",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "smtp_port": {
+ "name": "smtp_port",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "smtp_user": {
+ "name": "smtp_user",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "smtp_secure": {
+ "name": "smtp_secure",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "default_notification_channel_id": {
+ "name": "default_notification_channel_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "default_storage_channel_id": {
+ "name": "default_storage_channel_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "encryption": {
+ "name": "encryption",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "onboarding": {
+ "name": "onboarding",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "avatar_mode": {
+ "name": "avatar_mode",
+ "type": "avatar_mode",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'internal'"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "settings_default_notification_channel_id_notification_channel_id_fk": {
+ "name": "settings_default_notification_channel_id_notification_channel_id_fk",
+ "tableFrom": "settings",
+ "tableTo": "notification_channel",
+ "columnsFrom": [
+ "default_notification_channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "settings_default_storage_channel_id_storage_channel_id_fk": {
+ "name": "settings_default_storage_channel_id_storage_channel_id_fk",
+ "tableFrom": "settings",
+ "tableTo": "storage_channel",
+ "columnsFrom": [
+ "default_storage_channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "settings_name_unique": {
+ "name": "settings_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.account": {
+ "name": "account",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "account_id": {
+ "name": "account_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "access_token": {
+ "name": "access_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refresh_token": {
+ "name": "refresh_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "id_token": {
+ "name": "id_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "access_token_expires_at": {
+ "name": "access_token_expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refresh_token_expires_at": {
+ "name": "refresh_token_expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "scope": {
+ "name": "scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "password": {
+ "name": "password",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "account_user_id_user_id_fk": {
+ "name": "account_user_id_user_id_fk",
+ "tableFrom": "account",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.passkey": {
+ "name": "passkey",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "publicKey": {
+ "name": "publicKey",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "credentialID": {
+ "name": "credentialID",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "counter": {
+ "name": "counter",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "deviceType": {
+ "name": "deviceType",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "backedUp": {
+ "name": "backedUp",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "transports": {
+ "name": "transports",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "aaguid": {
+ "name": "aaguid",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "passkey_user_id_user_id_fk": {
+ "name": "passkey_user_id_user_id_fk",
+ "tableFrom": "passkey",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.session": {
+ "name": "session",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "impersonated_by": {
+ "name": "impersonated_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "active_organization_id": {
+ "name": "active_organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "session_user_id_user_id_fk": {
+ "name": "session_user_id_user_id_fk",
+ "tableFrom": "session",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "session_token_unique": {
+ "name": "session_token_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "token"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.sso_provider": {
+ "name": "sso_provider",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "issuer": {
+ "name": "issuer",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "oidc_config": {
+ "name": "oidc_config",
+ "type": "json",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "saml_config": {
+ "name": "saml_config",
+ "type": "json",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "domain": {
+ "name": "domain",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "sso_provider_user_id_user_id_fk": {
+ "name": "sso_provider_user_id_user_id_fk",
+ "tableFrom": "sso_provider",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "sso_provider_provider_id_unique": {
+ "name": "sso_provider_provider_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "provider_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.two_factor": {
+ "name": "two_factor",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "secret": {
+ "name": "secret",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "backup_codes": {
+ "name": "backup_codes",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "verified": {
+ "name": "verified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "two_factor_user_id_user_id_fk": {
+ "name": "two_factor_user_id_user_id_fk",
+ "tableFrom": "two_factor",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user": {
+ "name": "user",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email_verified": {
+ "name": "email_verified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "image": {
+ "name": "image",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "theme": {
+ "name": "theme",
+ "type": "user_themes",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'light'"
+ },
+ "banned": {
+ "name": "banned",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "ban_reason": {
+ "name": "ban_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "ban_expires": {
+ "name": "ban_expires",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "lastConnectedAt": {
+ "name": "lastConnectedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "lastChangedPasswordAt": {
+ "name": "lastChangedPasswordAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "two_factor_enabled": {
+ "name": "two_factor_enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "user_email_unique": {
+ "name": "user_email_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "email"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.verification": {
+ "name": "verification",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "identifier": {
+ "name": "identifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.organization": {
+ "name": "organization",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "logo": {
+ "name": "logo",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "organization_slug_unique": {
+ "name": "organization_slug_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "slug"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.member": {
+ "name": "member",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "member_organization_id_organization_id_fk": {
+ "name": "member_organization_id_organization_id_fk",
+ "tableFrom": "member",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "member_user_id_user_id_fk": {
+ "name": "member_user_id_user_id_fk",
+ "tableFrom": "member",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.invitation": {
+ "name": "invitation",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "inviter_id": {
+ "name": "inviter_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "invitation_organization_id_organization_id_fk": {
+ "name": "invitation_organization_id_organization_id_fk",
+ "tableFrom": "invitation",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "invitation_inviter_id_user_id_fk": {
+ "name": "invitation_inviter_id_user_id_fk",
+ "tableFrom": "invitation",
+ "tableTo": "user",
+ "columnsFrom": [
+ "inviter_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.projects": {
+ "name": "projects",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "is_archived": {
+ "name": "is_archived",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "projects_organization_id_organization_id_fk": {
+ "name": "projects_organization_id_organization_id_fk",
+ "tableFrom": "projects",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "projects_slug_unique": {
+ "name": "projects_slug_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "slug"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.backups": {
+ "name": "backups",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "status": {
+ "name": "status",
+ "type": "status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'waiting'"
+ },
+ "file": {
+ "name": "file",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "file_size": {
+ "name": "file_size",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "duration_ms": {
+ "name": "duration_ms",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "database_id": {
+ "name": "database_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "imported": {
+ "name": "imported",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "migrated": {
+ "name": "migrated",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "backups_database_id_databases_id_fk": {
+ "name": "backups_database_id_databases_id_fk",
+ "tableFrom": "backups",
+ "tableTo": "databases",
+ "columnsFrom": [
+ "database_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.databases": {
+ "name": "databases",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "agent_database_id": {
+ "name": "agent_database_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "dbms": {
+ "name": "dbms",
+ "type": "dbms_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "backup_policy": {
+ "name": "backup_policy",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_waiting_for_backup": {
+ "name": "is_waiting_for_backup",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "backup_to_restore": {
+ "name": "backup_to_restore",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "health_error_count": {
+ "name": "health_error_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_contact": {
+ "name": "last_contact",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "databases_agent_id_agents_id_fk": {
+ "name": "databases_agent_id_agents_id_fk",
+ "tableFrom": "databases",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "databases_project_id_projects_id_fk": {
+ "name": "databases_project_id_projects_id_fk",
+ "tableFrom": "databases",
+ "tableTo": "projects",
+ "columnsFrom": [
+ "project_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.restorations": {
+ "name": "restorations",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "status": {
+ "name": "status",
+ "type": "status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'waiting'"
+ },
+ "duration_ms": {
+ "name": "duration_ms",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "backup_storage_id": {
+ "name": "backup_storage_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "backup_id": {
+ "name": "backup_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "database_id": {
+ "name": "database_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "restorations_backup_storage_id_backup_storage_id_fk": {
+ "name": "restorations_backup_storage_id_backup_storage_id_fk",
+ "tableFrom": "restorations",
+ "tableTo": "backup_storage",
+ "columnsFrom": [
+ "backup_storage_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "restorations_backup_id_backups_id_fk": {
+ "name": "restorations_backup_id_backups_id_fk",
+ "tableFrom": "restorations",
+ "tableTo": "backups",
+ "columnsFrom": [
+ "backup_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "restorations_database_id_databases_id_fk": {
+ "name": "restorations_database_id_databases_id_fk",
+ "tableFrom": "restorations",
+ "tableTo": "databases",
+ "columnsFrom": [
+ "database_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.retention_policies": {
+ "name": "retention_policies",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "database_id": {
+ "name": "database_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "retention_policy_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "count": {
+ "name": "count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "default": 7
+ },
+ "days": {
+ "name": "days",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "default": 30
+ },
+ "gfs_daily": {
+ "name": "gfs_daily",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "default": 7
+ },
+ "gfs_weekly": {
+ "name": "gfs_weekly",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "default": 4
+ },
+ "gfs_monthly": {
+ "name": "gfs_monthly",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "default": 12
+ },
+ "gfs_yearly": {
+ "name": "gfs_yearly",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "default": 3
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "retention_policies_database_id_databases_id_fk": {
+ "name": "retention_policies_database_id_databases_id_fk",
+ "tableFrom": "retention_policies",
+ "tableTo": "databases",
+ "columnsFrom": [
+ "database_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.agents": {
+ "name": "agents",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "version": {
+ "name": "version",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "health_error_count": {
+ "name": "health_error_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "is_archived": {
+ "name": "is_archived",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "last_contact": {
+ "name": "last_contact",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "agents_organization_id_organization_id_fk": {
+ "name": "agents_organization_id_organization_id_fk",
+ "tableFrom": "agents",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "agents_slug_unique": {
+ "name": "agents_slug_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "slug"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.organization_agents": {
+ "name": "organization_agents",
+ "schema": "",
+ "columns": {
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "organization_agents_organization_id_organization_id_fk": {
+ "name": "organization_agents_organization_id_organization_id_fk",
+ "tableFrom": "organization_agents",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "organization_agents_agent_id_agents_id_fk": {
+ "name": "organization_agents_agent_id_agents_id_fk",
+ "tableFrom": "organization_agents",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "organization_agents_organization_id_agent_id_unique": {
+ "name": "organization_agents_organization_id_agent_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "organization_id",
+ "agent_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.notification_channel": {
+ "name": "notification_channel",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "provider": {
+ "name": "provider",
+ "type": "provider_kind",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "config": {
+ "name": "config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "notification_channel_organization_id_organization_id_fk": {
+ "name": "notification_channel_organization_id_organization_id_fk",
+ "tableFrom": "notification_channel",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.organization_notification_channels": {
+ "name": "organization_notification_channels",
+ "schema": "",
+ "columns": {
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "notification_channel_id": {
+ "name": "notification_channel_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "organization_notification_channels_organization_id_organization_id_fk": {
+ "name": "organization_notification_channels_organization_id_organization_id_fk",
+ "tableFrom": "organization_notification_channels",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "organization_notification_channels_notification_channel_id_notification_channel_id_fk": {
+ "name": "organization_notification_channels_notification_channel_id_notification_channel_id_fk",
+ "tableFrom": "organization_notification_channels",
+ "tableTo": "notification_channel",
+ "columnsFrom": [
+ "notification_channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "organization_notification_channels_organization_id_notification_channel_id_unique": {
+ "name": "organization_notification_channels_organization_id_notification_channel_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "organization_id",
+ "notification_channel_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.alert_policy": {
+ "name": "alert_policy",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "notification_channel_id": {
+ "name": "notification_channel_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "event_kind": {
+ "name": "event_kind",
+ "type": "event_kind[]",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "database_id": {
+ "name": "database_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "alert_policy_notification_channel_id_notification_channel_id_fk": {
+ "name": "alert_policy_notification_channel_id_notification_channel_id_fk",
+ "tableFrom": "alert_policy",
+ "tableTo": "notification_channel",
+ "columnsFrom": [
+ "notification_channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "alert_policy_database_id_databases_id_fk": {
+ "name": "alert_policy_database_id_databases_id_fk",
+ "tableFrom": "alert_policy",
+ "tableTo": "databases",
+ "columnsFrom": [
+ "database_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.notification_log": {
+ "name": "notification_log",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "channel_id": {
+ "name": "channel_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "policy_id": {
+ "name": "policy_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "event": {
+ "name": "event",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_name": {
+ "name": "provider_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "message": {
+ "name": "message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "level": {
+ "name": "level",
+ "type": "level",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "payload": {
+ "name": "payload",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "success": {
+ "name": "success",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "error": {
+ "name": "error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "provider_response": {
+ "name": "provider_response",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "sent_at": {
+ "name": "sent_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.organization_storage_channels": {
+ "name": "organization_storage_channels",
+ "schema": "",
+ "columns": {
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "storage_channel_id": {
+ "name": "storage_channel_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "organization_storage_channels_organization_id_organization_id_fk": {
+ "name": "organization_storage_channels_organization_id_organization_id_fk",
+ "tableFrom": "organization_storage_channels",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "organization_storage_channels_storage_channel_id_storage_channel_id_fk": {
+ "name": "organization_storage_channels_storage_channel_id_storage_channel_id_fk",
+ "tableFrom": "organization_storage_channels",
+ "tableTo": "storage_channel",
+ "columnsFrom": [
+ "storage_channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "organization_storage_channels_organization_id_storage_channel_id_unique": {
+ "name": "organization_storage_channels_organization_id_storage_channel_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "organization_id",
+ "storage_channel_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.storage_channel": {
+ "name": "storage_channel",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "provider": {
+ "name": "provider",
+ "type": "provider_storage_kind",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "config": {
+ "name": "config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "storage_channel_organization_id_organization_id_fk": {
+ "name": "storage_channel_organization_id_organization_id_fk",
+ "tableFrom": "storage_channel",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.storage_policy": {
+ "name": "storage_policy",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "storage_channel_id": {
+ "name": "storage_channel_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "database_id": {
+ "name": "database_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "storage_policy_storage_channel_id_storage_channel_id_fk": {
+ "name": "storage_policy_storage_channel_id_storage_channel_id_fk",
+ "tableFrom": "storage_policy",
+ "tableTo": "storage_channel",
+ "columnsFrom": [
+ "storage_channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "storage_policy_database_id_databases_id_fk": {
+ "name": "storage_policy_database_id_databases_id_fk",
+ "tableFrom": "storage_policy",
+ "tableTo": "databases",
+ "columnsFrom": [
+ "database_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.backup_storage": {
+ "name": "backup_storage",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "backup_id": {
+ "name": "backup_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "storage_channel_id": {
+ "name": "storage_channel_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "backup_storage_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "path": {
+ "name": "path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "size": {
+ "name": "size",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "checksum": {
+ "name": "checksum",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "backup_storage_backup_id_backups_id_fk": {
+ "name": "backup_storage_backup_id_backups_id_fk",
+ "tableFrom": "backup_storage",
+ "tableTo": "backups",
+ "columnsFrom": [
+ "backup_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "backup_storage_storage_channel_id_storage_channel_id_fk": {
+ "name": "backup_storage_storage_channel_id_storage_channel_id_fk",
+ "tableFrom": "backup_storage",
+ "tableTo": "storage_channel",
+ "columnsFrom": [
+ "storage_channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.healthcheck_log": {
+ "name": "healthcheck_log",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "kind": {
+ "name": "kind",
+ "type": "healthcheck_kind",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "date": {
+ "name": "date",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "healthcheck_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "object_id": {
+ "name": "object_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.apikey": {
+ "name": "apikey",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "config_id": {
+ "name": "config_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "start": {
+ "name": "start",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "prefix": {
+ "name": "prefix",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reference_id": {
+ "name": "reference_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "refill_interval": {
+ "name": "refill_interval",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refill_amount": {
+ "name": "refill_amount",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_refill_at": {
+ "name": "last_refill_at",
+ "type": "timestamp (6) with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "rate_limit_enabled": {
+ "name": "rate_limit_enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "rate_limit_time_window": {
+ "name": "rate_limit_time_window",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "rate_limit_max": {
+ "name": "rate_limit_max",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "request_count": {
+ "name": "request_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "remaining": {
+ "name": "remaining",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_request": {
+ "name": "last_request",
+ "type": "timestamp (6) with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp (6) with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp (6) with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp (6) with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "permissions": {
+ "name": "permissions",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.job_log": {
+ "name": "job_log",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "backup_id": {
+ "name": "backup_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "restoration_id": {
+ "name": "restoration_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "logged_at": {
+ "name": "logged_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "entry_type": {
+ "name": "entry_type",
+ "type": "job_log_entry_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "level": {
+ "name": "level",
+ "type": "job_log_level",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "message": {
+ "name": "message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "command": {
+ "name": "command",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "output": {
+ "name": "output",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "exit_code": {
+ "name": "exit_code",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "duration_ms": {
+ "name": "duration_ms",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "job_log_backup_id_backups_id_fk": {
+ "name": "job_log_backup_id_backups_id_fk",
+ "tableFrom": "job_log",
+ "tableTo": "backups",
+ "columnsFrom": [
+ "backup_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "job_log_restoration_id_restorations_id_fk": {
+ "name": "job_log_restoration_id_restorations_id_fk",
+ "tableFrom": "job_log",
+ "tableTo": "restorations",
+ "columnsFrom": [
+ "restoration_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {
+ "public.avatar_mode": {
+ "name": "avatar_mode",
+ "schema": "public",
+ "values": [
+ "internal",
+ "gravatar",
+ "dicebear"
+ ]
+ },
+ "public.user_themes": {
+ "name": "user_themes",
+ "schema": "public",
+ "values": [
+ "light",
+ "dark",
+ "system"
+ ]
+ },
+ "public.retention_policy_type": {
+ "name": "retention_policy_type",
+ "schema": "public",
+ "values": [
+ "count",
+ "days",
+ "gfs"
+ ]
+ },
+ "public.provider_kind": {
+ "name": "provider_kind",
+ "schema": "public",
+ "values": [
+ "slack",
+ "smtp",
+ "discord",
+ "telegram",
+ "gotify",
+ "ntfy",
+ "webhook",
+ "nextcloud",
+ "teams",
+ "pushover"
+ ]
+ },
+ "public.event_kind": {
+ "name": "event_kind",
+ "schema": "public",
+ "values": [
+ "error_backup",
+ "error_restore",
+ "success_restore",
+ "success_backup",
+ "weekly_report",
+ "error_health_agent",
+ "error_health_database"
+ ]
+ },
+ "public.level": {
+ "name": "level",
+ "schema": "public",
+ "values": [
+ "critical",
+ "warning",
+ "info"
+ ]
+ },
+ "public.provider_storage_kind": {
+ "name": "provider_storage_kind",
+ "schema": "public",
+ "values": [
+ "local",
+ "s3",
+ "google-drive",
+ "blob"
+ ]
+ },
+ "public.backup_storage_status": {
+ "name": "backup_storage_status",
+ "schema": "public",
+ "values": [
+ "pending",
+ "success",
+ "failed"
+ ]
+ },
+ "public.healthcheck_status": {
+ "name": "healthcheck_status",
+ "schema": "public",
+ "values": [
+ "success",
+ "failed"
+ ]
+ },
+ "public.healthcheck_kind": {
+ "name": "healthcheck_kind",
+ "schema": "public",
+ "values": [
+ "database",
+ "agent"
+ ]
+ },
+ "public.job_log_entry_type": {
+ "name": "job_log_entry_type",
+ "schema": "public",
+ "values": [
+ "log",
+ "command"
+ ]
+ },
+ "public.job_log_level": {
+ "name": "job_log_level",
+ "schema": "public",
+ "values": [
+ "debug",
+ "info",
+ "warn",
+ "error"
+ ]
+ },
+ "public.dbms_status": {
+ "name": "dbms_status",
+ "schema": "public",
+ "values": [
+ "postgresql",
+ "mysql",
+ "mariadb",
+ "mongodb",
+ "sqlite",
+ "redis",
+ "valkey",
+ "firebird",
+ "mssql"
+ ]
+ },
+ "public.status": {
+ "name": "status",
+ "schema": "public",
+ "values": [
+ "waiting",
+ "ongoing",
+ "failed",
+ "success"
+ ]
+ },
+ "public.type_storage": {
+ "name": "type_storage",
+ "schema": "public",
+ "values": [
+ "local",
+ "s3"
+ ]
+ }
+ },
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/src/db/migrations/meta/0068_snapshot.json b/src/db/migrations/meta/0068_snapshot.json
new file mode 100644
index 00000000..4a483ed0
--- /dev/null
+++ b/src/db/migrations/meta/0068_snapshot.json
@@ -0,0 +1,3001 @@
+{
+ "id": "e39ddcff-5791-4157-84ec-e38c27b81d67",
+ "prevId": "c2dda0eb-6358-43d0-8d13-17c2b3641f21",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.settings": {
+ "name": "settings",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "smtp_password": {
+ "name": "smtp_password",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "smtp_from": {
+ "name": "smtp_from",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "smtp_host": {
+ "name": "smtp_host",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "smtp_port": {
+ "name": "smtp_port",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "smtp_user": {
+ "name": "smtp_user",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "smtp_secure": {
+ "name": "smtp_secure",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "default_notification_channel_id": {
+ "name": "default_notification_channel_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "default_storage_channel_id": {
+ "name": "default_storage_channel_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "encryption": {
+ "name": "encryption",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "onboarding": {
+ "name": "onboarding",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "avatar_mode": {
+ "name": "avatar_mode",
+ "type": "avatar_mode",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'internal'"
+ },
+ "dicebear_style": {
+ "name": "dicebear_style",
+ "type": "varchar(64)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'thumbs'"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "settings_default_notification_channel_id_notification_channel_id_fk": {
+ "name": "settings_default_notification_channel_id_notification_channel_id_fk",
+ "tableFrom": "settings",
+ "tableTo": "notification_channel",
+ "columnsFrom": [
+ "default_notification_channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "settings_default_storage_channel_id_storage_channel_id_fk": {
+ "name": "settings_default_storage_channel_id_storage_channel_id_fk",
+ "tableFrom": "settings",
+ "tableTo": "storage_channel",
+ "columnsFrom": [
+ "default_storage_channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "settings_name_unique": {
+ "name": "settings_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.account": {
+ "name": "account",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "account_id": {
+ "name": "account_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "access_token": {
+ "name": "access_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refresh_token": {
+ "name": "refresh_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "id_token": {
+ "name": "id_token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "access_token_expires_at": {
+ "name": "access_token_expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refresh_token_expires_at": {
+ "name": "refresh_token_expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "scope": {
+ "name": "scope",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "password": {
+ "name": "password",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "account_user_id_user_id_fk": {
+ "name": "account_user_id_user_id_fk",
+ "tableFrom": "account",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.passkey": {
+ "name": "passkey",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "publicKey": {
+ "name": "publicKey",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "credentialID": {
+ "name": "credentialID",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "counter": {
+ "name": "counter",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "deviceType": {
+ "name": "deviceType",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "backedUp": {
+ "name": "backedUp",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "transports": {
+ "name": "transports",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "aaguid": {
+ "name": "aaguid",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "passkey_user_id_user_id_fk": {
+ "name": "passkey_user_id_user_id_fk",
+ "tableFrom": "passkey",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.session": {
+ "name": "session",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "impersonated_by": {
+ "name": "impersonated_by",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "active_organization_id": {
+ "name": "active_organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "session_user_id_user_id_fk": {
+ "name": "session_user_id_user_id_fk",
+ "tableFrom": "session",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "session_token_unique": {
+ "name": "session_token_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "token"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.sso_provider": {
+ "name": "sso_provider",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "issuer": {
+ "name": "issuer",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "oidc_config": {
+ "name": "oidc_config",
+ "type": "json",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "saml_config": {
+ "name": "saml_config",
+ "type": "json",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "provider_id": {
+ "name": "provider_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "domain": {
+ "name": "domain",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "sso_provider_user_id_user_id_fk": {
+ "name": "sso_provider_user_id_user_id_fk",
+ "tableFrom": "sso_provider",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "sso_provider_provider_id_unique": {
+ "name": "sso_provider_provider_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "provider_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.two_factor": {
+ "name": "two_factor",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "secret": {
+ "name": "secret",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "backup_codes": {
+ "name": "backup_codes",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "verified": {
+ "name": "verified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "two_factor_user_id_user_id_fk": {
+ "name": "two_factor_user_id_user_id_fk",
+ "tableFrom": "two_factor",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user": {
+ "name": "user",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email_verified": {
+ "name": "email_verified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "image": {
+ "name": "image",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "theme": {
+ "name": "theme",
+ "type": "user_themes",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'light'"
+ },
+ "banned": {
+ "name": "banned",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "ban_reason": {
+ "name": "ban_reason",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "ban_expires": {
+ "name": "ban_expires",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "lastConnectedAt": {
+ "name": "lastConnectedAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "lastChangedPasswordAt": {
+ "name": "lastChangedPasswordAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "two_factor_enabled": {
+ "name": "two_factor_enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "user_email_unique": {
+ "name": "user_email_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "email"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.verification": {
+ "name": "verification",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "identifier": {
+ "name": "identifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.organization": {
+ "name": "organization",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "logo": {
+ "name": "logo",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "organization_slug_unique": {
+ "name": "organization_slug_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "slug"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.member": {
+ "name": "member",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "member_organization_id_organization_id_fk": {
+ "name": "member_organization_id_organization_id_fk",
+ "tableFrom": "member",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "member_user_id_user_id_fk": {
+ "name": "member_user_id_user_id_fk",
+ "tableFrom": "member",
+ "tableTo": "user",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.invitation": {
+ "name": "invitation",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "inviter_id": {
+ "name": "inviter_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "invitation_organization_id_organization_id_fk": {
+ "name": "invitation_organization_id_organization_id_fk",
+ "tableFrom": "invitation",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "invitation_inviter_id_user_id_fk": {
+ "name": "invitation_inviter_id_user_id_fk",
+ "tableFrom": "invitation",
+ "tableTo": "user",
+ "columnsFrom": [
+ "inviter_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.projects": {
+ "name": "projects",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "is_archived": {
+ "name": "is_archived",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "projects_organization_id_organization_id_fk": {
+ "name": "projects_organization_id_organization_id_fk",
+ "tableFrom": "projects",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "projects_slug_unique": {
+ "name": "projects_slug_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "slug"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.backups": {
+ "name": "backups",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "status": {
+ "name": "status",
+ "type": "status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'waiting'"
+ },
+ "file": {
+ "name": "file",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "file_size": {
+ "name": "file_size",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "duration_ms": {
+ "name": "duration_ms",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "database_id": {
+ "name": "database_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "imported": {
+ "name": "imported",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "migrated": {
+ "name": "migrated",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "backups_database_id_databases_id_fk": {
+ "name": "backups_database_id_databases_id_fk",
+ "tableFrom": "backups",
+ "tableTo": "databases",
+ "columnsFrom": [
+ "database_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.databases": {
+ "name": "databases",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "agent_database_id": {
+ "name": "agent_database_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "dbms": {
+ "name": "dbms",
+ "type": "dbms_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "backup_policy": {
+ "name": "backup_policy",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_waiting_for_backup": {
+ "name": "is_waiting_for_backup",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "backup_to_restore": {
+ "name": "backup_to_restore",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "health_error_count": {
+ "name": "health_error_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_contact": {
+ "name": "last_contact",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "databases_agent_id_agents_id_fk": {
+ "name": "databases_agent_id_agents_id_fk",
+ "tableFrom": "databases",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "databases_project_id_projects_id_fk": {
+ "name": "databases_project_id_projects_id_fk",
+ "tableFrom": "databases",
+ "tableTo": "projects",
+ "columnsFrom": [
+ "project_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.restorations": {
+ "name": "restorations",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "status": {
+ "name": "status",
+ "type": "status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'waiting'"
+ },
+ "duration_ms": {
+ "name": "duration_ms",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "backup_storage_id": {
+ "name": "backup_storage_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "backup_id": {
+ "name": "backup_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "database_id": {
+ "name": "database_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "restorations_backup_storage_id_backup_storage_id_fk": {
+ "name": "restorations_backup_storage_id_backup_storage_id_fk",
+ "tableFrom": "restorations",
+ "tableTo": "backup_storage",
+ "columnsFrom": [
+ "backup_storage_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "restorations_backup_id_backups_id_fk": {
+ "name": "restorations_backup_id_backups_id_fk",
+ "tableFrom": "restorations",
+ "tableTo": "backups",
+ "columnsFrom": [
+ "backup_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "restorations_database_id_databases_id_fk": {
+ "name": "restorations_database_id_databases_id_fk",
+ "tableFrom": "restorations",
+ "tableTo": "databases",
+ "columnsFrom": [
+ "database_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.retention_policies": {
+ "name": "retention_policies",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "database_id": {
+ "name": "database_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "retention_policy_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "count": {
+ "name": "count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "default": 7
+ },
+ "days": {
+ "name": "days",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "default": 30
+ },
+ "gfs_daily": {
+ "name": "gfs_daily",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "default": 7
+ },
+ "gfs_weekly": {
+ "name": "gfs_weekly",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "default": 4
+ },
+ "gfs_monthly": {
+ "name": "gfs_monthly",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "default": 12
+ },
+ "gfs_yearly": {
+ "name": "gfs_yearly",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "default": 3
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "retention_policies_database_id_databases_id_fk": {
+ "name": "retention_policies_database_id_databases_id_fk",
+ "tableFrom": "retention_policies",
+ "tableTo": "databases",
+ "columnsFrom": [
+ "database_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.agents": {
+ "name": "agents",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "version": {
+ "name": "version",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "health_error_count": {
+ "name": "health_error_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "is_archived": {
+ "name": "is_archived",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "last_contact": {
+ "name": "last_contact",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "agents_organization_id_organization_id_fk": {
+ "name": "agents_organization_id_organization_id_fk",
+ "tableFrom": "agents",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "agents_slug_unique": {
+ "name": "agents_slug_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "slug"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.organization_agents": {
+ "name": "organization_agents",
+ "schema": "",
+ "columns": {
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "agent_id": {
+ "name": "agent_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "organization_agents_organization_id_organization_id_fk": {
+ "name": "organization_agents_organization_id_organization_id_fk",
+ "tableFrom": "organization_agents",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "organization_agents_agent_id_agents_id_fk": {
+ "name": "organization_agents_agent_id_agents_id_fk",
+ "tableFrom": "organization_agents",
+ "tableTo": "agents",
+ "columnsFrom": [
+ "agent_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "organization_agents_organization_id_agent_id_unique": {
+ "name": "organization_agents_organization_id_agent_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "organization_id",
+ "agent_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.notification_channel": {
+ "name": "notification_channel",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "provider": {
+ "name": "provider",
+ "type": "provider_kind",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "config": {
+ "name": "config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "notification_channel_organization_id_organization_id_fk": {
+ "name": "notification_channel_organization_id_organization_id_fk",
+ "tableFrom": "notification_channel",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.organization_notification_channels": {
+ "name": "organization_notification_channels",
+ "schema": "",
+ "columns": {
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "notification_channel_id": {
+ "name": "notification_channel_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "organization_notification_channels_organization_id_organization_id_fk": {
+ "name": "organization_notification_channels_organization_id_organization_id_fk",
+ "tableFrom": "organization_notification_channels",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "organization_notification_channels_notification_channel_id_notification_channel_id_fk": {
+ "name": "organization_notification_channels_notification_channel_id_notification_channel_id_fk",
+ "tableFrom": "organization_notification_channels",
+ "tableTo": "notification_channel",
+ "columnsFrom": [
+ "notification_channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "organization_notification_channels_organization_id_notification_channel_id_unique": {
+ "name": "organization_notification_channels_organization_id_notification_channel_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "organization_id",
+ "notification_channel_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.alert_policy": {
+ "name": "alert_policy",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "notification_channel_id": {
+ "name": "notification_channel_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "event_kind": {
+ "name": "event_kind",
+ "type": "event_kind[]",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "database_id": {
+ "name": "database_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "alert_policy_notification_channel_id_notification_channel_id_fk": {
+ "name": "alert_policy_notification_channel_id_notification_channel_id_fk",
+ "tableFrom": "alert_policy",
+ "tableTo": "notification_channel",
+ "columnsFrom": [
+ "notification_channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "alert_policy_database_id_databases_id_fk": {
+ "name": "alert_policy_database_id_databases_id_fk",
+ "tableFrom": "alert_policy",
+ "tableTo": "databases",
+ "columnsFrom": [
+ "database_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.notification_log": {
+ "name": "notification_log",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "channel_id": {
+ "name": "channel_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "policy_id": {
+ "name": "policy_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "event": {
+ "name": "event",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "provider": {
+ "name": "provider",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "provider_name": {
+ "name": "provider_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "message": {
+ "name": "message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "level": {
+ "name": "level",
+ "type": "level",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "payload": {
+ "name": "payload",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "success": {
+ "name": "success",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "error": {
+ "name": "error",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "provider_response": {
+ "name": "provider_response",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "sent_at": {
+ "name": "sent_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.organization_storage_channels": {
+ "name": "organization_storage_channels",
+ "schema": "",
+ "columns": {
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "storage_channel_id": {
+ "name": "storage_channel_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "organization_storage_channels_organization_id_organization_id_fk": {
+ "name": "organization_storage_channels_organization_id_organization_id_fk",
+ "tableFrom": "organization_storage_channels",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "organization_storage_channels_storage_channel_id_storage_channel_id_fk": {
+ "name": "organization_storage_channels_storage_channel_id_storage_channel_id_fk",
+ "tableFrom": "organization_storage_channels",
+ "tableTo": "storage_channel",
+ "columnsFrom": [
+ "storage_channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "organization_storage_channels_organization_id_storage_channel_id_unique": {
+ "name": "organization_storage_channels_organization_id_storage_channel_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "organization_id",
+ "storage_channel_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.storage_channel": {
+ "name": "storage_channel",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "provider": {
+ "name": "provider",
+ "type": "provider_storage_kind",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "config": {
+ "name": "config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "storage_channel_organization_id_organization_id_fk": {
+ "name": "storage_channel_organization_id_organization_id_fk",
+ "tableFrom": "storage_channel",
+ "tableTo": "organization",
+ "columnsFrom": [
+ "organization_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.storage_policy": {
+ "name": "storage_policy",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "storage_channel_id": {
+ "name": "storage_channel_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "database_id": {
+ "name": "database_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "storage_policy_storage_channel_id_storage_channel_id_fk": {
+ "name": "storage_policy_storage_channel_id_storage_channel_id_fk",
+ "tableFrom": "storage_policy",
+ "tableTo": "storage_channel",
+ "columnsFrom": [
+ "storage_channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "storage_policy_database_id_databases_id_fk": {
+ "name": "storage_policy_database_id_databases_id_fk",
+ "tableFrom": "storage_policy",
+ "tableTo": "databases",
+ "columnsFrom": [
+ "database_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.backup_storage": {
+ "name": "backup_storage",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "backup_id": {
+ "name": "backup_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "storage_channel_id": {
+ "name": "storage_channel_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "backup_storage_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "path": {
+ "name": "path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "size": {
+ "name": "size",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "checksum": {
+ "name": "checksum",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "backup_storage_backup_id_backups_id_fk": {
+ "name": "backup_storage_backup_id_backups_id_fk",
+ "tableFrom": "backup_storage",
+ "tableTo": "backups",
+ "columnsFrom": [
+ "backup_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "backup_storage_storage_channel_id_storage_channel_id_fk": {
+ "name": "backup_storage_storage_channel_id_storage_channel_id_fk",
+ "tableFrom": "backup_storage",
+ "tableTo": "storage_channel",
+ "columnsFrom": [
+ "storage_channel_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.healthcheck_log": {
+ "name": "healthcheck_log",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "kind": {
+ "name": "kind",
+ "type": "healthcheck_kind",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "date": {
+ "name": "date",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "healthcheck_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "object_id": {
+ "name": "object_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.apikey": {
+ "name": "apikey",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "config_id": {
+ "name": "config_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "start": {
+ "name": "start",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "prefix": {
+ "name": "prefix",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "reference_id": {
+ "name": "reference_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "refill_interval": {
+ "name": "refill_interval",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refill_amount": {
+ "name": "refill_amount",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_refill_at": {
+ "name": "last_refill_at",
+ "type": "timestamp (6) with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "rate_limit_enabled": {
+ "name": "rate_limit_enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "rate_limit_time_window": {
+ "name": "rate_limit_time_window",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "rate_limit_max": {
+ "name": "rate_limit_max",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "request_count": {
+ "name": "request_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "remaining": {
+ "name": "remaining",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_request": {
+ "name": "last_request",
+ "type": "timestamp (6) with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp (6) with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp (6) with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp (6) with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "permissions": {
+ "name": "permissions",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.job_log": {
+ "name": "job_log",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "backup_id": {
+ "name": "backup_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "restoration_id": {
+ "name": "restoration_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "logged_at": {
+ "name": "logged_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "entry_type": {
+ "name": "entry_type",
+ "type": "job_log_entry_type",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "level": {
+ "name": "level",
+ "type": "job_log_level",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "message": {
+ "name": "message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "command": {
+ "name": "command",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "output": {
+ "name": "output",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "exit_code": {
+ "name": "exit_code",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "duration_ms": {
+ "name": "duration_ms",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "deleted_at": {
+ "name": "deleted_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "job_log_backup_id_backups_id_fk": {
+ "name": "job_log_backup_id_backups_id_fk",
+ "tableFrom": "job_log",
+ "tableTo": "backups",
+ "columnsFrom": [
+ "backup_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "job_log_restoration_id_restorations_id_fk": {
+ "name": "job_log_restoration_id_restorations_id_fk",
+ "tableFrom": "job_log",
+ "tableTo": "restorations",
+ "columnsFrom": [
+ "restoration_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {
+ "public.avatar_mode": {
+ "name": "avatar_mode",
+ "schema": "public",
+ "values": [
+ "internal",
+ "gravatar",
+ "dicebear"
+ ]
+ },
+ "public.user_themes": {
+ "name": "user_themes",
+ "schema": "public",
+ "values": [
+ "light",
+ "dark",
+ "system"
+ ]
+ },
+ "public.retention_policy_type": {
+ "name": "retention_policy_type",
+ "schema": "public",
+ "values": [
+ "count",
+ "days",
+ "gfs"
+ ]
+ },
+ "public.provider_kind": {
+ "name": "provider_kind",
+ "schema": "public",
+ "values": [
+ "slack",
+ "smtp",
+ "discord",
+ "telegram",
+ "gotify",
+ "ntfy",
+ "webhook",
+ "nextcloud",
+ "teams",
+ "pushover"
+ ]
+ },
+ "public.event_kind": {
+ "name": "event_kind",
+ "schema": "public",
+ "values": [
+ "error_backup",
+ "error_restore",
+ "success_restore",
+ "success_backup",
+ "weekly_report",
+ "error_health_agent",
+ "error_health_database"
+ ]
+ },
+ "public.level": {
+ "name": "level",
+ "schema": "public",
+ "values": [
+ "critical",
+ "warning",
+ "info"
+ ]
+ },
+ "public.provider_storage_kind": {
+ "name": "provider_storage_kind",
+ "schema": "public",
+ "values": [
+ "local",
+ "s3",
+ "google-drive",
+ "blob"
+ ]
+ },
+ "public.backup_storage_status": {
+ "name": "backup_storage_status",
+ "schema": "public",
+ "values": [
+ "pending",
+ "success",
+ "failed"
+ ]
+ },
+ "public.healthcheck_status": {
+ "name": "healthcheck_status",
+ "schema": "public",
+ "values": [
+ "success",
+ "failed"
+ ]
+ },
+ "public.healthcheck_kind": {
+ "name": "healthcheck_kind",
+ "schema": "public",
+ "values": [
+ "database",
+ "agent"
+ ]
+ },
+ "public.job_log_entry_type": {
+ "name": "job_log_entry_type",
+ "schema": "public",
+ "values": [
+ "log",
+ "command"
+ ]
+ },
+ "public.job_log_level": {
+ "name": "job_log_level",
+ "schema": "public",
+ "values": [
+ "debug",
+ "info",
+ "warn",
+ "error"
+ ]
+ },
+ "public.dbms_status": {
+ "name": "dbms_status",
+ "schema": "public",
+ "values": [
+ "postgresql",
+ "mysql",
+ "mariadb",
+ "mongodb",
+ "sqlite",
+ "redis",
+ "valkey",
+ "firebird",
+ "mssql"
+ ]
+ },
+ "public.status": {
+ "name": "status",
+ "schema": "public",
+ "values": [
+ "waiting",
+ "ongoing",
+ "failed",
+ "success"
+ ]
+ },
+ "public.type_storage": {
+ "name": "type_storage",
+ "schema": "public",
+ "values": [
+ "local",
+ "s3"
+ ]
+ }
+ },
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/src/db/migrations/meta/_journal.json b/src/db/migrations/meta/_journal.json
index 9893e063..88b1bef7 100644
--- a/src/db/migrations/meta/_journal.json
+++ b/src/db/migrations/meta/_journal.json
@@ -463,6 +463,20 @@
"when": 1782118902777,
"tag": "0065_overjoyed_mantis",
"breakpoints": true
+ },
+ {
+ "idx": 67,
+ "version": "7",
+ "when": 1782133059337,
+ "tag": "0067_adorable_jean_grey",
+ "breakpoints": true
+ },
+ {
+ "idx": 68,
+ "version": "7",
+ "when": 1782134460680,
+ "tag": "0068_bitter_revanche",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/src/db/schema/01_setting.ts b/src/db/schema/01_setting.ts
index da66b0b6..cb3e3d82 100644
--- a/src/db/schema/01_setting.ts
+++ b/src/db/schema/01_setting.ts
@@ -1,4 +1,6 @@
-import {boolean, pgTable, uuid, varchar} from "drizzle-orm/pg-core";
+import {boolean, pgEnum, pgTable, uuid, varchar} from "drizzle-orm/pg-core";
+
+export const avatarModeEnum = pgEnum('avatar_mode', ['internal', 'gravatar', 'dicebear']);
import {createSelectSchema} from "drizzle-zod";
import {z} from "zod";
import {timestamps} from "@/db/schema/00_common";
@@ -21,6 +23,8 @@ export const setting = pgTable("settings", {
.references(() => storageChannel.id, {onDelete: "set null"}),
encryption: boolean("encryption").default(false),
onboarding: boolean("onboarding").default(false).notNull(),
+ avatarMode: avatarModeEnum('avatar_mode').default('internal').notNull(),
+ dicebearStyle: varchar('dicebear_style', { length: 64 }).default('thumbs').notNull(),
...timestamps
});
diff --git a/src/db/services/agent.ts b/src/db/services/agent.ts
index 6638f395..ce7ed2eb 100644
--- a/src/db/services/agent.ts
+++ b/src/db/services/agent.ts
@@ -1,43 +1,41 @@
-import {and, desc, eq, sql} from "drizzle-orm";
-import {db} from "@/db";
-import {Agent, agent, organizationAgent} from "@/db/schema/08_agent";
-import {Database, database} from "@/db/schema/07_database";
+"use server";
+
+import { and, desc, eq, sql } from "drizzle-orm";
+import { db } from "@/db";
+import { Agent, agent, organizationAgent } from "@/db/schema/08_agent";
+import { Database, database } from "@/db/schema/07_database";
export async function getOrganizationAgents(organizationId: string) {
-
- return await db
- .select({
- id: agent.id,
- name: agent.name,
- organizationId: agent.organizationId,
- slug: agent.slug,
- healthErrorCount: agent.healthErrorCount,
- description: agent.description,
- isArchived: agent.isArchived,
- lastContact: agent.lastContact,
- version: agent.version,
- updatedAt: agent.updatedAt,
- createdAt: agent.createdAt,
- deletedAt: agent.deletedAt,
- databases: sql`
+ return (await db
+ .select({
+ id: agent.id,
+ name: agent.name,
+ organizationId: agent.organizationId,
+ slug: agent.slug,
+ healthErrorCount: agent.healthErrorCount,
+ description: agent.description,
+ isArchived: agent.isArchived,
+ lastContact: agent.lastContact,
+ version: agent.version,
+ updatedAt: agent.updatedAt,
+ createdAt: agent.createdAt,
+ deletedAt: agent.deletedAt,
+ databases: sql`
COALESCE(
json_agg(${database}.*) FILTER (WHERE ${database}.id IS NOT NULL),
'[]'
)
`,
- })
- .from(organizationAgent)
- .innerJoin(
- agent,
- eq(organizationAgent.agentId, agent.id)
- )
- .leftJoin(database, eq(database.agentId, agent.id))
- .groupBy(agent.id)
- .orderBy(desc(agent.createdAt))
- .where(
- and(
- eq(organizationAgent.organizationId, organizationId),
- eq(agent.isArchived, false)
- )
- ) as unknown as Agent[];
+ })
+ .from(organizationAgent)
+ .innerJoin(agent, eq(organizationAgent.agentId, agent.id))
+ .leftJoin(database, eq(database.agentId, agent.id))
+ .groupBy(agent.id)
+ .orderBy(desc(agent.createdAt))
+ .where(
+ and(
+ eq(organizationAgent.organizationId, organizationId),
+ eq(agent.isArchived, false),
+ ),
+ )) as unknown as Agent[];
}
diff --git a/src/db/services/backup.ts b/src/db/services/backup.ts
index d9234be4..717d013b 100644
--- a/src/db/services/backup.ts
+++ b/src/db/services/backup.ts
@@ -1,19 +1,19 @@
-"use server"
-import {eq} from "drizzle-orm";
+"use server";
+import { eq } from "drizzle-orm";
import * as drizzleDb from "@/db";
-import {db} from "@/db";
+import { db } from "@/db";
export async function getDatabaseBackups(databaseId: string) {
- return await db.query.backup.findMany({
- where: eq(drizzleDb.schemas.backup.databaseId, databaseId),
+ return await db.query.backup.findMany({
+ where: eq(drizzleDb.schemas.backup.databaseId, databaseId),
+ with: {
+ restorations: true,
+ storages: {
with: {
- restorations: true,
- storages: {
- with: {
- storageChannel: true,
- },
- },
+ storageChannel: true,
},
- orderBy: (b, {desc}) => [desc(b.createdAt)],
- });
+ },
+ },
+ orderBy: (b, { desc }) => [desc(b.createdAt)],
+ });
}
diff --git a/src/db/services/database.ts b/src/db/services/database.ts
index f0ea0d4b..77113426 100644
--- a/src/db/services/database.ts
+++ b/src/db/services/database.ts
@@ -1,39 +1,123 @@
-"use server"
-import {db} from "@/db";
-import {DatabaseWith} from "@/db/schema/07_database";
-import {AgentWith} from "@/db/schema/08_agent";
+"use server";
+import { inArray } from "drizzle-orm";
+import { db } from "@/db";
+import { database, retentionPolicy } from "@/db/schema/07_database";
+import { alertPolicy } from "@/db/schema/10_alert-policy";
+import { storagePolicy } from "@/db/schema/13_storage-policy";
+import { DatabaseWith } from "@/db/schema/07_database";
+import { AgentWith } from "@/db/schema/08_agent";
+import type {
+ OnboardingDbSettings,
+ EventKind,
+} from "@/features/onboarding/types";
export async function getOrganizationAvailableDatabases(
- organizationId: string,
- projectId?: string
+ organizationId: string,
+ projectId?: string,
) {
+ const availableDatabases = (await db.query.database.findMany({
+ where: (db, { eq, or, isNull }) =>
+ projectId
+ ? or(isNull(db.projectId), eq(db.projectId, projectId))
+ : isNull(db.projectId),
+ with: {
+ agent: {
+ with: {
+ organizations: true,
+ },
+ },
+ project: true,
+ backups: true,
+ restorations: true,
+ },
+ orderBy: (db, { desc }) => [desc(db.createdAt)],
+ })) as DatabaseWith[];
- const availableDatabases = (
- await db.query.database.findMany({
- where: (db, { eq, or, isNull }) =>
- projectId
- ? or(isNull(db.projectId), eq(db.projectId, projectId))
- : isNull(db.projectId),
- with: {
- agent: {
- with: {
- organizations: true
- }
- },
- project: true,
- backups: true,
- restorations: true,
- },
- orderBy: (db, {desc}) => [desc(db.createdAt)],
- })
- ) as DatabaseWith[];
-
- return availableDatabases.filter(db => {
- const agent = db.agent as AgentWith;
- if (agent?.isArchived) return false;
- return (
- agent?.organizationId === organizationId ||
- agent?.organizations?.some(org => org.organizationId === organizationId)
- );
- })
+ return availableDatabases.filter((db) => {
+ const agent = db.agent as AgentWith;
+ if (agent?.isArchived) return false;
+ return (
+ agent?.organizationId === organizationId ||
+ agent?.organizations?.some((org) => org.organizationId === organizationId)
+ );
+ });
+}
+
+export async function getDatabasesSettings(
+ databaseIds: string[],
+): Promise> {
+ if (databaseIds.length === 0) return {};
+
+ const [retentionPolicies, dbs, alertPolicies, storagePolicies] =
+ await Promise.all([
+ db
+ .select()
+ .from(retentionPolicy)
+ .where(inArray(retentionPolicy.databaseId, databaseIds)),
+ db
+ .select({ id: database.id, backupPolicy: database.backupPolicy })
+ .from(database)
+ .where(inArray(database.id, databaseIds)),
+ db
+ .select()
+ .from(alertPolicy)
+ .where(inArray(alertPolicy.databaseId, databaseIds)),
+ db
+ .select()
+ .from(storagePolicy)
+ .where(inArray(storagePolicy.databaseId, databaseIds)),
+ ]);
+
+ const result: Record = {};
+
+ for (const dbId of databaseIds) {
+ const rp = retentionPolicies.find((r) => r.databaseId === dbId);
+ const dbRow = dbs.find((d) => d.id === dbId);
+ const alerts = alertPolicies.filter((a) => a.databaseId === dbId);
+ const storages = storagePolicies.filter((s) => s.databaseId === dbId);
+
+ const settings: OnboardingDbSettings = {};
+
+ if (rp) {
+ settings.retention = {
+ type: rp.type,
+ count: rp.count ?? 7,
+ days: rp.days ?? 30,
+ gfs: {
+ daily: rp.gfsDaily ?? 7,
+ weekly: rp.gfsWeekly ?? 4,
+ monthly: rp.gfsMonthly ?? 12,
+ yearly: rp.gfsYearly ?? 3,
+ },
+ };
+ }
+
+ if (dbRow) {
+ if (dbRow.backupPolicy) {
+ settings.backupMethod = "automatic";
+ settings.backupCron = dbRow.backupPolicy;
+ } else {
+ settings.backupMethod = "manual";
+ }
+ }
+
+ if (alerts.length > 0) {
+ settings.notificationPolicies = alerts.map((a) => ({
+ channelId: a.notificationChannelId,
+ eventKinds: a.eventKinds as EventKind[],
+ enabled: a.enabled,
+ }));
+ }
+
+ if (storages.length > 0) {
+ settings.storagePolicies = storages.map((s) => ({
+ channelId: s.storageChannelId,
+ enabled: s.enabled,
+ }));
+ }
+
+ result[dbId] = settings;
+ }
+
+ return result;
}
diff --git a/src/db/services/healthcheck.ts b/src/db/services/healthcheck.ts
index 6c16ba17..e5df9f45 100644
--- a/src/db/services/healthcheck.ts
+++ b/src/db/services/healthcheck.ts
@@ -1,185 +1,200 @@
-import {db} from "@/db";
+"use server";
+
+import { db } from "@/db";
import * as drizzleDb from "@/db";
-import {and, eq, gte, isNotNull, lt} from "drizzle-orm";
-import {dispatchNotification} from "@/features/notifications/notifications.dispatch";
-import {EventPayload} from "@/features/notifications/notifications.types";
-import {logger} from "@/lib/logger";
+import { and, eq, gte, isNotNull, lt } from "drizzle-orm";
+import { dispatchNotification } from "@/features/notifications/notifications.dispatch";
+import { EventPayload } from "@/features/notifications/notifications.types";
+import { logger } from "@/lib/logger";
-const log = logger.child({module: "tasks/healthcheck"});
+const log = logger.child({ module: "tasks/healthcheck" });
-export async function getHealthLast12hLogs({id}: { id: string }) {
- const now = new Date()
- const since = new Date(now.getTime() - 12 * 60 * 60 * 1000)
+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)
- )
- )
+ 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 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)
- )
+ const logsToDelete = await db
+ .select()
+ .from(drizzleDb.schemas.healthcheckLog)
+ .where(lt(drizzleDb.schemas.healthcheckLog.date, threshold));
- log.info({name: "deleteHealthLogsOlderThan12h"},`Number of logs found to delete: ${logsToDelete.length}`)
+ log.info(
+ { name: "deleteHealthLogsOlderThan12h" },
+ `Number of logs found to delete: ${logsToDelete.length}`,
+ );
- await db
- .delete(drizzleDb.schemas.healthcheckLog)
- .where(
- lt(drizzleDb.schemas.healthcheckLog.date, threshold)
- )
+ await db
+ .delete(drizzleDb.schemas.healthcheckLog)
+ .where(lt(drizzleDb.schemas.healthcheckLog.date, threshold));
- return logsToDelete.length
+ return logsToDelete.length;
}
export async function checkAgentsHealthError() {
- const agents = await db.query.agent.findMany({
- where: isNotNull(drizzleDb.schemas.agent.lastContact),
- });
+ 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"),
- });
+ 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) {
- log.error({name: "checkAgentsHealthError"},`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",
- },
- };
- log.info({name: "checkAgentsHealthError", payload: payload},`Agent Healthcheck Notification`)
-
- await dispatchNotification(
- payload,
- undefined,
- settings.defaultNotificationChannelId,
- undefined
- );
- }
-
- }
+ if (!settings) {
+ throw new Error("System settings not found");
+ }
+
+ if (!settings.defaultNotificationChannelId) {
+ log.error(
+ { name: "checkAgentsHealthError" },
+ `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",
+ },
+ };
+ log.info(
+ { name: "checkAgentsHealthError", payload: payload },
+ `Agent Healthcheck Notification`,
+ );
+
+ 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 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 now = new Date();
+ 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",
+ },
+ };
- for (const database of databases) {
- if (!database.lastContact) continue;
+ log.info(
+ { name: "checkDatabasesHealthError", payload: payload },
+ `Database Healthcheck Notification`,
+ );
- const lastContactDate = new Date(database.lastContact);
- const diffMinutes = (now.getTime() - lastContactDate.getTime()) / 1000 / 60;
+ return dispatchNotification(
+ payload,
+ alertPolicy.id == null ? undefined : alertPolicy.id,
+ alertPolicy.id ? undefined : alertPolicy.notificationChannelId,
+ undefined,
+ );
+ });
- 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",
- },
- };
-
- log.info({name: "checkDatabasesHealthError", payload: payload},`Database Healthcheck Notification`)
-
- return dispatchNotification(payload, alertPolicy.id == null ? undefined : alertPolicy.id, alertPolicy.id ? undefined : alertPolicy.notificationChannelId, undefined);
- });
-
- await Promise.all(promises);
- }
- }
+ await Promise.all(promises);
+ }
}
+ }
}
-
-
diff --git a/src/db/services/notification-channel.ts b/src/db/services/notification-channel.ts
index 06cafa21..50040e59 100644
--- a/src/db/services/notification-channel.ts
+++ b/src/db/services/notification-channel.ts
@@ -1,30 +1,62 @@
-import {desc, eq} from "drizzle-orm";
-import {db} from "@/db";
-import {
- NotificationChannel,
- notificationChannel,
- organizationNotificationChannel
-} from "@/db/schema/09_notification-channel";
-import {storageChannel} from "@/db/schema/12_storage-channel";
+"use server";
-export async function getOrganizationChannels(organizationId: string) {
- return await db
- .select({
- id: notificationChannel.id,
- name: notificationChannel.name,
- provider: notificationChannel.provider,
- config: notificationChannel.config,
- enabled: notificationChannel.enabled,
- updatedAt: notificationChannel.updatedAt,
- createdAt: notificationChannel.createdAt,
- deletedAt: notificationChannel.deletedAt,
- organizationId: notificationChannel.organizationId
- })
- .from(organizationNotificationChannel)
- .innerJoin(
- notificationChannel,
- eq(organizationNotificationChannel.notificationChannelId, notificationChannel.id)
- )
- .orderBy(desc(notificationChannel.createdAt))
- .where(eq(organizationNotificationChannel.organizationId, organizationId)) as unknown as NotificationChannel[];
+import { desc, eq, isNull } from "drizzle-orm";
+import { db } from "@/db";
+import {
+ NotificationChannel,
+ notificationChannel,
+ organizationNotificationChannel,
+} from "@/db/schema/09_notification-channel";
+
+export async function getOrganizationChannels(
+ organizationId: string,
+): Promise {
+ const [orgChannels, systemChannels] = await Promise.all([
+ db
+ .select({
+ id: notificationChannel.id,
+ name: notificationChannel.name,
+ provider: notificationChannel.provider,
+ config: notificationChannel.config,
+ enabled: notificationChannel.enabled,
+ updatedAt: notificationChannel.updatedAt,
+ createdAt: notificationChannel.createdAt,
+ deletedAt: notificationChannel.deletedAt,
+ organizationId: notificationChannel.organizationId,
+ })
+ .from(organizationNotificationChannel)
+ .innerJoin(
+ notificationChannel,
+ eq(
+ organizationNotificationChannel.notificationChannelId,
+ notificationChannel.id,
+ ),
+ )
+ .orderBy(desc(notificationChannel.createdAt))
+ .where(
+ eq(organizationNotificationChannel.organizationId, organizationId),
+ ),
+ db
+ .select({
+ id: notificationChannel.id,
+ name: notificationChannel.name,
+ provider: notificationChannel.provider,
+ config: notificationChannel.config,
+ enabled: notificationChannel.enabled,
+ updatedAt: notificationChannel.updatedAt,
+ createdAt: notificationChannel.createdAt,
+ deletedAt: notificationChannel.deletedAt,
+ organizationId: notificationChannel.organizationId,
+ })
+ .from(notificationChannel)
+ .orderBy(desc(notificationChannel.createdAt))
+ .where(isNull(notificationChannel.organizationId)),
+ ]);
+
+ const seen = new Set();
+ return [...orgChannels, ...systemChannels].filter((c) => {
+ if (seen.has(c.id)) return false;
+ seen.add(c.id);
+ return true;
+ }) as NotificationChannel[];
}
diff --git a/src/db/services/notification-log.ts b/src/db/services/notification-log.ts
index 6366d4bd..de497e57 100644
--- a/src/db/services/notification-log.ts
+++ b/src/db/services/notification-log.ts
@@ -1,81 +1,91 @@
-import {and, desc, eq, gte, lte} from 'drizzle-orm';
-import {NotificationLevel, notificationLog} from "@/db/schema/11_notification-log";
-import {notificationChannel} from "@/db/schema/09_notification-channel";
-import {db} from "@/db";
-import {Json} from "drizzle-zod";
+"use server";
+
+import { and, desc, eq, gte, lte } from "drizzle-orm";
+import {
+ NotificationLevel,
+ notificationLog,
+} from "@/db/schema/11_notification-log";
+import { notificationChannel } from "@/db/schema/09_notification-channel";
+import { db } from "@/db";
+import { Json } from "drizzle-zod";
export type NotificationLogWithRelations = {
- id: string;
+ id: string;
+ title: string;
+ level: NotificationLevel;
+ success: boolean;
+ error: string | null;
+ sentAt: Date;
+ payload: Json | null;
+ content: {
title: string;
- level: NotificationLevel;
- success: boolean;
- error: string | null;
- sentAt: Date;
- payload: Json | null;
- content: {
- title: string;
- message: string;
- },
- channel: {
- name: string;
- provider: string;
- } | null;
- policy: {
- event: string | null;
- } | null;
+ message: string;
+ };
+ channel: {
+ name: string;
+ provider: string;
+ } | null;
+ policy: {
+ event: string | null;
+ } | null;
};
-export async function getNotificationHistory(
- filters?: {
- channelId?: string;
- policyId?: string;
- organizationId?: string;
- level?: NotificationLevel;
- success?: boolean;
- from?: Date;
- to?: Date;
- limit?: number;
- }
-): Promise {
- 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));
+export async function getNotificationHistory(filters?: {
+ channelId?: string;
+ policyId?: string;
+ organizationId?: string;
+ level?: NotificationLevel;
+ success?: boolean;
+ from?: Date;
+ to?: Date;
+ limit?: number;
+}): Promise {
+ 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));
- const rows = await db
- .select({
- id: notificationLog.id,
- title: notificationLog.title,
- level: notificationLog.level,
- success: notificationLog.success,
- error: notificationLog.error,
- sentAt: notificationLog.sentAt,
- payload: notificationLog.payload,
- content: {
- title: notificationLog.title,
- message: notificationLog.message,
- },
- channel: {
- name: notificationLog.providerName,
- provider: notificationLog.provider,
- },
- policy: {
- event: notificationLog.event,
- },
- })
- .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);
+ const rows = await db
+ .select({
+ id: notificationLog.id,
+ title: notificationLog.title,
+ level: notificationLog.level,
+ success: notificationLog.success,
+ error: notificationLog.error,
+ sentAt: notificationLog.sentAt,
+ payload: notificationLog.payload,
+ content: {
+ title: notificationLog.title,
+ message: notificationLog.message,
+ },
+ channel: {
+ name: notificationLog.providerName,
+ provider: notificationLog.provider,
+ },
+ policy: {
+ event: notificationLog.event,
+ },
+ })
+ .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);
- return rows.map(row => ({
- ...row,
- payload: row.payload as Json,
- }));
+ return rows.map((row) => ({
+ ...row,
+ payload: row.payload as Json,
+ }));
}
diff --git a/src/db/services/organization.ts b/src/db/services/organization.ts
index a892bde9..51ac0ddf 100644
--- a/src/db/services/organization.ts
+++ b/src/db/services/organization.ts
@@ -1,15 +1,17 @@
+"use server";
+
import { db } from "@/db";
import { eq } from "drizzle-orm";
import { member } from "@/db/schema/04_member";
import { organization } from "@/db/schema/03_organization";
export async function getUserOrganization(userId: string) {
- const memberRow = await db.query.member.findFirst({
- columns: { organizationId: true },
- where: eq(member.userId, userId),
- });
- if (!memberRow) return null;
- return db.query.organization.findFirst({
- where: eq(organization.id, memberRow.organizationId),
- });
+ const memberRow = await db.query.member.findFirst({
+ columns: { organizationId: true },
+ where: eq(member.userId, userId),
+ });
+ if (!memberRow) return null;
+ return db.query.organization.findFirst({
+ where: eq(organization.id, memberRow.organizationId),
+ });
}
diff --git a/src/db/services/project.ts b/src/db/services/project.ts
index b87c04e8..7270b1dc 100644
--- a/src/db/services/project.ts
+++ b/src/db/services/project.ts
@@ -1,71 +1,77 @@
-import {getOrganization} from "@/lib/auth/auth";
-import {db} from "@/db";
-import {and, eq} from "drizzle-orm";
-import * as drizzleDb from "@/db";
-import {project} from "@/db/schema/06_project";
+"use server";
+import { getOrganization } from "@/lib/auth/auth";
+import { db } from "@/db";
+import { and, eq } from "drizzle-orm";
+import * as drizzleDb from "@/db";
+import { project } from "@/db/schema/06_project";
export async function getOrganizationProject(organizationId: string) {
- return db.query.project.findFirst({
- where: eq(project.organizationId, organizationId),
- with: {
- databases: true
- }
- });
+ return db.query.project.findFirst({
+ where: eq(project.organizationId, organizationId),
+ with: {
+ databases: true,
+ },
+ });
}
-export const getOrganizationProjectDatabases = async ({organizationSlug, projectId}: {
- organizationSlug: string, projectId: string
+export const getOrganizationProjectDatabases = async ({
+ organizationSlug,
+ projectId,
+}: {
+ organizationSlug: string;
+ projectId: string;
}) => {
- try {
+ try {
+ const organization = await getOrganization({});
- const organization = await getOrganization({});
-
- if (!organization) {
- return {
- name: "ErrorGettingOrganizationProjectDatabases",
- message: "No organization found.",
- status: 400,
- cause: "Unknown error occurred.",
- };
- }
- const databasesProject = await db.query.project.findFirst({
- where: and(eq(drizzleDb.schemas.project.organizationId, organization.id), eq(drizzleDb.schemas.project.id, projectId)),
- with: {
- databases: true
- }
- });
-
- if (!databasesProject) {
- return {
- name: "ErrorGettingOrganizationProjectDatabases",
- message: "No organization found.",
- status: 400,
- cause: "Unknown error occurred.",
- };
- }
-
- return {
- data: databasesProject.databases,
- ids: databasesProject.databases.map((project) => project.id)
- }
-
-
- } catch (e: any) {
- const errorMessage = e?.response?.data?.message || e?.message || "Unknown auth error";
- const status = e?.response?.status || 500;
-
- console.error("API GettingOrganizationProjectDatabases error:", {
- message: errorMessage,
- status,
- raw: e,
- });
-
- throw {
- name: "ErrorGettingOrganizationProjectDatabases",
- message: errorMessage,
- status,
- cause: e,
- };
+ if (!organization) {
+ return {
+ name: "ErrorGettingOrganizationProjectDatabases",
+ message: "No organization found.",
+ status: 400,
+ cause: "Unknown error occurred.",
+ };
}
+ const databasesProject = await db.query.project.findFirst({
+ where: and(
+ eq(drizzleDb.schemas.project.organizationId, organization.id),
+ eq(drizzleDb.schemas.project.id, projectId),
+ ),
+ with: {
+ databases: true,
+ },
+ });
+
+ if (!databasesProject) {
+ return {
+ name: "ErrorGettingOrganizationProjectDatabases",
+ message: "No organization found.",
+ status: 400,
+ cause: "Unknown error occurred.",
+ };
+ }
+
+ return {
+ data: databasesProject.databases,
+ ids: databasesProject.databases.map((project) => project.id),
+ };
+ } catch (e: any) {
+ const errorMessage =
+ e?.response?.data?.message || e?.message || "Unknown auth error";
+ const status = e?.response?.status || 500;
+
+ console.error("API GettingOrganizationProjectDatabases error:", {
+ message: errorMessage,
+ status,
+ raw: e,
+ });
+
+ throw {
+ name: "ErrorGettingOrganizationProjectDatabases",
+ message: errorMessage,
+ status,
+ cause: e,
+ };
+ }
};
diff --git a/src/db/services/setting.ts b/src/db/services/setting.ts
index a499db86..fb9f7b2c 100644
--- a/src/db/services/setting.ts
+++ b/src/db/services/setting.ts
@@ -1,5 +1,12 @@
+"use server";
+
import { db } from "@/db";
export async function getSettings() {
- return db.query.setting.findFirst();
+ return db.query.setting.findFirst();
+}
+
+export async function isOnboardingDone(): Promise {
+ const settings = await db.query.setting.findFirst();
+ return settings?.onboarding ?? false;
}
diff --git a/src/db/services/storage-channel.ts b/src/db/services/storage-channel.ts
index 9cf6227a..03dc3aa6 100644
--- a/src/db/services/storage-channel.ts
+++ b/src/db/services/storage-channel.ts
@@ -1,25 +1,57 @@
-import {desc, eq} from "drizzle-orm";
-import {db} from "@/db";
-import {organizationStorageChannel, StorageChannel, storageChannel} from "@/db/schema/12_storage-channel";
+"use server";
-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[];
+import { desc, eq, isNull } from "drizzle-orm";
+import { db } from "@/db";
+import {
+ organizationStorageChannel,
+ StorageChannel,
+ storageChannel,
+} from "@/db/schema/12_storage-channel";
+
+export async function getOrganizationStorageChannels(
+ organizationId: string,
+): Promise {
+ const [orgChannels, systemChannels] = await Promise.all([
+ 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)),
+ 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(storageChannel)
+ .orderBy(desc(storageChannel.createdAt))
+ .where(isNull(storageChannel.organizationId)),
+ ]);
+
+ const seen = new Set();
+ return [...orgChannels, ...systemChannels].filter((c) => {
+ if (seen.has(c.id)) return false;
+ seen.add(c.id);
+ return true;
+ }) as StorageChannel[];
}
diff --git a/src/db/services/user.ts b/src/db/services/user.ts
index df4a1ad0..676a780d 100644
--- a/src/db/services/user.ts
+++ b/src/db/services/user.ts
@@ -1,45 +1,49 @@
-import {SignUpUser} from "@/types/auth";
-import {hashPassword} from "better-auth/crypto";
-import {db} from "@/db";
-import * as drizzleDb from "@/db";
-import {User, UserThemeEnum} from "@/db/schema/02_user";
-import {assertValidPassword} from "@/utils/password";
+"use server";
+import { SignUpUser } from "@/types/auth";
+import { hashPassword } from "better-auth/crypto";
+import { db } from "@/db";
+import * as drizzleDb from "@/db";
+import { User, UserThemeEnum } from "@/db/schema/02_user";
+import { assertValidPassword } from "@/utils/password";
export async function hasUsers(): Promise {
- const result = await db.select().from(drizzleDb.schemas.user).limit(1);
- return result.length > 0;
+ const result = await db.select().from(drizzleDb.schemas.user).limit(1);
+ return result.length > 0;
}
export async function createUserDb(data: SignUpUser): Promise {
- assertValidPassword(data.password);
+ assertValidPassword(data.password);
- const now = new Date();
- const userId = crypto.randomUUID();
+ const now = new Date();
+ const userId = crypto.randomUUID();
- const [newUser] = await db.insert(drizzleDb.schemas.user).values({
- ...data,
- id: userId,
- name: data.name,
- email: data.email,
- emailVerified: true,
- role: data.role,
- createdAt: now,
- updatedAt: now,
- theme: data.theme as UserThemeEnum,
- }).returning();
+ const [newUser] = await db
+ .insert(drizzleDb.schemas.user)
+ .values({
+ ...data,
+ id: userId,
+ name: data.name,
+ email: data.email,
+ emailVerified: true,
+ role: data.role,
+ createdAt: now,
+ updatedAt: now,
+ theme: data.theme as UserThemeEnum,
+ })
+ .returning();
- if (data.password) {
- const hashedPassword = await hashPassword(data.password);
- await db.insert(drizzleDb.schemas.account).values({
- providerId: "credential",
- accountId: userId,
- userId: userId,
- password: hashedPassword,
- createdAt: now,
- updatedAt: now,
- });
- }
+ if (data.password) {
+ const hashedPassword = await hashPassword(data.password);
+ await db.insert(drizzleDb.schemas.account).values({
+ providerId: "credential",
+ accountId: userId,
+ userId: userId,
+ password: hashedPassword,
+ createdAt: now,
+ updatedAt: now,
+ });
+ }
- return newUser
+ return newUser;
}
diff --git a/src/env.mjs b/src/env.mjs
index 54fe41a1..4eacf2f6 100644
--- a/src/env.mjs
+++ b/src/env.mjs
@@ -88,6 +88,8 @@ export const env = createEnv({
ALLOWED_GROUP: z.string().optional(),
+ SKIP_ONBOARDING: z.string().optional().default("false"),
+
AUTH_EMAIL_PASSWORD_ENABLED: z.string().optional().default("true"),
AUTH_SIGNUP_ENABLED: z.string().optional().default("true"),
AUTH_PASSKEY_ENABLED: z.string().optional().default("false"),
@@ -174,6 +176,8 @@ export const env = createEnv({
ALLOWED_GROUP: process.env.ALLOWED_GROUP,
+ SKIP_ONBOARDING: process.env.SKIP_ONBOARDING,
+
AUTH_EMAIL_PASSWORD_ENABLED: process.env.AUTH_EMAIL_PASSWORD_ENABLED,
AUTH_SIGNUP_ENABLED: process.env.AUTH_SIGNUP_ENABLED,
AUTH_PASSKEY_ENABLED: process.env.AUTH_PASSKEY_ENABLED,
diff --git a/src/features/channel/channel-card.tsx b/src/features/channel/channel-card.tsx
index 5ee1cb92..819bf904 100644
--- a/src/features/channel/channel-card.tsx
+++ b/src/features/channel/channel-card.tsx
@@ -34,6 +34,7 @@ export const ChannelCard = (props: ChannelCardProps) => {
const isOwned = data.organizationId ? true : !organization;
const isLocalSystem = data.provider == "local";
+ const isSystemChannel = data.organizationId === null;
return (
@@ -69,7 +70,7 @@ export const ChannelCard = (props: ChannelCardProps) => {
channel={data}
kind={kind}
/>
- {!isLocalSystem && (
+ {!isLocalSystem && !isSystemChannel && (
void;
- channels: NotificationChannel[] | StorageChannel[];
- database: DatabaseWith;
- kind: ChannelKind
+ channels: ChannelEntry[];
+ defaultPolicies: PolicyType[];
+ kind: ChannelKind;
+ isBackupOnly?: boolean;
+ isPending?: boolean;
+ onSave: (policies: PolicyType[]) => Promise;
+ onCancel?: () => void;
+ noChannelsMessage?: ReactNode;
};
-
export const ChannelPoliciesForm = ({
- database,
- channels,
- onSuccess,
- kind
- }: ChannelPoliciesFormProps) => {
- const queryClient = useQueryClient();
- const router = useRouter();
+ channels,
+ defaultPolicies,
+ kind,
+ isBackupOnly = false,
+ isPending = false,
+ onSave,
+ onCancel,
+ noChannelsMessage,
+}: ChannelPoliciesFormProps) => {
const isMobile = useIsMobile();
const channelText = getChannelTextBasedOnKind(kind);
- const isBackupOnly = backupOnly.some((type) => database.dbms === type);
-
-
- const organizationChannels = channels.map(c => c.id);
-
- const filterByChannel = (
- items: T[] | undefined | null,
- channelKey: K
- ): T[] => items?.filter(item => organizationChannels.includes(item[channelKey] as string)) ?? [];
-
- const formattedAlertPolicies = filterByChannel(database.alertPolicies, "notificationChannelId")
- .map(({notificationChannelId, eventKinds, enabled}) => ({
- channelId: notificationChannelId,
- eventKinds,
- enabled
- }));
-
- const formattedStoragePolicies = filterByChannel(database.storagePolicies, "storageChannelId")
- .map(({storageChannelId, enabled}) => ({
- channelId: storageChannelId,
- enabled
- }));
-
- const defaultPolicies: PolicyType[] =
- kind === "notification"
- ? formattedAlertPolicies
- : formattedStoragePolicies.map(({ channelId, enabled }) => ({ channelId, enabled }));
-
const form = useZodForm({
schema: PoliciesSchema,
defaultValues: { policies: defaultPolicies },
- context: { kind }
+ context: { kind },
});
- const {fields, append, remove} = useFieldArray({ control: form.control, name: "policies" });
+ const { fields, append, remove } = useFieldArray({ control: form.control, name: "policies" });
- const addPolicy = () => append({channelId: "", eventKinds: [], enabled: true});
- const removePolicyHandler = (index: number) => remove(index);
- const onCancel = () => { form.reset(); onSuccess?.(); };
+ const addPolicy = () => append({ channelId: "", eventKinds: [], enabled: true });
- const mutation = useMutation({
- mutationFn: async ({policies}: PoliciesType) => {
- const payload = policies.map(p => kind === "notification" ? p : { ...p, eventKinds: undefined });
-
- const policiesToAdd = payload.filter(
- (policy) => !defaultPolicies.some((a) => a.channelId === policy.channelId)
- );
- const policiesToRemove = defaultPolicies.filter(
- (policy) => !payload.some((v) => v.channelId === policy.channelId)
- );
- const policiesToUpdate = payload.filter((policy) => {
- const existing = defaultPolicies.find((a) => a.channelId === policy.channelId);
- return existing &&
- (existing.eventKinds !== policy.eventKinds || existing.enabled !== policy.enabled);
- });
- const promises = kind === "notification"
- ? [
- policiesToAdd.length > 0 ? await createAlertPoliciesAction({databaseId: database.id, alertPolicies: policiesToAdd}) : null,
- policiesToUpdate.length > 0 ? await updateAlertPoliciesAction({databaseId: database.id, alertPolicies: policiesToUpdate}) : null,
- policiesToRemove.length > 0 ? await deleteAlertPoliciesAction({databaseId: database.id, alertPolicies: policiesToRemove}) : null,
- ]
- : [
- policiesToAdd.length > 0 ? await createStoragePoliciesAction({databaseId: database.id, storagePolicies: policiesToAdd}) : null,
- policiesToUpdate.length > 0 ? await updateStoragePoliciesAction({databaseId: database.id, storagePolicies: policiesToUpdate}) : null,
- policiesToRemove.length > 0 ? await deleteStoragePoliciesAction({databaseId: database.id, storagePolicies: policiesToRemove}) : null,
- ];
-
- const results = await Promise.allSettled(promises);
- const rejected = results.find((r): r is PromiseRejectedResult => r.status === "rejected");
- if (rejected) throw new Error(rejected.reason?.message || "Network or server error");
-
- const failedActions = results
- .filter((r): r is PromiseFulfilledResult => r.status === "fulfilled")
- .map(r => r.value)
- .filter((v): v is { data: { success: false; actionError: any } } => v !== null && v.data?.success === false);
-
- if (failedActions.length > 0) throw new Error(failedActions[0].data.actionError?.message || "One or more operations failed");
- return {success: true};
- },
- onSuccess: () => {
- toast.success("Policies saved successfully");
- queryClient.invalidateQueries({queryKey: ["database-data", database.id]});
- router.refresh();
- },
- onError: (error: any) => { toast.error(error.message || "Failed to save policies"); },
- });
+ const handleCancel = () => {
+ form.reset();
+ onCancel?.();
+ };
return (
-