Compare commits

...

9 Commits

Author SHA1 Message Date
Mauricio Siu
172694be30 Merge pull request #1530 from Dokploy/feat/add-date-to-restore-item
feat(backup): enhance RestoreBackup component and API to include serv…
2025-03-18 00:49:02 -06:00
Mauricio Siu
ea6cfc9d29 feat(backup): enhance RestoreBackup component and API to include serverId
- Added serverId prop to RestoreBackup component for better context during backup restoration.
- Updated ShowBackups component to pass serverId from the Postgres object.
- Modified backup API to handle serverId, allowing remote execution of backup commands when specified.
- Improved file display in RestoreBackup for better user experience.
2025-03-18 00:47:50 -06:00
Mauricio Siu
4fa5e10789 chore(package): bump version to v0.20.6 2025-03-18 00:18:39 -06:00
Mauricio Siu
cb7fbb777c Merge pull request #1528 from Dokploy/1524-getting-502-bad-gateway
1524 getting 502 bad gateway
2025-03-18 00:15:12 -06:00
Mauricio Siu
6a388fe370 feat(domain): add validation for traefik.me domain IP address requirement
- Implemented a check to ensure an IP address is set for traefik.me domains in the AddDomain and AddDomainCompose components.
- Integrated a new API query to determine if traefik.me domains can be generated based on the server's IP address.
- Added user feedback through alert messages when the IP address is not configured.
2025-03-18 00:13:55 -06:00
Mauricio Siu
0722182650 feat(auth): implement user creation validation and IP update logic
- Added validation for user creation to check for existing admin presence and validate x-dokploy-token.
- Integrated public IP retrieval for user updates when not in cloud environment.
- Enhanced error handling with APIError for better feedback during user creation process.
2025-03-17 23:59:39 -06:00
Mauricio Siu
5e1095d199 Merge pull request #1526 from Dokploy/fix/mongo-db-button-deploy
refactor: improve code formatting and structure in ShowGeneralMongo c…
2025-03-17 23:18:18 -06:00
Mauricio Siu
c80a31e8c4 refactor: improve code formatting and structure in ShowGeneralMongo component
- Standardized indentation and formatting for better readability.
- Enhanced tooltip integration within button elements for improved user experience.
- Maintained functionality for deploying, reloading, starting, and stopping MongoDB instances while ensuring consistent code style.
2025-03-17 23:16:29 -06:00
Mauricio Siu
7c17cfb5c7 refactor: improve button structure and tooltip integration across dashboard components
- Refactored button components in the dashboard to enhance structure and readability.
- Integrated tooltips directly within button elements for better user experience.
- Updated tooltip descriptions for clarity across various database actions (Deploy, Reload, Start, Stop) for Redis, MySQL, PostgreSQL, and MariaDB.
- Ensured consistent formatting and improved code maintainability.
2025-03-16 20:52:57 -06:00
15 changed files with 1353 additions and 1148 deletions

View File

@@ -42,6 +42,7 @@ import { domain } from "@/server/db/validations/domain";
import { zodResolver } from "@hookform/resolvers/zod";
import { Dices } from "lucide-react";
import type z from "zod";
import Link from "next/link";
type Domain = z.infer<typeof domain>;
@@ -83,6 +84,13 @@ export const AddDomain = ({
const { mutateAsync: generateDomain, isLoading: isLoadingGenerate } =
api.domain.generateDomain.useMutation();
const { data: canGenerateTraefikMeDomains } =
api.domain.canGenerateTraefikMeDomains.useQuery({
serverId: application?.serverId || "",
});
console.log("canGenerateTraefikMeDomains", canGenerateTraefikMeDomains);
const form = useForm<Domain>({
resolver: zodResolver(domain),
defaultValues: {
@@ -186,6 +194,21 @@ export const AddDomain = ({
name="host"
render={({ field }) => (
<FormItem>
{!canGenerateTraefikMeDomains &&
field.value.includes("traefik.me") && (
<AlertBlock type="warning">
You need to set an IP address in your{" "}
<Link
href="/dashboard/settings/server"
className="text-primary"
>
{application?.serverId
? "Remote Servers -> Server -> Edit Server -> Update IP Address"
: "Web Server -> Server -> Update Server IP"}
</Link>{" "}
to make your traefik.me domain work.
</AlertBlock>
)}
<FormLabel>Host</FormLabel>
<div className="flex gap-2">
<FormControl>

View File

@@ -76,25 +76,27 @@ export const ShowGeneralApplication = ({ applicationId }: Props) => {
});
}}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="default"
isLoading={data?.applicationStatus === "running"}
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Rocket className="size-4 mr-1" />
Deploy
</Button>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>
Downloads the source code and performs a complete build
</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
<Button
variant="default"
isLoading={data?.applicationStatus === "running"}
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center">
<Rocket className="size-4 mr-1" />
Deploy
</div>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>
Downloads the source code and performs a complete build
</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction>
<DialogAction
title="Reload Application"
@@ -114,23 +116,25 @@ export const ShowGeneralApplication = ({ applicationId }: Props) => {
});
}}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="secondary"
isLoading={isReloading}
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
>
<RefreshCcw className="size-4 mr-1" />
Reload
</Button>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Reload the application without rebuilding it</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
<Button
variant="secondary"
isLoading={isReloading}
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center">
<RefreshCcw className="size-4 mr-1" />
Reload
</div>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Reload the application without rebuilding it</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction>
<DialogAction
title="Rebuild Application"
@@ -149,25 +153,28 @@ export const ShowGeneralApplication = ({ applicationId }: Props) => {
});
}}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="secondary"
isLoading={data?.applicationStatus === "running"}
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Hammer className="size-4 mr-1" />
Rebuild
</Button>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>
Only rebuilds the application without downloading new code
</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
<Button
variant="secondary"
isLoading={data?.applicationStatus === "running"}
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center">
<Hammer className="size-4 mr-1" />
Rebuild
</div>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>
Only rebuilds the application without downloading new
code
</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction>
{data?.applicationStatus === "idle" ? (
@@ -188,26 +195,28 @@ export const ShowGeneralApplication = ({ applicationId }: Props) => {
});
}}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="secondary"
isLoading={isStarting}
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
>
<CheckCircle2 className="size-4 mr-1" />
Start
</Button>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>
Start the application (requires a previous successful
build)
</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
<Button
variant="secondary"
isLoading={isStarting}
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center">
<CheckCircle2 className="size-4 mr-1" />
Start
</div>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>
Start the application (requires a previous successful
build)
</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction>
) : (
<DialogAction
@@ -226,23 +235,25 @@ export const ShowGeneralApplication = ({ applicationId }: Props) => {
});
}}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="destructive"
isLoading={isStopping}
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Ban className="size-4 mr-1" />
Stop
</Button>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Stop the currently running application</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
<Button
variant="destructive"
isLoading={isStopping}
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center">
<Ban className="size-4 mr-1" />
Stop
</div>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Stop the currently running application</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction>
)}
</TooltipProvider>

