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
@@ -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>