mirror of
https://github.com/andwati/dokploy.git
synced 2026-11-11 14:35:17 +00:00
- Introduced new test files for permission checks, including `check-permission.test.ts`, `enterprise-only-resources.test.ts`, `resolve-permissions.test.ts`, and `service-access.test.ts`. - Implemented permission checks in various components to ensure actions are gated by user permissions, including `ShowTraefikConfig`, `UpdateTraefikConfig`, `ShowVolumes`, `ShowDomains`, and others. - Enhanced the logic for displaying UI elements based on user permissions, ensuring that only authorized users can access or modify resources.
269 lines
8.1 KiB
TypeScript
269 lines
8.1 KiB
TypeScript
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
import { Ban, CheckCircle2, RefreshCcw, Rocket, Terminal } from "lucide-react";
|
|
import { useState } from "react";
|
|
import { toast } from "sonner";
|
|
import { DialogAction } from "@/components/shared/dialog-action";
|
|
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,
|
|
} from "@/components/ui/tooltip";
|
|
import { api } from "@/utils/api";
|
|
import { type LogLine, parseLogs } from "../../docker/logs/utils";
|
|
import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal";
|
|
|
|
interface Props {
|
|
redisId: string;
|
|
}
|
|
|
|
export const ShowGeneralRedis = ({ redisId }: Props) => {
|
|
const { data: permissions } = api.user.getPermissions.useQuery();
|
|
const canDeploy = permissions?.deployment.create ?? false;
|
|
const { data, refetch } = api.redis.one.useQuery(
|
|
{
|
|
redisId,
|
|
},
|
|
{ enabled: !!redisId },
|
|
);
|
|
|
|
const { mutateAsync: reload, isPending: isReloading } =
|
|
api.redis.reload.useMutation();
|
|
const { mutateAsync: start, isPending: isStarting } =
|
|
api.redis.start.useMutation();
|
|
|
|
const { mutateAsync: stop, isPending: 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);
|
|
}
|
|
|
|
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}>
|
|
{canDeploy && (
|
|
<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>
|
|
)}
|
|
{canDeploy && (
|
|
<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>
|
|
)}
|
|
{canDeploy &&
|
|
(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>
|
|
</>
|
|
);
|
|
};
|