diff --git a/app/(deploy)/[workspace]/databases/database-new-form.tsx b/app/(deploy)/[workspace]/databases/database-new-form.tsx new file mode 100644 index 0000000..df00c5f --- /dev/null +++ b/app/(deploy)/[workspace]/databases/database-new-form.tsx @@ -0,0 +1,103 @@ +'use client'; + +import { Button } from '@/components/ui/button'; +import { Progress } from '@/components/ui/progress'; +import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'; +import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger } from '@/components/ui/sheet'; +import { useWorkspace } from '@/hooks/useWorkspace'; +import { cn } from '@/lib/utils'; +import { Plus } from 'lucide-react'; +import { useState } from 'react'; + +interface DatabaseNewSteps { + display: string; +} + +const steps: DatabaseNewSteps[] = [ + { + display: 'Database Type', + }, + { + display: 'Configuration', + }, + { + display: 'Review & Deploy', + }, +]; + +export default function DatabaseNewForm() { + const [open, setOpen] = useState(false); + const [currentSteps, setCurrentSteps] = useState(1); + + const { id } = useWorkspace(); + + return ( + setOpen((open) => !open)}> + + + + + + New Database + Deploy a new database + +
+
+
+

+ Step: {steps[currentSteps - 1].display} +

+

+ {currentSteps} / {steps.length} +

+
+ +
+ + {currentSteps == 1 && ( +
+ {/* +
+ + +
+
*/} +
+ )} + +
+ + {currentSteps == steps.length ? ( + + ) : ( + + )} +
+
+
+
+ ); +} diff --git a/app/(deploy)/[workspace]/databases/page.tsx b/app/(deploy)/[workspace]/databases/page.tsx index ba9e07d..ff56400 100644 --- a/app/(deploy)/[workspace]/databases/page.tsx +++ b/app/(deploy)/[workspace]/databases/page.tsx @@ -1,5 +1,5 @@ -import { Button } from '@/components/ui/button'; -import { Database, Plus } from 'lucide-react'; +import { Database } from 'lucide-react'; +import DatabaseNewForm from './database-new-form'; export default async function Databases() { return ( @@ -11,10 +11,11 @@ export default async function Databases() {

No database

Get started by creating a new database.

- + */} + ); diff --git a/app/(deploy)/[workspace]/layout.tsx b/app/(deploy)/[workspace]/layout.tsx index db7aeb5..c98d82c 100644 --- a/app/(deploy)/[workspace]/layout.tsx +++ b/app/(deploy)/[workspace]/layout.tsx @@ -1,8 +1,17 @@ +import { WorkspaceProvider } from '@/contexts/workspace'; import Navigation from './Navigation'; +import prisma from '@/lib/prisma'; + +export default async function WorkspaceLayout({ children, params }: Readonly<{ children: React.ReactNode; params: { workspace: string } }>) { + const workspace = await prisma.workspace.findUnique({ + where: { + slug: params.workspace, + }, + }); + if (!workspace) return
Workspace not found
; -export default async function WorkspaceLayout({ children }: Readonly<{ children: React.ReactNode }>) { return ( - <> +
@@ -13,6 +22,6 @@ export default async function WorkspaceLayout({ children }: Readonly<{ children:
{children}
- + ); } diff --git a/contexts/workspace.tsx b/contexts/workspace.tsx new file mode 100644 index 0000000..b281ace --- /dev/null +++ b/contexts/workspace.tsx @@ -0,0 +1,10 @@ +'use client'; + +import { Workspace } from '@prisma/client'; +import { createContext } from 'react'; + +export const Work = createContext(); + +export function WorkspaceProvider({ children, workspace }: { children: React.ReactNode; workspace: Workspace }) { + return {children}; +} diff --git a/hooks/useWorkspace.tsx b/hooks/useWorkspace.tsx new file mode 100644 index 0000000..781a16d --- /dev/null +++ b/hooks/useWorkspace.tsx @@ -0,0 +1,5 @@ +import { Work } from '@/contexts/workspace'; +import { Workspace } from '@prisma/client'; +import { useContext } from 'react'; + +export const useWorkspace = (): Workspace => useContext(Work); diff --git a/lib/deploy/databases/database.ts b/lib/deploy/databases/database.ts new file mode 100644 index 0000000..8bcbc91 --- /dev/null +++ b/lib/deploy/databases/database.ts @@ -0,0 +1,73 @@ +import prisma from "@/lib/prisma"; +import { Database, DatabaseProvider } from "@prisma/client"; + +type DatabaseType = DatabaseProvider; + +export class CreateDatabase { + + private workspaceId: string; + private id: string; + public name: string; + public type: DatabaseType; + + public username: string; + private password: string; + private host: string | undefined; + private port: number | undefined; + + constructor(workspaceId: string, name: string, type: DatabaseType) { + this.id = "new-database-uuid"; // TODO: Generate UUID + this.username = "root"; // TODO: Generate random username + this.password = "toor"; // TODO: Generate random password + + this.workspaceId = workspaceId; + this.name = name; + this.type = type; + } + + // Deploying the database to the cluster + public async deploy(): Promise { + throw new Error("Not implemented"); + } + + // Saving the newly created database to the database + protected async save(): Promise { + // Checking if the connection details are provided + if(!this.host || !this.port) throw new MissingConnectionDetails(); + + const database = await prisma.database.create({ + data: { + id: this.id, + provider: this.type, + name: this.name, + workspaceId: this.workspaceId, + username: this.username, + password: this.password, + + host: this.host, + port: this.port + } + }); + + return database; + } + +} + +class DatabaseError extends Error { + constructor(message: string) { + super(message); + } +} + +export class SavingDatabaseError extends DatabaseError { + constructor() { + super("Error saving the database"); + } +} + +export class MissingConnectionDetails extends DatabaseError { + constructor() { + super("Missing connection details"); + } +} \ No newline at end of file diff --git a/lib/deploy/databases/index.ts b/lib/deploy/databases/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/lib/deploy/databases/vitess-database.ts b/lib/deploy/databases/vitess-database.ts new file mode 100644 index 0000000..ae3b6fc --- /dev/null +++ b/lib/deploy/databases/vitess-database.ts @@ -0,0 +1,18 @@ +import { Database } from "@prisma/client"; +import { CreateDatabase, SavingDatabaseError } from "./database"; + +export class CreateVitessDatabase extends CreateDatabase { + constructor(workspaceId: string, name: string) { + super(workspaceId, name, "VITESS"); + } + + public async deploy(): Promise { + try { + // Saving the database to the database + const database = await this.save(); + return database; + } catch (error) { + throw new SavingDatabaseError(); + } + } +} \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 70b3123..b942fff 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -73,6 +73,29 @@ model Workspace { updatedAt DateTime @updatedAt Application Application[] Deployment Deployment[] + Database Database[] +} + +model Database { + id String @id @default(cuid()) + name String + provider DatabaseProvider + + host String + port Int + username String + password String + + workspaceId String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + Workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade) +} + +enum DatabaseProvider { + VITESS + REDIS } model Application {