refactor: standardize import statements and improve code structure across multiple components

- Updated import statements to maintain consistency and clarity.
- Refactored components to enhance readability and organization.
- Ensured proper usage of type imports and removed unnecessary comments.
- Improved user feedback mechanisms in forms and alerts for better user experience.
This commit is contained in:
Mauricio Siu
2025-03-18 00:52:34 -06:00
parent 2898a5e575
commit 17330ca71a
27 changed files with 864 additions and 876 deletions

View File

@@ -41,8 +41,8 @@ import { toast } from "sonner";
import { domain } from "@/server/db/validations/domain"; import { domain } from "@/server/db/validations/domain";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { Dices } from "lucide-react"; import { Dices } from "lucide-react";
import type z from "zod";
import Link from "next/link"; import Link from "next/link";
import type z from "zod";
type Domain = z.infer<typeof domain>; type Domain = z.infer<typeof domain>;

View File

@@ -41,8 +41,8 @@ import {
import { domainCompose } from "@/server/db/validations/domain"; import { domainCompose } from "@/server/db/validations/domain";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { DatabaseZap, Dices, RefreshCw } from "lucide-react"; import { DatabaseZap, Dices, RefreshCw } from "lucide-react";
import type z from "zod";
import Link from "next/link"; import Link from "next/link";
import type z from "zod";
type Domain = z.infer<typeof domainCompose>; type Domain = z.infer<typeof domainCompose>;

View File

@@ -8,7 +8,7 @@ import {
TooltipTrigger, TooltipTrigger,
} from "@/components/ui/tooltip"; } from "@/components/ui/tooltip";
import { api } from "@/utils/api"; import { api } from "@/utils/api";
import { Ban, CheckCircle2, Hammer, HelpCircle, Terminal, RefreshCcw, Rocket } from "lucide-react"; import { Ban, CheckCircle2, RefreshCcw, Rocket, Terminal } from "lucide-react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { toast } from "sonner"; import { toast } from "sonner";
import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal"; import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal";

View File

@@ -1,3 +1,5 @@
import { DrawerLogs } from "@/components/shared/drawer-logs";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
Command, Command,
@@ -23,6 +25,7 @@ import {
FormLabel, FormLabel,
FormMessage, FormMessage,
} from "@/components/ui/form"; } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { import {
Popover, Popover,
PopoverContent, PopoverContent,
@@ -32,18 +35,15 @@ import { ScrollArea } from "@/components/ui/scroll-area";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { api } from "@/utils/api"; import { api } from "@/utils/api";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import copy from "copy-to-clipboard";
import { debounce } from "lodash";
import { CheckIcon, ChevronsUpDown, Copy, RotateCcw } from "lucide-react"; import { CheckIcon, ChevronsUpDown, Copy, RotateCcw } from "lucide-react";
import { useState } from "react"; import { useState } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod"; import { z } from "zod";
import type { ServiceType } from "../../application/advanced/show-resources"; import type { ServiceType } from "../../application/advanced/show-resources";
import { debounce } from "lodash";
import { Input } from "@/components/ui/input";
import { type LogLine, parseLogs } from "../../docker/logs/utils"; import { type LogLine, parseLogs } from "../../docker/logs/utils";
import { DrawerLogs } from "@/components/shared/drawer-logs";
import { Badge } from "@/components/ui/badge";
import copy from "copy-to-clipboard";
import { toast } from "sonner";
interface Props { interface Props {
databaseId: string; databaseId: string;

View File

@@ -20,9 +20,8 @@ import { useState } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import type { ServiceType } from "../../application/advanced/show-resources"; import type { ServiceType } from "../../application/advanced/show-resources";
import { AddBackup } from "./add-backup"; import { AddBackup } from "./add-backup";
import { UpdateBackup } from "./update-backup";
import { RestoreBackup } from "./restore-backup"; import { RestoreBackup } from "./restore-backup";
import { useState } from "react"; import { UpdateBackup } from "./update-backup";
interface Props { interface Props {
id: string; id: string;

View File

@@ -27,145 +27,149 @@ import { toast } from "sonner";
import { z } from "zod"; import { z } from "zod";
const DockerProviderSchema = z.object({ const DockerProviderSchema = z.object({
externalPort: z.preprocess((a) => { externalPort: z.preprocess((a) => {
if (a !== null) { if (a !== null) {
const parsed = Number.parseInt(z.string().parse(a), 10); const parsed = Number.parseInt(z.string().parse(a), 10);
return Number.isNaN(parsed) ? null : parsed; return Number.isNaN(parsed) ? null : parsed;
} }
return null; return null;
}, z.number().gte(0, "Range must be 0 - 65535").lte(65535, "Range must be 0 - 65535").nullable()), }, z
.number()
.gte(0, "Range must be 0 - 65535")
.lte(65535, "Range must be 0 - 65535")
.nullable()),
}); });
type DockerProvider = z.infer<typeof DockerProviderSchema>; type DockerProvider = z.infer<typeof DockerProviderSchema>;
interface Props { interface Props {
mariadbId: string; mariadbId: string;
} }
export const ShowExternalMariadbCredentials = ({ mariadbId }: Props) => { export const ShowExternalMariadbCredentials = ({ mariadbId }: Props) => {
const { data: ip } = api.settings.getIp.useQuery(); const { data: ip } = api.settings.getIp.useQuery();
const { data, refetch } = api.mariadb.one.useQuery({ mariadbId }); const { data, refetch } = api.mariadb.one.useQuery({ mariadbId });
const { mutateAsync, isLoading } = api.mariadb.saveExternalPort.useMutation(); const { mutateAsync, isLoading } = api.mariadb.saveExternalPort.useMutation();
const [connectionUrl, setConnectionUrl] = useState(""); const [connectionUrl, setConnectionUrl] = useState("");
const getIp = data?.server?.ipAddress || ip; const getIp = data?.server?.ipAddress || ip;
const form = useForm<DockerProvider>({ const form = useForm<DockerProvider>({
defaultValues: {}, defaultValues: {},
resolver: zodResolver(DockerProviderSchema), resolver: zodResolver(DockerProviderSchema),
}); });
useEffect(() => { useEffect(() => {
if (data?.externalPort) { if (data?.externalPort) {
form.reset({ form.reset({
externalPort: data.externalPort, externalPort: data.externalPort,
}); });
} }
}, [form.reset, data, form]); }, [form.reset, data, form]);
const onSubmit = async (values: DockerProvider) => { const onSubmit = async (values: DockerProvider) => {
await mutateAsync({ await mutateAsync({
externalPort: values.externalPort, externalPort: values.externalPort,
mariadbId, mariadbId,
}) })
.then(async () => { .then(async () => {
toast.success("External Port updated"); toast.success("External Port updated");
await refetch(); await refetch();
}) })
.catch(() => { .catch(() => {
toast.error("Error saving the external port"); toast.error("Error saving the external port");
}); });
}; };
useEffect(() => { useEffect(() => {
const buildConnectionUrl = () => { const buildConnectionUrl = () => {
const port = form.watch("externalPort") || data?.externalPort; const port = form.watch("externalPort") || data?.externalPort;
return `mariadb://${data?.databaseUser}:${data?.databasePassword}@${getIp}:${port}/${data?.databaseName}`; return `mariadb://${data?.databaseUser}:${data?.databasePassword}@${getIp}:${port}/${data?.databaseName}`;
}; };
setConnectionUrl(buildConnectionUrl()); setConnectionUrl(buildConnectionUrl());
}, [ }, [
data?.appName, data?.appName,
data?.externalPort, data?.externalPort,
data?.databasePassword, data?.databasePassword,
form, form,
data?.databaseName, data?.databaseName,
data?.databaseUser, data?.databaseUser,
getIp, getIp,
]); ]);
return ( return (
<> <>
<div className="flex w-full flex-col gap-5 "> <div className="flex w-full flex-col gap-5 ">
<Card className="bg-background"> <Card className="bg-background">
<CardHeader> <CardHeader>
<CardTitle className="text-xl">External Credentials</CardTitle> <CardTitle className="text-xl">External Credentials</CardTitle>
<CardDescription> <CardDescription>
In order to make the database reachable trought internet is In order to make the database reachable trought internet is
required to set a port, make sure the port is not used by another required to set a port, make sure the port is not used by another
application or database application or database
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent className="flex w-full flex-col gap-4"> <CardContent className="flex w-full flex-col gap-4">
{!getIp && ( {!getIp && (
<AlertBlock type="warning"> <AlertBlock type="warning">
You need to set an IP address in your{" "} You need to set an IP address in your{" "}
<Link <Link
href="/dashboard/settings/server" href="/dashboard/settings/server"
className="text-primary" className="text-primary"
> >
{data?.serverId {data?.serverId
? "Remote Servers -> Server -> Edit Server -> Update IP Address" ? "Remote Servers -> Server -> Edit Server -> Update IP Address"
: "Web Server -> Server -> Update Server IP"} : "Web Server -> Server -> Update Server IP"}
</Link>{" "} </Link>{" "}
to fix the database url connection. to fix the database url connection.
</AlertBlock> </AlertBlock>
)} )}
<Form {...form}> <Form {...form}>
<form <form
onSubmit={form.handleSubmit(onSubmit)} onSubmit={form.handleSubmit(onSubmit)}
className="flex flex-col gap-4" className="flex flex-col gap-4"
> >
<div className="grid md:grid-cols-2 gap-4 "> <div className="grid md:grid-cols-2 gap-4 ">
<div className="md:col-span-2 space-y-4"> <div className="md:col-span-2 space-y-4">
<FormField <FormField
control={form.control} control={form.control}
name="externalPort" name="externalPort"
render={({ field }) => { render={({ field }) => {
return ( return (
<FormItem> <FormItem>
<FormLabel>External Port (Internet)</FormLabel> <FormLabel>External Port (Internet)</FormLabel>
<FormControl> <FormControl>
<Input <Input
placeholder="3306" placeholder="3306"
{...field} {...field}
value={field.value || ""} value={field.value || ""}
/> />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
); );
}} }}
/> />
</div> </div>
</div> </div>
{!!data?.externalPort && ( {!!data?.externalPort && (
<div className="grid w-full gap-8"> <div className="grid w-full gap-8">
<div className="flex flex-col gap-3"> <div className="flex flex-col gap-3">
{/* jdbc:mariadb://5.161.59.207:3306/pixel-calculate?user=mariadb&password=HdVXfq6hM7W7F1 */} {/* jdbc:mariadb://5.161.59.207:3306/pixel-calculate?user=mariadb&password=HdVXfq6hM7W7F1 */}
<Label>External Host</Label> <Label>External Host</Label>
<ToggleVisibilityInput value={connectionUrl} disabled /> <ToggleVisibilityInput value={connectionUrl} disabled />
</div> </div>
</div> </div>
)} )}
<div className="flex justify-end"> <div className="flex justify-end">
<Button type="submit" isLoading={isLoading}> <Button type="submit" isLoading={isLoading}>
Save Save
</Button> </Button>
</div> </div>
</form> </form>
</Form> </Form>
</CardContent> </CardContent>
</Card> </Card>
</div> </div>
</> </>
); );
}; };

View File

@@ -10,14 +10,7 @@ import {
} from "@/components/ui/tooltip"; } from "@/components/ui/tooltip";
import { api } from "@/utils/api"; import { api } from "@/utils/api";
import * as TooltipPrimitive from "@radix-ui/react-tooltip"; import * as TooltipPrimitive from "@radix-ui/react-tooltip";
import { import { Ban, CheckCircle2, RefreshCcw, Rocket, Terminal } from "lucide-react";
Ban,
CheckCircle2,
HelpCircle,
RefreshCcw,
Rocket,
Terminal,
} from "lucide-react";
import { useState } from "react"; import { useState } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import { type LogLine, parseLogs } from "../../docker/logs/utils"; import { type LogLine, parseLogs } from "../../docker/logs/utils";

View File

@@ -27,144 +27,148 @@ import { toast } from "sonner";
import { z } from "zod"; import { z } from "zod";
const DockerProviderSchema = z.object({ const DockerProviderSchema = z.object({
externalPort: z.preprocess((a) => { externalPort: z.preprocess((a) => {
if (a !== null) { if (a !== null) {
const parsed = Number.parseInt(z.string().parse(a), 10); const parsed = Number.parseInt(z.string().parse(a), 10);
return Number.isNaN(parsed) ? null : parsed; return Number.isNaN(parsed) ? null : parsed;
} }
return null; return null;
}, z.number().gte(0, "Range must be 0 - 65535").lte(65535, "Range must be 0 - 65535").nullable()), }, z
.number()
.gte(0, "Range must be 0 - 65535")
.lte(65535, "Range must be 0 - 65535")
.nullable()),
}); });
type DockerProvider = z.infer<typeof DockerProviderSchema>; type DockerProvider = z.infer<typeof DockerProviderSchema>;
interface Props { interface Props {
mongoId: string; mongoId: string;
} }
export const ShowExternalMongoCredentials = ({ mongoId }: Props) => { export const ShowExternalMongoCredentials = ({ mongoId }: Props) => {
const { data: ip } = api.settings.getIp.useQuery(); const { data: ip } = api.settings.getIp.useQuery();
const { data, refetch } = api.mongo.one.useQuery({ mongoId }); const { data, refetch } = api.mongo.one.useQuery({ mongoId });
const { mutateAsync, isLoading } = api.mongo.saveExternalPort.useMutation(); const { mutateAsync, isLoading } = api.mongo.saveExternalPort.useMutation();
const [connectionUrl, setConnectionUrl] = useState(""); const [connectionUrl, setConnectionUrl] = useState("");
const getIp = data?.server?.ipAddress || ip; const getIp = data?.server?.ipAddress || ip;
const form = useForm<DockerProvider>({ const form = useForm<DockerProvider>({
defaultValues: {}, defaultValues: {},
resolver: zodResolver(DockerProviderSchema), resolver: zodResolver(DockerProviderSchema),
}); });
useEffect(() => { useEffect(() => {
if (data?.externalPort) { if (data?.externalPort) {
form.reset({ form.reset({
externalPort: data.externalPort, externalPort: data.externalPort,
}); });
} }
}, [form.reset, data, form]); }, [form.reset, data, form]);
const onSubmit = async (values: DockerProvider) => { const onSubmit = async (values: DockerProvider) => {
await mutateAsync({ await mutateAsync({
externalPort: values.externalPort, externalPort: values.externalPort,
mongoId, mongoId,
}) })
.then(async () => { .then(async () => {
toast.success("External Port updated"); toast.success("External Port updated");
await refetch(); await refetch();
}) })
.catch(() => { .catch(() => {
toast.error("Error saving the external port"); toast.error("Error saving the external port");
}); });
}; };
useEffect(() => { useEffect(() => {
const buildConnectionUrl = () => { const buildConnectionUrl = () => {
const port = form.watch("externalPort") || data?.externalPort; const port = form.watch("externalPort") || data?.externalPort;
return `mongodb://${data?.databaseUser}:${data?.databasePassword}@${getIp}:${port}`; return `mongodb://${data?.databaseUser}:${data?.databasePassword}@${getIp}:${port}`;
}; };
setConnectionUrl(buildConnectionUrl()); setConnectionUrl(buildConnectionUrl());
}, [ }, [
data?.appName, data?.appName,
data?.externalPort, data?.externalPort,
data?.databasePassword, data?.databasePassword,
form, form,
data?.databaseUser, data?.databaseUser,
getIp, getIp,
]); ]);
return ( return (
<> <>
<div className="flex w-full flex-col gap-5 "> <div className="flex w-full flex-col gap-5 ">
<Card className="bg-background"> <Card className="bg-background">
<CardHeader> <CardHeader>
<CardTitle className="text-xl">External Credentials</CardTitle> <CardTitle className="text-xl">External Credentials</CardTitle>
<CardDescription> <CardDescription>
In order to make the database reachable trought internet is In order to make the database reachable trought internet is
required to set a port, make sure the port is not used by another required to set a port, make sure the port is not used by another
application or database application or database
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent className="flex w-full flex-col gap-4"> <CardContent className="flex w-full flex-col gap-4">
{!getIp && ( {!getIp && (
<AlertBlock type="warning"> <AlertBlock type="warning">
You need to set an IP address in your{" "} You need to set an IP address in your{" "}
<Link <Link
href="/dashboard/settings/server" href="/dashboard/settings/server"
className="text-primary" className="text-primary"
> >
{data?.serverId {data?.serverId
? "Remote Servers -> Server -> Edit Server -> Update IP Address" ? "Remote Servers -> Server -> Edit Server -> Update IP Address"
: "Web Server -> Server -> Update Server IP"} : "Web Server -> Server -> Update Server IP"}
</Link>{" "} </Link>{" "}
to fix the database url connection. to fix the database url connection.
</AlertBlock> </AlertBlock>
)} )}
<Form {...form}> <Form {...form}>
<form <form
onSubmit={form.handleSubmit(onSubmit)} onSubmit={form.handleSubmit(onSubmit)}
className="flex flex-col gap-4" className="flex flex-col gap-4"
> >
<div className="grid grid-cols-2 gap-4 "> <div className="grid grid-cols-2 gap-4 ">
<div className="col-span-2 space-y-4"> <div className="col-span-2 space-y-4">
<FormField <FormField
control={form.control} control={form.control}
name="externalPort" name="externalPort"
render={({ field }) => { render={({ field }) => {
return ( return (
<FormItem> <FormItem>
<FormLabel>External Port (Internet)</FormLabel> <FormLabel>External Port (Internet)</FormLabel>
<FormControl> <FormControl>
<Input <Input
placeholder="27017" placeholder="27017"
{...field} {...field}
value={field.value || ""} value={field.value || ""}
/> />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
); );
}} }}
/> />
</div> </div>
</div> </div>
{!!data?.externalPort && ( {!!data?.externalPort && (
<div className="grid w-full gap-8"> <div className="grid w-full gap-8">
<div className="flex flex-col gap-3"> <div className="flex flex-col gap-3">
<Label>External Host</Label> <Label>External Host</Label>
<ToggleVisibilityInput value={connectionUrl} disabled /> <ToggleVisibilityInput value={connectionUrl} disabled />
</div> </div>
</div> </div>
)} )}
<div className="flex justify-end"> <div className="flex justify-end">
<Button type="submit" isLoading={isLoading}> <Button type="submit" isLoading={isLoading}>
Save Save
</Button> </Button>
</div> </div>
</form> </form>
</Form> </Form>
</CardContent> </CardContent>
</Card> </Card>
</div> </div>
</> </>
); );
}; };

View File

@@ -10,14 +10,7 @@ import {
} from "@/components/ui/tooltip"; } from "@/components/ui/tooltip";
import { api } from "@/utils/api"; import { api } from "@/utils/api";
import * as TooltipPrimitive from "@radix-ui/react-tooltip"; import * as TooltipPrimitive from "@radix-ui/react-tooltip";
import { import { Ban, CheckCircle2, RefreshCcw, Rocket, Terminal } from "lucide-react";
Ban,
CheckCircle2,
HelpCircle,
RefreshCcw,
Rocket,
Terminal,
} from "lucide-react";
import { useState } from "react"; import { useState } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import { type LogLine, parseLogs } from "../../docker/logs/utils"; import { type LogLine, parseLogs } from "../../docker/logs/utils";

View File

@@ -27,144 +27,148 @@ import { toast } from "sonner";
import { z } from "zod"; import { z } from "zod";
const DockerProviderSchema = z.object({ const DockerProviderSchema = z.object({
externalPort: z.preprocess((a) => { externalPort: z.preprocess((a) => {
if (a !== null) { if (a !== null) {
const parsed = Number.parseInt(z.string().parse(a), 10); const parsed = Number.parseInt(z.string().parse(a), 10);
return Number.isNaN(parsed) ? null : parsed; return Number.isNaN(parsed) ? null : parsed;
} }
return null; return null;
}, z.number().gte(0, "Range must be 0 - 65535").lte(65535, "Range must be 0 - 65535").nullable()), }, z
.number()
.gte(0, "Range must be 0 - 65535")
.lte(65535, "Range must be 0 - 65535")
.nullable()),
}); });
type DockerProvider = z.infer<typeof DockerProviderSchema>; type DockerProvider = z.infer<typeof DockerProviderSchema>;
interface Props { interface Props {
mysqlId: string; mysqlId: string;
} }
export const ShowExternalMysqlCredentials = ({ mysqlId }: Props) => { export const ShowExternalMysqlCredentials = ({ mysqlId }: Props) => {
const { data: ip } = api.settings.getIp.useQuery(); const { data: ip } = api.settings.getIp.useQuery();
const { data, refetch } = api.mysql.one.useQuery({ mysqlId }); const { data, refetch } = api.mysql.one.useQuery({ mysqlId });
const { mutateAsync, isLoading } = api.mysql.saveExternalPort.useMutation(); const { mutateAsync, isLoading } = api.mysql.saveExternalPort.useMutation();
const [connectionUrl, setConnectionUrl] = useState(""); const [connectionUrl, setConnectionUrl] = useState("");
const getIp = data?.server?.ipAddress || ip; const getIp = data?.server?.ipAddress || ip;
const form = useForm<DockerProvider>({ const form = useForm<DockerProvider>({
defaultValues: {}, defaultValues: {},
resolver: zodResolver(DockerProviderSchema), resolver: zodResolver(DockerProviderSchema),
}); });
useEffect(() => { useEffect(() => {
if (data?.externalPort) { if (data?.externalPort) {
form.reset({ form.reset({
externalPort: data.externalPort, externalPort: data.externalPort,
}); });
} }
}, [form.reset, data, form]); }, [form.reset, data, form]);
const onSubmit = async (values: DockerProvider) => { const onSubmit = async (values: DockerProvider) => {
await mutateAsync({ await mutateAsync({
externalPort: values.externalPort, externalPort: values.externalPort,
mysqlId, mysqlId,
}) })
.then(async () => { .then(async () => {
toast.success("External Port updated"); toast.success("External Port updated");
await refetch(); await refetch();
}) })
.catch(() => { .catch(() => {
toast.error("Error saving the external port"); toast.error("Error saving the external port");
}); });
}; };
useEffect(() => { useEffect(() => {
const buildConnectionUrl = () => { const buildConnectionUrl = () => {
const port = form.watch("externalPort") || data?.externalPort; const port = form.watch("externalPort") || data?.externalPort;
return `mysql://${data?.databaseUser}:${data?.databasePassword}@${getIp}:${port}/${data?.databaseName}`; return `mysql://${data?.databaseUser}:${data?.databasePassword}@${getIp}:${port}/${data?.databaseName}`;
}; };
setConnectionUrl(buildConnectionUrl()); setConnectionUrl(buildConnectionUrl());
}, [ }, [
data?.appName, data?.appName,
data?.externalPort, data?.externalPort,
data?.databasePassword, data?.databasePassword,
data?.databaseName, data?.databaseName,
data?.databaseUser, data?.databaseUser,
form, form,
getIp, getIp,
]); ]);
return ( return (
<> <>
<div className="flex w-full flex-col gap-5 "> <div className="flex w-full flex-col gap-5 ">
<Card className="bg-background"> <Card className="bg-background">
<CardHeader> <CardHeader>
<CardTitle className="text-xl">External Credentials</CardTitle> <CardTitle className="text-xl">External Credentials</CardTitle>
<CardDescription> <CardDescription>
In order to make the database reachable trought internet is In order to make the database reachable trought internet is
required to set a port, make sure the port is not used by another required to set a port, make sure the port is not used by another
application or database application or database
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent className="flex w-full flex-col gap-4"> <CardContent className="flex w-full flex-col gap-4">
{!getIp && ( {!getIp && (
<AlertBlock type="warning"> <AlertBlock type="warning">
You need to set an IP address in your{" "} You need to set an IP address in your{" "}
<Link <Link
href="/dashboard/settings/server" href="/dashboard/settings/server"
className="text-primary" className="text-primary"
> >
{data?.serverId {data?.serverId
? "Remote Servers -> Server -> Edit Server -> Update IP Address" ? "Remote Servers -> Server -> Edit Server -> Update IP Address"
: "Web Server -> Server -> Update Server IP"} : "Web Server -> Server -> Update Server IP"}
</Link>{" "} </Link>{" "}
to fix the database url connection. to fix the database url connection.
</AlertBlock> </AlertBlock>
)} )}
<Form {...form}> <Form {...form}>
<form <form
onSubmit={form.handleSubmit(onSubmit)} onSubmit={form.handleSubmit(onSubmit)}
className="flex flex-col gap-4" className="flex flex-col gap-4"
> >
<div className="grid grid-cols-2 gap-4 "> <div className="grid grid-cols-2 gap-4 ">
<div className="col-span-2 space-y-4"> <div className="col-span-2 space-y-4">
<FormField <FormField
control={form.control} control={form.control}
name="externalPort" name="externalPort"
render={({ field }) => { render={({ field }) => {
return ( return (
<FormItem> <FormItem>
<FormLabel>External Port (Internet)</FormLabel> <FormLabel>External Port (Internet)</FormLabel>
<FormControl> <FormControl>
<Input <Input
placeholder="3306" placeholder="3306"
{...field} {...field}
value={field.value || ""} value={field.value || ""}
/> />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
); );
}} }}
/> />
</div> </div>
</div> </div>
{!!data?.externalPort && ( {!!data?.externalPort && (
<div className="grid w-full gap-8"> <div className="grid w-full gap-8">
<div className="flex flex-col gap-3"> <div className="flex flex-col gap-3">
<Label>External Host</Label> <Label>External Host</Label>
<ToggleVisibilityInput disabled value={connectionUrl} /> <ToggleVisibilityInput disabled value={connectionUrl} />
</div> </div>
</div> </div>
)} )}
<div className="flex justify-end"> <div className="flex justify-end">
<Button type="submit" isLoading={isLoading}> <Button type="submit" isLoading={isLoading}>
Save Save
</Button> </Button>
</div> </div>
</form> </form>
</Form> </Form>
</CardContent> </CardContent>
</Card> </Card>
</div> </div>
</> </>
); );
}; };

View File

@@ -10,14 +10,7 @@ import {
} from "@/components/ui/tooltip"; } from "@/components/ui/tooltip";
import { api } from "@/utils/api"; import { api } from "@/utils/api";
import * as TooltipPrimitive from "@radix-ui/react-tooltip"; import * as TooltipPrimitive from "@radix-ui/react-tooltip";
import { import { Ban, CheckCircle2, RefreshCcw, Rocket, Terminal } from "lucide-react";
Ban,
CheckCircle2,
HelpCircle,
RefreshCcw,
Rocket,
Terminal,
} from "lucide-react";
import { useState } from "react"; import { useState } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import { type LogLine, parseLogs } from "../../docker/logs/utils"; import { type LogLine, parseLogs } from "../../docker/logs/utils";

View File

@@ -27,146 +27,150 @@ import { toast } from "sonner";
import { z } from "zod"; import { z } from "zod";
const DockerProviderSchema = z.object({ const DockerProviderSchema = z.object({
externalPort: z.preprocess((a) => { externalPort: z.preprocess((a) => {
if (a !== null) { if (a !== null) {
const parsed = Number.parseInt(z.string().parse(a), 10); const parsed = Number.parseInt(z.string().parse(a), 10);
return Number.isNaN(parsed) ? null : parsed; return Number.isNaN(parsed) ? null : parsed;
} }
return null; return null;
}, z.number().gte(0, "Range must be 0 - 65535").lte(65535, "Range must be 0 - 65535").nullable()), }, z
.number()
.gte(0, "Range must be 0 - 65535")
.lte(65535, "Range must be 0 - 65535")
.nullable()),
}); });
type DockerProvider = z.infer<typeof DockerProviderSchema>; type DockerProvider = z.infer<typeof DockerProviderSchema>;
interface Props { interface Props {
postgresId: string; postgresId: string;
} }
export const ShowExternalPostgresCredentials = ({ postgresId }: Props) => { export const ShowExternalPostgresCredentials = ({ postgresId }: Props) => {
const { data: ip } = api.settings.getIp.useQuery(); const { data: ip } = api.settings.getIp.useQuery();
const { data, refetch } = api.postgres.one.useQuery({ postgresId }); const { data, refetch } = api.postgres.one.useQuery({ postgresId });
const { mutateAsync, isLoading } = const { mutateAsync, isLoading } =
api.postgres.saveExternalPort.useMutation(); api.postgres.saveExternalPort.useMutation();
const getIp = data?.server?.ipAddress || ip; const getIp = data?.server?.ipAddress || ip;
const [connectionUrl, setConnectionUrl] = useState(""); const [connectionUrl, setConnectionUrl] = useState("");
const form = useForm<DockerProvider>({ const form = useForm<DockerProvider>({
defaultValues: {}, defaultValues: {},
resolver: zodResolver(DockerProviderSchema), resolver: zodResolver(DockerProviderSchema),
}); });
useEffect(() => { useEffect(() => {
if (data?.externalPort) { if (data?.externalPort) {
form.reset({ form.reset({
externalPort: data.externalPort, externalPort: data.externalPort,
}); });
} }
}, [form.reset, data, form]); }, [form.reset, data, form]);
const onSubmit = async (values: DockerProvider) => { const onSubmit = async (values: DockerProvider) => {
await mutateAsync({ await mutateAsync({
externalPort: values.externalPort, externalPort: values.externalPort,
postgresId, postgresId,
}) })
.then(async () => { .then(async () => {
toast.success("External Port updated"); toast.success("External Port updated");
await refetch(); await refetch();
}) })
.catch(() => { .catch(() => {
toast.error("Error saving the external port"); toast.error("Error saving the external port");
}); });
}; };
useEffect(() => { useEffect(() => {
const buildConnectionUrl = () => { const buildConnectionUrl = () => {
const port = form.watch("externalPort") || data?.externalPort; const port = form.watch("externalPort") || data?.externalPort;
return `postgresql://${data?.databaseUser}:${data?.databasePassword}@${getIp}:${port}/${data?.databaseName}`; return `postgresql://${data?.databaseUser}:${data?.databasePassword}@${getIp}:${port}/${data?.databaseName}`;
}; };
setConnectionUrl(buildConnectionUrl()); setConnectionUrl(buildConnectionUrl());
}, [ }, [
data?.appName, data?.appName,
data?.externalPort, data?.externalPort,
data?.databasePassword, data?.databasePassword,
form, form,
data?.databaseName, data?.databaseName,
getIp, getIp,
]); ]);
return ( return (
<> <>
<div className="flex w-full flex-col gap-5 "> <div className="flex w-full flex-col gap-5 ">
<Card className="bg-background"> <Card className="bg-background">
<CardHeader> <CardHeader>
<CardTitle className="text-xl">External Credentials</CardTitle> <CardTitle className="text-xl">External Credentials</CardTitle>
<CardDescription> <CardDescription>
In order to make the database reachable trought internet is In order to make the database reachable trought internet is
required to set a port, make sure the port is not used by another required to set a port, make sure the port is not used by another
application or database application or database
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent className="flex w-full flex-col gap-4"> <CardContent className="flex w-full flex-col gap-4">
{!getIp && ( {!getIp && (
<AlertBlock type="warning"> <AlertBlock type="warning">
You need to set an IP address in your{" "} You need to set an IP address in your{" "}
<Link <Link
href="/dashboard/settings/server" href="/dashboard/settings/server"
className="text-primary" className="text-primary"
> >
{data?.serverId {data?.serverId
? "Remote Servers -> Server -> Edit Server -> Update IP Address" ? "Remote Servers -> Server -> Edit Server -> Update IP Address"
: "Web Server -> Server -> Update Server IP"} : "Web Server -> Server -> Update Server IP"}
</Link>{" "} </Link>{" "}
to fix the database url connection. to fix the database url connection.
</AlertBlock> </AlertBlock>
)} )}
<Form {...form}> <Form {...form}>
<form <form
onSubmit={form.handleSubmit(onSubmit)} onSubmit={form.handleSubmit(onSubmit)}
className="flex flex-col gap-4" className="flex flex-col gap-4"
> >
<div className="grid grid-cols-2 gap-4 "> <div className="grid grid-cols-2 gap-4 ">
<div className="col-span-2 space-y-4"> <div className="col-span-2 space-y-4">
<FormField <FormField
control={form.control} control={form.control}
name="externalPort" name="externalPort"
render={({ field }) => { render={({ field }) => {
return ( return (
<FormItem> <FormItem>
<FormLabel>External Port (Internet)</FormLabel> <FormLabel>External Port (Internet)</FormLabel>
<FormControl> <FormControl>
<Input <Input
placeholder="5432" placeholder="5432"
{...field} {...field}
value={field.value || ""} value={field.value || ""}
/> />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
); );
}} }}
/> />
</div> </div>
</div> </div>
{!!data?.externalPort && ( {!!data?.externalPort && (
<div className="grid w-full gap-8"> <div className="grid w-full gap-8">
<div className="flex flex-col gap-3"> <div className="flex flex-col gap-3">
<Label>External Host</Label> <Label>External Host</Label>
<ToggleVisibilityInput value={connectionUrl} disabled /> <ToggleVisibilityInput value={connectionUrl} disabled />
</div> </div>
</div> </div>
)} )}
<div className="flex justify-end"> <div className="flex justify-end">
<Button type="submit" isLoading={isLoading}> <Button type="submit" isLoading={isLoading}>
Save Save
</Button> </Button>
</div> </div>
</form> </form>
</Form> </Form>
</CardContent> </CardContent>
</Card> </Card>
</div> </div>
</> </>
); );
}; };

View File

@@ -10,14 +10,7 @@ import {
} from "@/components/ui/tooltip"; } from "@/components/ui/tooltip";
import { api } from "@/utils/api"; import { api } from "@/utils/api";
import * as TooltipPrimitive from "@radix-ui/react-tooltip"; import * as TooltipPrimitive from "@radix-ui/react-tooltip";
import { import { Ban, CheckCircle2, RefreshCcw, Rocket, Terminal } from "lucide-react";
Ban,
CheckCircle2,
HelpCircle,
RefreshCcw,
Rocket,
Terminal,
} from "lucide-react";
import { useState } from "react"; import { useState } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import { type LogLine, parseLogs } from "../../docker/logs/utils"; import { type LogLine, parseLogs } from "../../docker/logs/utils";

View File

@@ -5,58 +5,58 @@ import { Label } from "@/components/ui/label";
import { api } from "@/utils/api"; import { api } from "@/utils/api";
interface Props { interface Props {
postgresId: string; postgresId: string;
} }
export const ShowInternalPostgresCredentials = ({ postgresId }: Props) => { export const ShowInternalPostgresCredentials = ({ postgresId }: Props) => {
const { data } = api.postgres.one.useQuery({ postgresId }); const { data } = api.postgres.one.useQuery({ postgresId });
return ( return (
<> <>
<div className="flex w-full flex-col gap-5 "> <div className="flex w-full flex-col gap-5 ">
<Card className="bg-background"> <Card className="bg-background">
<CardHeader> <CardHeader>
<CardTitle className="text-xl">Internal Credentials</CardTitle> <CardTitle className="text-xl">Internal Credentials</CardTitle>
</CardHeader> </CardHeader>
<CardContent className="flex w-full flex-row gap-4"> <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="grid w-full md:grid-cols-2 gap-4 md:gap-8">
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<Label>User</Label> <Label>User</Label>
<Input disabled value={data?.databaseUser} /> <Input disabled value={data?.databaseUser} />
</div> </div>
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<Label>Database Name</Label> <Label>Database Name</Label>
<Input disabled value={data?.databaseName} /> <Input disabled value={data?.databaseName} />
</div> </div>
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<Label>Password</Label> <Label>Password</Label>
<div className="flex flex-row gap-4"> <div className="flex flex-row gap-4">
<ToggleVisibilityInput <ToggleVisibilityInput
value={data?.databasePassword} value={data?.databasePassword}
disabled disabled
/> />
</div> </div>
</div> </div>
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<Label>Internal Port (Container)</Label> <Label>Internal Port (Container)</Label>
<Input disabled value="5432" /> <Input disabled value="5432" />
</div> </div>
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<Label>Internal Host</Label> <Label>Internal Host</Label>
<Input disabled value={data?.appName} /> <Input disabled value={data?.appName} />
</div> </div>
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<Label>Internal Connection URL </Label> <Label>Internal Connection URL </Label>
<ToggleVisibilityInput <ToggleVisibilityInput
disabled disabled
value={`postgresql://${data?.databaseUser}:${data?.databasePassword}@${data?.appName}:5432/${data?.databaseName}`} value={`postgresql://${data?.databaseUser}:${data?.databasePassword}@${data?.appName}:5432/${data?.databaseName}`}
/> />
</div> </div>
</div> </div>
</CardContent> </CardContent>
</Card> </Card>
</div> </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 // 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

@@ -28,139 +28,139 @@ import { toast } from "sonner";
import { z } from "zod"; import { z } from "zod";
const updatePostgresSchema = z.object({ const updatePostgresSchema = z.object({
name: z.string().min(1, { name: z.string().min(1, {
message: "Name is required", message: "Name is required",
}), }),
description: z.string().optional(), description: z.string().optional(),
}); });
type UpdatePostgres = z.infer<typeof updatePostgresSchema>; type UpdatePostgres = z.infer<typeof updatePostgresSchema>;
interface Props { interface Props {
postgresId: string; postgresId: string;
} }
export const UpdatePostgres = ({ postgresId }: Props) => { export const UpdatePostgres = ({ postgresId }: Props) => {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const utils = api.useUtils(); const utils = api.useUtils();
const { mutateAsync, error, isError, isLoading } = const { mutateAsync, error, isError, isLoading } =
api.postgres.update.useMutation(); api.postgres.update.useMutation();
const { data } = api.postgres.one.useQuery( const { data } = api.postgres.one.useQuery(
{ {
postgresId, postgresId,
}, },
{ {
enabled: !!postgresId, enabled: !!postgresId,
} },
); );
const form = useForm<UpdatePostgres>({ const form = useForm<UpdatePostgres>({
defaultValues: { defaultValues: {
description: data?.description ?? "", description: data?.description ?? "",
name: data?.name ?? "", name: data?.name ?? "",
}, },
resolver: zodResolver(updatePostgresSchema), resolver: zodResolver(updatePostgresSchema),
}); });
useEffect(() => { useEffect(() => {
if (data) { if (data) {
form.reset({ form.reset({
description: data.description ?? "", description: data.description ?? "",
name: data.name, name: data.name,
}); });
} }
}, [data, form, form.reset]); }, [data, form, form.reset]);
const onSubmit = async (formData: UpdatePostgres) => { const onSubmit = async (formData: UpdatePostgres) => {
await mutateAsync({ await mutateAsync({
name: formData.name, name: formData.name,
postgresId: postgresId, postgresId: postgresId,
description: formData.description || "", description: formData.description || "",
}) })
.then(() => { .then(() => {
toast.success("Postgres updated successfully"); toast.success("Postgres updated successfully");
utils.postgres.one.invalidate({ utils.postgres.one.invalidate({
postgresId: postgresId, postgresId: postgresId,
}); });
setIsOpen(false); setIsOpen(false);
}) })
.catch(() => { .catch(() => {
toast.error("Error updating Postgres"); toast.error("Error updating Postgres");
}) })
.finally(() => {}); .finally(() => {});
}; };
return ( return (
<Dialog open={isOpen} onOpenChange={setIsOpen}> <Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button <Button
variant="ghost" variant="ghost"
size="icon" size="icon"
className="group hover:bg-blue-500/10 focus-visible:ring-2 focus-visible:ring-offset-2" className="group hover:bg-blue-500/10 focus-visible:ring-2 focus-visible:ring-offset-2"
> >
<PenBox className="size-3.5 text-primary group-hover:text-blue-500" /> <PenBox className="size-3.5 text-primary group-hover:text-blue-500" />
</Button> </Button>
</DialogTrigger> </DialogTrigger>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg"> <DialogContent className="max-h-screen overflow-y-auto sm:max-w-lg">
<DialogHeader> <DialogHeader>
<DialogTitle>Modify Postgres</DialogTitle> <DialogTitle>Modify Postgres</DialogTitle>
<DialogDescription>Update the Postgres data</DialogDescription> <DialogDescription>Update the Postgres data</DialogDescription>
</DialogHeader> </DialogHeader>
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>} {isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
<div className="grid gap-4"> <div className="grid gap-4">
<div className="grid items-center gap-4"> <div className="grid items-center gap-4">
<Form {...form}> <Form {...form}>
<form <form
onSubmit={form.handleSubmit(onSubmit)} onSubmit={form.handleSubmit(onSubmit)}
id="hook-form-update-postgres" id="hook-form-update-postgres"
className="grid w-full gap-4 " className="grid w-full gap-4 "
> >
<FormField <FormField
control={form.control} control={form.control}
name="name" name="name"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>Name</FormLabel> <FormLabel>Name</FormLabel>
<FormControl> <FormControl>
<Input placeholder="Vandelay Industries" {...field} /> <Input placeholder="Vandelay Industries" {...field} />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
)} )}
/> />
<FormField <FormField
control={form.control} control={form.control}
name="description" name="description"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>Description</FormLabel> <FormLabel>Description</FormLabel>
<FormControl> <FormControl>
<Textarea <Textarea
placeholder="Description about your project..." placeholder="Description about your project..."
className="resize-none" className="resize-none"
{...field} {...field}
/> />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
)} )}
/> />
<DialogFooter> <DialogFooter>
<Button <Button
isLoading={isLoading} isLoading={isLoading}
form="hook-form-update-postgres" form="hook-form-update-postgres"
type="submit" type="submit"
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2" className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
> >
Update Update
</Button> </Button>
</DialogFooter> </DialogFooter>
</form> </form>
</Form> </Form>
</div> </div>
</div> </div>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
); );
}; };

