mirror of
https://github.com/Portabase/portabase.git
synced 2026-07-14 11:16:13 +02:00
Finish migration prisma to drizzle.
This commit is contained in:
@@ -1,16 +1,21 @@
|
||||
import { PageParams } from "@/types/next";
|
||||
import * as drizzleDb from "@/db";
|
||||
|
||||
import { Page, PageContent, PageHeader, PageTitle } from "@/features/layout/page";
|
||||
import { AdminTabs } from "@/components/wrappers/dashboard/admin/admin-tabs";
|
||||
import { currentUser } from "@/lib/auth/current-user";
|
||||
import { db } from "@/db";
|
||||
import {eq, isNotNull, isNull} from "drizzle-orm";
|
||||
|
||||
|
||||
export default async function RoutePage(props: PageParams<{}>) {
|
||||
const user = await currentUser()!;
|
||||
const user = await currentUser();
|
||||
|
||||
const users = await db.query.user.findMany({
|
||||
where: (fields, { isNull, not }) => not(isNull(fields.deletedAt)),
|
||||
where: (fields) => isNull(fields.deletedAt)
|
||||
});
|
||||
// const users = await db.query.user.findMany();
|
||||
console.log("users",users);
|
||||
|
||||
const settings = await db.query.setting.findFirst({
|
||||
where: (fields, { eq }) => eq(fields.name, "system"),
|
||||
@@ -22,7 +27,7 @@ export default async function RoutePage(props: PageParams<{}>) {
|
||||
<PageTitle>Administration Panel</PageTitle>
|
||||
</PageHeader>
|
||||
<PageContent className="mt-10">
|
||||
<AdminTabs settings={settings!} currentUser={user} users={users} />
|
||||
<AdminTabs settings={settings!} users={users} />
|
||||
</PageContent>
|
||||
</Page>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import Link from "next/link";
|
||||
import {GearIcon} from "@radix-ui/react-icons";
|
||||
import {prisma} from "@/prisma";
|
||||
import {PageParams} from "@/types/next";
|
||||
import {Page, PageContent, PageDescription, PageTitle} from "@/features/layout/page";
|
||||
import {formatDateLastContact} from "@/utils/date-formatting";
|
||||
@@ -9,38 +8,55 @@ import {Card, CardContent, CardHeader} from "@/components/ui/card";
|
||||
import {CardsWithPagination} from "@/components/wrappers/common/cards-with-pagination";
|
||||
import {DatabaseCard} from "@/components/wrappers/dashboard/projects/ProjectCard/ProjectDatabaseCard";
|
||||
import {AgentCardKey} from "@/components/wrappers/dashboard/agent/AgentCardKey/AgentCardKey";
|
||||
import { db } from "@/db";
|
||||
import * as drizzleDb from "@/db";
|
||||
import {and, eq} from "drizzle-orm";
|
||||
import {notFound} from "next/navigation";
|
||||
|
||||
|
||||
export default async function RoutePage(props: PageParams<{ agentId: string }>) {
|
||||
|
||||
const {agentId} = await props.params
|
||||
|
||||
const agent = await prisma.agent.findUnique({
|
||||
where: {
|
||||
id: agentId,
|
||||
},
|
||||
include: {
|
||||
databases: {}
|
||||
const agent = await db.query.agent.findFirst({
|
||||
where: eq(drizzleDb.schemas.agent.id, agentId),
|
||||
with: {
|
||||
databases: true
|
||||
}
|
||||
})
|
||||
//
|
||||
console.log(agent)
|
||||
|
||||
const databaseId = 'db-123';
|
||||
|
||||
const totalBackups = await prisma.backup.count({
|
||||
where: {
|
||||
databaseId: databaseId,
|
||||
},
|
||||
});
|
||||
|
||||
const successfulBackups = await prisma.backup.count({
|
||||
where: {
|
||||
databaseId: databaseId,
|
||||
status: 'success',
|
||||
},
|
||||
});
|
||||
|
||||
const successRate = totalBackups > 0 ? (successfulBackups / totalBackups) * 100 : null
|
||||
|
||||
if (!agent) {
|
||||
notFound()
|
||||
}
|
||||
//
|
||||
// const databaseId = 'db-123';
|
||||
//
|
||||
// const totalBackupsResult = await db
|
||||
// .select({ count: drizzleDb.schemas.backup.id })
|
||||
// .from(drizzleDb.schemas.backup)
|
||||
// .where(eq(drizzleDb.schemas.backup.databaseId, databaseId))
|
||||
// .execute();
|
||||
//
|
||||
// const totalBackups = totalBackupsResult.length;
|
||||
//
|
||||
// const successfulBackupsResult = await db
|
||||
// .select({ count: drizzleDb.schemas.backup.id })
|
||||
// .from(drizzleDb.schemas.backup)
|
||||
// .where(
|
||||
// and(
|
||||
// eq(drizzleDb.schemas.backup.databaseId, databaseId),
|
||||
// eq(drizzleDb.schemas.backup.status, "success")
|
||||
// )
|
||||
// )
|
||||
// .execute();
|
||||
//
|
||||
//
|
||||
// const successfulBackups = successfulBackupsResult.length;
|
||||
//
|
||||
// const successRate =
|
||||
// totalBackups > 0 ? (successfulBackups / totalBackups) * 100 : null;
|
||||
|
||||
return (
|
||||
<Page>
|
||||
@@ -73,7 +89,7 @@ export default async function RoutePage(props: PageParams<{ agentId: string }>)
|
||||
Success rate
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{successRate ?? "Unavailable for now."}
|
||||
{/*{successRate ?? "Unavailable for now."}*/}
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card className="w-full sm:w-auto flex-1">
|
||||
|
||||
@@ -6,7 +6,6 @@ import Link from "next/link";
|
||||
import { Page, PageActions, PageContent, PageHeader, PageTitle } from "@/features/layout/page";
|
||||
import { notFound } from "next/navigation";
|
||||
import { db } from "@/db";
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export default async function RoutePage(props: PageParams<{}>) {
|
||||
const agents = await db.query.agent.findMany();
|
||||
|
||||
@@ -22,7 +22,7 @@ export default async function RoutePage(props: PageParams<{ projectId: string }>
|
||||
notFound();
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
const org = await db.query.organization.findFirst({
|
||||
where: eq(drizzleDb.schemas.organization.slug, "default"),
|
||||
});
|
||||
|
||||
@@ -46,7 +46,7 @@ export default async function RoutePage(props: PageParams<{
|
||||
<div className="justify-between gap-2 sm:flex">
|
||||
<PageTitle className="flex items-center">
|
||||
{proj.name}
|
||||
<Link className={buttonVariants({ variant: "outline" })} href={`/dashboard/${organization.slug}/projects/${proj.id}/edit`}>
|
||||
<Link className={buttonVariants({ variant: "outline" })} href={`/dashboard/projects/${proj.id}/edit`}>
|
||||
<GearIcon className="w-7 h-7" />
|
||||
</Link>
|
||||
</PageTitle>
|
||||
|
||||
@@ -1,26 +1,19 @@
|
||||
import {PageParams} from "@/types/next";
|
||||
import {Page, PageContent, PageHeader, PageTitle} from "@/features/layout/page";
|
||||
import {getCurrentOrganizationSlug} from "@/features/dashboard/organization-cookie";
|
||||
import {prisma} from "@/prisma";
|
||||
import {requiredCurrentUser} from "@/auth/current-user";
|
||||
import {notFound} from "next/navigation";
|
||||
import {OrganizationForm} from "@/components/wrappers/dashboard/organization/OrganizationForm/OrganizationForm";
|
||||
import {getOrganization} from "@/lib/auth/auth";
|
||||
|
||||
|
||||
export default async function RoutePage(props: PageParams<{
|
||||
slug: string;
|
||||
|
||||
}>) {
|
||||
|
||||
const {slug: organizationSlug} = await props.params;
|
||||
|
||||
const organization = await getOrganization({organizationSlug});
|
||||
const organization = await getOrganization({});
|
||||
|
||||
if (!organization) {
|
||||
notFound()
|
||||
notFound();
|
||||
}
|
||||
|
||||
|
||||
return(
|
||||
<Page>
|
||||
<PageHeader>
|
||||
|
||||
@@ -3,9 +3,6 @@ import {Page, PageActions, PageContent, PageDescription, PageHeader, PageTitle}
|
||||
import {currentUser} from "@/lib/auth/current-user";
|
||||
import {getOrganization} from "@/lib/auth/auth";
|
||||
import {notFound} from "next/navigation";
|
||||
import {requiredCurrentUser} from "@/auth/current-user";
|
||||
import {SettingsTabs} from "@/components/wrappers/dashboard/settings/SettingsTabs/SettingsTabs";
|
||||
import {getCurrentOrganizationSlug} from "@/features/dashboard/organization-cookie";
|
||||
import {
|
||||
DeleteOrganizationButton
|
||||
} from "@/components/wrappers/dashboard/organization/DeleteOrganization/DeleteOrganizationButton";
|
||||
@@ -13,50 +10,13 @@ import {EditButtonSettings} from "@/components/wrappers/dashboard/settings/EditB
|
||||
|
||||
|
||||
export default async function RoutePage(props: PageParams<{ slug: string }>) {
|
||||
const {slug: organizationSlug} = await props.params;
|
||||
|
||||
const organization = await getOrganization({});
|
||||
const user = await currentUser();
|
||||
|
||||
const organization = await getOrganization({organizationSlug});
|
||||
|
||||
if (!organization || organization?.slug !== organizationSlug) {
|
||||
if (!organization) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// const organization = await prisma.organization.findUnique({
|
||||
// where: {
|
||||
// slug: currentOrganizationSlug,
|
||||
// },
|
||||
// include: {
|
||||
// users:{
|
||||
// include:{
|
||||
// user: {}
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
//
|
||||
// const currentOrganizationUser = await prisma.userOrganization.findFirst({
|
||||
// where:{
|
||||
// userId: user.id,
|
||||
// organization:{
|
||||
// slug: currentOrganizationSlug != "" ? currentOrganizationSlug : "default",
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// if (currentOrganizationUser.role != "admin") {
|
||||
// notFound()
|
||||
// }
|
||||
//
|
||||
//
|
||||
// const settings = await prisma.settings.findUnique({
|
||||
// where: {
|
||||
// name: "system"
|
||||
// }
|
||||
// })
|
||||
|
||||
return (
|
||||
<Page>
|
||||
<PageHeader>
|
||||
@@ -76,7 +36,7 @@ export default async function RoutePage(props: PageParams<{ slug: string }>) {
|
||||
Manage your organization settings.
|
||||
</PageDescription>
|
||||
<PageContent>
|
||||
{/*<SettingsTabs settings={settings} currentUser={user} users={organization.users}/>*/}
|
||||
{/* TODO add the list of organisation members (add, remove, edit) */}
|
||||
</PageContent>
|
||||
</Page>
|
||||
)
|
||||
|
||||
@@ -8,17 +8,17 @@ import { notFound } from "next/navigation";
|
||||
import { db } from "@/db";
|
||||
import { asc, count, eq, inArray } from "drizzle-orm";
|
||||
import * as drizzleDb from "@/db";
|
||||
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({});
|
||||
|
||||
const currentOrganizationSlug = await getCurrentOrganizationSlug();
|
||||
if (currentOrganizationSlug !== organizationSlug) {
|
||||
if (!organization) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
const org = await db.query.organization.findFirst({
|
||||
where: eq(drizzleDb.schemas.organization.slug, currentOrganizationSlug),
|
||||
where: eq(drizzleDb.schemas.organization.slug, organization.slug),
|
||||
});
|
||||
|
||||
if (!org) notFound();
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
import {NextResponse} from "next/server";
|
||||
import {prisma} from "@/prisma";
|
||||
import {isUuidv4} from "@/utils/verify-uuid";
|
||||
import {uploadLocalPrivate} from "@/features/upload/private/upload.action";
|
||||
import {v4 as uuidv4} from "uuid";
|
||||
import {Backup, Database} from "@prisma/client";
|
||||
import {eventEmitter} from "../../../events/route";
|
||||
import {db} from "@/db";
|
||||
import {Backup} from "@/db/schema/06_database";
|
||||
import {and, eq} from "drizzle-orm";
|
||||
import * as drizzleDb from "@/db";
|
||||
|
||||
export async function POST(
|
||||
request: Request,
|
||||
{ params }: { params: Promise<{ agentId: string }> }
|
||||
{params}: { params: Promise<{ agentId: string }> }
|
||||
) {
|
||||
try {
|
||||
const contentType = request.headers.get("Content-Type");
|
||||
|
||||
if (!contentType || !contentType.includes("multipart/form-data")) {
|
||||
return NextResponse.json(
|
||||
{ error: "Unsupported or missing Content-Type" },
|
||||
{ status: 400 }
|
||||
{error: "Unsupported or missing Content-Type"},
|
||||
{status: 400}
|
||||
);
|
||||
}
|
||||
eventEmitter.emit('modification', { update: true });
|
||||
eventEmitter.emit('modification', {update: true});
|
||||
|
||||
const agentId = (await params).agentId;
|
||||
const formData = await request.formData();
|
||||
@@ -28,61 +30,64 @@ export async function POST(
|
||||
|
||||
if (!generatedId || !isUuidv4(generatedId)) {
|
||||
return NextResponse.json(
|
||||
{ error: "generatedId is not a valid UUID" },
|
||||
{ status: 400 }
|
||||
{error: "generatedId is not a valid UUID"},
|
||||
{status: 400}
|
||||
);
|
||||
}
|
||||
|
||||
const agent = await prisma.agent.findFirst({
|
||||
where: { id: agentId },
|
||||
const agent = await db.query.agent.findFirst({
|
||||
where: eq(drizzleDb.schemas.agent.id, agentId),
|
||||
});
|
||||
|
||||
if (!agent) {
|
||||
return NextResponse.json(
|
||||
{ error: "Agent not found" },
|
||||
{ status: 404 }
|
||||
{error: "Agent not found"},
|
||||
{status: 404}
|
||||
);
|
||||
}
|
||||
|
||||
const database = await prisma.database.findFirst({
|
||||
where: { generatedId },
|
||||
const database = await db.query.database.findFirst({
|
||||
where: eq(drizzleDb.schemas.database.agentDatabaseId, generatedId),
|
||||
});
|
||||
|
||||
if (!database) {
|
||||
return NextResponse.json(
|
||||
{ error: "Database associated with generatedId not found" },
|
||||
{ status: 404 }
|
||||
{error: "Database associated with generatedId not found"},
|
||||
{status: 404}
|
||||
);
|
||||
}
|
||||
|
||||
let backup: Backup | null = null;
|
||||
let backup: Backup | null | undefined = null;
|
||||
|
||||
if (method === "automatic") {
|
||||
backup = await prisma.backup.create({
|
||||
data: {
|
||||
status: "ongoing",
|
||||
[backup] = await db
|
||||
.insert(drizzleDb.schemas.backup)
|
||||
.values({
|
||||
status: 'ongoing',
|
||||
databaseId: database.id,
|
||||
},
|
||||
});
|
||||
})
|
||||
.returning();
|
||||
|
||||
|
||||
if (!backup) {
|
||||
return NextResponse.json(
|
||||
{ error: "Unable to create an automatic backup" },
|
||||
{ status: 500 }
|
||||
{error: "Unable to create an automatic backup"},
|
||||
{status: 500}
|
||||
);
|
||||
}
|
||||
} else {
|
||||
backup = await prisma.backup.findFirst({
|
||||
where: {
|
||||
status: "ongoing",
|
||||
databaseId: database.id,
|
||||
},
|
||||
backup = await db.query.backup.findFirst({
|
||||
where: and(
|
||||
eq(drizzleDb.schemas.backup.status, 'ongoing'),
|
||||
eq(drizzleDb.schemas.backup.databaseId, database.id),
|
||||
),
|
||||
});
|
||||
|
||||
|
||||
if (!backup) {
|
||||
return NextResponse.json(
|
||||
{ error: "Unable to find the corresponding backup" },
|
||||
{ status: 404 }
|
||||
{error: "Unable to find the corresponding backup"},
|
||||
{status: 404}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -94,8 +99,8 @@ export async function POST(
|
||||
|
||||
if (!file) {
|
||||
return NextResponse.json(
|
||||
{ error: "File is required for successful backup" },
|
||||
{ status: 400 }
|
||||
{error: "File is required for successful backup"},
|
||||
{status: 400}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -103,49 +108,52 @@ export async function POST(
|
||||
const fileName = `${uuid}.dump`;
|
||||
const buffer = Buffer.from(await file.arrayBuffer());
|
||||
|
||||
const { success, message, filePath } = await uploadLocalPrivate(fileName, buffer);
|
||||
const {success, message, filePath} = await uploadLocalPrivate(fileName, buffer);
|
||||
|
||||
if (!success) {
|
||||
return NextResponse.json(
|
||||
{ error: message },
|
||||
{ status: 500 }
|
||||
{error: message},
|
||||
{status: 500}
|
||||
);
|
||||
}
|
||||
|
||||
await prisma.backup.update({
|
||||
where: { id: backup.id },
|
||||
data: {
|
||||
await db
|
||||
.update(drizzleDb.schemas.backup)
|
||||
.set({
|
||||
file: fileName,
|
||||
status: "success",
|
||||
},
|
||||
});
|
||||
eventEmitter.emit('modification', { update: true });
|
||||
status: 'success',
|
||||
})
|
||||
.where(eq(drizzleDb.schemas.backup.id, backup.id));
|
||||
|
||||
eventEmitter.emit('modification', {update: true});
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
message: "Backup successfully uploaded",
|
||||
},
|
||||
{ status: 200 }
|
||||
{status: 200}
|
||||
);
|
||||
} else {
|
||||
await prisma.backup.update({
|
||||
where: { id: backup.id },
|
||||
data: { status: "failed" },
|
||||
});
|
||||
eventEmitter.emit('modification', { update: true });
|
||||
|
||||
await db
|
||||
.update(drizzleDb.schemas.backup)
|
||||
.set({status: 'failed'})
|
||||
.where(eq(drizzleDb.schemas.backup.id, backup.id));
|
||||
|
||||
eventEmitter.emit('modification', {update: true});
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
message: "Backup successfully updated with status failed",
|
||||
},
|
||||
{ status: 200 }
|
||||
{status: 200}
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error in POST handler:", error);
|
||||
return NextResponse.json(
|
||||
{ error: "Internal server error" },
|
||||
{ status: 500 }
|
||||
{error: "Internal server error"},
|
||||
{status: 500}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,17 @@
|
||||
import {NextResponse} from "next/server";
|
||||
import {isUuidv4} from "@/utils/verify-uuid";
|
||||
import {prisma} from "@/prisma";
|
||||
import {eventEmitter} from "../../../events/route";
|
||||
|
||||
import * as drizzleDb from "@/db";
|
||||
import {db} from "@/db";
|
||||
import {and, eq} from "drizzle-orm";
|
||||
|
||||
|
||||
export type BodyResultRestore = {
|
||||
generatedId: string
|
||||
status: string
|
||||
}
|
||||
type RestorationStatus = 'waiting' | 'ongoing' | 'failed' | 'success';
|
||||
|
||||
|
||||
|
||||
export async function POST(
|
||||
@@ -17,7 +20,7 @@ export async function POST(
|
||||
) {
|
||||
|
||||
try {
|
||||
eventEmitter.emit('modification', { update: true });
|
||||
eventEmitter.emit('modification', {update: true});
|
||||
|
||||
const agentId = (await params).agentId
|
||||
const body: BodyResultRestore = await request.json();
|
||||
@@ -33,53 +36,44 @@ export async function POST(
|
||||
);
|
||||
}
|
||||
|
||||
const agent = await prisma.agent.findFirst({
|
||||
where: {
|
||||
id: agentId
|
||||
}
|
||||
const agent = await db.query.agent.findFirst({
|
||||
where: eq(drizzleDb.schemas.agent.id, agentId)
|
||||
})
|
||||
if (!agent) {
|
||||
return NextResponse.json({error: "Agent not found"}, {status: 404})
|
||||
}
|
||||
|
||||
const database = await prisma.database.findFirst({
|
||||
where: {
|
||||
generatedId: body.generatedId
|
||||
}
|
||||
const database = await db.query.database.findFirst({
|
||||
where: eq(drizzleDb.schemas.database.agentDatabaseId, body.generatedId)
|
||||
|
||||
})
|
||||
|
||||
if (!database) {
|
||||
return NextResponse.json({error: "Database associated with generatedId provided not found"}, {status: 404})
|
||||
}
|
||||
|
||||
const restoration = await prisma.restoration.findFirst({
|
||||
where: {
|
||||
status : "ongoing",
|
||||
databaseId: database.id
|
||||
}
|
||||
const restoration = await db.query.restoration.findFirst({
|
||||
where: and(eq(drizzleDb.schemas.restoration.status, "ongoing"), eq(drizzleDb.schemas.restoration.databaseId, database.id),)
|
||||
})
|
||||
|
||||
if (!restoration) {
|
||||
return NextResponse.json({error: "Unable to fin the corresponding restoration"}, {status: 404})
|
||||
}
|
||||
|
||||
await prisma.restoration.update({
|
||||
where:{
|
||||
id : restoration.id
|
||||
},
|
||||
data: {
|
||||
status: body.status
|
||||
}
|
||||
})
|
||||
await db
|
||||
.update(drizzleDb.schemas.restoration)
|
||||
.set({ status: body.status as RestorationStatus })
|
||||
.where(eq(drizzleDb.schemas.restoration.id, restoration.id));
|
||||
|
||||
const response = {
|
||||
message: true,
|
||||
details: "Restoration successfully updated"
|
||||
}
|
||||
|
||||
eventEmitter.emit('modification', { update: true });
|
||||
eventEmitter.emit('modification', {update: true});
|
||||
|
||||
|
||||
return Response.json(response , {status: 200})
|
||||
return Response.json(response, {status: 200})
|
||||
|
||||
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import {Agent, Database} from "@prisma/client";
|
||||
import {prisma} from "@/prisma";
|
||||
import {NextResponse} from "next/server";
|
||||
import {Body} from "./route";
|
||||
import {isUuidv4} from "@/utils/verify-uuid";
|
||||
import {getFileUrlPresignedLocal} from "@/features/upload/private/upload.action";
|
||||
|
||||
import {Agent} from "@/db/schema/07_agent";
|
||||
import {Database} from "@/db/schema/06_database";
|
||||
import * as drizzleDb from "@/db";
|
||||
import {db as dbClient} from "@/db";
|
||||
import {and, eq} from "drizzle-orm";
|
||||
|
||||
export async function handleDatabases(body: Body, agent: Agent, lastContact: Date) {
|
||||
const databasesResponse = [];
|
||||
|
||||
const formatDatabase = (database: Database, backupAction: boolean, restoreAction: boolean, UrlBackup: string) => ({
|
||||
generatedId: database.generatedId,
|
||||
generatedId: database.agentDatabaseId,
|
||||
dbms: database.dbms,
|
||||
data: {
|
||||
backup: {
|
||||
@@ -25,15 +27,14 @@ export async function handleDatabases(body: Body, agent: Agent, lastContact: Dat
|
||||
});
|
||||
|
||||
for (const db of body.databases) {
|
||||
const existingDatabase = await prisma.database.findFirst({
|
||||
where: {
|
||||
generatedId: db.generatedId,
|
||||
},
|
||||
|
||||
const existingDatabase = await dbClient.query.database.findFirst({
|
||||
where: eq(drizzleDb.schemas.database.agentDatabaseId, db.generatedId)
|
||||
});
|
||||
|
||||
let backupAction: boolean = false
|
||||
let restoreAction: boolean = false
|
||||
let UrlBackup: string = null
|
||||
let UrlBackup: string = ""
|
||||
|
||||
if (!existingDatabase) {
|
||||
if (!isUuidv4(db.generatedId)) {
|
||||
@@ -42,74 +43,69 @@ export async function handleDatabases(body: Body, agent: Agent, lastContact: Dat
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
const databaseCreated = await prisma.database.create({
|
||||
data: {
|
||||
|
||||
const [databaseCreated] = await dbClient
|
||||
.insert(drizzleDb.schemas.database)
|
||||
.values({
|
||||
agentId: agent.id,
|
||||
name: db.name,
|
||||
dbms: db.dbms,
|
||||
generatedId: db.generatedId,
|
||||
agentDatabaseId: db.generatedId,
|
||||
lastContact: lastContact,
|
||||
},
|
||||
});
|
||||
})
|
||||
.returning();
|
||||
|
||||
if (databaseCreated) {
|
||||
databasesResponse.push(formatDatabase(databaseCreated, backupAction,restoreAction, UrlBackup));
|
||||
}
|
||||
} else {
|
||||
const databaseUpdated = await prisma.database.update({
|
||||
where: {
|
||||
id: existingDatabase.id,
|
||||
},
|
||||
data: {
|
||||
lastContact: lastContact,
|
||||
},
|
||||
});
|
||||
|
||||
const backup = await prisma.backup.findFirst({
|
||||
where: {
|
||||
databaseId: databaseUpdated.id,
|
||||
status: "waiting"
|
||||
}
|
||||
|
||||
|
||||
const [databaseUpdated] = await dbClient
|
||||
.update(drizzleDb.schemas.database)
|
||||
.set({ lastContact: lastContact })
|
||||
.where(eq(drizzleDb.schemas.database.id, existingDatabase.id))
|
||||
.returning();
|
||||
|
||||
|
||||
|
||||
const backup = await dbClient.query.backup.findFirst({
|
||||
where: and(eq(drizzleDb.schemas.backup.databaseId, databaseUpdated.id), eq(drizzleDb.schemas.backup.status,"waiting"))
|
||||
})
|
||||
|
||||
const restoration = await prisma.restoration.findFirst({
|
||||
where:{
|
||||
databaseId: databaseUpdated.id,
|
||||
status: "waiting"
|
||||
}
|
||||
|
||||
const restoration = await dbClient.query.restoration.findFirst({
|
||||
where: and(eq(drizzleDb.schemas.restoration.databaseId, databaseUpdated.id), eq(drizzleDb.schemas.restoration.status,"waiting"))
|
||||
})
|
||||
|
||||
|
||||
if(backup){
|
||||
backupAction = true
|
||||
await prisma.backup.update({
|
||||
where:{
|
||||
id: backup.id
|
||||
},
|
||||
data: {
|
||||
status: "ongoing"
|
||||
}
|
||||
})
|
||||
|
||||
await dbClient
|
||||
.update(drizzleDb.schemas.backup)
|
||||
.set({ status: "ongoing" })
|
||||
.where(eq(drizzleDb.schemas.backup.id, backup.id));
|
||||
}
|
||||
|
||||
if(restoration){
|
||||
restoreAction = true
|
||||
|
||||
const backupToRestore = await prisma.backup.findFirst({
|
||||
where:{
|
||||
id: restoration.backupId
|
||||
}
|
||||
})
|
||||
const fileName = backupToRestore.file
|
||||
UrlBackup = await getFileUrlPresignedLocal(fileName)
|
||||
await prisma.restoration.update({
|
||||
where: {
|
||||
id: restoration.id
|
||||
},
|
||||
data:{
|
||||
status: "ongoing"
|
||||
}
|
||||
|
||||
const backupToRestore = await dbClient.query.backup.findFirst({
|
||||
where: eq(drizzleDb.schemas.backup.id, restoration.backupId),
|
||||
})
|
||||
|
||||
|
||||
const fileName = backupToRestore?.file
|
||||
UrlBackup = await getFileUrlPresignedLocal(fileName ?? "")
|
||||
|
||||
await dbClient
|
||||
.update(drizzleDb.schemas.restoration)
|
||||
.set({ status: "ongoing" })
|
||||
.where(eq(drizzleDb.schemas.restoration.id, restoration.id));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import {prisma} from "@/prisma";
|
||||
import {NextResponse} from "next/server";
|
||||
import {Dbms} from "@prisma/client";
|
||||
import {getFileUrlPresignedLocal} from "@/features/upload/private/upload.action";
|
||||
import {handleDatabases} from "./helpers";
|
||||
import {eventEmitter} from "../../../events/route";
|
||||
|
||||
import * as drizzleDb from "@/db";
|
||||
import {db} from "@/db";
|
||||
import {EDbmsSchema} from "@/db/schema/types";
|
||||
import {eq} from "drizzle-orm";
|
||||
|
||||
export type databaseAgent = {
|
||||
name: string,
|
||||
dbms: Dbms,
|
||||
dbms: EDbmsSchema,
|
||||
generatedId: string
|
||||
}
|
||||
|
||||
@@ -33,25 +34,23 @@ export async function POST(
|
||||
const body: Body = await request.json();
|
||||
const lastContact = new Date();
|
||||
|
||||
const agent = await prisma.agent.findFirst({
|
||||
where: {
|
||||
id: agentId
|
||||
}
|
||||
|
||||
const agent = await db.query.agent.findFirst({
|
||||
where: eq(drizzleDb.schemas.agent.id, agentId),
|
||||
})
|
||||
|
||||
if (!agent) {
|
||||
return NextResponse.json({error: "Agent not found"}, {status: 404})
|
||||
}
|
||||
const databasesResponse = await handleDatabases(body, agent, lastContact)
|
||||
await prisma.agent.update({
|
||||
where:{
|
||||
id: agentId,
|
||||
},
|
||||
data:{
|
||||
lastContact: lastContact,
|
||||
}
|
||||
})
|
||||
|
||||
eventEmitter.emit('modification', { update: true });
|
||||
|
||||
await db
|
||||
.update(drizzleDb.schemas.agent)
|
||||
.set({ lastContact: lastContact })
|
||||
.where(eq(drizzleDb.schemas.agent.id, agentId));
|
||||
|
||||
eventEmitter.emit('modification', {update: true});
|
||||
|
||||
const response = {
|
||||
agent: {
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
"use client";
|
||||
|
||||
import { ColumnDef } from "@tanstack/react-table";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { User } from "@prisma/client";
|
||||
import { updateUserAction } from "@/components/wrappers/dashboard/profile/UserForm/user-form.action";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { toast } from "sonner";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import { Trash2 } from "lucide-react";
|
||||
import { deleteUserAction } from "@/components/wrappers/dashboard/profile/ButtonDeleteAccount/delete-account.action";
|
||||
import { ButtonWithLoading } from "@/components/wrappers/common/button/button-with-loading";
|
||||
"use client"
|
||||
import {ColumnDef} from "@tanstack/react-table";
|
||||
import {Badge} from "@/components/ui/badge";
|
||||
import {updateUserAction} from "@/components/wrappers/dashboard/profile/UserForm/user-form.action";
|
||||
import {useMutation} from "@tanstack/react-query";
|
||||
import {toast} from "sonner";
|
||||
import {useRouter} from "next/navigation";
|
||||
import {useState} from "react";
|
||||
import {Trash2} from "lucide-react";
|
||||
import {deleteUserAction} from "@/components/wrappers/dashboard/profile/ButtonDeleteAccount/delete-account.action";
|
||||
import {ButtonWithLoading} from "@/components/wrappers/common/button/button-with-loading";
|
||||
import {User} from "@/db/schema/01_user";
|
||||
import {useSession} from "@/lib/auth/auth-client";
|
||||
|
||||
export const usersColumnsAdmin: ColumnDef<User>[] = [
|
||||
{
|
||||
accessorKey: "role",
|
||||
header: "Role",
|
||||
cell: ({ row }) => {
|
||||
cell: ({row}) => {
|
||||
const [role, setRole] = useState<string>(row.getValue("role"));
|
||||
|
||||
const updateMutation = useMutation({
|
||||
mutationFn: () => updateUserAction({ id: row.original.id, data: { role: role } }),
|
||||
mutationFn: () => updateUserAction({id: row.original.id, data: {role: role}}),
|
||||
onSuccess: () => {
|
||||
toast.success(`User updated successfully.`);
|
||||
},
|
||||
@@ -53,22 +53,19 @@ export const usersColumnsAdmin: ColumnDef<User>[] = [
|
||||
{
|
||||
accessorKey: "updatedAt",
|
||||
header: "Updated At",
|
||||
cell: ({ row }) => {
|
||||
return new Date(row.getValue("updatedAt")).toLocaleString("fr-FR");
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "authMethod",
|
||||
header: "Method",
|
||||
cell: ({ row }) => {
|
||||
return <Badge variant="outline">{row.getValue("authMethod")}</Badge>;
|
||||
cell: ({row}) => {
|
||||
return new Date(row.getValue("updatedAt")).toLocaleString("fr-FR", {
|
||||
timeZone: "Europe/Paris",
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "Action",
|
||||
id: "actions",
|
||||
cell: ({ row }) => {
|
||||
cell: ({row}) => {
|
||||
const router = useRouter();
|
||||
const {data: session, isPending} = useSession();
|
||||
|
||||
const mutation = useMutation({
|
||||
mutationFn: () => deleteUserAction(row.original.id),
|
||||
onSuccess: async () => {
|
||||
@@ -77,17 +74,23 @@ export const usersColumnsAdmin: ColumnDef<User>[] = [
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<ButtonWithLoading
|
||||
variant="outline"
|
||||
text=""
|
||||
icon={<Trash2 color="red" size={15} />}
|
||||
onClick={async () => {
|
||||
await mutation.mutateAsync();
|
||||
}}
|
||||
size="icon"
|
||||
/>
|
||||
{!session || session?.user.email === row.original.email ? null :
|
||||
<ButtonWithLoading
|
||||
variant="outline"
|
||||
text=""
|
||||
icon={<Trash2 color="red" size={15}/>}
|
||||
onClick={async () => {
|
||||
await mutation.mutateAsync();
|
||||
}}
|
||||
size="icon"
|
||||
/>
|
||||
|
||||
}
|
||||
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
||||
import { ProjectWith } from "@/db/schema";
|
||||
import Link from "next/link";
|
||||
import {ProjectWith} from "@/db/schema/05_project";
|
||||
|
||||
export type projectCardProps = {
|
||||
data: ProjectWith;
|
||||
@@ -13,7 +13,7 @@ export const ProjectCard = (props: projectCardProps) => {
|
||||
const { data: project, organizationSlug } = props;
|
||||
|
||||
return (
|
||||
<Link href={`/dashboard/${organizationSlug}/projects/${project.id}`}>
|
||||
<Link href={`/dashboard/projects/${project.id}`}>
|
||||
<Card className="flex flex-row justify-between">
|
||||
<div className="">
|
||||
<CardHeader>{project.name}</CardHeader>
|
||||
|
||||
@@ -17,7 +17,7 @@ export const ProjectDatabaseCard = (props: projectDatabaseCardProps) => {
|
||||
const { organizationSlug, data: database, extendedProps: extendedProps } = props;
|
||||
|
||||
return (
|
||||
<Link href={`/dashboard/${organizationSlug}/projects/${extendedProps.id}/database/${database.id}`}>
|
||||
<Link href={`/dashboard/projects/${extendedProps.id}/database/${database.id}`}>
|
||||
<DatabaseCard data={database} />
|
||||
</Link>
|
||||
);
|
||||
|
||||
@@ -66,7 +66,7 @@ export const ProjectForm = (props: projectFormProps) => {
|
||||
if (project && project.data) {
|
||||
if (project.data.success) {
|
||||
project.data.actionSuccess && toast.success(project.data.actionSuccess.message);
|
||||
router.push(`/dashboard/${props.organization.slug}/projects/${project.data.value!.id}`);
|
||||
router.push(`/dashboard/projects/${project.data.value!.id}`);
|
||||
router.refresh();
|
||||
} else {
|
||||
project.data.actionError && toast.error(project.data.actionError.message || "Unknown error occurred.");
|
||||
|
||||
@@ -2,20 +2,14 @@ import {
|
||||
Sidebar,
|
||||
SidebarContent,
|
||||
SidebarFooter,
|
||||
SidebarGroup,
|
||||
SidebarGroupContent,
|
||||
SidebarGroupLabel,
|
||||
SidebarHeader,
|
||||
SidebarMenu as SM,
|
||||
SidebarMenuItem,
|
||||
} from "@/components/ui/sidebar";
|
||||
import {SidebarItem, SidebarMenu} from "@/components/wrappers/dashboard/sideBar/SideBarMenu/SideBarMenu";
|
||||
import {OrganizationCombobox} from "@/components/wrappers/dashboard/organization/organization-combobox";
|
||||
import {SideBarLogo} from "@/components/wrappers/dashboard/sideBar/SideBarLogo/SideBarLogo";
|
||||
import {SideBarFooterCredit} from "@/components/wrappers/dashboard/sideBar/SideBarFooterCredit/SideBarFooterCredit";
|
||||
import {LoggedInButton} from "@/components/wrappers/dashboard/loggedInButton/LoggedInButton";
|
||||
import {Layers, ChartArea, Settings, ShieldHalf} from "lucide-react";
|
||||
import {authClient} from "@/lib/auth/auth-client";
|
||||
import {SidebarContentA} from "./sidebar-content";
|
||||
import {getActiveMember, getOrganization} from "@/lib/auth/auth";
|
||||
import {notFound} from "next/navigation";
|
||||
@@ -31,7 +25,6 @@ export async function AppSidebar() {
|
||||
|
||||
const organization = await getOrganization({organizationId: member.organizationId});
|
||||
|
||||
//todo: à revoir
|
||||
console.log("organization", organization);
|
||||
|
||||
|
||||
|
||||
@@ -41,6 +41,8 @@ export const SidebarContentA = () => {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
const adminItems: SidebarItem[] = [
|
||||
{
|
||||
type: "list",
|
||||
@@ -62,13 +64,24 @@ export const SidebarContentA = () => {
|
||||
|
||||
return (
|
||||
activeOrganization && (
|
||||
<>
|
||||
<SidebarGroup>
|
||||
<SidebarGroupLabel>Application</SidebarGroupLabel>
|
||||
<SidebarGroupContent>
|
||||
<SidebarMenu items={appItems} baseUrl={`/dashboard`} />
|
||||
</SidebarGroupContent>
|
||||
</SidebarGroup>
|
||||
{(session && (session.user.role === "admin" || session.user.role === "superadmin")) && (
|
||||
<SidebarGroup>
|
||||
<SidebarGroupLabel>Administration</SidebarGroupLabel>
|
||||
<SidebarGroupContent>
|
||||
<SidebarMenu items={adminItems} baseUrl={`/dashboard`} />
|
||||
</SidebarGroupContent>
|
||||
</SidebarGroup>
|
||||
)}
|
||||
|
||||
</>
|
||||
|
||||
<SidebarGroup>
|
||||
<SidebarGroupLabel>Application</SidebarGroupLabel>
|
||||
<SidebarGroupContent>
|
||||
<SidebarMenu items={appItems} baseUrl={`/dashboard/${activeOrganization.slug}`} />
|
||||
</SidebarGroupContent>
|
||||
</SidebarGroup>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -11,7 +11,7 @@ export const database = pgTable("databases", {
|
||||
agentDatabaseId: uuid("agent_database_id").notNull().defaultRandom(),
|
||||
name: text("name").notNull(),
|
||||
dbms: dbmsEnum("dbms").notNull(),
|
||||
description: text("description").notNull(),
|
||||
description: text("description"),
|
||||
backupPolicy: text("backup_policy"),
|
||||
isWaitingForBackup: boolean("is_waiting_for_backup").default(false).notNull(),
|
||||
backupToRestore: text("backup_to_restore"),
|
||||
@@ -23,8 +23,7 @@ export const database = pgTable("databases", {
|
||||
lastContact: timestamp("last_contact"),
|
||||
|
||||
projectId: uuid("project_id")
|
||||
.references(() => project.id)
|
||||
.notNull(),
|
||||
.references(() => project.id),
|
||||
});
|
||||
|
||||
export const backup = pgTable(
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { 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 {relations} from "drizzle-orm";
|
||||
|
||||
export const agent = pgTable("agents", {
|
||||
id: uuid("id").primaryKey().defaultRandom(),
|
||||
@@ -14,3 +16,8 @@ export const agent = pgTable("agents", {
|
||||
|
||||
export const agentSchema = createSelectSchema(agent);
|
||||
export type Agent = z.infer<typeof agentSchema>;
|
||||
|
||||
|
||||
export const agentRelations = relations(agent, ({ many }) => ({
|
||||
databases: many(database),
|
||||
}));
|
||||
@@ -97,7 +97,7 @@ export const auth = betterAuth({
|
||||
await db.insert(drizzleDb.schemas.member).values({
|
||||
userId: user.id,
|
||||
organizationId: defaultOrg.id,
|
||||
role: "orgOwner",
|
||||
role: "owner",
|
||||
});
|
||||
} else {
|
||||
console.warn("Default organization not found. Cannot assign member.");
|
||||
|
||||
Reference in New Issue
Block a user