View File

@@ -42,6 +42,7 @@ import { domainCompose } from "@/server/db/validations/domain";
import { zodResolver } from "@hookform/resolvers/zod";
import { DatabaseZap, Dices, RefreshCw } from "lucide-react";
import type z from "zod";
import Link from "next/link";
type Domain = z.infer<typeof domainCompose>;
@@ -102,6 +103,11 @@ export const AddDomainCompose = ({
? api.domain.update.useMutation()
: api.domain.create.useMutation();
const { data: canGenerateTraefikMeDomains } =
api.domain.canGenerateTraefikMeDomains.useQuery({
serverId: compose?.serverId || "",
});
const form = useForm<Domain>({
resolver: zodResolver(domainCompose),
defaultValues: {
@@ -313,6 +319,21 @@ export const AddDomainCompose = ({
name="host"
render={({ field }) => (
<FormItem>
{!canGenerateTraefikMeDomains &&
field.value.includes("traefik.me") && (
<AlertBlock type="warning">
You need to set an IP address in your{" "}
<Link
href="/dashboard/settings/server"
className="text-primary"
>
{compose?.serverId
? "Remote Servers -> Server -> Edit Server -> Update IP Address"
: "Web Server -> Server -> Update Server IP"}
</Link>{" "}
to make your traefik.me domain work.
</AlertBlock>
)}
<FormLabel>Host</FormLabel>
<div className="flex gap-2">
<FormControl>

View File

@@ -55,23 +55,25 @@ export const ComposeActions = ({ composeId }: Props) => {
});
}}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="default"
isLoading={data?.composeStatus === "running"}
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Rocket className="size-4 mr-1" />
Deploy
</Button>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Downloads the source code and performs a complete build</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
<Button
variant="default"
isLoading={data?.composeStatus === "running"}
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center">
<Rocket className="size-4 mr-1" />
Deploy
</div>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Downloads the source code and performs a complete build</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction>
<DialogAction
title="Reload Compose"
@@ -90,23 +92,25 @@ export const ComposeActions = ({ composeId }: Props) => {
});
}}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="secondary"
isLoading={data?.composeStatus === "running"}
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
>
<RefreshCcw className="size-4 mr-1" />
Reload
</Button>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Reload the compose without rebuilding it</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
<Button
variant="secondary"
isLoading={data?.composeStatus === "running"}
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center">
<RefreshCcw className="size-4 mr-1" />
Reload
</div>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Reload the compose without rebuilding it</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction>
{data?.composeType === "docker-compose" &&
data?.composeStatus === "idle" ? (
@@ -127,25 +131,27 @@ export const ComposeActions = ({ composeId }: Props) => {
});
}}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="secondary"
isLoading={isStarting}
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
>
<CheckCircle2 className="size-4 mr-1" />
Start
</Button>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>
Start the compose (requires a previous successful build)
</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
<Button
variant="secondary"
isLoading={isStarting}
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center">
<CheckCircle2 className="size-4 mr-1" />
Start
</div>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>
Start the compose (requires a previous successful build)
</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction>
) : (
<DialogAction
@@ -164,23 +170,25 @@ export const ComposeActions = ({ composeId }: Props) => {
});
}}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="destructive"
isLoading={isStopping}
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Ban className="size-4 mr-1" />
Stop
</Button>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Stop the currently running compose</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
<Button
variant="destructive"
isLoading={isStopping}
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center">
<Ban className="size-4 mr-1" />
Stop
</div>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Stop the currently running compose</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction>
)}
</TooltipProvider>

View File

