add: databases WIP

This commit is contained in:
Elie Baier 2024-05-03 15:18:40 +02:00
parent c05af7e7aa
commit 9ba19c386f
10 changed files with 207 additions and 101 deletions

View File

@ -1,18 +1,40 @@
'use client';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
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 { deployDatabase } from '@/lib/deploy/database';
import { cn } from '@/lib/utils';
import { Plus } from 'lucide-react';
import Image from 'next/image';
import { useState } from 'react';
interface DatabaseNewSteps {
display: string;
}
interface IDatabaseProvider {
id: string;
display: string;
image: {
alt: string;
src: string;
};
}
export interface IDatabaseConfig {
name: string;
provider: IDatabaseProvider;
user: {
username: string;
password: string;
};
}
const steps: DatabaseNewSteps[] = [
{
display: 'Database Type',
@ -20,15 +42,49 @@ const steps: DatabaseNewSteps[] = [
{
display: 'Configuration',
},
{
display: 'Name & User',
},
{
display: 'Review & Deploy',
},
];
const databaseProviders: IDatabaseProvider[] = [
{
id: 'vitess',
display: 'Vitess',
image: {
alt: 'Vitess',
src: '/vitess.png',
},
},
{
id: 'redis',
display: 'Redis',
image: {
alt: 'Redis',
src: '/redis.svg',
},
},
];
export default function DatabaseNewForm() {
const [open, setOpen] = useState<boolean>(false);
const [currentSteps, setCurrentSteps] = useState<number>(1);
// TODO: Generate all data, but leave the user the choice to modify
const defaultDatabaseConfig: IDatabaseConfig = {
name: 'my-new-cool-db',
provider: databaseProviders[0],
user: {
username: 'my-new-super-user',
password: 'a-super-strong-generated-password',
},
};
const [databaseConfig, setDatabaseConfig] = useState<IDatabaseConfig>(defaultDatabaseConfig);
const { id } = useWorkspace();
return (
@ -59,15 +115,80 @@ export default function DatabaseNewForm() {
{currentSteps == 1 && (
<div>
{/* <RadioGroup defaultValue={'vitess'} className="flex flex-col gap-2 mb-4" onValueChange={}>
<div className={cn('flex flex-row items-center gap-4 border-[1px] rounded-lg py-2 px-4', provider.value == newApplication.serviceProvider ? 'border-[#3A7BFE]' : 'border-gray-300')}>
<RadioGroupItem value={'vitess'} id={'vitess'} />
<label htmlFor={'vitess'} className="flex flex-row items-center gap-4">
<img src={provider.image} alt={'Vitess'} className="w-8 h-8 rounded-full" />
Vitess
</label>
</div>
</RadioGroup> */}
<RadioGroup
defaultValue={'vitess'}
className="flex flex-col gap-2 mb-4"
onValueChange={(id) => {
setDatabaseConfig((prev) => ({ ...prev, provider: databaseProviders.filter((provider) => provider.id == id)[0] }));
}}
>
{databaseProviders.map((provider) => (
<div className={cn('flex flex-row items-center gap-4 border-[1px] rounded-lg py-2 px-4', databaseConfig?.provider.id == provider.id ? 'border-[#3A7BFE]' : 'border-gray-300')}>
<RadioGroupItem value={provider.id} id={provider.id} />
<label htmlFor={provider.id} className="flex flex-row items-center gap-4">
<div className="w-8 h-8 rounded-full relative">
<Image src={provider.image.src} alt={provider.image.alt} fill />
</div>
{provider.display}
</label>
</div>
))}
</RadioGroup>
</div>
)}
{currentSteps == 2 && (
<div>
<p>WIP for now 1CPU 2048Mb RAM</p>
<p>Storage 1 GB</p>
</div>
)}
{currentSteps == 3 && (
<div>
<Label>Database Name</Label>
<Input
className="mb-2"
type="text"
placeholder="super-cool-database"
value={databaseConfig.name}
onChange={(e) => {
setDatabaseConfig((prev) => ({ ...prev, name: e.target.value }));
}}
/>
<Label>Username</Label>
<Input
className="mb-2"
type="text"
placeholder="root"
value={databaseConfig.user.username}
onChange={(e) => {
setDatabaseConfig((prev) => ({ ...prev, user: { ...prev.user, username: e.target.value } }));
}}
/>
<Label>Password</Label>
<Input
className="mb-2"
type="password"
placeholder="Strong Password"
value={databaseConfig.user.password}
onChange={(e) => {
setDatabaseConfig((prev) => ({ ...prev, user: { ...prev.user, password: e.target.value } }));
}}
/>
<div className="" />
</div>
)}
{currentSteps == 4 && (
<div>
<h2>Review your new database information :</h2>
<p>Type : {databaseConfig.provider.display}</p>
<p>Name : {databaseConfig.name}</p>
<p>Username : {databaseConfig.user.username}</p>
<p>Password : {databaseConfig.user.password}</p>
</div>
)}
@ -82,7 +203,14 @@ export default function DatabaseNewForm() {
Back
</Button>
{currentSteps == steps.length ? (
<Button className="bg-[#3A7BFE]" onClick={() => {}}>
<Button
className="bg-[#3A7BFE]"
onClick={() => {
deployDatabase(databaseConfig).then((res) => {
console.log(res);
});
}}
>
Deploy
</Button>
) : (

26
components/ui/label.tsx Normal file
View File

@ -0,0 +1,26 @@
"use client"
import * as React from "react"
import * as LabelPrimitive from "@radix-ui/react-label"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const labelVariants = cva(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
)
const Label = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
VariantProps<typeof labelVariants>
>(({ className, ...props }, ref) => (
<LabelPrimitive.Root
ref={ref}
className={cn(labelVariants(), className)}
{...props}
/>
))
Label.displayName = LabelPrimitive.Root.displayName
export { Label }

16
lib/deploy/database.ts Normal file
View File

@ -0,0 +1,16 @@
"use server";
import { IDatabaseConfig } from "@/app/(deploy)/[workspace]/databases/database-new-form";
export async function deployDatabase(config: IDatabaseConfig) {
return fetch("http://127.0.0.1:8080/databases", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(config),
}).then((res) => res.json()).catch((err) => {
console.error(err);
});
}

View File

@ -1,73 +0,0 @@
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<Database> {
throw new Error("Not implemented");
}
// Saving the newly created database to the database
protected async save(): Promise<Database> {
// 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");
}
}

View File

@ -1,18 +0,0 @@
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<Database> {
try {
// Saving the database to the database
const database = await this.save();
return database;
} catch (error) {
throw new SavingDatabaseError();
}
}
}

24
package-lock.json generated
View File

@ -14,6 +14,7 @@
"@radix-ui/react-avatar": "^1.0.4",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-progress": "^1.0.3",
"@radix-ui/react-radio-group": "^1.1.3",
@ -915,6 +916,29 @@
}
}
},
"node_modules/@radix-ui/react-label": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.0.2.tgz",
"integrity": "sha512-N5ehvlM7qoTLx7nWPodsPYPgMzA5WM8zZChQg8nyFJKnDO5WHdba1vv5/H6IO5LtJMfD2Q3wh1qHFGNtK0w3bQ==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/react-primitive": "1.0.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-popover": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.0.7.tgz",

View File

@ -14,6 +14,7 @@
"@radix-ui/react-avatar": "^1.0.4",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-progress": "^1.0.3",
"@radix-ui/react-radio-group": "^1.1.3",

2
public/redis.svg Normal file
View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 -18 256 256" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet"><path d="M245.97 168.943c-13.662 7.121-84.434 36.22-99.501 44.075-15.067 7.856-23.437 7.78-35.34 2.09-11.902-5.69-87.216-36.112-100.783-42.597C3.566 169.271 0 166.535 0 163.951v-25.876s98.05-21.345 113.879-27.024c15.828-5.679 21.32-5.884 34.79-.95 13.472 4.936 94.018 19.468 107.331 24.344l-.006 25.51c.002 2.558-3.07 5.364-10.024 8.988" fill="#912626"/><path d="M245.965 143.22c-13.661 7.118-84.431 36.218-99.498 44.072-15.066 7.857-23.436 7.78-35.338 2.09-11.903-5.686-87.214-36.113-100.78-42.594-13.566-6.485-13.85-10.948-.524-16.166 13.326-5.22 88.224-34.605 104.055-40.284 15.828-5.677 21.319-5.884 34.789-.948 13.471 4.934 83.819 32.935 97.13 37.81 13.316 4.881 13.827 8.9.166 16.02" fill="#C6302B"/><path d="M245.97 127.074c-13.662 7.122-84.434 36.22-99.501 44.078-15.067 7.853-23.437 7.777-35.34 2.087-11.903-5.687-87.216-36.112-100.783-42.597C3.566 127.402 0 124.67 0 122.085V96.206s98.05-21.344 113.879-27.023c15.828-5.679 21.32-5.885 34.79-.95C162.142 73.168 242.688 87.697 256 92.574l-.006 25.513c.002 2.557-3.07 5.363-10.024 8.987" fill="#912626"/><path d="M245.965 101.351c-13.661 7.12-84.431 36.218-99.498 44.075-15.066 7.854-23.436 7.777-35.338 2.087-11.903-5.686-87.214-36.112-100.78-42.594-13.566-6.483-13.85-10.947-.524-16.167C23.151 83.535 98.05 54.148 113.88 48.47c15.828-5.678 21.319-5.884 34.789-.949 13.471 4.934 83.819 32.933 97.13 37.81 13.316 4.88 13.827 8.9.166 16.02" fill="#C6302B"/><path d="M245.97 83.653c-13.662 7.12-84.434 36.22-99.501 44.078-15.067 7.854-23.437 7.777-35.34 2.087-11.903-5.687-87.216-36.113-100.783-42.595C3.566 83.98 0 81.247 0 78.665v-25.88s98.05-21.343 113.879-27.021c15.828-5.68 21.32-5.884 34.79-.95C162.142 29.749 242.688 44.278 256 49.155l-.006 25.512c.002 2.555-3.07 5.361-10.024 8.986" fill="#912626"/><path d="M245.965 57.93c-13.661 7.12-84.431 36.22-99.498 44.074-15.066 7.854-23.436 7.777-35.338 2.09C99.227 98.404 23.915 67.98 10.35 61.497-3.217 55.015-3.5 50.55 9.825 45.331 23.151 40.113 98.05 10.73 113.88 5.05c15.828-5.679 21.319-5.883 34.789-.948 13.471 4.935 83.819 32.934 97.13 37.811 13.316 4.876 13.827 8.897.166 16.017" fill="#C6302B"/><path d="M159.283 32.757l-22.01 2.285-4.927 11.856-7.958-13.23-25.415-2.284 18.964-6.839-5.69-10.498 17.755 6.944 16.738-5.48-4.524 10.855 17.067 6.391M131.032 90.275L89.955 73.238l58.86-9.035-17.783 26.072M74.082 39.347c17.375 0 31.46 5.46 31.46 12.194 0 6.736-14.085 12.195-31.46 12.195s-31.46-5.46-31.46-12.195c0-6.734 14.085-12.194 31.46-12.194" fill="#FFF"/><path d="M185.295 35.998l34.836 13.766-34.806 13.753-.03-27.52" fill="#621B1C"/><path d="M146.755 51.243l38.54-15.245.03 27.519-3.779 1.478-34.791-13.752" fill="#9A2928"/></svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
public/vitess.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB