feat: initial commit

This commit is contained in:
Mauricio Siu
2024-04-28 23:57:52 -06:00
parent 8857a20344
commit be56ba046c
412 changed files with 60777 additions and 1 deletions

View File

@@ -0,0 +1,77 @@
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
import { api } from "@/utils/api";
import { toast } from "sonner";
interface Props {
postgresId: string;
}
export const DeployPostgres = ({ postgresId }: Props) => {
const { data, refetch } = api.postgres.one.useQuery(
{
postgresId,
},
{ enabled: !!postgresId },
);
const { mutateAsync: deploy } = api.postgres.deploy.useMutation();
const { mutateAsync: changeStatus } = api.postgres.changeStatus.useMutation();
return (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button isLoading={data?.applicationStatus === "running"}>
Deploy
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This will deploy the postgres database
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={async () => {
await changeStatus({
postgresId,
applicationStatus: "running",
})
.then(async () => {
toast.success("Database Deploying....");
await refetch();
await deploy({
postgresId,
})
.then(() => {
toast.success("Database Deployed Succesfully");
})
.catch(() => {
toast.error("Error to deploy Database");
});
await refetch();
})
.catch((e) => {
toast.error(e.message || "Error to deploy Database");
});
}}
>
Confirm
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
};

View File

@@ -0,0 +1,69 @@
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
import { api } from "@/utils/api";
import { RefreshCcw } from "lucide-react";
import { toast } from "sonner";
interface Props {
postgresId: string;
appName: string;
}
export const ResetPostgres = ({ postgresId, appName }: Props) => {
const { refetch } = api.postgres.one.useQuery(
{
postgresId,
},
{ enabled: !!postgresId },
);
const { mutateAsync: reload, isLoading } = api.postgres.reload.useMutation();
return (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="secondary" isLoading={isLoading}>
Reload
<RefreshCcw className="size-4" />
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This will reload the service
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={async () => {
await reload({
postgresId,
appName,
})
.then(() => {
toast.success("Service Reloaded");
})
.catch(() => {
toast.error("Error to reload the service");
});
await refetch();
}}
>
Confirm
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
};

View File

@@ -0,0 +1,157 @@
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { api } from "@/utils/api";
import { zodResolver } from "@hookform/resolvers/zod";
import React, { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";
const DockerProviderSchema = z.object({
externalPort: z.preprocess((a) => {
if (a !== null) {
const parsed = Number.parseInt(z.string().parse(a), 10);
return Number.isNaN(parsed) ? null : parsed;
}
return null;
}, z
.number()
.gte(0, "Range must be 0 - 65535")
.lte(65535, "Range must be 0 - 65535")
.nullable()),
});
type DockerProvider = z.infer<typeof DockerProviderSchema>;
interface Props {
postgresId: string;
}
export const ShowExternalPostgresCredentials = ({ postgresId }: Props) => {
const { data, refetch } = api.postgres.one.useQuery({ postgresId });
const { mutateAsync, isLoading } =
api.postgres.saveExternalPort.useMutation();
const [connectionUrl, setConnectionUrl] = useState("");
const form = useForm<DockerProvider>({
defaultValues: {},
resolver: zodResolver(DockerProviderSchema),
});
useEffect(() => {
if (data?.externalPort) {
form.reset({
externalPort: data.externalPort,
});
}
}, [form.reset, data, form]);
const onSubmit = async (values: DockerProvider) => {
await mutateAsync({
externalPort: values.externalPort,
postgresId,
})
.then(async () => {
toast.success("External Port updated");
await refetch();
})
.catch(() => {
toast.error("Error to save the external port");
});
};
useEffect(() => {
const buildConnectionUrl = () => {
const hostname = window.location.hostname;
const port = form.watch("externalPort") || data?.externalPort;
return `postgresql://${data?.databasePassword}:${data?.databasePassword}@${hostname}:${port}/${data?.databaseName}`;
};
setConnectionUrl(buildConnectionUrl());
}, [
data?.appName,
data?.externalPort,
data?.databasePassword,
form,
data?.databaseName,
]);
return (
<>
<div className="flex w-full flex-col gap-5 ">
<Card className="bg-background">
<CardHeader>
<CardTitle className="text-xl">External Credentials</CardTitle>
<CardDescription>
In order to make the database reachable trought internet is
required to set a port, make sure the port is not used by another
application or database
</CardDescription>
</CardHeader>
<CardContent className="flex w-full flex-col gap-4">
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="flex flex-col gap-4"
>
<div className="grid grid-cols-2 gap-4 ">
<div className="col-span-2 space-y-4">
<FormField
control={form.control}
name="externalPort"
render={({ field }) => {
return (
<FormItem>
<FormLabel>External Port (Internet)</FormLabel>
<FormControl>
<Input
placeholder="5432"
{...field}
value={field.value || ""}
/>
</FormControl>
<FormMessage />
</FormItem>
);
}}
/>
</div>
</div>
{!!data?.externalPort && (
<div className="grid w-full gap-8">
<div className="flex flex-col gap-3">
<Label>External Host</Label>
<Input disabled value={connectionUrl} />
</div>
</div>
)}
<div className="flex justify-end">
<Button type="submit" isLoading={isLoading}>
Save
</Button>
</div>
</form>
</Form>
</CardContent>
</Card>
</div>
</>
);
};

View File

@@ -0,0 +1,51 @@
import React from "react";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { api } from "@/utils/api";
import { StopPostgres } from "./stop-postgres";
import { StartPostgres } from "../start-postgres";
import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal";
import { Terminal } from "lucide-react";
import { DeployPostgres } from "./deploy-postgres";
import { ResetPostgres } from "./reset-postgres";
interface Props {
postgresId: string;
}
export const ShowGeneralPostgres = ({ postgresId }: Props) => {
const { data } = api.postgres.one.useQuery(
{
postgresId,
},
{ enabled: !!postgresId },
);
return (
<div className="flex w-full flex-col gap-5 ">
<Card className="bg-background">
<CardHeader>
<CardTitle className="text-xl">Deploy Settings</CardTitle>
</CardHeader>
<CardContent className="flex flex-row gap-4 flex-wrap">
<DeployPostgres postgresId={postgresId} />
<ResetPostgres
postgresId={postgresId}
appName={data?.appName || ""}
/>
{data?.applicationStatus === "idle" ? (
<StartPostgres postgresId={postgresId} />
) : (
<StopPostgres postgresId={postgresId} />
)}
<DockerTerminalModal appName={data?.appName || ""}>
<Button variant="outline">
<Terminal />
Open Terminal
</Button>
</DockerTerminalModal>
</CardContent>
</Card>
</div>
);
};

View File

@@ -0,0 +1,63 @@
import React from "react";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { api } from "@/utils/api";
interface Props {
postgresId: string;
}
export const ShowInternalPostgresCredentials = ({ postgresId }: Props) => {
const { data } = api.postgres.one.useQuery({ postgresId });
return (
<>
<div className="flex w-full flex-col gap-5 ">
<Card className="bg-background">
<CardHeader>
<CardTitle className="text-xl">Internal Credentials</CardTitle>
</CardHeader>
<CardContent className="flex w-full flex-row gap-4">
<div className="grid w-full md:grid-cols-2 gap-4 md:gap-8">
<div className="flex flex-col gap-2">
<Label>User</Label>
<Input disabled value={data?.databaseUser} />
</div>
<div className="flex flex-col gap-2">
<Label>Database Name</Label>
<Input disabled value={data?.databaseName} />
</div>
<div className="flex flex-col gap-2">
<Label>Password</Label>
<div className="flex flex-row gap-4">
<Input
disabled
value={data?.databasePassword}
type="password"
/>
</div>
</div>
<div className="flex flex-col gap-2">
<Label>Internal Port (Container)</Label>
<Input disabled value="5432" />
</div>
<div className="flex flex-col gap-2">
<Label>Internal Host</Label>
<Input disabled value={data?.appName} />
</div>
<div className="flex flex-col gap-2">
<Label>Internal Connection URL </Label>
<Input
disabled
value={`postgresql://${data?.databaseUser}:${data?.databasePassword}@${data?.appName}:5432/${data?.databaseName}`}
/>
</div>
</div>
</CardContent>
</Card>
</div>
</>
);
};
// ReplyError: MISCONF Redis is configured to save RDB snapshots, but it's currently unable to persist to disk. Commands that may modify the data set are disabled, because this instance is configured to report errors during writes if RDB snapshotting fails (stop-w

View File

@@ -0,0 +1,65 @@
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
import { api } from "@/utils/api";
import { Ban } from "lucide-react";
import { toast } from "sonner";
interface Props {
postgresId: string;
}
export const StopPostgres = ({ postgresId }: Props) => {
const { mutateAsync, isLoading } = api.postgres.stop.useMutation();
const utils = api.useUtils();
return (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="destructive" isLoading={isLoading}>
Stop
<Ban className="size-4" />
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>
Are you absolutely sure to stop the database?
</AlertDialogTitle>
<AlertDialogDescription>
This will stop the database
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={async () => {
await mutateAsync({
postgresId,
})
.then(async () => {
await utils.postgres.one.invalidate({
postgresId,
});
toast.success("Application stopped succesfully");
})
.catch(() => {
toast.error("Error to stop the Application");
});
}}
>
Confirm
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
};