Working on the organization system.

This commit is contained in:
charlesgauthereau
2025-07-16 12:36:11 +02:00
parent 405de7b323
commit 385777535f
61 changed files with 514 additions and 366 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
import {PageParams} from "@/types/next";
import {RegisterForm} from "@/components/wrappers/auth/Register/register-form/RegisterForm";
import {RegisterForm} from "@/components/wrappers/auth/register/register-form/register-form";
export default async function RoutePage(props: PageParams<{}>) {
return (
@@ -0,0 +1,41 @@
import {PageParams} from "@/types/next";
import {Page, PageContent, PageHeader, PageTitle} from "@/features/layout/page";
import {notFound} from "next/navigation";
import {RestoreForm} from "@/components/wrappers/dashboard/database/RestoreForm";
import {currentUser} from "@/lib/auth/current-user";
import {db} from "@/db";
import {eq} from "drizzle-orm";
import * as drizzleDb from "@/db";
export default async function RoutePage(
props: PageParams<{
databaseId: string;
}>
) {
const {databaseId} = await props.params;
const user = await currentUser();
if (!user) notFound();
const [dbToRestore] = await db.select().from(drizzleDb.schemas.database).where(eq(drizzleDb.schemas.database.id, databaseId));
if (!dbToRestore) {
notFound();
}
const dbsOfSameType = await db.select().from(drizzleDb.schemas.database).where(eq(drizzleDb.schemas.database.dbms!, dbToRestore.dbms!));
const successfulBackups = await db.select().from(drizzleDb.schemas.backup).where(eq(drizzleDb.schemas.backup.status, "success"));
return (
<Page>
<PageHeader>
<PageTitle>Restore {dbToRestore.name}</PageTitle>
</PageHeader>
<PageContent>
<RestoreForm databaseToRestore={dbToRestore} databases={dbsOfSameType} backups={successfulBackups}/>
</PageContent>
</Page>
);
}
@@ -4,14 +4,15 @@ import { notFound } from "next/navigation";
import { DatabaseForm } from "@/components/wrappers/dashboard/database/DatabaseForm/DatabaseForm";
import { db } from "@/db";
import { database } from "@/db/schema";
import * as drizzleDb from "@/db";
import { eq } from "drizzle-orm";
export default async function RoutePage(props: PageParams<{ databaseId: string }>) {
const { databaseId } = await props.params;
const dbItem = await db.query.database.findFirst({
where: eq(database.id, databaseId),
where: eq(drizzleDb.schemas.database.id, databaseId),
});
if (!dbItem) {
@@ -9,13 +9,13 @@ import { CronButton } from "@/components/wrappers/dashboard/database/CronButton/
import { db } from "@/db";
import { eq, and } from "drizzle-orm";
import { backup as drizzleBackup, database as drizzleDatabase, restoration as drizzleRestoration } from "@/db/schema";
import * as drizzleDb from "@/db";
export default async function RoutePage(props: PageParams<{ databaseId: string }>) {
const { databaseId } = await props.params;
const dbItem = await db.query.database.findFirst({
where: eq(drizzleDatabase.id, databaseId),
where: eq(drizzleDb.schemas.database.id, databaseId),
});
if (!dbItem) {
@@ -23,7 +23,7 @@ export default async function RoutePage(props: PageParams<{ databaseId: string }
}
const backups = await db.query.backup.findMany({
where: eq(drizzleBackup.databaseId, dbItem.id),
where: eq(drizzleDb.schemas.backup.databaseId, dbItem.id),
with: {
restorations: true,
},
@@ -31,7 +31,7 @@ export default async function RoutePage(props: PageParams<{ databaseId: string }
});
const restorations = await db.query.restoration.findMany({
where: eq(drizzleRestoration.databaseId, dbItem.id),
where: eq(drizzleDb.schemas.restoration.databaseId, dbItem.id),
orderBy: (r, { desc }) => [desc(r.createdAt)],
});
@@ -40,14 +40,14 @@ export default async function RoutePage(props: PageParams<{ databaseId: string }
const [totalBackups, successfulBackups] = await Promise.all([
db
.select({ count: drizzleBackup.id })
.from(drizzleBackup)
.where(eq(drizzleBackup.databaseId, dbItem.id))
.select({ count: drizzleDb.schemas.backup.id })
.from(drizzleDb.schemas.backup)
.where(eq(drizzleDb.schemas.backup.databaseId, dbItem.id))
.then((rows) => rows.length),
db
.select({ count: drizzleBackup.id })
.from(drizzleBackup)
.where(and(eq(drizzleBackup.databaseId, dbItem.id), eq(drizzleBackup.status, "success")))
.select({ count: drizzleDb.schemas.backup.id })
.from(drizzleDb.schemas.backup)
.where(and(eq(drizzleDb.schemas.backup.databaseId, dbItem.id), eq(drizzleDb.schemas.backup.status, "success")))
.then((rows) => rows.length),
]);
@@ -1,17 +1,18 @@
import { notFound } from "next/navigation";
import { PageParams } from "@/types/next";
import { Page, PageContent, PageHeader, PageTitle } from "@/features/layout/page";
import { ProjectForm } from "@/components/wrappers/dashboard/projects/ProjectsForm/ProjectForm";
import {notFound} from "next/navigation";
import {PageParams} from "@/types/next";
import {Page, PageContent, PageHeader, PageTitle} from "@/features/layout/page";
import {ProjectForm} from "@/components/wrappers/dashboard/projects/ProjectsForm/ProjectForm";
import * as drizzleDb from "@/db";
import { db } from "@/db";
import { eq } from "drizzle-orm";
import { project as drizzleProject, organization as drizzleOrganization, DatabaseWith } from "@/db/schema";
import {db} from "@/db";
import {eq} from "drizzle-orm";
import {DatabaseWith} from "@/db/schema/06_database";
export default async function RoutePage(props: PageParams<{ projectId: string }>) {
const { projectId } = await props.params;
const {projectId} = await props.params;
const proj = await db.query.project.findFirst({
where: eq(drizzleProject.id, projectId),
where: eq(drizzleDb.schemas.project.id, projectId),
with: {
databases: true,
},
@@ -23,7 +24,7 @@ export default async function RoutePage(props: PageParams<{ projectId: string }>
//
const org = await db.query.organization.findFirst({
where: eq(drizzleOrganization.slug, "default"),
where: eq(drizzleDb.schemas.organization.slug, "default"),
});
if (!org) {
@@ -32,14 +33,14 @@ export default async function RoutePage(props: PageParams<{ projectId: string }>
const availableDatabases = (
await db.query.database.findMany({
where: (db, { or, eq, isNull }) => or(isNull(db.projectId), eq(db.projectId, proj.id)),
where: (db, {or, eq, isNull}) => or(isNull(db.projectId), eq(db.projectId, proj.id)),
with: {
agent: true,
project: true,
backups: true,
restorations: true,
},
orderBy: (db, { desc }) => [desc(db.createdAt)],
orderBy: (db, {desc}) => [desc(db.createdAt)],
})
).filter((db): db is DatabaseWith => db.project !== null);
@@ -52,7 +53,7 @@ export default async function RoutePage(props: PageParams<{ projectId: string }>
<ProjectForm
organization={org}
databases={availableDatabases}
defaultValues={{ name: proj.name, slug: proj.slug, databases: proj.databases.map((db) => db.id) }}
defaultValues={{name: proj.name, slug: proj.slug, databases: proj.databases.map((db) => db.id)}}
projectId={proj.id}
/>
</PageContent>
@@ -10,19 +10,24 @@ import { notFound } from "next/navigation";
import { db } from "@/db";
import { eq } from "drizzle-orm";
import { organization as drizzleOrganization } from "@/db/schema";
import {getOrganization} from "@/lib/auth/auth";
import * as drizzleDb from "@/db";
export default async function RoutePage(props: PageParams<{ slug: string; projectId: string }>) {
const { slug: organizationSlug, projectId } = await props.params;
export default async function RoutePage(props: PageParams<{
// slug: string;
projectId: string
}>) {
const {
// slug: organizationSlug,
projectId } = await props.params;
const organization = await getOrganization({organizationSlug});
const organization = await getOrganization({});
if (!organization || organization?.slug !== organizationSlug) {
if (!organization) {
notFound();
}
const org = await db.query.organization.findFirst({
where: eq(drizzleOrganization.slug, organization.slug),
where: eq(drizzleDb.schemas.organization.slug, organization.slug),
});
if (!org) notFound();
@@ -56,7 +61,7 @@ export default async function RoutePage(props: PageParams<{ slug: string; projec
{proj.databases.length > 0 ? (
<CardsWithPagination
data={proj.databases}
organizationSlug={organizationSlug}
organizationSlug={organization.slug}
cardItem={ProjectDatabaseCard}
cardsPerPage={4}
numberOfColumns={1}
@@ -3,16 +3,16 @@ import {Page, PageContent, PageHeader, PageTitle} from "@/features/layout/page";
import {ProjectForm} from "@/components/wrappers/dashboard/projects/ProjectsForm/ProjectForm";
import {notFound} from "next/navigation";
import {db} from "@/db";
import {DatabaseWith, organization as drizzleOrganization} from "@/db/schema";
import {eq} from "drizzle-orm";
import {getOrganization} from "@/lib/auth/auth";
import * as drizzleDb from "@/db";
import {DatabaseWith} from "@/db/schema/06_database";
export default async function RoutePage(props: PageParams<{ slug: string }>) {
const {slug: organizationSlug} = await props.params;
export default async function RoutePage(props: PageParams<{ }>) {
const organization = await getOrganization({organizationSlug});
const organization = await getOrganization({});
if (!organization || organization?.slug !== organizationSlug) {
if (!organization ) {
notFound();
}
@@ -30,7 +30,7 @@ export default async function RoutePage(props: PageParams<{ slug: string }>) {
).filter((db) => db.project !== null) as DatabaseWith[];
const org = await db.query.organization.findFirst({
where: eq(drizzleOrganization.slug, organization.slug),
where: eq(drizzleDb.schemas.organization.slug, organization.slug),
});
if (!org) notFound();
@@ -9,12 +9,11 @@ import {db} from "@/db";
import {notFound} from "next/navigation";
import {getOrganization} from "@/lib/auth/auth";
export default async function RoutePage(props: PageParams<{ slug: string }>) {
const {slug: organizationSlug} = await props.params;
export default async function RoutePage(props: PageParams<{ }>) {
const organization = await getOrganization({organizationSlug});
const organization = await getOrganization({});
if (!organization || organization?.slug !== organizationSlug) {
if (!organization) {
notFound();
}
@@ -35,7 +34,7 @@ export default async function RoutePage(props: PageParams<{ slug: string }>) {
<PageTitle>Projects</PageTitle>
{projects.length > 0 && (
<PageActions>
<Link href={`/dashboard/${organizationSlug}/projects/new`}>
<Link href={`/dashboard/projects/new`}>
<Button>+ Create Project</Button>
</Link>
</PageActions>
@@ -44,11 +43,11 @@ export default async function RoutePage(props: PageParams<{ slug: string }>) {
<PageContent className="mt-10">
{projects.length > 0 ? (
<CardsWithPagination organizationSlug={organizationSlug} data={projects} cardItem={ProjectCard}
<CardsWithPagination organizationSlug={organization.slug} data={projects} cardItem={ProjectCard}
cardsPerPage={4} numberOfColumns={1}/>
) : (
<Link
href={`/dashboard/${organizationSlug}/projects/new`}
href={`/dashboard/projects/new`}
className=" flex item-center justify-center border-2 border-dashed transition-colors border-primary p-8 lg:p-12 w-full rounded-md"
>
Create new Project
@@ -7,7 +7,7 @@ import { getCurrentOrganizationSlug } from "@/features/dashboard/organization-co
import { notFound } from "next/navigation";
import { db } from "@/db";
import { asc, count, eq, inArray } from "drizzle-orm";
import { organization as drizzleOrganization, project as drizzleProject, backup as drizzleBackup } from "@/db/schema";
import * as drizzleDb from "@/db";
export default async function RoutePage(props: PageParams<{ slug: string }>) {
const { slug: organizationSlug } = await props.params;
@@ -18,13 +18,13 @@ export default async function RoutePage(props: PageParams<{ slug: string }>) {
}
const org = await db.query.organization.findFirst({
where: eq(drizzleOrganization.slug, currentOrganizationSlug),
where: eq(drizzleDb.schemas.organization.slug, currentOrganizationSlug),
});
if (!org) notFound();
const projects = await db.query.project.findMany({
where: eq(drizzleProject.organizationId, org.id),
where: eq(drizzleDb.schemas.project.organizationId, org.id),
});
const projectsCount = projects.length;
@@ -34,19 +34,19 @@ export default async function RoutePage(props: PageParams<{ slug: string }>) {
id: true,
createdAt: true,
},
orderBy: [asc(drizzleBackup.id)],
orderBy: [asc(drizzleDb.schemas.backup.id)],
});
const backupsRate = await db
.select({
createdAt: drizzleBackup.createdAt,
status: drizzleBackup.status,
createdAt: drizzleDb.schemas.backup.createdAt,
status: drizzleDb.schemas.backup.status,
_count: count(),
})
.from(drizzleBackup)
.where(inArray(drizzleBackup.status, ["success", "failed"]))
.groupBy(drizzleBackup.createdAt, drizzleBackup.status)
.orderBy(drizzleBackup.createdAt);
.from(drizzleDb.schemas.backup)
.where(inArray(drizzleDb.schemas.backup.status, ["success", "failed"]))
.groupBy(drizzleDb.schemas.backup.createdAt, drizzleDb.schemas.backup.status)
.orderBy(drizzleDb.schemas.backup.createdAt);
return (
<Page>
@@ -1,41 +0,0 @@
import { PageParams } from "@/types/next";
import { Page, PageContent, PageHeader, PageTitle } from "@/features/layout/page";
import { notFound } from "next/navigation";
import { RestoreForm } from "@/components/wrappers/dashboard/database/RestoreForm";
import { currentUser } from "@/lib/auth/current-user";
import { db } from "@/db";
import { eq } from "drizzle-orm";
import { database, backup } from "@/db/schema";
export default async function RoutePage(
props: PageParams<{
databaseId: string;
}>
) {
const { databaseId } = await props.params;
const user = await currentUser();
if (!user) notFound();
const [dbToRestore] = await db.select().from(database).where(eq(database.id, databaseId));
if (!dbToRestore) {
notFound();
}
const dbsOfSameType = await db.select().from(database).where(eq(database.dbms!, dbToRestore.dbms!));
const successfulBackups = await db.select().from(backup).where(eq(backup.status, "success"));
return (
<Page>
<PageHeader>
<PageTitle>Restore {dbToRestore.name}</PageTitle>
</PageHeader>
<PageContent>
<RestoreForm databaseToRestore={dbToRestore} databases={dbsOfSameType} backups={successfulBackups} />
</PageContent>
</Page>
);
}
+8 -9
View File
@@ -1,23 +1,22 @@
import React from "react";
import { redirect } from "next/navigation";
import {redirect} from "next/navigation";
import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar";
import { AppSidebar } from "@/components/wrappers/dashboard/sideBar/app-sidebar";
import { Header } from "@/features/layout/Header";
import { currentUser } from "@/lib/auth/current-user";
import {SidebarInset, SidebarProvider} from "@/components/ui/sidebar";
import {AppSidebar} from "@/components/wrappers/dashboard/sideBar/app-sidebar";
import {Header} from "@/features/layout/Header";
import {currentUser} from "@/lib/auth/current-user";
export default async function Layout({ children }: { children: React.ReactNode }) {
export default async function Layout({children}: { children: React.ReactNode }) {
const user = await currentUser();
if (!user) redirect("/login");
return (
<>
<SidebarProvider>
<div className="flex flex-col lg:flex-row w-full">
<AppSidebar />
<AppSidebar/>
<SidebarInset>
<Header />
<Header/>
<main className="h-full">{children}</main>
</SidebarInset>
</div>
+15 -13
View File
@@ -1,26 +1,28 @@
import { PageParams } from "@/types/next";
import { Page, PageActions, PageContent, PageHeader, PageTitle } from "@/features/layout/page";
import { notFound } from "next/navigation";
import { UserForm } from "@/components/wrappers/dashboard/profile/UserForm/UserForm";
import { Badge } from "@/components/ui/badge";
import { ButtonDeleteAccount } from "@/components/wrappers/dashboard/profile/ButtonDeleteAccount/ButtonDeleteAccount";
import { AvatarWithUpload } from "@/components/wrappers/dashboard/profile/Avatar/AvatarWithUpload";
import { currentUser } from "@/lib/auth/current-user";
import {PageParams} from "@/types/next";
import {Page, PageActions, PageContent, PageHeader, PageTitle} from "@/features/layout/page";
import {notFound} from "next/navigation";
import {UserForm} from "@/components/wrappers/dashboard/profile/UserForm/UserForm";
import {Badge} from "@/components/ui/badge";
import {ButtonDeleteAccount} from "@/components/wrappers/dashboard/profile/ButtonDeleteAccount/ButtonDeleteAccount";
import {AvatarWithUpload} from "@/components/wrappers/dashboard/profile/Avatar/AvatarWithUpload";
import {currentUser} from "@/lib/auth/current-user";
import {getAccounts, getSessions} from "@/lib/auth/auth";
//import { getAccounts, getSessions } from "@/lib/auth/auth";
export default async function RoutePage(props: PageParams<{}>) {
const user = await currentUser();
console.log("my user",user);
if (!user) {
return notFound();
}
if (user.role !== "user" && user.role !== "admin" && user.role !== "superadmin") {
return notFound();
}
// const sessions = await getSessions();
// const accounts = await getAccounts();
// const sessions = await getSessions();
// const accounts = await getAccounts();
return (
<Page>
@@ -41,7 +43,7 @@ export default async function RoutePage(props: PageParams<{}>) {
<Badge className="ml-3 hidden lg:block">{user.role}</Badge>
</PageTitle>
<PageActions className="mt-2 hidden sm:block">
<ButtonDeleteAccount text="Delete my account" />
<ButtonDeleteAccount text="Delete my account"/>
</PageActions>
</div>
<PageContent>
@@ -54,7 +56,7 @@ export default async function RoutePage(props: PageParams<{}>) {
}}
/>
<div className="mt-4 sm:hidden">
<ButtonDeleteAccount text="Delete my account" />
<ButtonDeleteAccount text="Delete my account"/>
</div>
</PageContent>
</Page>
+6 -1
View File
@@ -1,5 +1,10 @@
import { redirect } from "next/navigation";
export default async function NotFound() {
redirect("/dashboard/profile");
// redirect("/dashboard/profile");
return(
<>
Error
</>
)
}
@@ -1,28 +0,0 @@
"use server";
import { RegisterSchema } from "@/components/wrappers/auth/Register/register-form/register-form.schema";
import { action } from "@/safe-actions";
import { signUp } from "@/lib/auth/auth-client";
import { db } from "@/db";
import { user as drizzleUser } from "@/db/schema/01_user";
import { eq } from "drizzle-orm";
export const registerUserAction = action.schema(RegisterSchema).action(async ({ parsedInput }: { parsedInput: typeof RegisterSchema._type }) => {
const [user] = await db.select().from(drizzleUser).where(eq(drizzleUser.email, parsedInput.email)).limit(1);
if (!user && parsedInput.password === parsedInput.confirmPassword) {
const { data, error } = await signUp.email({
email: parsedInput.email,
password: parsedInput.password,
name: parsedInput.name,
});
if (error) {
throw new Error(error.message);
}
return {
data: data?.user,
};
}
throw new Error("An error occured while creating user");
});
@@ -26,6 +26,7 @@ export const LoginForm = (props: loginFormProps) => {
const form = useZodForm({
schema: LoginSchema,
});
const mutation = useMutation({
mutationFn: async (values: LoginType) => {
const { error } = await signIn.email(values, {
@@ -11,36 +11,37 @@ import { Input } from "@/components/ui/input";
import { Form } from "@/components/ui/form";
import { Button } from "@/components/ui/button";
import { TooltipProvider, TooltipTrigger, Tooltip, TooltipContent } from "@/components/ui/tooltip";
import { RegisterSchema, RegisterType } from "@/components/wrappers/auth/Register/register-form/register-form.schema";
import { registerUserAction } from "@/components/wrappers/auth/Register/register-form/register-form.action";
import { RegisterSchema, RegisterType } from "@/components/wrappers/auth/register/register-form/register-form.schema";
import { PasswordInput } from "@/components/wrappers/auth/PaswordInput/password-input";
import {signUp} from "@/lib/auth/auth-client";
export type registerFormProps = {
defaultValues?: RegisterType;
};
export const RegisterForm = (props: registerFormProps) => {
const form = useZodForm({
schema: RegisterSchema,
});
const router = useRouter();
const mutation = useMutation({
mutationFn: async (values: RegisterType) => {
console.log(values);
const createUser = await registerUserAction(values);
console.log(createUser);
const data = createUser?.data?.data;
if (createUser?.serverError || !data) {
console.log(createUser?.serverError);
toast.error(createUser?.serverError);
return;
}
toast.success(`Success`);
router.push(`/login`);
router.refresh();
await signUp.email(values, {
onSuccess: () => {
toast.success(`Success`);
router.push(`/login`);
router.refresh();
},
onError: (error) => {
console.log(error);
toast.error(error.error.message);
},
});
},
});
return (
<TooltipProvider>
<Card>
@@ -2,9 +2,9 @@
import { userAction } from "@/safe-actions";
import { z } from "zod";
import { EmailFormSchema } from "@/components/wrappers/dashboard/admin/AdminEmailTab/EmailForm/email-form.schema";
import { setting as drizzleSetting } from "@/db/schema";
import { eq } from "drizzle-orm";
import { db } from "@/db";
import * as drizzleDb from "@/db";
export const updateEmailSettingsAction = userAction
.schema(
@@ -17,11 +17,11 @@ export const updateEmailSettingsAction = userAction
const { name, data } = parsedInput;
const [updatedSettings] = await db
.update(drizzleSetting)
.update(drizzleDb.schemas.setting)
.set({
...data,
})
.where(eq(drizzleSetting.name, name))
.where(eq(drizzleDb.schemas.setting.name, name))
.returning();
return {
@@ -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";
import {Setting} from "@/db/schema/00_setting";
export type SettingsEmailTabProps = {
settings: Setting;
@@ -10,7 +10,7 @@ import { checkConnexionToS3 } from "@/features/upload/public/upload.action";
import { toast } from "sonner";
import { useRouter } from "next/navigation";
import { updateStorageSettingsAction } from "@/components/wrappers/dashboard/admin/AdminStorageTab/StorageS3Form/s3-form.action";
import { Setting } from "@/db/schema";
import {Setting} from "@/db/schema/00_setting";
export type SettingsStorageTabProps = {
settings: Setting;
@@ -3,9 +3,9 @@
import { userAction } from "@/safe-actions";
import { z } from "zod";
import { db } from "@/db";
import { setting as drizzleSetting } from "@/db/schema";
import { S3FormSchema, StorageSwitchSchema } from "@/components/wrappers/dashboard/admin/AdminStorageTab/StorageS3Form/s3-form.schema";
import { eq } from "drizzle-orm";
import * as drizzleDb from "@/db";
export const updateS3SettingsAction = userAction
.schema(
@@ -18,9 +18,9 @@ export const updateS3SettingsAction = userAction
const { name, data } = parsedInput;
const [updatedSettings] = await db
.update(drizzleSetting)
.update(drizzleDb.schemas.setting)
.set({ ...data })
.where(eq(drizzleSetting.name, name))
.where(eq(drizzleDb.schemas.setting.name, name))
.returning();
return {
@@ -39,9 +39,9 @@ export const updateStorageSettingsAction = userAction
const { name, data } = parsedInput;
const [updatedSettings] = await db
.update(drizzleSetting)
.update(drizzleDb.schemas.setting)
.set({ ...data })
.where(eq(drizzleSetting.name, name))
.where(eq(drizzleDb.schemas.setting.name, name))
.returning();
return {
@@ -4,8 +4,8 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { SettingsEmailTab } from "@/components/wrappers/dashboard/admin/AdminEmailTab/SettingsEmailTab";
import { SettingsStorageTab } from "@/components/wrappers/dashboard/admin/AdminStorageTab/SettingsStorageTab";
import { AdminUsersTable } from "@/components/wrappers/dashboard/admin/admin-user-table";
import { Setting } from "@/db/schema";
import { User } from "@/db/schema/01_user";
import {Setting} from "@/db/schema/00_setting";
export type AdminTabsProps = {
users: User[];
@@ -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";
import {Agent} from "@/db/schema/07_agent";
export type agentCardProps = {
data: Agent;
@@ -1,10 +1,10 @@
"use client";
import { generateEdgeKey } from "@/utils/edge_key";
import { getServerUrl } from "@/utils/get-server-url";
import { PasswordInput } from "@/components/wrappers/auth/PaswordInput/password-input";
import { useState } from "react";
import { CopyButton } from "@/components/wrappers/common/button/copy-button";
import { Agent } from "@/db/schema";
import {generateEdgeKey} from "@/utils/edge_key";
import {getServerUrl} from "@/utils/get-server-url";
import {PasswordInput} from "@/components/wrappers/auth/PaswordInput/password-input";
import {useState} from "react";
import {CopyButton} from "@/components/wrappers/common/button/copy-button";
import {Agent} from "@/db/schema/07_agent";
export type AgentCardKeyProps = {
agent: Agent;
@@ -22,7 +22,7 @@ export const AgentCardKey = (props: AgentCardKeyProps) => {
setCode(edge_key);
}}
/>
<CopyButton className="mt-5" value={code} />
<CopyButton className="mt-5" value={code}/>
</>
);
};
@@ -4,12 +4,12 @@ import { AgentSchema } from "@/components/wrappers/dashboard/agent/AgentForm/age
import { z } from "zod";
import { eq, and, ne, count } from "drizzle-orm";
import { db } from "@/db";
import { agent } from "@/db/schema";
import * as drizzleDb from "@/db";
const verifySlugUniqueness = async (slug: string, agentId?: string) => {
const conditions = agentId ? and(eq(agent.slug, slug), ne(agent.id, agentId)) : eq(agent.slug, slug);
const conditions = agentId ? and(eq(drizzleDb.schemas.agent.slug, slug), ne(drizzleDb.schemas.agent.id, agentId)) : eq(drizzleDb.schemas.agent.slug, slug);
const [countResult] = await db.select({ count: count() }).from(agent).where(conditions);
const [countResult] = await db.select({ count: count() }).from(drizzleDb.schemas.agent).where(conditions);
if (countResult.count > 0) {
throw new ActionError("Slug already exists");
@@ -19,7 +19,7 @@ const verifySlugUniqueness = async (slug: string, agentId?: string) => {
export const createAgentAction = userAction.schema(AgentSchema).action(async ({ parsedInput }) => {
await verifySlugUniqueness(parsedInput.slug);
const [createdAgent] = await db.insert(agent).values(parsedInput).returning();
const [createdAgent] = await db.insert(drizzleDb.schemas.agent).values(parsedInput).returning();
return {
data: createdAgent,
@@ -36,7 +36,7 @@ export const updateAgentAction = userAction
.action(async ({ parsedInput }) => {
await verifySlugUniqueness(parsedInput.data.slug, parsedInput.id);
const [updatedAgent] = await db.update(agent).set(parsedInput.data).where(eq(agent.id, parsedInput.id)).returning();
const [updatedAgent] = await db.update(drizzleDb.schemas.agent).set(parsedInput.data).where(eq(drizzleDb.schemas.agent.id, parsedInput.id)).returning();
return {
data: updatedAgent,
@@ -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/CodeSnippet";
import { Agent } from "@/db/schema";
import {Agent} from "@/db/schema/07_agent";
export type agentRegistrationDialogProps = PropsWithChildren<{
agent: Agent;
@@ -3,14 +3,14 @@
import { z } from "zod";
import { userAction } from "@/safe-actions";
import { db } from "@/db";
import { backup } from "@/db/schema";
import { ServerActionResult } from "@/types/action-type";
import { Backup } from "@/db/schema";
import * as drizzleDb from "@/db";
import {Backup} from "@/db/schema/06_database";
export const backupButtonAction = userAction.schema(z.string()).action(async ({ parsedInput }): Promise<ServerActionResult<Backup>> => {
try {
const [createdBackup] = await db
.insert(backup)
.insert(drizzleDb.schemas.backup)
.values({
databaseId: parsedInput,
status: "waiting",
@@ -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/CronButton/cron.action";
import { Database } from "@/db/schema";
import {Database} from "@/db/schema/06_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";
import {Database} from "@/db/schema/06_database";
export type CronInputProps = {
database: Database;
@@ -3,8 +3,8 @@
import { userAction } from "@/safe-actions";
import { z } from "zod";
import { db } from "@/db";
import { database } from "@/db/schema";
import { eq } from "drizzle-orm";
import * as drizzleDb from "@/db";
export const updateDatabaseBackupPolicyAction = userAction
.schema(
@@ -17,11 +17,11 @@ export const updateDatabaseBackupPolicyAction = userAction
const cronPolicy = parsedInput.backupPolicy === "" ? null : parsedInput.backupPolicy;
const [updated] = await db
.update(database)
.update(drizzleDb.schemas.database)
.set({
backupPolicy: cronPolicy,
})
.where(eq(database.id, parsedInput.databaseId))
.where(eq(drizzleDb.schemas.database.id, parsedInput.databaseId))
.returning()
.execute();
@@ -3,9 +3,9 @@
import { z } from "zod";
import { userAction } from "@/safe-actions";
import { db } from "@/db";
import { database } from "@/db/schema";
import { eq } from "drizzle-orm";
import { DatabaseSchema } from "@/components/wrappers/dashboard/database/DatabaseForm/form-database.schema";
import * as drizzleDb from "@/db";
export const updateDatabaseAction = userAction
.schema(
@@ -15,7 +15,7 @@ export const updateDatabaseAction = userAction
})
)
.action(async ({ parsedInput }) => {
const [updated] = await db.update(database).set(parsedInput.data).where(eq(database.id, parsedInput.id)).returning().execute();
const [updated] = await db.update(drizzleDb.schemas.database).set(parsedInput.data).where(eq(drizzleDb.schemas.database.id, parsedInput.id)).returning().execute();
return updated;
});
@@ -7,6 +7,7 @@ import { authClient } from "@/lib/auth/auth-client";
export function OrganizationCombobox() {
const router = useRouter();
const { state } = useSidebar();
const { data: organizations } = authClient.useListOrganizations();
const { data: activeOrganization } = authClient.useActiveOrganization();
@@ -27,10 +28,9 @@ export function OrganizationCombobox() {
await authClient.organization.setActive({
organizationSlug: slug,
});
router.replace(`/dashboard/${slug}/home`);
// router.replace(`/dashboard/${slug}/home`);
router.refresh();
};
const { state } = useSidebar();
return <>{state === "expanded" && <ComboBox sideBar values={values} defaultValue={activeOrganization?.slug} onValueChange={onValueChange} />}</>;
}
@@ -1,12 +1,13 @@
"use server";
import { db } from "@/db";
import { user as drizzleUser } from "@/db/schema";
import { userAction } from "@/safe-actions";
import { eq } from "drizzle-orm";
import { z } from "zod";
import * as drizzleDb from "@/db";
export const updateImageUserAction = userAction.schema(z.string()).action(async ({ parsedInput, ctx }) => {
const [updatedUser] = await db.update(drizzleUser).set({ image: parsedInput }).where(eq(drizzleUser.id, ctx.user.id)).returning();
const [updatedUser] = await db.update(drizzleDb.schemas.user).set({ image: parsedInput }).where(eq(drizzleDb.schemas.user.id, ctx.user.id)).returning();
return {
data: updatedUser,
@@ -3,22 +3,23 @@ import { userAction } from "@/safe-actions";
import { z } from "zod";
import { v4 as uuidv4 } from "uuid";
import { db } from "@/db";
import { user as drizzleUser } from "@/db/schema";
import { eq } from "drizzle-orm";
import * as drizzleDb from "@/db";
export const deleteUserAction = userAction.schema(z.string()).action(async ({ parsedInput, ctx }) => {
const userId = parsedInput.length > 0 ? parsedInput : ctx.user.id;
const uuid = uuidv4();
const [updatedUser] = await db
.update(drizzleUser)
.update(drizzleDb.schemas.user)
.set({
email: `${uuid}@portabase.com`,
name: `${uuid}`,
//deleted: true,
//todo: add deleted
})
.where(eq(drizzleUser.id, userId))
.where(eq(drizzleDb.schemas.user.id, userId))
.returning();
return {
@@ -3,8 +3,8 @@ import { userAction } from "@/safe-actions";
import { z } from "zod";
import { UserSchema } from "@/components/wrappers/dashboard/profile/UserForm/user-form.schema";
import { db } from "@/db";
import { user as drizzleUser } from "@/db/schema";
import { eq } from "drizzle-orm";
import * as drizzleDb from "@/db";
export const updateUserAction = userAction
.schema(
@@ -14,8 +14,7 @@ export const updateUserAction = userAction
})
)
.action(async ({ parsedInput }) => {
const [updatedUser] = await db.update(drizzleUser).set(parsedInput.data).where(eq(drizzleUser.id, parsedInput.id)).returning();
const [updatedUser] = await db.update(drizzleDb.schemas.user).set(parsedInput.data).where(eq(drizzleDb.schemas.user.id, parsedInput.id)).returning();
return {
data: updatedUser,
};
@@ -1,24 +1,24 @@
"use server";
import { userAction } from "@/safe-actions";
import { z } from "zod";
import { v4 as uuidv4 } from "uuid";
import { ServerActionResult } from "@/types/action-type";
import { eq } from "drizzle-orm";
import { db } from "@/db";
import { project } from "@/db/schema";
import {userAction} from "@/safe-actions";
import {z} from "zod";
import {v4 as uuidv4} from "uuid";
import {ServerActionResult} from "@/types/action-type";
import {eq} from "drizzle-orm";
import {db} from "@/db";
import * as drizzleDb from "@/db";
export const deleteProjectAction = userAction.schema(z.string()).action(async ({ parsedInput }): Promise<ServerActionResult<typeof project.$inferSelect>> => {
export const deleteProjectAction = userAction.schema(z.string()).action(async ({parsedInput}): Promise<ServerActionResult<typeof drizzleDb.schemas.project.$inferSelect>> => {
try {
const uuid = uuidv4();
const updatedProjects = await db
.update(project)
.update(drizzleDb.schemas.project)
.set({
isArchived: true,
slug: uuid,
})
.where(eq(project.id, parsedInput))
.where(eq(drizzleDb.schemas.project.id, parsedInput))
.returning();
const updatedProject = updatedProjects[0];
@@ -32,7 +32,7 @@ export const deleteProjectAction = userAction.schema(z.string()).action(async ({
value: updatedProject,
actionSuccess: {
message: "Projects has been successfully archived.",
messageParams: { projectId: parsedInput },
messageParams: {projectId: parsedInput},
},
};
} catch (error) {
@@ -42,7 +42,7 @@ export const deleteProjectAction = userAction.schema(z.string()).action(async ({
message: "Failed to archive Projects.",
status: 500,
cause: error instanceof Error ? error.message : "Unknown error",
messageParams: { projectId: parsedInput },
messageParams: {projectId: parsedInput},
},
};
}
@@ -7,7 +7,7 @@ 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, Restoration } from "@/db/schema";
import {Backup, Database, Restoration} from "@/db/schema/06_database";
export type DatabaseTabsProps = {
backups: Backup[];
@@ -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";
import {Database} from "@/db/schema/06_database";
export type projectDatabaseCardProps = {
data: Database;
@@ -11,7 +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, Organization } from "@/db/schema";
import {DatabaseWith} from "@/db/schema/06_database";
import {Organization} from "@/db/schema/02_organization";
export type projectFormProps = {
defaultValues?: ProjectType;
@@ -4,9 +4,11 @@ import { userAction } from "@/safe-actions";
import { ProjectSchema } from "@/components/wrappers/dashboard/projects/ProjectsForm/ProjectForm.schema";
import { z } from "zod";
import { ServerActionResult } from "@/types/action-type";
import { Database, database as drizzleDatabase, project as drizzleProject, Project } from "@/db/schema";
import { db } from "@/db";
import { eq, inArray } from "drizzle-orm";
import {Project} from "@/db/schema/05_project";
import * as drizzleDb from "@/db";
import {Database} from "@/db/schema/06_database";
export const createProjectAction = userAction
.schema(
@@ -18,7 +20,7 @@ export const createProjectAction = userAction
.action(async ({ parsedInput }): Promise<ServerActionResult<Project>> => {
try {
const [createdProject] = await db
.insert(drizzleProject)
.insert(drizzleDb.schemas.project)
.values({
name: parsedInput.data.name,
slug: parsedInput.data.slug,
@@ -27,7 +29,7 @@ export const createProjectAction = userAction
.returning();
if (parsedInput.data.databases.length > 0) {
await db.update(drizzleDatabase).set({ projectId: createdProject.id }).where(inArray(drizzleDatabase.id, parsedInput.data.databases));
await db.update(drizzleDb.schemas.database).set({ projectId: createdProject.id }).where(inArray(drizzleDb.schemas.database.id, parsedInput.data.databases));
}
return {
@@ -62,7 +64,7 @@ export const updateProjectAction = userAction
.action(async ({ parsedInput }): Promise<ServerActionResult<Project>> => {
try {
const existing = await db.query.project.findFirst({
where: eq(drizzleProject.id, parsedInput.projectId),
where: eq(drizzleDb.schemas.project.id, parsedInput.projectId),
with: {
databases: true,
},
@@ -79,20 +81,20 @@ export const updateProjectAction = userAction
const databasesToRemove = existingDbIds.filter((id: string) => !newDbIds.includes(id));
if (databasesToAdd.length > 0) {
await db.update(drizzleDatabase).set({ projectId: parsedInput.projectId }).where(inArray(drizzleDatabase.id, databasesToAdd));
await db.update(drizzleDb.schemas.database).set({ projectId: parsedInput.projectId }).where(inArray(drizzleDb.schemas.database.id, databasesToAdd));
}
if (databasesToRemove.length > 0) {
await db.update(drizzleDatabase).set({ projectId: null }).where(inArray(drizzleDatabase.id, databasesToRemove));
await db.update(drizzleDb.schemas.database).set({ projectId: null }).where(inArray(drizzleDb.schemas.database.id, databasesToRemove));
}
const [updatedProject] = await db
.update(drizzleProject)
.update(drizzleDb.schemas.project)
.set({
name: parsedInput.data.name,
slug: parsedInput.data.slug,
})
.where(eq(drizzleProject.id, parsedInput.projectId))
.where(eq(drizzleDb.schemas.project.id, parsedInput.projectId))
.returning();
return {
@@ -8,7 +8,7 @@ import { useRouter } from "next/navigation";
import { useState } from "react";
import { updateUserOrganizationAction } from "@/components/wrappers/dashboard/settings/SettingsUsersTab/settings-user-tab.action";
import { OrganizationMember } from "@/db/schema/02_organization";
import {OrganizationMember} from "@/db/schema/03_member";
export const usersColumns: ColumnDef<OrganizationMember>[] = [
{
@@ -32,9 +32,8 @@ export async function AppSidebar() {
const organization = await getOrganization({organizationId: member.organizationId});
//todo: à revoir
console.log("organization", organization);
console.log("membrer", member);
console.log("aoaoaoaoaoa", organization);
return (
<Sidebar collapsible="icon">
+53 -6
View File
@@ -1,13 +1,60 @@
import "dotenv/config";
import { drizzle } from "drizzle-orm/node-postgres";
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 {Pool} from "pg";
// Do not delete
import dotenv from "dotenv";
import { env } from "@/env.mjs";
import * as schema from "./schema";
import {migrate} from "drizzle-orm/node-postgres/migrator";
dotenv.config({
path: ".env",
});
export const db = drizzle(env.DATABASE_URL!, {
schema,
const pool = new Pool({
connectionString: process.env.DATABASE_URL!,
});
export const schemas = {
...settings,
...user,
...organisation,
...invitation,
...member,
...project,
...agent,
...database
};
export const db = drizzle({
client: pool,
logger: true,
schema: schemas,
});
export async function makeMigration() {
if (process.env.NODE_ENV != "development") {
const pool = new Pool({
connectionString: process.env.DATABASE_URL!,
});
const database = drizzle({client: pool});
console.log("Running migrations...");
try {
await migrate(database, {migrationsFolder: "./src/db/migrations"});
console.log("Migrations applied successfully.");
} catch (error) {
console.error("Error applying migrations:", error);
}
}
}
-7
View File
@@ -1,7 +0,0 @@
export * from "./schema/00_setting";
export { user, session, userRelations } from "./schema/01_user";
export type { Organization } from "./schema/02_organization";
export { organization, member as organizationMember, invitation as organizationInvitation } from "./schema/02_organization";
export * from "./schema/03_project";
export * from "./schema/04_agent";
export * from "./schema/05_database";
+4 -2
View File
@@ -2,8 +2,10 @@ 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 "./03_project";
import { member, invitation, organization } from "./02_organization";
import { project } from "./05_project";
import {member} from "@/db/schema/03_member";
import {invitation} from "@/db/schema/04_invitation";
import {organization} from "@/db/schema/02_organization";
export const user = pgTable("user", {
id: uuid("id").defaultRandom().primaryKey(),
+3 -53
View File
@@ -1,9 +1,10 @@
import { pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
import { user } from "./01_user";
import { relations } from "drizzle-orm";
import { project } from "./03_project";
import { project } from "./05_project";
import { createSelectSchema } from "drizzle-zod";
import { z } from "zod";
import {invitation} from "@/db/schema/04_invitation";
import {member} from "@/db/schema/03_member";
export const organization = pgTable("organization", {
id: uuid("id").defaultRandom().primaryKey(),
@@ -15,32 +16,6 @@ export const organization = pgTable("organization", {
metadata: text("metadata"),
});
export const member = pgTable("member", {
id: uuid("id").defaultRandom().primaryKey(),
organizationId: uuid("organization_id")
.notNull()
.references(() => organization.id, { onDelete: "cascade" }),
userId: uuid("user_id")
.notNull()
.references(() => user.id, { onDelete: "cascade" }),
role: text("role").notNull(),
createdAt: timestamp("created_at").notNull().defaultNow(),
updatedAt: timestamp("updated_at"),
});
export const invitation = pgTable("invitation", {
id: uuid("id").defaultRandom().primaryKey(),
organizationId: uuid("organization_id")
.notNull()
.references(() => organization.id, { onDelete: "cascade" }),
email: text("email").notNull(),
role: text("role"),
status: text("status").notNull(),
expiresAt: timestamp("expires_at").notNull(),
inviterId: uuid("inviter_id")
.notNull()
.references(() => user.id, { onDelete: "cascade" }),
});
export const organizationRelations = relations(organization, ({ many }) => ({
members: many(member),
@@ -48,33 +23,8 @@ export const organizationRelations = relations(organization, ({ many }) => ({
projects: many(project),
}));
export const memberRelations = relations(member, ({ one }) => ({
user: one(user, {
fields: [member.userId],
references: [user.id],
}),
organization: one(organization, {
fields: [member.organizationId],
references: [organization.id],
}),
}));
export const invitationRelations = relations(invitation, ({ one }) => ({
organization: one(organization, {
fields: [invitation.organizationId],
references: [organization.id],
}),
inviter: one(user, {
fields: [invitation.inviterId],
references: [user.id],
}),
}));
export const organizationSchema = createSelectSchema(organization);
export type Organization = z.infer<typeof organizationSchema>;
export const organizationMemberSchema = createSelectSchema(member);
export type OrganizationMember = z.infer<typeof organizationMemberSchema>;
export const organizationInvitationSchema = createSelectSchema(invitation);
export type OrganizationInvitation = z.infer<typeof organizationInvitationSchema>;
+35
View File
@@ -0,0 +1,35 @@
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 {relations} from "drizzle-orm";
import {createSelectSchema} from "drizzle-zod";
import {z} from "zod";
export const member = pgTable("member", {
id: uuid("id").defaultRandom().primaryKey(),
organizationId: uuid("organization_id")
.notNull()
.references(() => organization.id, { onDelete: "cascade" }),
userId: uuid("user_id")
.notNull()
.references(() => user.id, { onDelete: "cascade" }),
role: text("role").notNull(),
createdAt: timestamp("created_at").notNull().defaultNow(),
updatedAt: timestamp("updated_at"),
});
export const memberRelations = relations(member, ({ one }) => ({
user: one(user, {
fields: [member.userId],
references: [user.id],
}),
organization: one(organization, {
fields: [member.organizationId],
references: [organization.id],
}),
}));
export const organizationMemberSchema = createSelectSchema(member);
export type OrganizationMember = z.infer<typeof organizationMemberSchema>;
+39
View File
@@ -0,0 +1,39 @@
import {relations} from "drizzle-orm";
import {user} from "@/db/schema/01_user";
import {organization} from "@/db/schema/02_organization";
import {createSelectSchema} from "drizzle-zod";
import {z} from "zod";
import {pgTable, text, timestamp, uuid} from "drizzle-orm/pg-core";
export const invitation = pgTable("invitation", {
id: uuid("id").defaultRandom().primaryKey(),
organizationId: uuid("organization_id")
.notNull()
.references(() => organization.id, { onDelete: "cascade" }),
email: text("email").notNull(),
role: text("role"),
status: text("status").notNull(),
expiresAt: timestamp("expires_at").notNull(),
inviterId: uuid("inviter_id")
.notNull()
.references(() => user.id, { onDelete: "cascade" }),
});
export const invitationRelations = relations(invitation, ({ one }) => ({
organization: one(organization, {
fields: [invitation.organizationId],
references: [organization.id],
}),
inviter: one(user, {
fields: [invitation.inviterId],
references: [user.id],
}),
}));
export const organizationInvitationSchema = createSelectSchema(invitation);
export type OrganizationInvitation = z.infer<typeof organizationInvitationSchema>;
@@ -3,7 +3,7 @@ import { relations } from "drizzle-orm";
import { Organization, organization } from "./02_organization";
import { createSelectSchema } from "drizzle-zod";
import { z } from "zod";
import { Database, database } from "./05_database";
import { Database, database } from "./06_database";
export const project = pgTable("projects", {
id: uuid("id").primaryKey().defaultRandom(),
@@ -1,6 +1,6 @@
import { pgTable, text, boolean, timestamp, uuid, integer, pgEnum, uniqueIndex } from "drizzle-orm/pg-core";
import { Agent, agent } from "./04_agent";
import { Project, project } from "./03_project";
import { Agent, agent } from "./07_agent";
import { Project, project } from "./05_project";
import { relations } from "drizzle-orm";
import { dbmsEnum, statusEnum } from "./types";
import { createSelectSchema } from "drizzle-zod";
+1 -1
View File
@@ -6,7 +6,7 @@ import { Button } from "@/components/ui/button";
import { MoreHorizontal } from "lucide-react";
import { ReloadIcon } from "@radix-ui/react-icons";
import { StatusBadge } from "@/components/wrappers/common/status-badge";
import { Restoration } from "@/db/schema";
import {Restoration} from "@/db/schema/06_database";
export const restoreColumns: ColumnDef<Restoration>[] = [
{
@@ -1,9 +1,11 @@
import { userAction } from "@/safe-actions";
import { z } from "zod";
import { ServerActionResult } from "@/types/action-type";
import { Backup, backup, Restoration, restoration } from "@/db/schema";
import * as drizzleDb from "@/db";
import { db } from "@/db";
import { and, eq } from "drizzle-orm";
import {Backup, Restoration} from "@/db/schema/06_database";
export const deleteBackupAction = userAction
.schema(
@@ -15,14 +17,14 @@ export const deleteBackupAction = userAction
.action(async ({ parsedInput }): Promise<ServerActionResult<Backup>> => {
try {
await db
.delete(backup)
.where(and(eq(backup.id, parsedInput.backupId), eq(backup.databaseId, parsedInput.databaseId)))
.delete(drizzleDb.schemas.backup)
.where(and(eq(drizzleDb.schemas.backup.id, parsedInput.backupId), eq(drizzleDb.schemas.backup.databaseId, parsedInput.databaseId)))
.execute();
const backupExists = await db
.select()
.from(backup)
.where(and(eq(backup.id, parsedInput.backupId), eq(backup.databaseId, parsedInput.databaseId)))
.from(drizzleDb.schemas.backup)
.where(and(eq(drizzleDb.schemas.backup.id, parsedInput.backupId), eq(drizzleDb.schemas.backup.databaseId, parsedInput.databaseId)))
.execute();
if (backupExists.length === 0) {
@@ -68,7 +70,7 @@ export const createRestorationAction = userAction
try {
// Insert new restoration into the database
const restorationData = await db
.insert(restoration)
.insert(drizzleDb.schemas.restoration)
.values({
databaseId: parsedInput.databaseId,
backupId: parsedInput.backupId,
+3 -3
View File
@@ -9,9 +9,10 @@ import { checkMinioAlive, createPublicBucket, saveFileInBucket } from "@/utils/s
//@ts-ignore
import { UploadedObjectInfo } from "minio/src/internal/type";
import { getServerUrl } from "@/utils/get-server-url";
import { setting as drizzleSetting, Setting } from "@/db/schema";
import { db } from "@/db";
import { eq } from "drizzle-orm";
import {Setting} from "@/db/schema/00_setting";
import * as drizzleDb from "@/db";
export const uploadImageAction = userAction.schema(z.instanceof(FormData)).action(async ({ parsedInput: formData, ctx }) => {
const file = formData.get("file") as File;
@@ -21,8 +22,7 @@ export const uploadImageAction = userAction.schema(z.instanceof(FormData)).actio
const arrayBuffer = await file.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
const [settings] = await db.select().from(drizzleSetting).where(eq(drizzleSetting.name, "system")).limit(1);
const [settings] = await db.select().from(drizzleDb.schemas.setting).where(eq(drizzleDb.schemas.setting.name, "system")).limit(1);
if (!settings) {
throw new Error("System settings not found.");
}
+108 -22
View File
@@ -5,19 +5,14 @@ import {env} from "@/env.mjs";
import {nextCookies} from "better-auth/next-js";
import {admin as adminPlugin, openAPI, organization} from "better-auth/plugins";
import {ac, admin, orgAdmin, orgMember, orgOwner, pending, superadmin, user} from "@/lib/auth/permissions";
import * as drizzleDb from "@/db";
import {headers} from "next/headers";
import {count, eq} from "drizzle-orm";
import * as drizzleUser from "@/db/schema/01_user";
import * as drizzleOrganization from "@/db/schema/02_organization";
export const auth = betterAuth({
database: drizzleAdapter(db, {
provider: "pg",
schema: {
...drizzleUser,
...drizzleOrganization,
},
schema: drizzleDb.schemas,
}),
emailAndPassword: {
enabled: true,
@@ -51,7 +46,7 @@ export const auth = betterAuth({
}),
adminPlugin({
adminRoles: ["admin", "superadmin"],
defaultRole: (await db.select({ count: count() }).from(drizzleUser["user"]))[0].count === 0 ? "superadmin" : "pending",
defaultRole: "pending",
ac,
roles: {
admin,
@@ -75,6 +70,67 @@ export const auth = betterAuth({
},
},
},
databaseHooks: {
user: {
create: {
async before(user, context) {
const userCount = (await db.select({count: count()}).from(drizzleDb.schemas.user))[0].count;
const role = userCount === 0 ? "superadmin" : "pending";
return {
data: {
...user,
role,
},
};
},
async after(user, context) {
const userCount = (await db.select({count: count()}).from(drizzleDb.schemas.user))[0].count;
if (userCount === 1) {
const defaultOrgSlug = "default"; // change this if your default org has a different slug
const defaultOrg = await db.query.organization.findFirst({
where: eq(drizzleDb.schemas.organization.slug, defaultOrgSlug),
});
if (defaultOrg) {
await db.insert(drizzleDb.schemas.member).values({
userId: user.id,
organizationId: defaultOrg.id,
role: "orgOwner",
});
} else {
console.warn("Default organization not found. Cannot assign member.");
}
}
},
},
},
session: {
create: {
before: async (session, context) => {
const userId = session.userId;
const memberships = await db.query.member.findMany({
where: eq(drizzleDb.schemas.member.userId, userId),
});
if (!memberships.length) {
// Fail the login attempt explicitly
throw new Error("User is not part of any organization.");
}
const firstOrgId = memberships[0].organizationId;
return {
data: {
activeOrganizationId: firstOrgId,
},
};
},
},
},
},
session: {
additionalFields: {
activeOrganizationId: {
@@ -165,14 +221,15 @@ export const getSession = async () => {
export const revokeSession = async (e: string) => {
try {
const { status } = await auth.api.revokeSession({
const {status} = await auth.api.revokeSession({
body: {
token: e,
},
headers: await headers(),
});
return status;
} catch (e) {}
} catch (e) {
}
};
export const getAccounts = async () => {
@@ -183,7 +240,7 @@ export const getAccounts = async () => {
export const unlinkAccount = async (provider: string, account: string) => {
try {
const { status } = await auth.api.unlinkAccount({
const {status} = await auth.api.unlinkAccount({
body: {
providerId: provider,
accountId: account,
@@ -192,26 +249,53 @@ export const unlinkAccount = async (provider: string, account: string) => {
});
return status;
} catch (e) {}
} catch (e) {
}
};
//
// export const getOrganization = async ({
// organizationId,
// organizationSlug,
// }: {
// organizationId?: string;
// organizationSlug?: string;
// }) => {
// const query = organizationId
// ? {organizationId}
// : {organizationSlug};
//
// console.log(query);
//
// try {
// return await auth.api.getFullOrganization({
// headers: await headers(),
// // query,
// });
// } catch (e) {
// console.error(e);
// return null;
// }
// };
export const getOrganization = async ({
organizationId,
organizationSlug,
}: {
organizationId?: string;
organizationSlug?: string;
}) => {
const query = organizationId
? { organizationId }
: { organizationSlug };
} = {}) => {
const query =
organizationId != null
? { organizationId }
: organizationSlug != null
? { organizationSlug }
: undefined;
console.log(query);
try {
return await auth.api.getFullOrganization({
headers: await headers(),
query,
...(query ? { query } : {}),
});
} catch (e) {
console.error(e);
@@ -224,13 +308,14 @@ export const listOrganizations = async () => {
return await auth.api.listOrganizations({
headers: await headers(),
});
} catch (e) {}
} catch (e) {
}
};
export const getLastOrganizationOrFirst = async (userId: string) => {
try {
const organizations = await db.query.organizationMember.findMany({
where: eq(drizzleOrganization.member.userId, userId),
const organizations = await db.query.organization.findMany({
where: eq(drizzleDb.schemas.member.userId, userId),
});
if (organizations.length > 0) {
@@ -260,7 +345,7 @@ export const createOrganization = async (name: string, slug: string) => {
export const checkSlugOrganization = async (slug: string) => {
try {
const { status } = await auth.api.checkOrganizationSlug({
const {status} = await auth.api.checkOrganizationSlug({
headers: await headers(),
body: {
slug,
@@ -278,6 +363,7 @@ export const getActiveMember = async () => {
const member = await auth.api.getActiveMember({
headers: await headers(),
});
console.log(member);
return member;
} catch (e) {
+3 -3
View File
@@ -1,9 +1,9 @@
"use server";
import { db } from "@/db";
import { setting as drizzleSetting } from "@/db/schema";
import { eq } from "drizzle-orm";
import nodemailer from "nodemailer";
import * as drizzleDb from "@/db";
type Payload = {
to: string;
@@ -33,8 +33,8 @@ type EmailMassProps = {
export const sendEmail = async (data: Payload) => {
const settings = await db
.select()
.from(drizzleSetting)
.where(eq(drizzleSetting.name, "system"))
.from(drizzleDb.schemas.setting)
.where(eq(drizzleDb.schemas.setting.name, "system"))
.then((res) => res[0]);
if (!settings) {
+7 -6
View File
@@ -1,7 +1,8 @@
import { env } from "@/env.mjs";
import { db } from "@/db";
import { setting, organization } from "@/db/schema";
import { eq } from "drizzle-orm";
import * as drizzleDb from "@/db";
export function init() {
consoleAscii();
@@ -32,14 +33,14 @@ async function createSettingsIfNotExist() {
S3BucketName: env.S3_BUCKET_NAME ?? null,
};
const [existing] = await db.select().from(setting).where(eq(setting.name, "system")).limit(1);
const [existing] = await db.select().from(drizzleDb.schemas.setting).where(eq(drizzleDb.schemas.setting.name, "system")).limit(1);
if (!existing) {
console.log("====Init Setting : Create ====");
await db.insert(setting).values(configSettings);
await db.insert(drizzleDb.schemas.setting).values(configSettings);
} else {
console.log("====Init Setting : Update ====");
await db.update(setting).set(configSettings).where(eq(setting.name, "system"));
await db.update(drizzleDb.schemas.setting).set(configSettings).where(eq(drizzleDb.schemas.setting.name, "system"));
}
}
@@ -50,11 +51,11 @@ async function createDefaultOrganization() {
createdAt: new Date(),
};
const [existing] = await db.select().from(organization).where(eq(organization.slug, "default")).limit(1);
const [existing] = await db.select().from(drizzleDb.schemas.organization).where(eq(drizzleDb.schemas.organization.slug, "default")).limit(1);
if (!existing) {
console.log("==== Creating default Organization... ====\n");
await db.insert(organization).values(defaultOrganizationConf);
await db.insert(drizzleDb.schemas.organization).values(defaultOrganizationConf);
}
}
+25 -21
View File
@@ -1,9 +1,9 @@
import * as Minio from "minio";
import { env } from "@/env.mjs";
import {env} from "@/env.mjs";
import internal from "node:stream";
import { db } from "@/db";
import { setting as drizzleSetting } from "@/db/schema";
import { eq } from "drizzle-orm";
import {db} from "@/db";
import * as drizzleDb from "@/db";
import {eq} from "drizzle-orm";
// const settings = await prisma.settings.findUnique({
// where:{
@@ -40,8 +40,8 @@ import { eq } from "drizzle-orm";
async function getS3Client() {
const settings = await db
.select()
.from(drizzleSetting)
.where(eq(drizzleSetting.name, "system"))
.from(drizzleDb.schemas.setting)
.where(eq(drizzleDb.schemas.setting.name, "system"))
.then((res) => res[0]);
if (!settings) {
@@ -57,13 +57,13 @@ async function getS3Client() {
const s3Client =
env.NODE_ENV === "production"
? new Minio.Client({
...baseConfig,
})
...baseConfig,
})
: new Minio.Client({
...baseConfig,
port: Number(env.S3_PORT ?? 0),
useSSL: env.S3_USE_SSL === "true",
});
...baseConfig,
port: Number(env.S3_PORT ?? 0),
useSSL: env.S3_USE_SSL === "true",
});
return s3Client;
}
@@ -75,10 +75,10 @@ export async function checkMinioAlive() {
// Try to list buckets to check connectivity
const buckets = await s3Client.listBuckets();
console.log("MinIO is up and running. Buckets:", buckets);
return { message: true };
return {message: true};
} catch (error) {
console.error("Error connecting to MinIO:", error);
return { error: error };
return {error: error};
}
}
@@ -98,7 +98,11 @@ export async function createBucketIfNotExists(bucketName: string) {
* @param fileName name of the file
* @param file file to save
*/
export async function saveFileInBucket({ bucketName, fileName, file }: { bucketName: string; fileName: string; file: Buffer | internal.Readable }) {
export async function saveFileInBucket({bucketName, fileName, file}: {
bucketName: string;
fileName: string;
file: Buffer | internal.Readable
}) {
// Check if Minio is Alive
await checkMinioAlive();
// Create bucket if it doesn't exist
@@ -128,7 +132,7 @@ export async function saveFileInBucket({ bucketName, fileName, file }: { bucketN
* @param fileName name of the file
* @returns true if file exists, false if not
*/
export async function checkFileExistsInBucket({ bucketName, fileName }: { bucketName: string; fileName: string }) {
export async function checkFileExistsInBucket({bucketName, fileName}: { bucketName: string; fileName: string }) {
const s3Client = await getS3Client();
try {
@@ -145,10 +149,10 @@ export async function checkFileExistsInBucket({ bucketName, fileName }: { bucket
* @returns promise with array of presigned urls
*/
export async function createPresignedUrlToUpload({
bucketName,
fileName,
expiry = 60 * 60, // 1 hour
}: {
bucketName,
fileName,
expiry = 60 * 60, // 1 hour
}: {
bucketName: string;
fileName: string;
expiry?: number;
@@ -161,7 +165,7 @@ export async function createPresignedUrlToUpload({
}
// Function to create a bucket and make it public
export async function createPublicBucket({ bucketName }: { bucketName: string }) {
export async function createPublicBucket({bucketName}: { bucketName: string }) {
const s3Client = await getS3Client();
try {