View File

@@ -22,7 +22,7 @@ const examples = [
"Sendgrid service opensource analogue", "Sendgrid service opensource analogue",
]; ];
export const StepOne = ({ nextStep, setTemplateInfo, templateInfo }: any) => { export const StepOne = ({ setTemplateInfo, templateInfo }: any) => {
// Get servers from the API // Get servers from the API
const { data: servers } = api.server.withSSHKey.useQuery(); const { data: servers } = api.server.withSSHKey.useQuery();

View File

@@ -27,138 +27,142 @@ import { toast } from "sonner";
import { z } from "zod"; import { z } from "zod";
const DockerProviderSchema = z.object({ const DockerProviderSchema = z.object({
externalPort: z.preprocess((a) => { externalPort: z.preprocess((a) => {
if (a !== null) { if (a !== null) {
const parsed = Number.parseInt(z.string().parse(a), 10); const parsed = Number.parseInt(z.string().parse(a), 10);
return Number.isNaN(parsed) ? null : parsed; return Number.isNaN(parsed) ? null : parsed;
} }
return null; return null;
}, z.number().gte(0, "Range must be 0 - 65535").lte(65535, "Range must be 0 - 65535").nullable()), }, z
.number()
.gte(0, "Range must be 0 - 65535")
.lte(65535, "Range must be 0 - 65535")
.nullable()),
}); });
type DockerProvider = z.infer<typeof DockerProviderSchema>; type DockerProvider = z.infer<typeof DockerProviderSchema>;
interface Props { interface Props {
redisId: string; redisId: string;
} }
export const ShowExternalRedisCredentials = ({ redisId }: Props) => { export const ShowExternalRedisCredentials = ({ redisId }: Props) => {
const { data: ip } = api.settings.getIp.useQuery(); const { data: ip } = api.settings.getIp.useQuery();
const { data, refetch } = api.redis.one.useQuery({ redisId }); const { data, refetch } = api.redis.one.useQuery({ redisId });
const { mutateAsync, isLoading } = api.redis.saveExternalPort.useMutation(); const { mutateAsync, isLoading } = api.redis.saveExternalPort.useMutation();
const [connectionUrl, setConnectionUrl] = useState(""); const [connectionUrl, setConnectionUrl] = useState("");
const getIp = data?.server?.ipAddress || ip; const getIp = data?.server?.ipAddress || ip;
const form = useForm<DockerProvider>({ const form = useForm<DockerProvider>({
defaultValues: {}, defaultValues: {},
resolver: zodResolver(DockerProviderSchema), resolver: zodResolver(DockerProviderSchema),
}); });
useEffect(() => { useEffect(() => {
if (data?.externalPort) { if (data?.externalPort) {
form.reset({ form.reset({
externalPort: data.externalPort, externalPort: data.externalPort,
}); });
} }
}, [form.reset, data, form]); }, [form.reset, data, form]);
const onSubmit = async (values: DockerProvider) => { const onSubmit = async (values: DockerProvider) => {
await mutateAsync({ await mutateAsync({
externalPort: values.externalPort, externalPort: values.externalPort,
redisId, redisId,
}) })
.then(async () => { .then(async () => {
toast.success("External Port updated"); toast.success("External Port updated");
await refetch(); await refetch();
}) })
.catch(() => { .catch(() => {
toast.error("Error saving the external port"); toast.error("Error saving the external port");
}); });
}; };
useEffect(() => { useEffect(() => {
const buildConnectionUrl = () => { const buildConnectionUrl = () => {
const _hostname = window.location.hostname; const _hostname = window.location.hostname;
const port = form.watch("externalPort") || data?.externalPort; const port = form.watch("externalPort") || data?.externalPort;
return `redis://default:${data?.databasePassword}@${getIp}:${port}`; return `redis://default:${data?.databasePassword}@${getIp}:${port}`;
}; };
setConnectionUrl(buildConnectionUrl()); setConnectionUrl(buildConnectionUrl());
}, [data?.appName, data?.externalPort, data?.databasePassword, form, getIp]); }, [data?.appName, data?.externalPort, data?.databasePassword, form, getIp]);
return ( return (
<> <>
<div className="flex w-full flex-col gap-5 "> <div className="flex w-full flex-col gap-5 ">
<Card className="bg-background"> <Card className="bg-background">
<CardHeader> <CardHeader>
<CardTitle className="text-xl">External Credentials</CardTitle> <CardTitle className="text-xl">External Credentials</CardTitle>
<CardDescription> <CardDescription>
In order to make the database reachable trought internet is In order to make the database reachable trought internet is
required to set a port, make sure the port is not used by another required to set a port, make sure the port is not used by another
application or database application or database
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent className="flex w-full flex-col gap-4"> <CardContent className="flex w-full flex-col gap-4">
{!getIp && ( {!getIp && (
<AlertBlock type="warning"> <AlertBlock type="warning">
You need to set an IP address in your{" "} You need to set an IP address in your{" "}
<Link <Link
href="/dashboard/settings/server" href="/dashboard/settings/server"
className="text-primary" className="text-primary"
> >
{data?.serverId {data?.serverId
? "Remote Servers -> Server -> Edit Server -> Update IP Address" ? "Remote Servers -> Server -> Edit Server -> Update IP Address"
: "Web Server -> Server -> Update Server IP"} : "Web Server -> Server -> Update Server IP"}
</Link>{" "} </Link>{" "}
to fix the database url connection. to fix the database url connection.
</AlertBlock> </AlertBlock>
)} )}
<Form {...form}> <Form {...form}>
<form <form
onSubmit={form.handleSubmit(onSubmit)} onSubmit={form.handleSubmit(onSubmit)}
className="flex flex-col gap-4" className="flex flex-col gap-4"
> >
<div className="grid grid-cols-2 gap-4 "> <div className="grid grid-cols-2 gap-4 ">
<div className="col-span-2 space-y-4"> <div className="col-span-2 space-y-4">
<FormField <FormField
control={form.control} control={form.control}
name="externalPort" name="externalPort"
render={({ field }) => { render={({ field }) => {
return ( return (
<FormItem> <FormItem>
<FormLabel>External Port (Internet)</FormLabel> <FormLabel>External Port (Internet)</FormLabel>
<FormControl> <FormControl>
<Input <Input
placeholder="6379" placeholder="6379"
{...field} {...field}
value={field.value || ""} value={field.value || ""}
/> />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
); );
}} }}
/> />
</div> </div>
</div> </div>
{!!data?.externalPort && ( {!!data?.externalPort && (
<div className="grid w-full gap-8"> <div className="grid w-full gap-8">
<div className="flex flex-col gap-3"> <div className="flex flex-col gap-3">
<Label>External Host</Label> <Label>External Host</Label>
<ToggleVisibilityInput value={connectionUrl} disabled /> <ToggleVisibilityInput value={connectionUrl} disabled />
</div> </div>
</div> </div>
)} )}
<div className="flex justify-end"> <div className="flex justify-end">
<Button type="submit" isLoading={isLoading}> <Button type="submit" isLoading={isLoading}>
Save Save
</Button> </Button>
</div> </div>
</form> </form>
</Form> </Form>
</CardContent> </CardContent>
</Card> </Card>
</div> </div>
</> </>
); );
}; };

View File

@@ -10,14 +10,7 @@ import {
} from "@/components/ui/tooltip"; } from "@/components/ui/tooltip";
import { api } from "@/utils/api"; import { api } from "@/utils/api";
import * as TooltipPrimitive from "@radix-ui/react-tooltip"; import * as TooltipPrimitive from "@radix-ui/react-tooltip";
import { import { Ban, CheckCircle2, RefreshCcw, Rocket, Terminal } from "lucide-react";
Ban,
CheckCircle2,
HelpCircle,
RefreshCcw,
Rocket,
Terminal,
} from "lucide-react";
import { useState } from "react"; import { useState } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import { type LogLine, parseLogs } from "../../docker/logs/utils"; import { type LogLine, parseLogs } from "../../docker/logs/utils";

View File

@@ -124,10 +124,10 @@ export const ShowGitProviders = () => {
gitProvider.gitlab?.accessToken && gitProvider.gitlab?.accessToken &&
gitProvider.gitlab?.refreshToken; gitProvider.gitlab?.refreshToken;
const haveGiteaRequirements = // const haveGiteaRequirements =
isGitea && // isGitea &&
gitProvider.gitea?.accessToken && // gitProvider.gitea?.accessToken &&
gitProvider.gitea?.refreshToken; // gitProvider.gitea?.refreshToken;
return ( return (
<div <div

View File

@@ -39,7 +39,11 @@ const NumberInput = React.forwardRef<HTMLInputElement, InputProps>(
className={cn("text-left", className)} className={cn("text-left", className)}
ref={ref} ref={ref}
{...props} {...props}
value={props.value === undefined || props.value === "" ? "" : String(props.value)} value={
props.value === undefined || props.value === ""
? ""
: String(props.value)
}
onChange={(e) => { onChange={(e) => {
const value = e.target.value; const value = e.target.value;
if (value === "") { if (value === "") {

View File

@@ -29,21 +29,21 @@ import {
updateBackupById, updateBackupById,
} from "@dokploy/server"; } from "@dokploy/server";
import { TRPCError } from "@trpc/server"; import { findDestinationById } from "@dokploy/server/services/destination";
import { z } from "zod"; import { getS3Credentials } from "@dokploy/server/utils/backups/utils";
import { import {
execAsync, execAsync,
execAsyncRemote, execAsyncRemote,
} from "@dokploy/server/utils/process/execAsync"; } from "@dokploy/server/utils/process/execAsync";
import { getS3Credentials } from "@dokploy/server/utils/backups/utils";
import { findDestinationById } from "@dokploy/server/services/destination";
import { import {
restoreMariadbBackup, restoreMariadbBackup,
restoreMongoBackup, restoreMongoBackup,
restoreMySqlBackup, restoreMySqlBackup,
restorePostgresBackup, restorePostgresBackup,
} from "@dokploy/server/utils/restore"; } from "@dokploy/server/utils/restore";
import { TRPCError } from "@trpc/server";
import { observable } from "@trpc/server/observable"; import { observable } from "@trpc/server/observable";
import { z } from "zod";
export const backupRouter = createTRPCRouter({ export const backupRouter = createTRPCRouter({
create: protectedProcedure create: protectedProcedure

View File

@@ -2,16 +2,16 @@ import type { IncomingMessage } from "node:http";
import * as bcrypt from "bcrypt"; import * as bcrypt from "bcrypt";
import { betterAuth } from "better-auth"; import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle"; import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { APIError } from "better-auth/api";
import { apiKey, organization, twoFactor } from "better-auth/plugins"; import { apiKey, organization, twoFactor } from "better-auth/plugins";
import { and, desc, eq } from "drizzle-orm"; import { and, desc, eq } from "drizzle-orm";
import { IS_CLOUD } from "../constants";
import { db } from "../db"; import { db } from "../db";
import * as schema from "../db/schema"; import * as schema from "../db/schema";
import { sendEmail } from "../verification/send-verification-email";
import { IS_CLOUD } from "../constants";
import { getPublicIpWithFallback } from "../wss/utils";
import { updateUser } from "../services/user";
import { getUserByToken } from "../services/admin"; import { getUserByToken } from "../services/admin";
import { APIError } from "better-auth/api"; import { updateUser } from "../services/user";
import { sendEmail } from "../verification/send-verification-email";
import { getPublicIpWithFallback } from "../wss/utils";
const { handler, api } = betterAuth({ const { handler, api } = betterAuth({
database: drizzleAdapter(db, { database: drizzleAdapter(db, {

View File

@@ -101,11 +101,11 @@ export const initializeTraefik = async ({
console.log("Waiting for service cleanup..."); console.log("Waiting for service cleanup...");
await new Promise((resolve) => setTimeout(resolve, 5000)); await new Promise((resolve) => setTimeout(resolve, 5000));
attempts++; attempts++;
} catch (e) { } catch (_e) {
break; break;
} }
} }
} catch (err) { } catch (_err) {
console.log("No existing service to remove"); console.log("No existing service to remove");
} }
@@ -120,7 +120,7 @@ export const initializeTraefik = async ({
await container.remove({ force: true }); await container.remove({ force: true });
await new Promise((resolve) => setTimeout(resolve, 5000)); await new Promise((resolve) => setTimeout(resolve, 5000));
} catch (error) { } catch (_err) {
console.log("No existing container to remove"); console.log("No existing container to remove");
} }

View File

@@ -1,11 +1,11 @@
import type { Mariadb } from "@dokploy/server/services/mariadb";
import type { Destination } from "@dokploy/server/services/destination"; import type { Destination } from "@dokploy/server/services/destination";
import type { Mariadb } from "@dokploy/server/services/mariadb";
import { getS3Credentials } from "../backups/utils";
import { import {
getRemoteServiceContainer, getRemoteServiceContainer,
getServiceContainer, getServiceContainer,
} from "../docker/utils"; } from "../docker/utils";
import { execAsync, execAsyncRemote } from "../process/execAsync"; import { execAsync, execAsyncRemote } from "../process/execAsync";
import { getS3Credentials } from "../backups/utils";
export const restoreMariadbBackup = async ( export const restoreMariadbBackup = async (
mariadb: Mariadb, mariadb: Mariadb,

View File

@@ -1,11 +1,11 @@
import type { Mongo } from "@dokploy/server/services/mongo";
import type { Destination } from "@dokploy/server/services/destination"; import type { Destination } from "@dokploy/server/services/destination";
import type { Mongo } from "@dokploy/server/services/mongo";
import { getS3Credentials } from "../backups/utils";
import { import {
getRemoteServiceContainer, getRemoteServiceContainer,
getServiceContainer, getServiceContainer,
} from "../docker/utils"; } from "../docker/utils";
import { execAsync, execAsyncRemote } from "../process/execAsync"; import { execAsync, execAsyncRemote } from "../process/execAsync";
import { getS3Credentials } from "../backups/utils";
export const restoreMongoBackup = async ( export const restoreMongoBackup = async (
mongo: Mongo, mongo: Mongo,

View File

@@ -1,11 +1,11 @@
import type { MySql } from "@dokploy/server/services/mysql";
import type { Destination } from "@dokploy/server/services/destination"; import type { Destination } from "@dokploy/server/services/destination";
import type { MySql } from "@dokploy/server/services/mysql";
import { getS3Credentials } from "../backups/utils";
import { import {
getRemoteServiceContainer, getRemoteServiceContainer,
getServiceContainer, getServiceContainer,
} from "../docker/utils"; } from "../docker/utils";
import { execAsync, execAsyncRemote } from "../process/execAsync"; import { execAsync, execAsyncRemote } from "../process/execAsync";
import { getS3Credentials } from "../backups/utils";
export const restoreMySqlBackup = async ( export const restoreMySqlBackup = async (
mysql: MySql, mysql: MySql,

View File

@@ -1,11 +1,11 @@
import type { Postgres } from "@dokploy/server/services/postgres";
import type { Destination } from "@dokploy/server/services/destination"; import type { Destination } from "@dokploy/server/services/destination";
import type { Postgres } from "@dokploy/server/services/postgres";
import { getS3Credentials } from "../backups/utils";
import { import {
getRemoteServiceContainer, getRemoteServiceContainer,
getServiceContainer, getServiceContainer,
} from "../docker/utils"; } from "../docker/utils";
import { execAsync, execAsyncRemote } from "../process/execAsync"; import { execAsync, execAsyncRemote } from "../process/execAsync";
import { getS3Credentials } from "../backups/utils";
export const restorePostgresBackup = async ( export const restorePostgresBackup = async (
postgres: Postgres, postgres: Postgres,