mirror of
https://github.com/Portabase/portabase.git
synced 2026-07-14 11:16:13 +02:00
Adding methods to delete backups on remote storage s3 and local. Some refactoring on models db and adding common.ts model with createdAt, updatedAt, and deletedAt.
This commit is contained in:
@@ -6,7 +6,7 @@ import { sendEmail } from "@/utils/email-helper";
|
||||
import TestEmailSettings from "../../../../../../emails/TestEmailSettings";
|
||||
import { render } from "@react-email/render";
|
||||
import { toast } from "sonner";
|
||||
import {Setting} from "@/db/schema/00_setting";
|
||||
import {Setting} from "@/db/schema/01_setting";
|
||||
import {EmailFormType} from "@/components/wrappers/dashboard/admin/admin-email-tab/email-form/email-form.schema";
|
||||
|
||||
export type SettingsEmailTabProps = {
|
||||
|
||||
@@ -12,7 +12,7 @@ import {useRouter} from "next/navigation";
|
||||
import {
|
||||
updateStorageSettingsAction
|
||||
} from "@/components/wrappers/dashboard/admin/admin-storage-tab/storage-s3/s3-form.action";
|
||||
import {Setting} from "@/db/schema/00_setting";
|
||||
import {Setting} from "@/db/schema/01_setting";
|
||||
import {S3FormType} from "@/components/wrappers/dashboard/admin/admin-storage-tab/storage-s3/s3-form.schema";
|
||||
|
||||
export type SettingsStorageTabProps = {
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
import {Tabs, TabsContent, TabsList, TabsTrigger} from "@/components/ui/tabs";
|
||||
import {SettingsEmailTab} from "@/components/wrappers/dashboard/admin/admin-email-tab/settings-email-tab";
|
||||
import {SettingsStorageTab} from "@/components/wrappers/dashboard/admin/admin-storage-tab/settings-storage-tab";
|
||||
import {User, UserWithAccounts} from "@/db/schema/01_user";
|
||||
import {Setting} from "@/db/schema/00_setting";
|
||||
import {User, UserWithAccounts} from "@/db/schema/02_user";
|
||||
import {Setting} from "@/db/schema/01_setting";
|
||||
import {useEffect, useState} from "react";
|
||||
import {useRouter, useSearchParams} from "next/navigation";
|
||||
import {AdminUsersTable} from "@/components/wrappers/dashboard/admin/admin-user-tab/admin-user-table";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {User, UserWithAccounts} from "@/db/schema/01_user";
|
||||
import {User, UserWithAccounts} from "@/db/schema/02_user";
|
||||
import {Card, CardContent, CardDescription, CardHeader, CardTitle} from "@/components/ui/card";
|
||||
import {DataTable} from "@/components/wrappers/common/table/data-table";
|
||||
import {usersColumnsAdmin} from "@/components/wrappers/dashboard/admin/admin-user-tab/columns-users";
|
||||
|
||||
@@ -9,7 +9,7 @@ import {useState} from "react";
|
||||
import {Trash2} from "lucide-react";
|
||||
import {deleteUserAction} from "@/components/wrappers/dashboard/profile/button-delete-account/delete-account.action";
|
||||
import {ButtonWithLoading} from "@/components/wrappers/common/button/button-with-loading";
|
||||
import {UserWithAccounts} from "@/db/schema/01_user";
|
||||
import {UserWithAccounts} from "@/db/schema/02_user";
|
||||
import {authClient, useSession} from "@/lib/auth/auth-client";
|
||||
import {formatFrenchDate} from "@/utils/date-formatting";
|
||||
import {providerSwitch} from "@/components/wrappers/common/provider-switch";
|
||||
|
||||
@@ -4,7 +4,7 @@ import {getServerUrl} from "@/utils/get-server-url";
|
||||
import {PasswordInput} from "@/components/wrappers/auth/password-input/password-input";
|
||||
import {useState} from "react";
|
||||
import {CopyButton} from "@/components/wrappers/common/button/copy-button";
|
||||
import {Agent} from "@/db/schema/07_agent";
|
||||
import {Agent} from "@/db/schema/08_agent";
|
||||
|
||||
export type AgentCardKeyProps = {
|
||||
agent: Agent;
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
||||
import Link from "next/link";
|
||||
import { formatDateLastContact } from "@/utils/date-formatting";
|
||||
import { ConnectionCircle } from "@/components/wrappers/common/connection-circle";
|
||||
import {Agent} from "@/db/schema/07_agent";
|
||||
import {Agent} from "@/db/schema/08_agent";
|
||||
|
||||
export type agentCardProps = {
|
||||
data: Agent;
|
||||
|
||||
@@ -8,7 +8,7 @@ import { PropsWithChildren } from "react";
|
||||
import { CopyButton } from "@/components/wrappers/common/button/copy-button";
|
||||
import { getServerUrl } from "@/utils/get-server-url";
|
||||
import { CodeSnippet } from "@/components/wrappers/code-snippet/code-snippet";
|
||||
import {Agent} from "@/db/schema/07_agent";
|
||||
import {Agent} from "@/db/schema/08_agent";
|
||||
|
||||
export type agentRegistrationDialogProps = PropsWithChildren<{
|
||||
agent: Agent;
|
||||
|
||||
@@ -7,7 +7,7 @@ import {ServerActionResult} from "@/types/action-type";
|
||||
import {eq} from "drizzle-orm";
|
||||
import {db} from "@/db";
|
||||
import * as drizzleDb from "@/db";
|
||||
import {Agent} from "@/db/schema/07_agent";
|
||||
import {Agent} from "@/db/schema/08_agent";
|
||||
|
||||
export const deleteAgentAction = userAction.schema(z.string()).action(async ({parsedInput}): Promise<ServerActionResult<Agent>> => {
|
||||
try {
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
"use server";
|
||||
|
||||
import { z } from "zod";
|
||||
import { userAction } from "@/safe-actions";
|
||||
import { db } from "@/db";
|
||||
import { ServerActionResult } from "@/types/action-type";
|
||||
import {z} from "zod";
|
||||
import {userAction} from "@/safe-actions";
|
||||
import {db} from "@/db";
|
||||
import {ServerActionResult} from "@/types/action-type";
|
||||
import * as drizzleDb from "@/db";
|
||||
import {Backup} from "@/db/schema/06_database";
|
||||
import {Backup} from "@/db/schema/07_database";
|
||||
import {withUpdatedAt} from "@/db/utils";
|
||||
|
||||
export const backupButtonAction = userAction.schema(z.string()).action(async ({ parsedInput }): Promise<ServerActionResult<Backup>> => {
|
||||
export const backupButtonAction = userAction.schema(z.string()).action(async ({parsedInput}): Promise<ServerActionResult<Backup>> => {
|
||||
try {
|
||||
const [createdBackup] = await db
|
||||
.insert(drizzleDb.schemas.backup)
|
||||
@@ -22,7 +23,7 @@ export const backupButtonAction = userAction.schema(z.string()).action(async ({
|
||||
value: createdBackup,
|
||||
actionSuccess: {
|
||||
message: "Backup has been successfully created.",
|
||||
messageParams: { databaseId: parsedInput },
|
||||
messageParams: {databaseId: parsedInput},
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
@@ -34,7 +35,7 @@ export const backupButtonAction = userAction.schema(z.string()).action(async ({
|
||||
message: "Failed to create backup.",
|
||||
status: 500,
|
||||
cause: error instanceof Error ? error.message : "Unknown error",
|
||||
messageParams: { databaseId: parsedInput },
|
||||
messageParams: {databaseId: parsedInput},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { redirect } from "next/navigation";
|
||||
import { CircleUser, LogOut, ShieldHalf } from "lucide-react";
|
||||
import { signOut } from "@/lib/auth/auth-client";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { User } from "@/db/schema/01_user";
|
||||
import { User } from "@/db/schema/02_user";
|
||||
|
||||
export type LoggedInDropdownProps = PropsWithChildren<{
|
||||
user: User;
|
||||
|
||||
@@ -11,7 +11,7 @@ import { useMutation } from "@tanstack/react-query";
|
||||
import { toast } from "sonner";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { updateDatabaseBackupPolicyAction } from "@/components/wrappers/dashboard/database/cron-button/cron.action";
|
||||
import {Database} from "@/db/schema/06_database";
|
||||
import {Database} from "@/db/schema/07_database";
|
||||
|
||||
export type CronButtonProps = {
|
||||
database: Database;
|
||||
|
||||
@@ -6,7 +6,7 @@ import {useRouter} from "next/navigation";
|
||||
import {toast} from "sonner";
|
||||
import {Button} from "@/components/ui/button";
|
||||
import {Separator} from "@/components/ui/separator";
|
||||
import {Database} from "@/db/schema/06_database";
|
||||
import {Database} from "@/db/schema/07_database";
|
||||
|
||||
export type CronInputProps = {
|
||||
database: Database;
|
||||
|
||||
+2
-2
@@ -20,14 +20,14 @@ import {
|
||||
OrganizationFormSchema,
|
||||
OrganizationFormType
|
||||
} from "@/components/wrappers/dashboard/organization/organization-form/organization-form.schema";
|
||||
import {MemberWithUser, OrganizationWithMembers} from "@/db/schema/02_organization";
|
||||
import {MemberWithUser, OrganizationWithMembers} from "@/db/schema/03_organization";
|
||||
import {
|
||||
deleteOrganizationAction,
|
||||
updateOrganizationAction
|
||||
} from "@/components/wrappers/dashboard/organization/organization.action";
|
||||
import {toast} from "sonner";
|
||||
import {User as BetterAuthUser} from "better-auth";
|
||||
import {User} from "@/db/schema/01_user";
|
||||
import {User} from "@/db/schema/02_user";
|
||||
|
||||
export type organizationFormProps = {
|
||||
defaultValues?: OrganizationWithMembers;
|
||||
|
||||
@@ -11,7 +11,7 @@ import {db} from "@/db";
|
||||
import {and, eq, inArray} from "drizzle-orm";
|
||||
import {auth, checkSlugOrganization, createOrganization, deleteOrganization} from "@/lib/auth/auth";
|
||||
import {slugify} from "@/utils/slugify";
|
||||
import {Organization} from "@/db/schema/02_organization";
|
||||
import {Organization} from "@/db/schema/03_organization";
|
||||
import * as drizzleDb from "@/db";
|
||||
import {headers} from "next/headers";
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import { uploadImageAction } from "@/features/upload/public/upload.action";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { updateImageUserAction } from "@/components/wrappers/dashboard/profile/avatar/avatar.action";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { User } from "@/db/schema/01_user";
|
||||
import { User } from "@/db/schema/02_user";
|
||||
import {ChangeEvent} from "react";
|
||||
|
||||
export type AvatarWithUploadProps = {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import {Card, CardContent, CardHeader} from "@/components/ui/card";
|
||||
import {formatDateLastContact} from "@/utils/date-formatting";
|
||||
import {Database} from "@/db/schema/06_database";
|
||||
import {Database} from "@/db/schema/07_database";
|
||||
|
||||
export type DatabaseKpiPro = {
|
||||
successRate: any;
|
||||
|
||||
@@ -7,8 +7,8 @@ import {eventUpdate} from "@/types/events";
|
||||
import {backupColumns} from "@/features/dashboard/backup/columns";
|
||||
import {restoreColumns} from "@/features/dashboard/restore/columns";
|
||||
import {DataTable} from "@/components/wrappers/common/table/data-table";
|
||||
import {Backup, Database, DatabaseWith, Restoration} from "@/db/schema/06_database";
|
||||
import {Setting} from "@/db/schema/00_setting";
|
||||
import {Backup, Database, DatabaseWith, Restoration} from "@/db/schema/07_database";
|
||||
import {Setting} from "@/db/schema/01_setting";
|
||||
import {DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger} from "@/components/ui/dropdown-menu";
|
||||
import {MoreHorizontal, Trash2} from "lucide-react";
|
||||
import {useMutation} from "@tanstack/react-query";
|
||||
@@ -62,6 +62,8 @@ export const DatabaseTabs = (props: DatabaseTabsProps) => {
|
||||
const backupDeleted = await deleteBackupAction({
|
||||
backupId: backup.id,
|
||||
databaseId: backup.databaseId,
|
||||
file: backup.file!,
|
||||
projectSlug: props.database?.project?.slug!
|
||||
});
|
||||
return {
|
||||
success: backupDeleted?.data?.success,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
||||
import Link from "next/link";
|
||||
import {ProjectWith} from "@/db/schema/05_project";
|
||||
import {ProjectWith} from "@/db/schema/06_project";
|
||||
|
||||
export type projectCardProps = {
|
||||
data: ProjectWith;
|
||||
|
||||
@@ -5,7 +5,7 @@ import Image from "next/image";
|
||||
import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
||||
import { ConnectionCircle } from "@/components/wrappers/common/connection-circle";
|
||||
import { formatDateLastContact } from "@/utils/date-formatting";
|
||||
import {Database} from "@/db/schema/06_database";
|
||||
import {Database} from "@/db/schema/07_database";
|
||||
import {Avatar, AvatarFallback, AvatarImage} from "@/components/ui/avatar";
|
||||
|
||||
export type projectDatabaseCardProps = {
|
||||
|
||||
@@ -6,9 +6,9 @@ import { z } from "zod";
|
||||
import { ServerActionResult } from "@/types/action-type";
|
||||
import { db } from "@/db";
|
||||
import { eq, inArray } from "drizzle-orm";
|
||||
import {Project} from "@/db/schema/05_project";
|
||||
import {Project} from "@/db/schema/06_project";
|
||||
import * as drizzleDb from "@/db";
|
||||
import {Database} from "@/db/schema/06_database";
|
||||
import {Database} from "@/db/schema/07_database";
|
||||
import {slugify} from "@/utils/slugify";
|
||||
|
||||
export const createProjectAction = userAction
|
||||
|
||||
@@ -11,8 +11,8 @@ import { createProjectAction, updateProjectAction } from "@/components/wrappers/
|
||||
import { useRouter } from "next/navigation";
|
||||
import { MultiSelect } from "@/components/wrappers/common/multiselect/multi-select";
|
||||
import { toast } from "sonner";
|
||||
import {DatabaseWith} from "@/db/schema/06_database";
|
||||
import {Organization} from "@/db/schema/02_organization";
|
||||
import {DatabaseWith} from "@/db/schema/07_database";
|
||||
import {Organization} from "@/db/schema/03_organization";
|
||||
|
||||
export type projectFormProps = {
|
||||
defaultValues?: ProjectType;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import {ColumnDef} from "@tanstack/react-table";
|
||||
import {MemberWithUser} from "@/db/schema/02_organization";
|
||||
import {MemberWithUser} from "@/db/schema/03_organization";
|
||||
import {useState} from "react";
|
||||
import {authClient, useSession} from "@/lib/auth/auth-client";
|
||||
import {useMutation} from "@tanstack/react-query";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {DataTable} from "@/components/wrappers/common/table/data-table";
|
||||
import {usersColumnsAdmin} from "@/components/wrappers/dashboard/admin/columns-users";
|
||||
import {MemberWithUser, Organization, OrganizationWithMembers} from "@/db/schema/02_organization";
|
||||
import {OrganizationInvitation} from "@/db/schema/04_invitation";
|
||||
import {MemberWithUser, Organization, OrganizationWithMembers} from "@/db/schema/03_organization";
|
||||
import {OrganizationInvitation} from "@/db/schema/05_invitation";
|
||||
import {organizationMemberColumns} from "@/components/wrappers/dashboard/settings/columns-organization-members";
|
||||
|
||||
|
||||
|
||||
+8
-8
@@ -1,13 +1,13 @@
|
||||
import {drizzle} from "drizzle-orm/node-postgres";
|
||||
|
||||
import * as settings from "./schema/00_setting";
|
||||
import * as user from "./schema/01_user";
|
||||
import * as organisation from "./schema/02_organization";
|
||||
import * as invitation from "./schema/03_member";
|
||||
import * as member from "./schema/04_invitation";
|
||||
import * as project from "./schema/05_project";
|
||||
import * as agent from "./schema/07_agent";
|
||||
import * as database from "./schema/06_database";
|
||||
import * as settings from "./schema/01_setting";
|
||||
import * as user from "./schema/02_user";
|
||||
import * as organisation from "./schema/03_organization";
|
||||
import * as invitation from "./schema/04_member";
|
||||
import * as member from "./schema/05_invitation";
|
||||
import * as project from "./schema/06_project";
|
||||
import * as agent from "./schema/08_agent";
|
||||
import * as database from "./schema/07_database";
|
||||
|
||||
|
||||
import {Pool} from "pg";
|
||||
|
||||
Executable → Regular
+7
@@ -29,6 +29,13 @@
|
||||
"when": 1756321312852,
|
||||
"tag": "0003_absent_maestro",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 4,
|
||||
"version": "7",
|
||||
"when": 1756473637441,
|
||||
"tag": "0004_dazzling_hawkeye",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import {timestamp} from "drizzle-orm/pg-core";
|
||||
import {z} from "zod";
|
||||
|
||||
export const timestamps = {
|
||||
updatedAt: timestamp("updated_at"),
|
||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||
deletedAt: timestamp("deleted_at"),
|
||||
}
|
||||
|
||||
export function schemaWithoutMeta<
|
||||
T extends z.ZodTypeAny
|
||||
>(schema: T) {
|
||||
// @ts-ignore
|
||||
return schema.omit({id: true, createdAt: true, updatedAt: true, deletedAt: true});
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { pgTable, timestamp, uuid, varchar } from "drizzle-orm/pg-core";
|
||||
import { typeStorageEnum } from "./types";
|
||||
import { createSelectSchema } from "drizzle-zod";
|
||||
import { z } from "zod";
|
||||
import {timestamps} from "@/db/schema/00_common";
|
||||
|
||||
export const setting = pgTable("settings", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
@@ -16,8 +17,7 @@ export const setting = pgTable("settings", {
|
||||
smtpHost: varchar("smtp_host", { length: 255 }),
|
||||
smtpPort: varchar("smtp_port", { length: 255 }),
|
||||
smtpUser: varchar("smtp_user", { length: 255 }),
|
||||
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at"),
|
||||
...timestamps
|
||||
});
|
||||
|
||||
export const settingSchema = createSelectSchema(setting);
|
||||
@@ -1,12 +1,13 @@
|
||||
import { relations } from "drizzle-orm";
|
||||
import { boolean, pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
|
||||
import { createSelectSchema } from "drizzle-zod";
|
||||
import { z } from "zod";
|
||||
import { project } from "./05_project";
|
||||
import {member, OrganizationMember} from "@/db/schema/03_member";
|
||||
import {invitation} from "@/db/schema/04_invitation";
|
||||
import {organization} from "@/db/schema/02_organization";
|
||||
import {relations} from "drizzle-orm";
|
||||
import {boolean, pgTable, text, timestamp, uuid} from "drizzle-orm/pg-core";
|
||||
import {createSelectSchema} from "drizzle-zod";
|
||||
import {z} from "zod";
|
||||
import {project} from "./06_project";
|
||||
import {member, OrganizationMember} from "@/db/schema/04_member";
|
||||
import {invitation} from "@/db/schema/05_invitation";
|
||||
import {organization} from "@/db/schema/03_organization";
|
||||
import {Account} from "better-auth";
|
||||
import {timestamps} from "@/db/schema/00_common";
|
||||
|
||||
export const user = pgTable("user", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
@@ -14,28 +15,26 @@ export const user = pgTable("user", {
|
||||
email: text("email").notNull().unique(),
|
||||
emailVerified: boolean("email_verified").notNull(),
|
||||
image: text("image"),
|
||||
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at"),
|
||||
role: text("role"),
|
||||
banned: boolean("banned"),
|
||||
banReason: text("ban_reason"),
|
||||
banExpires: timestamp("ban_expires"),
|
||||
deletedAt: timestamp("deleted_at"),
|
||||
...timestamps
|
||||
});
|
||||
|
||||
export const session = pgTable("session", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
expiresAt: timestamp("expires_at").notNull(),
|
||||
token: text("token").notNull().unique(),
|
||||
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at"),
|
||||
ipAddress: text("ip_address"),
|
||||
userAgent: text("user_agent"),
|
||||
userId: uuid("user_id")
|
||||
.notNull()
|
||||
.references(() => user.id, { onDelete: "cascade" }),
|
||||
.references(() => user.id, {onDelete: "cascade"}),
|
||||
impersonatedBy: text("impersonated_by"), //id or name ????
|
||||
activeOrganizationId: text("active_organization_id"),
|
||||
...timestamps
|
||||
|
||||
});
|
||||
|
||||
export const account = pgTable("account", {
|
||||
@@ -44,7 +43,7 @@ export const account = pgTable("account", {
|
||||
providerId: text("provider_id").notNull(),
|
||||
userId: uuid("user_id")
|
||||
.notNull()
|
||||
.references(() => user.id, { onDelete: "cascade" }),
|
||||
.references(() => user.id, {onDelete: "cascade"}),
|
||||
accessToken: text("access_token"),
|
||||
refreshToken: text("refresh_token"),
|
||||
idToken: text("id_token"),
|
||||
@@ -52,8 +51,8 @@ export const account = pgTable("account", {
|
||||
refreshTokenExpiresAt: timestamp("refresh_token_expires_at"),
|
||||
scope: text("scope"),
|
||||
password: text("password"),
|
||||
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at"),
|
||||
...timestamps
|
||||
|
||||
});
|
||||
|
||||
export const verification = pgTable("verification", {
|
||||
@@ -61,32 +60,32 @@ export const verification = pgTable("verification", {
|
||||
identifier: text("identifier").notNull(),
|
||||
value: text("value").notNull(),
|
||||
expiresAt: timestamp("expires_at").notNull(),
|
||||
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at"),
|
||||
...timestamps
|
||||
|
||||
});
|
||||
|
||||
export const userRelations = relations(user, ({ many }) => ({
|
||||
export const userRelations = relations(user, ({many}) => ({
|
||||
sessions: many(session),
|
||||
accounts: many(account),
|
||||
memberships: many(member),
|
||||
invitations: many(invitation),
|
||||
}));
|
||||
|
||||
export const sessionRelations = relations(session, ({ one }) => ({
|
||||
export const sessionRelations = relations(session, ({one}) => ({
|
||||
user: one(user, {
|
||||
fields: [session.userId],
|
||||
references: [user.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const accountRelations = relations(account, ({ one }) => ({
|
||||
export const accountRelations = relations(account, ({one}) => ({
|
||||
user: one(user, {
|
||||
fields: [account.userId],
|
||||
references: [user.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const projectRelations = relations(project, ({ one }) => ({
|
||||
export const projectRelations = relations(project, ({one}) => ({
|
||||
organization: one(organization, {
|
||||
fields: [project.organizationId],
|
||||
references: [organization.id],
|
||||
@@ -1,20 +1,20 @@
|
||||
import { pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
|
||||
import { relations } from "drizzle-orm";
|
||||
import { project } from "./05_project";
|
||||
import { project } from "./06_project";
|
||||
import { createSelectSchema } from "drizzle-zod";
|
||||
import { z } from "zod";
|
||||
import {invitation, OrganizationInvitation} from "@/db/schema/04_invitation";
|
||||
import {member, OrganizationMember} from "@/db/schema/03_member";
|
||||
import {User} from "@/db/schema/01_user";
|
||||
import {invitation, OrganizationInvitation} from "@/db/schema/05_invitation";
|
||||
import {member, OrganizationMember} from "@/db/schema/04_member";
|
||||
import {User} from "@/db/schema/02_user";
|
||||
import {timestamps} from "@/db/schema/00_common";
|
||||
|
||||
export const organization = pgTable("organization", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
name: text("name").notNull(),
|
||||
slug: text("slug").unique().notNull(),
|
||||
logo: text("logo"),
|
||||
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at"),
|
||||
metadata: text("metadata"),
|
||||
...timestamps
|
||||
});
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import {pgTable, text, timestamp, uuid} from "drizzle-orm/pg-core";
|
||||
import {user} from "@/db/schema/01_user";
|
||||
import {organization} from "@/db/schema/02_organization";
|
||||
import {user} from "@/db/schema/02_user";
|
||||
import {organization} from "@/db/schema/03_organization";
|
||||
import {relations} from "drizzle-orm";
|
||||
import {createSelectSchema} from "drizzle-zod";
|
||||
import {z} from "zod";
|
||||
import {timestamps} from "@/db/schema/00_common";
|
||||
|
||||
|
||||
export const member = pgTable("member", {
|
||||
@@ -15,8 +16,7 @@ export const member = pgTable("member", {
|
||||
.notNull()
|
||||
.references(() => user.id, { onDelete: "cascade" }),
|
||||
role: text("role").notNull(),
|
||||
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at"),
|
||||
...timestamps
|
||||
});
|
||||
|
||||
export const memberRelations = relations(member, ({ one }) => ({
|
||||
@@ -1,9 +1,10 @@
|
||||
import {relations} from "drizzle-orm";
|
||||
import {user} from "@/db/schema/01_user";
|
||||
import {organization} from "@/db/schema/02_organization";
|
||||
import {user} from "@/db/schema/02_user";
|
||||
import {organization} from "@/db/schema/03_organization";
|
||||
import {createSelectSchema} from "drizzle-zod";
|
||||
import {z} from "zod";
|
||||
import {pgTable, text, timestamp, uuid} from "drizzle-orm/pg-core";
|
||||
import {timestamps} from "@/db/schema/00_common";
|
||||
|
||||
|
||||
export const invitation = pgTable("invitation", {
|
||||
@@ -18,6 +19,7 @@ export const invitation = pgTable("invitation", {
|
||||
inviterId: uuid("inviter_id")
|
||||
.notNull()
|
||||
.references(() => user.id, { onDelete: "cascade" }),
|
||||
...timestamps
|
||||
});
|
||||
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
import { pgTable, text, boolean, uuid, timestamp } from "drizzle-orm/pg-core";
|
||||
import { relations } from "drizzle-orm";
|
||||
import { Organization, organization } from "./02_organization";
|
||||
import { Organization, organization } from "./03_organization";
|
||||
import { createSelectSchema } from "drizzle-zod";
|
||||
import { z } from "zod";
|
||||
import { Database, database } from "./06_database";
|
||||
import { Database, database } from "./07_database";
|
||||
import {timestamps} from "@/db/schema/00_common";
|
||||
|
||||
export const project = pgTable("projects", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
slug: text("slug").notNull().unique(),
|
||||
name: text("name").notNull().notNull(),
|
||||
isArchived: boolean("is_archived").default(false),
|
||||
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at"),
|
||||
organizationId: uuid("organization_id")
|
||||
.notNull()
|
||||
.references(() => organization.id),
|
||||
...timestamps
|
||||
|
||||
});
|
||||
|
||||
export const projectRelations = relations(project, ({ one, many }) => ({
|
||||
@@ -1,10 +1,11 @@
|
||||
import {pgTable, text, boolean, timestamp, uuid, integer, pgEnum, uniqueIndex} from "drizzle-orm/pg-core";
|
||||
import {Agent, agent} from "./07_agent";
|
||||
import {Project, project} from "./05_project";
|
||||
import {Agent, agent} from "./08_agent";
|
||||
import {Project, project} from "./06_project";
|
||||
import {relations} from "drizzle-orm";
|
||||
import {dbmsEnum, statusEnum} from "./types";
|
||||
import {createSelectSchema} from "drizzle-zod";
|
||||
import {z} from "zod";
|
||||
import {timestamps} from "@/db/schema/00_common";
|
||||
|
||||
export const database = pgTable("databases", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
@@ -15,8 +16,6 @@ export const database = pgTable("databases", {
|
||||
backupPolicy: text("backup_policy"),
|
||||
isWaitingForBackup: boolean("is_waiting_for_backup").default(false).notNull(),
|
||||
backupToRestore: text("backup_to_restore"),
|
||||
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at"),
|
||||
agentId: uuid("agent_id")
|
||||
.notNull()
|
||||
.references(() => agent.id, {onDelete: "cascade"}),
|
||||
@@ -24,6 +23,7 @@ export const database = pgTable("databases", {
|
||||
|
||||
projectId: uuid("project_id")
|
||||
.references(() => project.id),
|
||||
...timestamps
|
||||
|
||||
});
|
||||
|
||||
@@ -33,11 +33,10 @@ export const backup = pgTable(
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
status: statusEnum("status").default("waiting").notNull(),
|
||||
file: text("file"),
|
||||
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at"),
|
||||
databaseId: uuid("database_id")
|
||||
.notNull()
|
||||
.references(() => database.id, {onDelete: "cascade"}),
|
||||
...timestamps
|
||||
},
|
||||
// (table) => [uniqueIndex("database_id_status_unique").on(table.databaseId, table.status)]
|
||||
);
|
||||
@@ -54,20 +53,19 @@ export const retentionPolicy = pgTable("retention_policies", {
|
||||
gfsWeekly: integer("gfs_weekly").default(4),
|
||||
gfsMonthly: integer("gfs_monthly").default(12),
|
||||
gfsYearly: integer("gfs_yearly").default(3),
|
||||
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at"),
|
||||
...timestamps
|
||||
});
|
||||
|
||||
|
||||
export const restoration = pgTable("restorations", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
status: statusEnum("status").default("waiting").notNull(),
|
||||
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at"),
|
||||
backupId: uuid("backup_id")
|
||||
.notNull()
|
||||
.references(() => backup.id, {onDelete: "cascade"}),
|
||||
databaseId: uuid("database_id").references(() => database.id, {onDelete: "cascade"}),
|
||||
...timestamps
|
||||
|
||||
});
|
||||
|
||||
export const databaseRelations = relations(database, ({one, many}) => ({
|
||||
@@ -107,6 +105,5 @@ export type DatabaseWith = Database & {
|
||||
backups?: Backup[] | null;
|
||||
restorations?: Restoration[] | null;
|
||||
retentionPolicy?: RetentionPolicy | null;
|
||||
|
||||
};
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import {boolean, pgTable, text, timestamp, uuid} from "drizzle-orm/pg-core";
|
||||
import { createSelectSchema } from "drizzle-zod";
|
||||
import { z } from "zod";
|
||||
import {database} from "@/db/schema/06_database";
|
||||
import {database} from "@/db/schema/07_database";
|
||||
import {relations} from "drizzle-orm";
|
||||
import {timestamps} from "@/db/schema/00_common";
|
||||
|
||||
export const agent = pgTable("agents", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
@@ -10,9 +11,9 @@ export const agent = pgTable("agents", {
|
||||
name: text("name").notNull().notNull(),
|
||||
description: text("description").notNull(),
|
||||
isArchived: boolean("is_archived").default(false),
|
||||
createdAt: timestamp("created_at").notNull().defaultNow(),
|
||||
updatedAt: timestamp("updated_at"),
|
||||
lastContact: timestamp("last_contact"),
|
||||
...timestamps
|
||||
|
||||
});
|
||||
|
||||
export const agentSchema = createSelectSchema(agent);
|
||||
@@ -0,0 +1,3 @@
|
||||
export function withUpdatedAt<T extends object>(data: T): T & { updatedAt: Date } {
|
||||
return {...data, updatedAt: new Date()};
|
||||
}
|
||||
@@ -21,10 +21,10 @@ import {createRestorationAction, deleteBackupAction} from "@/features/dashboard/
|
||||
import {toast} from "sonner";
|
||||
import {useRouter} from "next/navigation";
|
||||
import {StatusBadge} from "@/components/wrappers/common/status-badge";
|
||||
import {Backup, DatabaseWith} from "@/db/schema/06_database";
|
||||
import {Backup, DatabaseWith} from "@/db/schema/07_database";
|
||||
import {formatFrenchDate} from "@/utils/date-formatting";
|
||||
import {TooltipCustom} from "@/components/wrappers/common/tooltip-custom";
|
||||
import {Setting} from "@/db/schema/00_setting";
|
||||
import {Setting} from "@/db/schema/01_setting";
|
||||
import {SafeActionResult} from "next-safe-action";
|
||||
import {ZodString} from "zod";
|
||||
import {ServerActionResult} from "@/types/action-type";
|
||||
@@ -80,18 +80,22 @@ export function backupColumns(isAlreadyRestore: boolean, settings: Setting, data
|
||||
|
||||
const mutationDeleteBackup = useMutation({
|
||||
mutationFn: async () => {
|
||||
const restoration = await deleteBackupAction({
|
||||
|
||||
|
||||
const deletion = await deleteBackupAction({
|
||||
backupId: rowData.id,
|
||||
databaseId: rowData.databaseId,
|
||||
file: rowData.file!,
|
||||
projectSlug: database.project?.slug!
|
||||
});
|
||||
// @ts-ignore
|
||||
if (restoration.data.success) {
|
||||
if (deletion.data.success) {
|
||||
// @ts-ignore
|
||||
toast.success(restoration.data.actionSuccess.message);
|
||||
toast.success(deletion.data.actionSuccess.message);
|
||||
router.refresh();
|
||||
} else {
|
||||
// @ts-ignore
|
||||
toast.error(restoration.data.actionError.message);
|
||||
toast.error(deletion.data.actionError.message);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -13,7 +13,7 @@ import { Button } from "@/components/ui/button";
|
||||
import {MoreHorizontal, Trash2} from "lucide-react";
|
||||
import { ReloadIcon } from "@radix-ui/react-icons";
|
||||
import { StatusBadge } from "@/components/wrappers/common/status-badge";
|
||||
import {Backup, Restoration} from "@/db/schema/06_database";
|
||||
import {Backup, Restoration} from "@/db/schema/07_database";
|
||||
import {formatFrenchDate} from "@/utils/date-formatting";
|
||||
import {useMutation} from "@tanstack/react-query";
|
||||
import {
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
"use server"
|
||||
import { userAction } from "@/safe-actions";
|
||||
import { z } from "zod";
|
||||
import { ServerActionResult } from "@/types/action-type";
|
||||
import {userAction} from "@/safe-actions";
|
||||
import {z} from "zod";
|
||||
import {ServerActionResult} from "@/types/action-type";
|
||||
import * as drizzleDb from "@/db";
|
||||
|
||||
import { db } from "@/db";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import {Backup, Restoration} from "@/db/schema/06_database";
|
||||
import {db} from "@/db";
|
||||
import {and, eq} from "drizzle-orm";
|
||||
import {Backup, Restoration} from "@/db/schema/07_database";
|
||||
import {NextResponse} from "next/server";
|
||||
import {
|
||||
deleteFileS3Private,
|
||||
deleteLocalPrivate,
|
||||
uploadLocalPrivate,
|
||||
uploadS3Private
|
||||
} from "@/features/upload/private/upload.action";
|
||||
import {env} from "@/env.mjs";
|
||||
|
||||
export const deleteRestoreAction = userAction
|
||||
.schema(
|
||||
@@ -14,7 +22,7 @@ export const deleteRestoreAction = userAction
|
||||
restorationId: z.string(),
|
||||
})
|
||||
)
|
||||
.action(async ({ parsedInput }): Promise<ServerActionResult<Backup>> => {
|
||||
.action(async ({parsedInput}): Promise<ServerActionResult<Backup>> => {
|
||||
try {
|
||||
await db
|
||||
.delete(drizzleDb.schemas.restoration)
|
||||
@@ -36,24 +44,60 @@ export const deleteRestoreAction = userAction
|
||||
message: "Failed to delete restoration.",
|
||||
status: 500,
|
||||
cause: error instanceof Error ? error.message : "Unknown error",
|
||||
messageParams: { message: "Error deleting the restoration" },
|
||||
messageParams: {message: "Error deleting the restoration"},
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
export const deleteBackupAction = userAction
|
||||
.schema(
|
||||
z.object({
|
||||
backupId: z.string(),
|
||||
databaseId: z.string(),
|
||||
projectSlug: z.string(),
|
||||
file: z.string(),
|
||||
})
|
||||
)
|
||||
.action(async ({ parsedInput }): Promise<ServerActionResult<Backup>> => {
|
||||
.action(async ({parsedInput}): Promise<ServerActionResult<Backup>> => {
|
||||
try {
|
||||
|
||||
const [settings] = await db.select().from(drizzleDb.schemas.setting).where(eq(drizzleDb.schemas.setting.name, "system")).limit(1);
|
||||
if (!settings) {
|
||||
return {
|
||||
success: false,
|
||||
actionError: {
|
||||
message: "No settings found.",
|
||||
status: 404,
|
||||
cause: "No settings found.",
|
||||
messageParams: {message: "Error deleting the backup"},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
let success: boolean, message: string;
|
||||
|
||||
const result =
|
||||
settings.storage === "local"
|
||||
? await deleteLocalPrivate(parsedInput.file)
|
||||
: await deleteFileS3Private(`${parsedInput.projectSlug}/${parsedInput.file}`, env.S3_BUCKET_NAME!);
|
||||
|
||||
({success, message} = result);
|
||||
|
||||
if (!success) {
|
||||
return {
|
||||
success: false,
|
||||
actionError: {
|
||||
message: message,
|
||||
status: 404,
|
||||
cause: "Unable to delete backup from storage",
|
||||
messageParams: {message: "Error deleting the backup"},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
await db
|
||||
.delete(drizzleDb.schemas.backup)
|
||||
.where(and(eq(drizzleDb.schemas.backup.id, parsedInput.backupId), eq(drizzleDb.schemas.backup.databaseId, parsedInput.databaseId)))
|
||||
@@ -65,6 +109,7 @@ export const deleteBackupAction = userAction
|
||||
.where(and(eq(drizzleDb.schemas.backup.id, parsedInput.backupId), eq(drizzleDb.schemas.backup.databaseId, parsedInput.databaseId)))
|
||||
.execute();
|
||||
|
||||
|
||||
if (backupExists.length === 0) {
|
||||
return {
|
||||
success: true,
|
||||
@@ -78,8 +123,8 @@ export const deleteBackupAction = userAction
|
||||
actionError: {
|
||||
message: "Backup not found or already deleted.",
|
||||
status: 404,
|
||||
cause: "Backup could not be deleted.",
|
||||
messageParams: { message: "Error deleting the backup" },
|
||||
cause: "Backup could not be deleted (from database or remote storage).",
|
||||
messageParams: {message: "Error deleting the backup"},
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -90,25 +135,24 @@ export const deleteBackupAction = userAction
|
||||
message: "Failed to delete backup.",
|
||||
status: 500,
|
||||
cause: error instanceof Error ? error.message : "Unknown error",
|
||||
messageParams: { message: "Error deleting the backup" },
|
||||
messageParams: {message: "Error deleting the backup"},
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
export const rerunRestorationAction = userAction
|
||||
.schema(
|
||||
z.object({
|
||||
restorationId: z.string(),
|
||||
})
|
||||
)
|
||||
.action(async ({ parsedInput }): Promise<ServerActionResult<Restoration>> => {
|
||||
.action(async ({parsedInput}): Promise<ServerActionResult<Restoration>> => {
|
||||
try {
|
||||
const updateResult = await db
|
||||
.update(drizzleDb.schemas.restoration)
|
||||
.set({ status: "waiting" })
|
||||
.set({status: "waiting"})
|
||||
.where(eq(drizzleDb.schemas.restoration.id, parsedInput.restorationId))
|
||||
.returning()
|
||||
.execute();
|
||||
@@ -122,7 +166,7 @@ export const rerunRestorationAction = userAction
|
||||
message: "Restoration not found.",
|
||||
status: 404,
|
||||
cause: "No restoration with the given ID exists.",
|
||||
messageParams: { message: "Restoration not found" },
|
||||
messageParams: {message: "Restoration not found"},
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -132,7 +176,7 @@ export const rerunRestorationAction = userAction
|
||||
value: updatedRestoration,
|
||||
actionSuccess: {
|
||||
message: "Restoration has been requeued.",
|
||||
messageParams: { restorationId: updatedRestoration.id },
|
||||
messageParams: {restorationId: updatedRestoration.id},
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
@@ -142,7 +186,7 @@ export const rerunRestorationAction = userAction
|
||||
message: "Failed to rerun restoration.",
|
||||
status: 500,
|
||||
cause: error instanceof Error ? error.message : "Unknown error",
|
||||
messageParams: { message: "Error updating the restoration" },
|
||||
messageParams: {message: "Error updating the restoration"},
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -157,7 +201,7 @@ export const createRestorationAction = userAction
|
||||
databaseId: z.string(),
|
||||
})
|
||||
)
|
||||
.action(async ({ parsedInput }): Promise<ServerActionResult<Restoration>> => {
|
||||
.action(async ({parsedInput}): Promise<ServerActionResult<Restoration>> => {
|
||||
try {
|
||||
// Insert new restoration into the database
|
||||
const restorationData = await db
|
||||
@@ -177,7 +221,7 @@ export const createRestorationAction = userAction
|
||||
value: createdRestoration,
|
||||
actionSuccess: {
|
||||
message: "Restoration has been successfully created.",
|
||||
messageParams: { restorationId: createdRestoration.id },
|
||||
messageParams: {restorationId: createdRestoration.id},
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
@@ -187,7 +231,7 @@ export const createRestorationAction = userAction
|
||||
message: "Failed to create restoration.",
|
||||
status: 500,
|
||||
cause: error instanceof Error ? error.message : "Unknown error",
|
||||
messageParams: { message: "Error creating the restoration" },
|
||||
messageParams: {message: "Error creating the restoration"},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,15 +4,12 @@ import {mkdir, writeFile} from "fs/promises";
|
||||
import path from "path";
|
||||
import * as fs from "node:fs";
|
||||
import {getServerUrl} from "@/utils/get-server-url";
|
||||
import {createPresignedUrlToDownload, saveFileInBucket} from "@/utils/s3-file-management";
|
||||
import crypto from "crypto";
|
||||
import {createPresignedUrlToDownload, deleteFileFromBucket, saveFileInBucket} from "@/utils/s3-file-management";
|
||||
import {env} from "@/env.mjs";
|
||||
import {action, userAction} from "@/safe-actions";
|
||||
import {action} from "@/safe-actions";
|
||||
import {z} from "zod";
|
||||
import {ServerActionResult} from "@/types/action-type";
|
||||
import {Backup} from "@/db/schema/06_database";
|
||||
import {db} from "@/db";
|
||||
import * as drizzleDb from "@/db";
|
||||
import {unlink} from "fs/promises";
|
||||
|
||||
const privateLocalDir = "private/uploads/files/";
|
||||
const privateS3Dir = "backups/";
|
||||
@@ -54,6 +51,53 @@ export async function uploadS3Private(fileName: string, buffer: any, bucketName:
|
||||
}
|
||||
|
||||
|
||||
export async function deleteFileS3Private(fileName: string, bucketName: string) {
|
||||
try {
|
||||
|
||||
await deleteFileFromBucket({
|
||||
bucketName,
|
||||
fileName: `${privateS3Dir}${fileName}`,
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "File deleted successfully",
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error occurred:", error);
|
||||
throw new Error("An error occurred while deleting the private file");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delete a file from local private storage
|
||||
*/
|
||||
export async function deleteLocalPrivate(fileName: string) {
|
||||
try {
|
||||
const filePath = path.join(process.cwd(), privateLocalDir, fileName);
|
||||
|
||||
// Delete locally
|
||||
await unlink(filePath);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `File '${fileName}' deleted successfully`,
|
||||
};
|
||||
} catch (error: any) {
|
||||
if (error.code === "ENOENT") {
|
||||
return {
|
||||
success: false,
|
||||
message: `File '${fileName}' not found`,
|
||||
};
|
||||
}
|
||||
|
||||
console.error("Error occurred while deleting file:", error);
|
||||
throw new Error("An error occurred while deleting the file");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// export async function getFileUrlPresignedLocal(fileName: string) {
|
||||
// try {
|
||||
// const filePath = path.join(privateLocalDir, fileName);
|
||||
|
||||
@@ -11,7 +11,7 @@ import { UploadedObjectInfo } from "minio/src/internal/type";
|
||||
import { getServerUrl } from "@/utils/get-server-url";
|
||||
import { db } from "@/db";
|
||||
import { eq } from "drizzle-orm";
|
||||
import {Setting} from "@/db/schema/00_setting";
|
||||
import {Setting} from "@/db/schema/01_setting";
|
||||
import * as drizzleDb from "@/db";
|
||||
|
||||
export const uploadImageAction = userAction.schema(z.instanceof(FormData)).action(async ({ parsedInput: formData, ctx }) => {
|
||||
|
||||
@@ -8,7 +8,7 @@ import {admin as adminPlugin, openAPI, Organization, organization} from "better-
|
||||
import {ac, admin, orgAdmin, orgMember, orgOwner, pending, superadmin, user} from "@/lib/auth/permissions";
|
||||
import {headers} from "next/headers";
|
||||
import {count, eq} from "drizzle-orm";
|
||||
import {OrganizationWithMembers, OrganizationWithMembersAndUsers} from "@/db/schema/02_organization";
|
||||
import {OrganizationWithMembers, OrganizationWithMembersAndUsers} from "@/db/schema/03_organization";
|
||||
|
||||
export const auth = betterAuth({
|
||||
database: drizzleAdapter(db, {
|
||||
|
||||
@@ -2,7 +2,7 @@ import {db} from "@/db";
|
||||
import {enforceRetentionCount} from "@/lib/tasks/database/retention-count";
|
||||
import {enforceRetentionDays} from "@/lib/tasks/database/retention-days";
|
||||
import {enforceRetentionGFS} from "@/lib/tasks/database/retention-gsf";
|
||||
import {retentionPolicy} from "@/db/schema/06_database";
|
||||
import {retentionPolicy} from "@/db/schema/07_database";
|
||||
|
||||
|
||||
export const retentionCleanTask = async () => {
|
||||
|
||||
@@ -68,6 +68,46 @@ export async function createBucketIfNotExists(bucketName: string) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a file from a bucket
|
||||
* @param bucketName name of the bucket
|
||||
* @param fileName name of the file
|
||||
* @returns true if deleted, false if not
|
||||
*/
|
||||
export async function deleteFileFromBucket({
|
||||
bucketName,
|
||||
fileName,
|
||||
}: {
|
||||
bucketName: string;
|
||||
fileName: string;
|
||||
}): Promise<boolean> {
|
||||
const s3Client = await getS3Client();
|
||||
|
||||
try {
|
||||
const fileExists = await checkFileExistsInBucket({ bucketName, fileName });
|
||||
|
||||
if (!fileExists) {
|
||||
console.warn(`File not found: ${bucketName}/${fileName}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
await s3Client.removeObject(bucketName, fileName);
|
||||
console.log(`Deleted file: ${bucketName}/${fileName}`);
|
||||
return true;
|
||||
} catch (error: any) {
|
||||
console.error("Error deleting file from bucket:", {
|
||||
bucketName,
|
||||
fileName,
|
||||
error: error.message,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Save file in S3 bucket
|
||||
* @param bucketName name of the bucket
|
||||
|
||||
Reference in New Issue
Block a user