@@ -48,6 +48,7 @@ import { toast } from "sonner";
interface Props {
databaseId: string;
databaseType: Exclude<ServiceType, "application" | "redis">;
serverId: string | null;
}
const RestoreBackupSchema = z.object({
@@ -76,7 +77,11 @@ const RestoreBackupSchema = z.object({
type RestoreBackup = z.infer<typeof RestoreBackupSchema>;
export const RestoreBackup = ({ databaseId, databaseType }: Props) => {
export const RestoreBackup = ({
databaseId,
databaseType,
serverId,
}: Props) => {
const [isOpen, setIsOpen] = useState(false);
const [search, setSearch] = useState("");
@@ -101,6 +106,7 @@ export const RestoreBackup = ({ databaseId, databaseType }: Props) => {
{
destinationId: destionationId,
search,
serverId: serverId ?? "",
},
{
enabled: isOpen && !!destionationId,
@@ -304,7 +310,9 @@ export const RestoreBackup = ({ databaseId, databaseType }: Props) => {
form.setValue("backupFile", file);
}}
>
{file}
<div className="flex w-full justify-between">
<span>{file}</span>
</div>
<CheckIcon
className={cn(
"ml-auto h-4 w-4",

View File

@@ -74,7 +74,11 @@ export const ShowBackups = ({ id, type }: Props) => {
{postgres && postgres?.backups?.length > 0 && (
<div className="flex flex-col lg:flex-row gap-4 w-full lg:w-auto">
<AddBackup databaseId={id} databaseType={type} refetch={refetch} />
<RestoreBackup databaseId={id} databaseType={type} />
<RestoreBackup
databaseId={id}
databaseType={type}
serverId={postgres.serverId}
/>
</div>
)}
</CardHeader>
@@ -108,7 +112,11 @@ export const ShowBackups = ({ id, type }: Props) => {
databaseType={type}
refetch={refetch}
/>
<RestoreBackup databaseId={id} databaseType={type} />
<RestoreBackup
databaseId={id}
databaseType={type}
serverId={postgres.serverId}
/>
</div>
</div>
) : (

View File

@@ -17,236 +17,252 @@ import { type LogLine, parseLogs } from "../../docker/logs/utils";
import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal";
interface Props {
mariadbId: string;
mariadbId: string;
}
export const ShowGeneralMariadb = ({ mariadbId }: Props) => {
const { data, refetch } = api.mariadb.one.useQuery(
{
mariadbId,
},
{ enabled: !!mariadbId }
);
const { data, refetch } = api.mariadb.one.useQuery(
{
mariadbId,
},
{ enabled: !!mariadbId },
);
const { mutateAsync: reload, isLoading: isReloading } =
api.mariadb.reload.useMutation();
const { mutateAsync: reload, isLoading: isReloading } =
api.mariadb.reload.useMutation();
const { mutateAsync: start, isLoading: isStarting } =
api.mariadb.start.useMutation();
const { mutateAsync: start, isLoading: isStarting } =
api.mariadb.start.useMutation();
const { mutateAsync: stop, isLoading: isStopping } =
api.mariadb.stop.useMutation();
const { mutateAsync: stop, isLoading: isStopping } =
api.mariadb.stop.useMutation();
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
const [filteredLogs, setFilteredLogs] = useState<LogLine[]>([]);
const [isDeploying, setIsDeploying] = useState(false);
api.mariadb.deployWithLogs.useSubscription(
{
mariadbId: mariadbId,
},
{
enabled: isDeploying,
onData(log) {
if (!isDrawerOpen) {
setIsDrawerOpen(true);
}
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
const [filteredLogs, setFilteredLogs] = useState<LogLine[]>([]);
const [isDeploying, setIsDeploying] = useState(false);
api.mariadb.deployWithLogs.useSubscription(
{
mariadbId: mariadbId,
},
{
enabled: isDeploying,
onData(log) {
if (!isDrawerOpen) {
setIsDrawerOpen(true);
}
if (log === "Deployment completed successfully!") {
setIsDeploying(false);
}
const parsedLogs = parseLogs(log);
setFilteredLogs((prev) => [...prev, ...parsedLogs]);
},
onError(error) {
console.error("Deployment logs error:", error);
setIsDeploying(false);
},
}
);
if (log === "Deployment completed successfully!") {
setIsDeploying(false);
}
const parsedLogs = parseLogs(log);
setFilteredLogs((prev) => [...prev, ...parsedLogs]);
},
onError(error) {
console.error("Deployment logs error:", error);
setIsDeploying(false);
},
},
);
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">
<TooltipProvider delayDuration={0}>
<DialogAction
title="Deploy Mariadb"
description="Are you sure you want to deploy this mariadb?"
type="default"
onClick={async () => {
setIsDeploying(true);
await new Promise((resolve) => setTimeout(resolve, 1000));
refetch();
}}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="default"
isLoading={data?.applicationStatus === "running"}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Rocket className="size-4 mr-1" />
Deploy
</Button>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Downloads and sets up the MariaDB database</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</DialogAction>
<DialogAction
title="Reload Mariadb"
description="Are you sure you want to reload this mariadb?"
type="default"
onClick={async () => {
await reload({
mariadbId: mariadbId,
appName: data?.appName || "",
})
.then(() => {
toast.success("Mariadb reloaded successfully");
refetch();
})
.catch(() => {
toast.error("Error reloading Mariadb");
});
}}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="secondary"
isLoading={isReloading}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<RefreshCcw className="size-4 mr-1" />
Reload
</Button>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Restart the MariaDB service without rebuilding</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</DialogAction>
{data?.applicationStatus === "idle" ? (
<DialogAction
title="Start Mariadb"
description="Are you sure you want to start this mariadb?"
type="default"
onClick={async () => {
await start({
mariadbId: mariadbId,
})
.then(() => {
toast.success("Mariadb started successfully");
refetch();
})
.catch(() => {
toast.error("Error starting Mariadb");
});
}}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="secondary"
isLoading={isStarting}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<CheckCircle2 className="size-4 mr-1" />
Start
</Button>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>
Start the MariaDB database (requires a previous
successful setup)
</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</DialogAction>
) : (
<DialogAction
title="Stop Mariadb"
description="Are you sure you want to stop this mariadb?"
onClick={async () => {
await stop({
mariadbId: mariadbId,
})
.then(() => {
toast.success("Mariadb stopped successfully");
refetch();
})
.catch(() => {
toast.error("Error stopping Mariadb");
});
}}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="destructive"
isLoading={isStopping}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Ban className="size-4 mr-1" />
Stop
</Button>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Stop the currently running MariaDB database</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</DialogAction>
)}
</TooltipProvider>
<DockerTerminalModal
appName={data?.appName || ""}
serverId={data?.serverId || ""}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="outline"
className="flex items-center gap-2 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Terminal className="size-4" />
Open Terminal
</Button>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Open a terminal to the MariaDB container</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</DockerTerminalModal>
</CardContent>
</Card>
<DrawerLogs
isOpen={isDrawerOpen}
onClose={() => {
setIsDrawerOpen(false);
setFilteredLogs([]);
setIsDeploying(false);
refetch();
}}
filteredLogs={filteredLogs}
/>
</div>
</>
);
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">
<TooltipProvider delayDuration={0}>
<DialogAction
title="Deploy Mariadb"
description="Are you sure you want to deploy this mariadb?"
type="default"
onClick={async () => {
setIsDeploying(true);
await new Promise((resolve) => setTimeout(resolve, 1000));
refetch();
}}
>
<Button
variant="default"
isLoading={data?.applicationStatus === "running"}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center">
<Rocket className="size-4 mr-1" />
Deploy
</div>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Downloads and sets up the MariaDB database</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction>
</TooltipProvider>
<TooltipProvider delayDuration={0}>
<DialogAction
title="Reload Mariadb"
description="Are you sure you want to reload this mariadb?"
type="default"
onClick={async () => {
await reload({
mariadbId: mariadbId,
appName: data?.appName || "",
})
.then(() => {
toast.success("Mariadb reloaded successfully");
refetch();
})
.catch(() => {
toast.error("Error reloading Mariadb");
});
}}
>
<Button
variant="secondary"
isLoading={isReloading}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center">
<RefreshCcw className="size-4 mr-1" />
Reload
</div>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Restart the MariaDB service without rebuilding</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction>
</TooltipProvider>
{data?.applicationStatus === "idle" ? (
<TooltipProvider delayDuration={0}>
<DialogAction
title="Start Mariadb"
description="Are you sure you want to start this mariadb?"
type="default"
onClick={async () => {
await start({
mariadbId: mariadbId,
})
.then(() => {
toast.success("Mariadb started successfully");
refetch();
})
.catch(() => {
toast.error("Error starting Mariadb");
});
}}
>
<Button
variant="secondary"
isLoading={isStarting}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center">
<CheckCircle2 className="size-4 mr-1" />
Start
</div>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>
Start the MariaDB database (requires a previous
successful setup)
</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction>
</TooltipProvider>
) : (
<TooltipProvider delayDuration={0}>
<DialogAction
title="Stop Mariadb"
description="Are you sure you want to stop this mariadb?"
onClick={async () => {
await stop({
mariadbId: mariadbId,
})
.then(() => {
toast.success("Mariadb stopped successfully");
refetch();
})
.catch(() => {
toast.error("Error stopping Mariadb");
});
}}
>
<Button
variant="destructive"
isLoading={isStopping}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center">
<Ban className="size-4 mr-1" />
Stop
</div>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Stop the currently running MariaDB database</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction>
</TooltipProvider>
)}
<DockerTerminalModal
appName={data?.appName || ""}
serverId={data?.serverId || ""}
>
<Button
variant="outline"
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center">
<Terminal className="size-4 mr-1" />
Open Terminal
</div>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Open a terminal to the MariaDB container</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DockerTerminalModal>
</CardContent>
</Card>
<DrawerLogs
isOpen={isDrawerOpen}
onClose={() => {
setIsDrawerOpen(false);
setFilteredLogs([]);
setIsDeploying(false);
refetch();
}}
filteredLogs={filteredLogs}
/>
</div>
</>
);
};

View File

@@ -3,10 +3,10 @@ import { DrawerLogs } from "@/components/shared/drawer-logs";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { api } from "@/utils/api";
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
@@ -16,236 +16,246 @@ import { toast } from "sonner";
import { type LogLine, parseLogs } from "../../docker/logs/utils";
import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal";
interface Props {
mongoId: string;
mongoId: string;
}
export const ShowGeneralMongo = ({ mongoId }: Props) => {
const { data, refetch } = api.mongo.one.useQuery(
{
mongoId,
},
{ enabled: !!mongoId }
);
const { data, refetch } = api.mongo.one.useQuery(
{
mongoId,
},
{ enabled: !!mongoId },
);
const { mutateAsync: reload, isLoading: isReloading } =
api.mongo.reload.useMutation();
const { mutateAsync: reload, isLoading: isReloading } =
api.mongo.reload.useMutation();
const { mutateAsync: start, isLoading: isStarting } =
api.mongo.start.useMutation();
const { mutateAsync: start, isLoading: isStarting } =
api.mongo.start.useMutation();
const { mutateAsync: stop, isLoading: isStopping } =
api.mongo.stop.useMutation();
const { mutateAsync: stop, isLoading: isStopping } =
api.mongo.stop.useMutation();
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
const [filteredLogs, setFilteredLogs] = useState<LogLine[]>([]);
const [isDeploying, setIsDeploying] = useState(false);
api.mongo.deployWithLogs.useSubscription(
{
mongoId: mongoId,
},
{
enabled: isDeploying,
onData(log) {
if (!isDrawerOpen) {
setIsDrawerOpen(true);
}
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
const [filteredLogs, setFilteredLogs] = useState<LogLine[]>([]);
const [isDeploying, setIsDeploying] = useState(false);
api.mongo.deployWithLogs.useSubscription(
{
mongoId: mongoId,
},
{
enabled: isDeploying,
onData(log) {
if (!isDrawerOpen) {
setIsDrawerOpen(true);
}
if (log === "Deployment completed successfully!") {
setIsDeploying(false);
}
if (log === "Deployment completed successfully!") {
setIsDeploying(false);
}
const parsedLogs = parseLogs(log);
setFilteredLogs((prev) => [...prev, ...parsedLogs]);
},
onError(error) {
console.error("Deployment logs error:", error);
setIsDeploying(false);
},
}
);
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">
<TooltipProvider delayDuration={0}>
<DialogAction
title="Deploy Mongo"
description="Are you sure you want to deploy this mongo?"
type="default"
onClick={async () => {
setIsDeploying(true);
await new Promise((resolve) => setTimeout(resolve, 1000));
refetch();
}}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="default"
isLoading={data?.applicationStatus === "running"}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Rocket className="size-4 mr-1" />
Deploy
</Button>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Downloads and sets up the MongoDB database</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</DialogAction>
<DialogAction
title="Reload Mongo"
description="Are you sure you want to reload this mongo?"
type="default"
onClick={async () => {
await reload({
mongoId: mongoId,
appName: data?.appName || "",
})
.then(() => {
toast.success("Mongo reloaded successfully");
refetch();
})
.catch(() => {
toast.error("Error reloading Mongo");
});
}}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="secondary"
isLoading={isReloading}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<RefreshCcw className="size-4 mr-1" />
Reload
</Button>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Restart the MongoDB service without rebuilding</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</DialogAction>
{data?.applicationStatus === "idle" ? (
<DialogAction
title="Start Mongo"
description="Are you sure you want to start this mongo?"
type="default"
onClick={async () => {
await start({
mongoId: mongoId,
})
.then(() => {
toast.success("Mongo started successfully");
refetch();
})
.catch(() => {
toast.error("Error starting Mongo");
});
}}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="secondary"
isLoading={isStarting}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<CheckCircle2 className="size-4 mr-1" />
Start
</Button>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>
Start the MongoDB database (requires a previous
successful setup)
</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</DialogAction>
) : (
<DialogAction
title="Stop Mongo"
description="Are you sure you want to stop this mongo?"
onClick={async () => {
await stop({
mongoId: mongoId,
})
.then(() => {
toast.success("Mongo stopped successfully");
refetch();
})
.catch(() => {
toast.error("Error stopping Mongo");
});
}}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="destructive"
isLoading={isStopping}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Ban className="size-4 mr-1" />
Stop
</Button>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Stop the currently running MongoDB database</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</DialogAction>
)}
</TooltipProvider>
<DockerTerminalModal
appName={data?.appName || ""}
serverId={data?.serverId || ""}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="outline"
className="flex items-center gap-2 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Terminal className="size-4" />
Open Terminal
</Button>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Open a terminal to the MongoDB container</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</DockerTerminalModal>
</CardContent>
</Card>
<DrawerLogs
isOpen={isDrawerOpen}
onClose={() => {
setIsDrawerOpen(false);
setFilteredLogs([]);
setIsDeploying(false);
refetch();
}}
filteredLogs={filteredLogs}
/>
</div>
</>
);
const parsedLogs = parseLogs(log);
setFilteredLogs((prev) => [...prev, ...parsedLogs]);
},
onError(error) {
console.error("Deployment logs error:", error);
setIsDeploying(false);
},
},
);
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">
<TooltipProvider delayDuration={0}>
<DialogAction
title="Deploy Mongo"
description="Are you sure you want to deploy this mongo?"
type="default"
onClick={async () => {
setIsDeploying(true);
await new Promise((resolve) => setTimeout(resolve, 1000));
refetch();
}}
>
<Button
variant="default"
isLoading={data?.applicationStatus === "running"}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center">
<Rocket className="size-4 mr-1" />
Deploy
</div>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Downloads and sets up the MongoDB database</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction>
<DialogAction
title="Reload Mongo"
description="Are you sure you want to reload this mongo?"
type="default"
onClick={async () => {
await reload({
mongoId: mongoId,
appName: data?.appName || "",
})
.then(() => {
toast.success("Mongo reloaded successfully");
refetch();
})
.catch(() => {
toast.error("Error reloading Mongo");
});
}}
>
<Button
variant="secondary"
isLoading={isReloading}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center">
<RefreshCcw className="size-4 mr-1" />
Reload
</div>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Restart the MongoDB service without rebuilding</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction>
{data?.applicationStatus === "idle" ? (
<DialogAction
title="Start Mongo"
description="Are you sure you want to start this mongo?"
type="default"
onClick={async () => {
await start({
mongoId: mongoId,
})
.then(() => {
toast.success("Mongo started successfully");
refetch();
})
.catch(() => {
toast.error("Error starting Mongo");
});
}}
>
<Button
variant="secondary"
isLoading={isStarting}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center">
<CheckCircle2 className="size-4 mr-1" />
Start
</div>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>
Start the MongoDB database (requires a previous
successful setup)
</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction>
) : (
<DialogAction
title="Stop Mongo"
description="Are you sure you want to stop this mongo?"
onClick={async () => {
await stop({
mongoId: mongoId,
})
.then(() => {
toast.success("Mongo stopped successfully");
refetch();
})
.catch(() => {
toast.error("Error stopping Mongo");
});
}}
>
<Button
variant="destructive"
isLoading={isStopping}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center">
<Ban className="size-4 mr-1" />
Stop
</div>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Stop the currently running MongoDB database</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction>
)}
</TooltipProvider>
<DockerTerminalModal
appName={data?.appName || ""}
serverId={data?.serverId || ""}
>
<Button
variant="outline"
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center">
<Terminal className="size-4 mr-1" />
Open Terminal
</div>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Open a terminal to the MongoDB container</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DockerTerminalModal>
</CardContent>
</Card>
<DrawerLogs
isOpen={isDrawerOpen}
onClose={() => {
setIsDrawerOpen(false);
setFilteredLogs([]);
setIsDeploying(false);
refetch();
}}
filteredLogs={filteredLogs}
/>
</div>
</>
);
};

View File

@@ -16,234 +16,244 @@ import { toast } from "sonner";
import { type LogLine, parseLogs } from "../../docker/logs/utils";
import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal";
interface Props {
mysqlId: string;
mysqlId: string;
}
export const ShowGeneralMysql = ({ mysqlId }: Props) => {
const { data, refetch } = api.mysql.one.useQuery(
{
mysqlId,
},
{ enabled: !!mysqlId }
);
const { data, refetch } = api.mysql.one.useQuery(
{
mysqlId,
},
{ enabled: !!mysqlId },
);
const { mutateAsync: reload, isLoading: isReloading } =
api.mysql.reload.useMutation();
const { mutateAsync: start, isLoading: isStarting } =
api.mysql.start.useMutation();
const { mutateAsync: reload, isLoading: isReloading } =
api.mysql.reload.useMutation();
const { mutateAsync: start, isLoading: isStarting } =
api.mysql.start.useMutation();
const { mutateAsync: stop, isLoading: isStopping } =
api.mysql.stop.useMutation();
const { mutateAsync: stop, isLoading: isStopping } =
api.mysql.stop.useMutation();
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
const [filteredLogs, setFilteredLogs] = useState<LogLine[]>([]);
const [isDeploying, setIsDeploying] = useState(false);
api.mysql.deployWithLogs.useSubscription(
{
mysqlId: mysqlId,
},
{
enabled: isDeploying,
onData(log) {
if (!isDrawerOpen) {
setIsDrawerOpen(true);
}
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
const [filteredLogs, setFilteredLogs] = useState<LogLine[]>([]);
const [isDeploying, setIsDeploying] = useState(false);
api.mysql.deployWithLogs.useSubscription(
{
mysqlId: mysqlId,
},
{
enabled: isDeploying,
onData(log) {
if (!isDrawerOpen) {
setIsDrawerOpen(true);
}
if (log === "Deployment completed successfully!") {
setIsDeploying(false);
}
const parsedLogs = parseLogs(log);
setFilteredLogs((prev) => [...prev, ...parsedLogs]);
},
onError(error) {
console.error("Deployment logs error:", error);
setIsDeploying(false);
},
}
);
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">
<TooltipProvider delayDuration={0}>
<DialogAction
title="Deploy Mysql"
description="Are you sure you want to deploy this mysql?"
type="default"
onClick={async () => {
setIsDeploying(true);
await new Promise((resolve) => setTimeout(resolve, 1000));
refetch();
}}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="default"
isLoading={data?.applicationStatus === "running"}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Rocket className="size-4 mr-1" />
Deploy
</Button>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Downloads and sets up the MySQL database</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</DialogAction>
<DialogAction
title="Reload Mysql"
description="Are you sure you want to reload this mysql?"
type="default"
onClick={async () => {
await reload({
mysqlId: mysqlId,
appName: data?.appName || "",
})
.then(() => {
toast.success("Mysql reloaded successfully");
refetch();
})
.catch(() => {
toast.error("Error reloading Mysql");
});
}}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="secondary"
isLoading={isReloading}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<RefreshCcw className="size-4 mr-1" />
Reload
</Button>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Restart the MySQL service without rebuilding</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</DialogAction>
{data?.applicationStatus === "idle" ? (
<DialogAction
title="Start Mysql"
description="Are you sure you want to start this mysql?"
type="default"
onClick={async () => {
await start({
mysqlId: mysqlId,
})
.then(() => {
toast.success("Mysql started successfully");
refetch();
})
.catch(() => {
toast.error("Error starting Mysql");
});
}}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="secondary"
isLoading={isStarting}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<CheckCircle2 className="size-4 mr-1" />
Start
</Button>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>
Start the MySQL database (requires a previous
successful setup)
</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</DialogAction>
) : (
<DialogAction
title="Stop Mysql"
description="Are you sure you want to stop this mysql?"
onClick={async () => {
await stop({
mysqlId: mysqlId,
})
.then(() => {
toast.success("Mysql stopped successfully");
refetch();
})
.catch(() => {
toast.error("Error stopping Mysql");
});
}}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="destructive"
isLoading={isStopping}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Ban className="size-4 mr-1" />
Stop
</Button>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Stop the currently running MySQL database</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</DialogAction>
)}
</TooltipProvider>
<DockerTerminalModal
appName={data?.appName || ""}
serverId={data?.serverId || ""}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="outline"
className="flex items-center gap-2 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Terminal className="size-4" />
Open Terminal
</Button>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Open a terminal to the MySQL container</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</DockerTerminalModal>
</CardContent>
</Card>
<DrawerLogs
isOpen={isDrawerOpen}
onClose={() => {
setIsDrawerOpen(false);
setFilteredLogs([]);
setIsDeploying(false);
refetch();
}}
filteredLogs={filteredLogs}
/>
</div>
</>
);
if (log === "Deployment completed successfully!") {
setIsDeploying(false);
}
const parsedLogs = parseLogs(log);
setFilteredLogs((prev) => [...prev, ...parsedLogs]);
},
onError(error) {
console.error("Deployment logs error:", error);
setIsDeploying(false);
},
},
);
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">
<TooltipProvider delayDuration={0}>
<DialogAction
title="Deploy MySQL"
description="Are you sure you want to deploy this mysql?"
type="default"
onClick={async () => {
setIsDeploying(true);
await new Promise((resolve) => setTimeout(resolve, 1000));
refetch();
}}
>
<Button
variant="default"
isLoading={data?.applicationStatus === "running"}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center">
<Rocket className="size-4 mr-1" />
Deploy
</div>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Downloads and sets up the MySQL database</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction>
<DialogAction
title="Reload MySQL"
description="Are you sure you want to reload this mysql?"
type="default"
onClick={async () => {
await reload({
mysqlId: mysqlId,
appName: data?.appName || "",
})
.then(() => {
toast.success("MySQL reloaded successfully");
refetch();
})
.catch(() => {
toast.error("Error reloading MySQL");
});
}}
>
<Button
variant="secondary"
isLoading={isReloading}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center">
<RefreshCcw className="size-4 mr-1" />
Reload
</div>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Restart the MySQL service without rebuilding</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction>
{data?.applicationStatus === "idle" ? (
<DialogAction
title="Start MySQL"
description="Are you sure you want to start this mysql?"
type="default"
onClick={async () => {
await start({
mysqlId: mysqlId,
})
.then(() => {
toast.success("MySQL started successfully");
refetch();
})
.catch(() => {
toast.error("Error starting MySQL");
});
}}
>
<Button
variant="secondary"
isLoading={isStarting}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center">
<CheckCircle2 className="size-4 mr-1" />
Start
</div>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>
Start the MySQL database (requires a previous
successful setup)
</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction>
) : (
<DialogAction
title="Stop MySQL"
description="Are you sure you want to stop this mysql?"
onClick={async () => {
await stop({
mysqlId: mysqlId,
})
.then(() => {
toast.success("MySQL stopped successfully");
refetch();
})
.catch(() => {
toast.error("Error stopping MySQL");
});
}}
>
<Button
variant="destructive"
isLoading={isStopping}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center">
<Ban className="size-4 mr-1" />
Stop
</div>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Stop the currently running MySQL database</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction>
)}
</TooltipProvider>
<DockerTerminalModal
appName={data?.appName || ""}
serverId={data?.serverId || ""}
>
<Button
variant="outline"
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center">
<Terminal className="size-4 mr-1" />
Open Terminal
</div>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Open a terminal to the MySQL container</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DockerTerminalModal>
</CardContent>
</Card>
<DrawerLogs
isOpen={isDrawerOpen}
onClose={() => {
setIsDrawerOpen(false);
setFilteredLogs([]);
setIsDeploying(false);
refetch();
}}
filteredLogs={filteredLogs}
/>
</div>
</>
);
};

View File

@@ -74,7 +74,7 @@ export const ShowGeneralPostgres = ({ postgresId }: Props) => {
<CardContent className="flex flex-row gap-4 flex-wrap">
<TooltipProvider disableHoverableContent={false}>
<DialogAction
title="Deploy Postgres"
title="Deploy PostgreSQL"
description="Are you sure you want to deploy this postgres?"
type="default"
onClick={async () => {
@@ -83,26 +83,28 @@ export const ShowGeneralPostgres = ({ postgresId }: Props) => {
refetch();
}}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="default"
isLoading={data?.applicationStatus === "running"}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Rocket className="size-4 mr-1" />
Deploy
</Button>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Downloads and sets up the PostgreSQL database</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
<Button
variant="default"
isLoading={data?.applicationStatus === "running"}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center">
<Rocket className="size-4 mr-1" />
Deploy
</div>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Downloads and sets up the PostgreSQL database</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction>
<DialogAction
title="Reload Postgres"
title="Reload PostgreSQL"
description="Are you sure you want to reload this postgres?"
type="default"
onClick={async () => {
@@ -111,35 +113,37 @@ export const ShowGeneralPostgres = ({ postgresId }: Props) => {
appName: data?.appName || "",
})
.then(() => {
toast.success("Postgres reloaded successfully");
toast.success("PostgreSQL reloaded successfully");
refetch();
})
.catch(() => {
toast.error("Error reloading Postgres");
toast.error("Error reloading PostgreSQL");
});
}}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="secondary"
isLoading={isReloading}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<RefreshCcw className="size-4 mr-1" />
Reload
</Button>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Reload the PostgreSQL without rebuilding it</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
<Button
variant="secondary"
isLoading={isReloading}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center">
<RefreshCcw className="size-4 mr-1" />
Reload
</div>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Restart the PostgreSQL service without rebuilding</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction>
{data?.applicationStatus === "idle" ? (
<DialogAction
title="Start Postgres"
title="Start PostgreSQL"
description="Are you sure you want to start this postgres?"
type="default"
onClick={async () => {
@@ -147,69 +151,73 @@ export const ShowGeneralPostgres = ({ postgresId }: Props) => {
postgresId: postgresId,
})
.then(() => {
toast.success("Postgres started successfully");
toast.success("PostgreSQL started successfully");
refetch();
})
.catch(() => {
toast.error("Error starting Postgres");
toast.error("Error starting PostgreSQL");
});
}}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="secondary"
isLoading={isStarting}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<CheckCircle2 className="size-4 mr-1" />
Start
</Button>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>
Start the PostgreSQL database (requires a previous
successful setup)
</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
<Button
variant="secondary"
isLoading={isStarting}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center">
<CheckCircle2 className="size-4 mr-1" />
Start
</div>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>
Start the PostgreSQL database (requires a previous
successful setup)
</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction>
) : (
<DialogAction
title="Stop Postgres"
title="Stop PostgreSQL"
description="Are you sure you want to stop this postgres?"
onClick={async () => {
await stop({
postgresId: postgresId,
})
.then(() => {
toast.success("Postgres stopped successfully");
toast.success("PostgreSQL stopped successfully");
refetch();
})
.catch(() => {
toast.error("Error stopping Postgres");
toast.error("Error stopping PostgreSQL");
});
}}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="destructive"
isLoading={isStopping}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Ban className="size-4 mr-1" />
Stop
</Button>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Stop the currently running PostgreSQL database</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
<Button
variant="destructive"
isLoading={isStopping}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center">
<Ban className="size-4 mr-1" />
Stop
</div>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Stop the currently running PostgreSQL database</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction>
)}
</TooltipProvider>
@@ -221,8 +229,19 @@ export const ShowGeneralPostgres = ({ postgresId }: Props) => {
variant="outline"
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Terminal className="size-4 mr-1" />
Open Terminal
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center">
<Terminal className="size-4 mr-1" />
Open Terminal
</div>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Open a terminal to the PostgreSQL container</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DockerTerminalModal>
</CardContent>

View File

@@ -17,235 +17,245 @@ import { type LogLine, parseLogs } from "../../docker/logs/utils";
import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal";
interface Props {
redisId: string;
redisId: string;
}
export const ShowGeneralRedis = ({ redisId }: Props) => {
const { data, refetch } = api.redis.one.useQuery(
{
redisId,
},
{ enabled: !!redisId }
);
const { data, refetch } = api.redis.one.useQuery(
{
redisId,
},
{ enabled: !!redisId },
);
const { mutateAsync: reload, isLoading: isReloading } =
api.redis.reload.useMutation();
const { mutateAsync: start, isLoading: isStarting } =
api.redis.start.useMutation();
const { mutateAsync: reload, isLoading: isReloading } =
api.redis.reload.useMutation();
const { mutateAsync: start, isLoading: isStarting } =
api.redis.start.useMutation();
const { mutateAsync: stop, isLoading: isStopping } =
api.redis.stop.useMutation();
const { mutateAsync: stop, isLoading: isStopping } =
api.redis.stop.useMutation();
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
const [filteredLogs, setFilteredLogs] = useState<LogLine[]>([]);
const [isDeploying, setIsDeploying] = useState(false);
api.redis.deployWithLogs.useSubscription(
{
redisId: redisId,
},
{
enabled: isDeploying,
onData(log) {
if (!isDrawerOpen) {
setIsDrawerOpen(true);
}
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
const [filteredLogs, setFilteredLogs] = useState<LogLine[]>([]);
const [isDeploying, setIsDeploying] = useState(false);
api.redis.deployWithLogs.useSubscription(
{
redisId: redisId,
},
{
enabled: isDeploying,
onData(log) {
if (!isDrawerOpen) {
setIsDrawerOpen(true);
}
if (log === "Deployment completed successfully!") {
setIsDeploying(false);
}
const parsedLogs = parseLogs(log);
setFilteredLogs((prev) => [...prev, ...parsedLogs]);
},
onError(error) {
console.error("Deployment logs error:", error);
setIsDeploying(false);
},
}
);
if (log === "Deployment completed successfully!") {
setIsDeploying(false);
}
const parsedLogs = parseLogs(log);
setFilteredLogs((prev) => [...prev, ...parsedLogs]);
},
onError(error) {
console.error("Deployment logs error:", error);
setIsDeploying(false);
},
},
);
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">
<TooltipProvider delayDuration={0}>
<DialogAction
title="Deploy Redis"
description="Are you sure you want to deploy this redis?"
type="default"
onClick={async () => {
setIsDeploying(true);
await new Promise((resolve) => setTimeout(resolve, 1000));
refetch();
}}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="default"
isLoading={data?.applicationStatus === "running"}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Rocket className="size-4 mr-1" />
Deploy
</Button>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Downloads and sets up the Redis database</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</DialogAction>
<DialogAction
title="Reload Redis"
description="Are you sure you want to reload this redis?"
type="default"
onClick={async () => {
await reload({
redisId: redisId,
appName: data?.appName || "",
})
.then(() => {
toast.success("Redis reloaded successfully");
refetch();
})
.catch(() => {
toast.error("Error reloading Redis");
});
}}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="secondary"
isLoading={isReloading}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<RefreshCcw className="size-4 mr-1" />
Reload
</Button>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Restart the Redis service without rebuilding</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</DialogAction>
{data?.applicationStatus === "idle" ? (
<DialogAction
title="Start Redis"
description="Are you sure you want to start this redis?"
type="default"
onClick={async () => {
await start({
redisId: redisId,
})
.then(() => {
toast.success("Redis started successfully");
refetch();
})
.catch(() => {
toast.error("Error starting Redis");
});
}}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="secondary"
isLoading={isStarting}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<CheckCircle2 className="size-4 mr-1" />
Start
</Button>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>
Start the Redis database (requires a previous
successful setup)
</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</DialogAction>
) : (
<DialogAction
title="Stop Redis"
description="Are you sure you want to stop this redis?"
onClick={async () => {
await stop({
redisId: redisId,
})
.then(() => {
toast.success("Redis stopped successfully");
refetch();
})
.catch(() => {
toast.error("Error stopping Redis");
});
}}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="destructive"
isLoading={isStopping}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Ban className="size-4 mr-1" />
Stop
</Button>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Stop the currently running Redis database</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</DialogAction>
)}
</TooltipProvider>
<DockerTerminalModal
appName={data?.appName || ""}
serverId={data?.serverId || ""}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="outline"
className="flex items-center gap-2 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Terminal className="size-4" />
Open Terminal
</Button>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Open a terminal to the Redis container</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</DockerTerminalModal>
</CardContent>
</Card>
<DrawerLogs
isOpen={isDrawerOpen}
onClose={() => {
setIsDrawerOpen(false);
setFilteredLogs([]);
setIsDeploying(false);
refetch();
}}
filteredLogs={filteredLogs}
/>
</div>
</>
);
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">
<TooltipProvider delayDuration={0}>
<DialogAction
title="Deploy Redis"
description="Are you sure you want to deploy this redis?"
type="default"
onClick={async () => {
setIsDeploying(true);
await new Promise((resolve) => setTimeout(resolve, 1000));
refetch();
}}
>
<Button
variant="default"
isLoading={data?.applicationStatus === "running"}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center">
<Rocket className="size-4 mr-1" />
Deploy
</div>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Downloads and sets up the Redis database</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction>
<DialogAction
title="Reload Redis"
description="Are you sure you want to reload this redis?"
type="default"
onClick={async () => {
await reload({
redisId: redisId,
appName: data?.appName || "",
})
.then(() => {
toast.success("Redis reloaded successfully");
refetch();
})
.catch(() => {
toast.error("Error reloading Redis");
});
}}
>
<Button
variant="secondary"
isLoading={isReloading}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center">
<RefreshCcw className="size-4 mr-1" />
Reload
</div>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Restart the Redis service without rebuilding</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction>
{data?.applicationStatus === "idle" ? (
<DialogAction
title="Start Redis"
description="Are you sure you want to start this redis?"
type="default"
onClick={async () => {
await start({
redisId: redisId,
})
.then(() => {
toast.success("Redis started successfully");
refetch();
})
.catch(() => {
toast.error("Error starting Redis");
});
}}
>
<Button
variant="secondary"
isLoading={isStarting}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center">
<CheckCircle2 className="size-4 mr-1" />
Start
</div>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>
Start the Redis database (requires a previous
successful setup)
</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction>
) : (
<DialogAction
title="Stop Redis"
description="Are you sure you want to stop this redis?"
onClick={async () => {
await stop({
redisId: redisId,
})
.then(() => {
toast.success("Redis stopped successfully");
refetch();
})
.catch(() => {
toast.error("Error stopping Redis");
});
}}
>
<Button
variant="destructive"
isLoading={isStopping}
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center">
<Ban className="size-4 mr-1" />
Stop
</div>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Stop the currently running Redis database</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DialogAction>
)}
</TooltipProvider>
<DockerTerminalModal
appName={data?.appName || ""}
serverId={data?.serverId || ""}
>
<Button
variant="outline"
className="flex items-center gap-1.5 focus-visible:ring-2 focus-visible:ring-offset-2"
>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center">
<Terminal className="size-4 mr-1" />
Open Terminal
</div>
</TooltipTrigger>
<TooltipPrimitive.Portal>
<TooltipContent sideOffset={5} className="z-[60]">
<p>Open a terminal to the Redis container</p>
</TooltipContent>
</TooltipPrimitive.Portal>
</Tooltip>
</Button>
</DockerTerminalModal>
</CardContent>
</Card>
<DrawerLogs
isOpen={isDrawerOpen}
onClose={() => {
setIsDrawerOpen(false);
setFilteredLogs([]);
setIsDeploying(false);
refetch();
}}
filteredLogs={filteredLogs}
/>
</div>
</>
);
};

View File

@@ -1,6 +1,6 @@
{
"name": "dokploy",
"version": "v0.20.5",
"version": "v0.20.6",
"private": true,
"license": "Apache-2.0",
"type": "module",

View File

@@ -31,7 +31,10 @@ import {
import { TRPCError } from "@trpc/server";
import { z } from "zod";
import { execAsync } from "@dokploy/server/utils/process/execAsync";
import {
execAsync,
execAsyncRemote,
} from "@dokploy/server/utils/process/execAsync";
import { getS3Credentials } from "@dokploy/server/utils/backups/utils";
import { findDestinationById } from "@dokploy/server/services/destination";
import {
@@ -229,6 +232,7 @@ export const backupRouter = createTRPCRouter({
z.object({
destinationId: z.string(),
search: z.string(),
serverId: z.string().optional(),
}),
)
.query(async ({ input }) => {
@@ -250,7 +254,16 @@ export const backupRouter = createTRPCRouter({
const searchPath = baseDir ? `${bucketPath}/${baseDir}` : bucketPath;
const listCommand = `rclone lsf ${rcloneFlags.join(" ")} "${searchPath}" | head -n 100`;
const { stdout } = await execAsync(listCommand);
let stdout = "";
if (input.serverId) {
const result = await execAsyncRemote(listCommand, input.serverId);
stdout = result.stdout;
} else {
const result = await execAsync(listCommand);
stdout = result.stdout;
}
const files = stdout.split("\n").filter(Boolean);
const results = baseDir

View File

@@ -13,7 +13,9 @@ import {
findDomainById,
findDomainsByApplicationId,
findDomainsByComposeId,
findOrganizationById,
findPreviewDeploymentById,
findServerById,
generateTraefikMeDomain,
manageDomain,
removeDomain,
@@ -94,6 +96,19 @@ export const domainRouter = createTRPCRouter({
input.serverId,
);
}),
canGenerateTraefikMeDomains: protectedProcedure
.input(z.object({ serverId: z.string() }))
.query(async ({ input, ctx }) => {
const organization = await findOrganizationById(
ctx.session.activeOrganizationId,
);
if (input.serverId) {
const server = await findServerById(input.serverId);
return server.ipAddress;
}
return organization?.owner.serverIp;
}),
update: protectedProcedure
.input(apiUpdateDomain)

View File

@@ -8,6 +8,10 @@ import { db } from "../db";
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 { APIError } from "better-auth/api";
const { handler, api } = betterAuth({
database: drizzleAdapter(db, {
@@ -88,11 +92,40 @@ const { handler, api } = betterAuth({
databaseHooks: {
user: {
create: {
before: async (_user, context) => {
if (!IS_CLOUD) {
const xDokployToken =
context?.request?.headers?.get("x-dokploy-token");
if (xDokployToken) {
const user = await getUserByToken(xDokployToken);
if (!user) {
throw new APIError("BAD_REQUEST", {
message: "User not found",
});
}
} else {
const isAdminPresent = await db.query.member.findFirst({
where: eq(schema.member.role, "owner"),
});
if (isAdminPresent) {
throw new APIError("BAD_REQUEST", {
message: "Admin is already created",
});
}
}
}
},
after: async (user) => {
const isAdminPresent = await db.query.member.findFirst({
where: eq(schema.member.role, "owner"),
});
if (!IS_CLOUD) {
await updateUser(user.id, {
serverIp: await getPublicIpWithFallback(),
});
}
if (IS_CLOUD || !isAdminPresent) {
await db.transaction(async (tx) => {
const organization = await tx