mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
Compare commits
1 Commits
407-featur
...
1365-creat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da0e726326 |
@@ -132,8 +132,8 @@ export const ShowEnvironment = ({ id, type }: Props) => {
|
|||||||
control={form.control}
|
control={form.control}
|
||||||
name="environment"
|
name="environment"
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem className="w-full">
|
||||||
<FormControl className="">
|
<FormControl>
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
style={
|
style={
|
||||||
{
|
{
|
||||||
@@ -142,14 +142,14 @@ export const ShowEnvironment = ({ id, type }: Props) => {
|
|||||||
}
|
}
|
||||||
language="properties"
|
language="properties"
|
||||||
disabled={isEnvVisible}
|
disabled={isEnvVisible}
|
||||||
className="font-mono"
|
|
||||||
wrapperClassName="compose-file-editor"
|
|
||||||
placeholder={`NODE_ENV=production
|
placeholder={`NODE_ENV=production
|
||||||
PORT=3000
|
PORT=3000
|
||||||
`}
|
`}
|
||||||
|
className="h-96 font-mono"
|
||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import { z } from "zod";
|
|||||||
const addEnvironmentSchema = z.object({
|
const addEnvironmentSchema = z.object({
|
||||||
env: z.string(),
|
env: z.string(),
|
||||||
buildArgs: z.string(),
|
buildArgs: z.string(),
|
||||||
buildSecrets: z.record(z.string(), z.string()),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
type EnvironmentSchema = z.infer<typeof addEnvironmentSchema>;
|
type EnvironmentSchema = z.infer<typeof addEnvironmentSchema>;
|
||||||
@@ -37,7 +36,6 @@ export const ShowEnvironment = ({ applicationId }: Props) => {
|
|||||||
defaultValues: {
|
defaultValues: {
|
||||||
env: data?.env || "",
|
env: data?.env || "",
|
||||||
buildArgs: data?.buildArgs || "",
|
buildArgs: data?.buildArgs || "",
|
||||||
buildSecrets: data?.buildSecrets || {},
|
|
||||||
},
|
},
|
||||||
resolver: zodResolver(addEnvironmentSchema),
|
resolver: zodResolver(addEnvironmentSchema),
|
||||||
});
|
});
|
||||||
@@ -46,7 +44,6 @@ export const ShowEnvironment = ({ applicationId }: Props) => {
|
|||||||
mutateAsync({
|
mutateAsync({
|
||||||
env: data.env,
|
env: data.env,
|
||||||
buildArgs: data.buildArgs,
|
buildArgs: data.buildArgs,
|
||||||
buildSecrets: data.buildSecrets,
|
|
||||||
applicationId,
|
applicationId,
|
||||||
})
|
})
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
@@ -72,63 +69,25 @@ export const ShowEnvironment = ({ applicationId }: Props) => {
|
|||||||
placeholder={["NODE_ENV=production", "PORT=3000"].join("\n")}
|
placeholder={["NODE_ENV=production", "PORT=3000"].join("\n")}
|
||||||
/>
|
/>
|
||||||
{data?.buildType === "dockerfile" && (
|
{data?.buildType === "dockerfile" && (
|
||||||
<>
|
<Secrets
|
||||||
<Secrets
|
name="buildArgs"
|
||||||
name="buildArgs"
|
title="Build-time Variables"
|
||||||
title="Build-time Variables"
|
description={
|
||||||
description={
|
<span>
|
||||||
<span>
|
Available only at build-time. See documentation
|
||||||
Available only at build-time. See documentation
|
<a
|
||||||
<a
|
className="text-primary"
|
||||||
className="text-primary"
|
href="https://docs.docker.com/build/guide/build-args/"
|
||||||
href="https://docs.docker.com/build/guide/build-args/"
|
target="_blank"
|
||||||
target="_blank"
|
rel="noopener noreferrer"
|
||||||
rel="noopener noreferrer"
|
>
|
||||||
>
|
here
|
||||||
here
|
</a>
|
||||||
</a>
|
.
|
||||||
.
|
</span>
|
||||||
</span>
|
}
|
||||||
}
|
placeholder="NPM_TOKEN=xyz"
|
||||||
placeholder="NPM_TOKEN=xyz"
|
/>
|
||||||
/>
|
|
||||||
<Secrets
|
|
||||||
name="buildSecrets"
|
|
||||||
title="Build Secrets"
|
|
||||||
description={
|
|
||||||
<span>
|
|
||||||
Secrets available only during build-time and not in the
|
|
||||||
final image. See documentation
|
|
||||||
<a
|
|
||||||
className="text-primary"
|
|
||||||
href="https://docs.docker.com/build/building/secrets/"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
here
|
|
||||||
</a>
|
|
||||||
.
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
placeholder="API_TOKEN=xyz"
|
|
||||||
transformValue={(value) => {
|
|
||||||
// Convert the string format to object
|
|
||||||
const lines = value.split("\n").filter((line) => line.trim());
|
|
||||||
return Object.fromEntries(
|
|
||||||
lines.map((line) => {
|
|
||||||
const [key, ...valueParts] = line.split("=");
|
|
||||||
return [key.trim(), valueParts.join("=").trim()];
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
formatValue={(value) => {
|
|
||||||
// Convert the object back to string format
|
|
||||||
return Object.entries(value as Record<string, string>)
|
|
||||||
.map(([key, val]) => `${key}=${val}`)
|
|
||||||
.join("\n");
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
<div className="flex flex-row justify-end">
|
<div className="flex flex-row justify-end">
|
||||||
<Button isLoading={isLoading} className="w-fit" type="submit">
|
<Button isLoading={isLoading} className="w-fit" type="submit">
|
||||||
|
|||||||
@@ -4,22 +4,8 @@ import { DialogAction } from "@/components/shared/dialog-action";
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
import {
|
|
||||||
Tooltip,
|
|
||||||
TooltipContent,
|
|
||||||
TooltipProvider,
|
|
||||||
TooltipTrigger,
|
|
||||||
} from "@/components/ui/tooltip";
|
|
||||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
import {
|
import { Ban, CheckCircle2, Hammer, RefreshCcw, Terminal } from "lucide-react";
|
||||||
Ban,
|
|
||||||
CheckCircle2,
|
|
||||||
Hammer,
|
|
||||||
HelpCircle,
|
|
||||||
RefreshCcw,
|
|
||||||
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";
|
||||||
@@ -55,188 +41,128 @@ export const ShowGeneralApplication = ({ applicationId }: Props) => {
|
|||||||
<CardTitle className="text-xl">Deploy Settings</CardTitle>
|
<CardTitle className="text-xl">Deploy Settings</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="flex flex-row gap-4 flex-wrap">
|
<CardContent className="flex flex-row gap-4 flex-wrap">
|
||||||
<TooltipProvider delayDuration={0}>
|
<DialogAction
|
||||||
<DialogAction
|
title="Deploy Application"
|
||||||
title="Deploy Application"
|
description="Are you sure you want to deploy this application?"
|
||||||
description="Are you sure you want to deploy this application?"
|
type="default"
|
||||||
type="default"
|
onClick={async () => {
|
||||||
onClick={async () => {
|
await deploy({
|
||||||
await deploy({
|
applicationId: applicationId,
|
||||||
applicationId: applicationId,
|
})
|
||||||
|
.then(() => {
|
||||||
|
toast.success("Application deployed successfully");
|
||||||
|
refetch();
|
||||||
|
router.push(
|
||||||
|
`/dashboard/project/${data?.projectId}/services/application/${applicationId}?tab=deployments`,
|
||||||
|
);
|
||||||
})
|
})
|
||||||
.then(() => {
|
.catch(() => {
|
||||||
toast.success("Application deployed successfully");
|
toast.error("Error deploying application");
|
||||||
refetch();
|
});
|
||||||
router.push(
|
}}
|
||||||
`/dashboard/project/${data?.projectId}/services/application/${applicationId}?tab=deployments`,
|
>
|
||||||
);
|
<Button
|
||||||
})
|
variant="default"
|
||||||
.catch(() => {
|
isLoading={data?.applicationStatus === "running"}
|
||||||
toast.error("Error deploying application");
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Button
|
Deploy
|
||||||
variant="default"
|
</Button>
|
||||||
isLoading={data?.applicationStatus === "running"}
|
</DialogAction>
|
||||||
className="flex items-center gap-1.5"
|
<DialogAction
|
||||||
>
|
title="Reload Application"
|
||||||
Deploy
|
description="Are you sure you want to reload this application?"
|
||||||
<Tooltip>
|
type="default"
|
||||||
<TooltipTrigger asChild>
|
onClick={async () => {
|
||||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
await reload({
|
||||||
</TooltipTrigger>
|
applicationId: applicationId,
|
||||||
<TooltipPrimitive.Portal>
|
appName: data?.appName || "",
|
||||||
<TooltipContent sideOffset={5} className="z-[60]">
|
})
|
||||||
<p>
|
.then(() => {
|
||||||
Downloads the source code and performs a complete build
|
toast.success("Application reloaded successfully");
|
||||||
</p>
|
refetch();
|
||||||
</TooltipContent>
|
|
||||||
</TooltipPrimitive.Portal>
|
|
||||||
</Tooltip>
|
|
||||||
</Button>
|
|
||||||
</DialogAction>
|
|
||||||
<DialogAction
|
|
||||||
title="Reload Application"
|
|
||||||
description="Are you sure you want to reload this application?"
|
|
||||||
type="default"
|
|
||||||
onClick={async () => {
|
|
||||||
await reload({
|
|
||||||
applicationId: applicationId,
|
|
||||||
appName: data?.appName || "",
|
|
||||||
})
|
})
|
||||||
.then(() => {
|
.catch(() => {
|
||||||
toast.success("Application reloaded successfully");
|
toast.error("Error reloading application");
|
||||||
refetch();
|
});
|
||||||
})
|
}}
|
||||||
.catch(() => {
|
>
|
||||||
toast.error("Error reloading application");
|
<Button variant="secondary" isLoading={isReloading}>
|
||||||
});
|
Reload
|
||||||
}}
|
<RefreshCcw className="size-4" />
|
||||||
>
|
</Button>
|
||||||
<Button variant="secondary" isLoading={isReloading}>
|
</DialogAction>
|
||||||
Reload
|
<DialogAction
|
||||||
<RefreshCcw className="size-4" />
|
title="Rebuild Application"
|
||||||
</Button>
|
description="Are you sure you want to rebuild this application?"
|
||||||
</DialogAction>
|
type="default"
|
||||||
<DialogAction
|
onClick={async () => {
|
||||||
title="Rebuild Application"
|
await redeploy({
|
||||||
description="Are you sure you want to rebuild this application?"
|
applicationId: applicationId,
|
||||||
type="default"
|
})
|
||||||
onClick={async () => {
|
.then(() => {
|
||||||
await redeploy({
|
toast.success("Application rebuilt successfully");
|
||||||
applicationId: applicationId,
|
refetch();
|
||||||
})
|
})
|
||||||
.then(() => {
|
.catch(() => {
|
||||||
toast.success("Application rebuilt successfully");
|
toast.error("Error rebuilding application");
|
||||||
refetch();
|
});
|
||||||
})
|
}}
|
||||||
.catch(() => {
|
>
|
||||||
toast.error("Error rebuilding application");
|
<Button
|
||||||
});
|
variant="secondary"
|
||||||
}}
|
isLoading={data?.applicationStatus === "running"}
|
||||||
>
|
>
|
||||||
<Button
|
Rebuild
|
||||||
variant="secondary"
|
<Hammer className="size-4" />
|
||||||
isLoading={data?.applicationStatus === "running"}
|
</Button>
|
||||||
className="flex items-center gap-1.5"
|
</DialogAction>
|
||||||
>
|
|
||||||
Rebuild
|
|
||||||
<Hammer className="size-4" />
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
|
||||||
</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" ? (
|
{data?.applicationStatus === "idle" ? (
|
||||||
<DialogAction
|
<DialogAction
|
||||||
title="Start Application"
|
title="Start Application"
|
||||||
description="Are you sure you want to start this application?"
|
description="Are you sure you want to start this application?"
|
||||||
type="default"
|
type="default"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await start({
|
await start({
|
||||||
applicationId: applicationId,
|
applicationId: applicationId,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
toast.success("Application started successfully");
|
||||||
|
refetch();
|
||||||
})
|
})
|
||||||
.then(() => {
|
.catch(() => {
|
||||||
toast.success("Application started successfully");
|
toast.error("Error starting application");
|
||||||
refetch();
|
});
|
||||||
})
|
}}
|
||||||
.catch(() => {
|
>
|
||||||
toast.error("Error starting application");
|
<Button variant="secondary" isLoading={isStarting}>
|
||||||
});
|
Start
|
||||||
}}
|
<CheckCircle2 className="size-4" />
|
||||||
>
|
</Button>
|
||||||
<Button
|
</DialogAction>
|
||||||
variant="secondary"
|
) : (
|
||||||
isLoading={isStarting}
|
<DialogAction
|
||||||
className="flex items-center gap-1.5"
|
title="Stop Application"
|
||||||
>
|
description="Are you sure you want to stop this application?"
|
||||||
Start
|
onClick={async () => {
|
||||||
<CheckCircle2 className="size-4" />
|
await stop({
|
||||||
<Tooltip>
|
applicationId: applicationId,
|
||||||
<TooltipTrigger asChild>
|
})
|
||||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
.then(() => {
|
||||||
</TooltipTrigger>
|
toast.success("Application stopped successfully");
|
||||||
<TooltipPrimitive.Portal>
|
refetch();
|
||||||
<TooltipContent sideOffset={5} className="z-[60]">
|
|
||||||
<p>
|
|
||||||
Start the application (requires a previous successful
|
|
||||||
build)
|
|
||||||
</p>
|
|
||||||
</TooltipContent>
|
|
||||||
</TooltipPrimitive.Portal>
|
|
||||||
</Tooltip>
|
|
||||||
</Button>
|
|
||||||
</DialogAction>
|
|
||||||
) : (
|
|
||||||
<DialogAction
|
|
||||||
title="Stop Application"
|
|
||||||
description="Are you sure you want to stop this application?"
|
|
||||||
onClick={async () => {
|
|
||||||
await stop({
|
|
||||||
applicationId: applicationId,
|
|
||||||
})
|
})
|
||||||
.then(() => {
|
.catch(() => {
|
||||||
toast.success("Application stopped successfully");
|
toast.error("Error stopping application");
|
||||||
refetch();
|
});
|
||||||
})
|
}}
|
||||||
.catch(() => {
|
>
|
||||||
toast.error("Error stopping application");
|
<Button variant="destructive" isLoading={isStopping}>
|
||||||
});
|
Stop
|
||||||
}}
|
<Ban className="size-4" />
|
||||||
>
|
</Button>
|
||||||
<Button
|
</DialogAction>
|
||||||
variant="destructive"
|
)}
|
||||||
isLoading={isStopping}
|
|
||||||
className="flex items-center gap-1.5"
|
|
||||||
>
|
|
||||||
Stop
|
|
||||||
<Ban className="size-4" />
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipPrimitive.Portal>
|
|
||||||
<TooltipContent sideOffset={5} className="z-[60]">
|
|
||||||
<p>Stop the currently running application</p>
|
|
||||||
</TooltipContent>
|
|
||||||
</TooltipPrimitive.Portal>
|
|
||||||
</Tooltip>
|
|
||||||
</Button>
|
|
||||||
</DialogAction>
|
|
||||||
)}
|
|
||||||
</TooltipProvider>
|
|
||||||
<DockerTerminalModal
|
<DockerTerminalModal
|
||||||
appName={data?.appName || ""}
|
appName={data?.appName || ""}
|
||||||
serverId={data?.serverId || ""}
|
serverId={data?.serverId || ""}
|
||||||
|
|||||||
@@ -1,15 +1,8 @@
|
|||||||
import { DialogAction } from "@/components/shared/dialog-action";
|
import { DialogAction } from "@/components/shared/dialog-action";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
import {
|
|
||||||
Tooltip,
|
|
||||||
TooltipContent,
|
|
||||||
TooltipProvider,
|
|
||||||
TooltipTrigger,
|
|
||||||
} from "@/components/ui/tooltip";
|
|
||||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
import { Ban, CheckCircle2, Hammer, HelpCircle, Terminal } from "lucide-react";
|
import { Ban, CheckCircle2, Hammer, 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";
|
||||||
@@ -34,159 +27,103 @@ export const ComposeActions = ({ composeId }: Props) => {
|
|||||||
api.compose.stop.useMutation();
|
api.compose.stop.useMutation();
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-row gap-4 w-full flex-wrap ">
|
<div className="flex flex-row gap-4 w-full flex-wrap ">
|
||||||
<TooltipProvider delayDuration={0}>
|
<DialogAction
|
||||||
|
title="Deploy Compose"
|
||||||
|
description="Are you sure you want to deploy this compose?"
|
||||||
|
type="default"
|
||||||
|
onClick={async () => {
|
||||||
|
await deploy({
|
||||||
|
composeId: composeId,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
toast.success("Compose deployed successfully");
|
||||||
|
refetch();
|
||||||
|
router.push(
|
||||||
|
`/dashboard/project/${data?.project.projectId}/services/compose/${composeId}?tab=deployments`,
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
toast.error("Error deploying compose");
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button variant="default" isLoading={data?.composeStatus === "running"}>
|
||||||
|
Deploy
|
||||||
|
</Button>
|
||||||
|
</DialogAction>
|
||||||
|
<DialogAction
|
||||||
|
title="Rebuild Compose"
|
||||||
|
description="Are you sure you want to rebuild this compose?"
|
||||||
|
type="default"
|
||||||
|
onClick={async () => {
|
||||||
|
await redeploy({
|
||||||
|
composeId: composeId,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
toast.success("Compose rebuilt successfully");
|
||||||
|
refetch();
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
toast.error("Error rebuilding compose");
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
isLoading={data?.composeStatus === "running"}
|
||||||
|
>
|
||||||
|
Rebuild
|
||||||
|
<Hammer className="size-4" />
|
||||||
|
</Button>
|
||||||
|
</DialogAction>
|
||||||
|
{data?.composeType === "docker-compose" &&
|
||||||
|
data?.composeStatus === "idle" ? (
|
||||||
<DialogAction
|
<DialogAction
|
||||||
title="Deploy Compose"
|
title="Start Compose"
|
||||||
description="Are you sure you want to deploy this compose?"
|
description="Are you sure you want to start this compose?"
|
||||||
type="default"
|
type="default"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await deploy({
|
await start({
|
||||||
composeId: composeId,
|
composeId: composeId,
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
toast.success("Compose deployed successfully");
|
toast.success("Compose started successfully");
|
||||||
refetch();
|
refetch();
|
||||||
router.push(
|
|
||||||
`/dashboard/project/${data?.project.projectId}/services/compose/${composeId}?tab=deployments`,
|
|
||||||
);
|
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
toast.error("Error deploying compose");
|
toast.error("Error starting compose");
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button
|
<Button variant="secondary" isLoading={isStarting}>
|
||||||
variant="default"
|
Start
|
||||||
isLoading={data?.composeStatus === "running"}
|
<CheckCircle2 className="size-4" />
|
||||||
className="flex items-center gap-1.5"
|
|
||||||
>
|
|
||||||
Deploy
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
|
||||||
</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>
|
</Button>
|
||||||
</DialogAction>
|
</DialogAction>
|
||||||
|
) : (
|
||||||
<DialogAction
|
<DialogAction
|
||||||
title="Rebuild Compose"
|
title="Stop Compose"
|
||||||
description="Are you sure you want to rebuild this compose?"
|
description="Are you sure you want to stop this compose?"
|
||||||
type="default"
|
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await redeploy({
|
await stop({
|
||||||
composeId: composeId,
|
composeId: composeId,
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
toast.success("Compose rebuilt successfully");
|
toast.success("Compose stopped successfully");
|
||||||
refetch();
|
refetch();
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
toast.error("Error rebuilding compose");
|
toast.error("Error stopping compose");
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button
|
<Button variant="destructive" isLoading={isStopping}>
|
||||||
variant="secondary"
|
Stop
|
||||||
isLoading={data?.composeStatus === "running"}
|
<Ban className="size-4" />
|
||||||
className="flex items-center gap-1.5"
|
|
||||||
>
|
|
||||||
Rebuild
|
|
||||||
<Hammer className="size-4" />
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipPrimitive.Portal>
|
|
||||||
<TooltipContent sideOffset={5} className="z-[60]">
|
|
||||||
<p>Only rebuilds the compose without downloading new code</p>
|
|
||||||
</TooltipContent>
|
|
||||||
</TooltipPrimitive.Portal>
|
|
||||||
</Tooltip>
|
|
||||||
</Button>
|
</Button>
|
||||||
</DialogAction>
|
</DialogAction>
|
||||||
{data?.composeType === "docker-compose" &&
|
)}
|
||||||
data?.composeStatus === "idle" ? (
|
|
||||||
<DialogAction
|
|
||||||
title="Start Compose"
|
|
||||||
description="Are you sure you want to start this compose?"
|
|
||||||
type="default"
|
|
||||||
onClick={async () => {
|
|
||||||
await start({
|
|
||||||
composeId: composeId,
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
toast.success("Compose started successfully");
|
|
||||||
refetch();
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
toast.error("Error starting compose");
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
variant="secondary"
|
|
||||||
isLoading={isStarting}
|
|
||||||
className="flex items-center gap-1.5"
|
|
||||||
>
|
|
||||||
Start
|
|
||||||
<CheckCircle2 className="size-4" />
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
|
||||||
</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
|
|
||||||
title="Stop Compose"
|
|
||||||
description="Are you sure you want to stop this compose?"
|
|
||||||
onClick={async () => {
|
|
||||||
await stop({
|
|
||||||
composeId: composeId,
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
toast.success("Compose stopped successfully");
|
|
||||||
refetch();
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
toast.error("Error stopping compose");
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
variant="destructive"
|
|
||||||
isLoading={isStopping}
|
|
||||||
className="flex items-center gap-1.5"
|
|
||||||
>
|
|
||||||
Stop
|
|
||||||
<Ban className="size-4" />
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipPrimitive.Portal>
|
|
||||||
<TooltipContent sideOffset={5} className="z-[60]">
|
|
||||||
<p>Stop the currently running compose</p>
|
|
||||||
</TooltipContent>
|
|
||||||
</TooltipPrimitive.Portal>
|
|
||||||
</Tooltip>
|
|
||||||
</Button>
|
|
||||||
</DialogAction>
|
|
||||||
)}
|
|
||||||
</TooltipProvider>
|
|
||||||
<DockerTerminalModal
|
<DockerTerminalModal
|
||||||
appName={data?.appName || ""}
|
appName={data?.appName || ""}
|
||||||
serverId={data?.serverId || ""}
|
serverId={data?.serverId || ""}
|
||||||
|
|||||||
@@ -2,21 +2,8 @@ import { DialogAction } from "@/components/shared/dialog-action";
|
|||||||
import { DrawerLogs } from "@/components/shared/drawer-logs";
|
import { DrawerLogs } from "@/components/shared/drawer-logs";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import {
|
|
||||||
Tooltip,
|
|
||||||
TooltipContent,
|
|
||||||
TooltipProvider,
|
|
||||||
TooltipTrigger,
|
|
||||||
} from "@/components/ui/tooltip";
|
|
||||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
import {
|
import { Ban, CheckCircle2, RefreshCcw, Terminal } from "lucide-react";
|
||||||
Ban,
|
|
||||||
CheckCircle2,
|
|
||||||
HelpCircle,
|
|
||||||
RefreshCcw,
|
|
||||||
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";
|
||||||
@@ -78,150 +65,92 @@ export const ShowGeneralMariadb = ({ mariadbId }: Props) => {
|
|||||||
<CardTitle className="text-xl">Deploy Settings</CardTitle>
|
<CardTitle className="text-xl">Deploy Settings</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="flex flex-row gap-4 flex-wrap">
|
<CardContent className="flex flex-row gap-4 flex-wrap">
|
||||||
<TooltipProvider delayDuration={0}>
|
<DialogAction
|
||||||
<DialogAction
|
title="Deploy Mariadb"
|
||||||
title="Deploy Mariadb"
|
description="Are you sure you want to deploy this mariadb?"
|
||||||
description="Are you sure you want to deploy this mariadb?"
|
type="default"
|
||||||
type="default"
|
onClick={async () => {
|
||||||
onClick={async () => {
|
setIsDeploying(true);
|
||||||
setIsDeploying(true);
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
refetch();
|
||||||
refetch();
|
}}
|
||||||
}}
|
>
|
||||||
|
<Button
|
||||||
|
variant="default"
|
||||||
|
isLoading={data?.applicationStatus === "running"}
|
||||||
>
|
>
|
||||||
<Button
|
Deploy
|
||||||
variant="default"
|
</Button>
|
||||||
isLoading={data?.applicationStatus === "running"}
|
</DialogAction>
|
||||||
className="flex items-center gap-1.5"
|
<DialogAction
|
||||||
>
|
title="Reload Mariadb"
|
||||||
Deploy
|
description="Are you sure you want to reload this mariadb?"
|
||||||
<Tooltip>
|
type="default"
|
||||||
<TooltipTrigger asChild>
|
onClick={async () => {
|
||||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
await reload({
|
||||||
</TooltipTrigger>
|
mariadbId: mariadbId,
|
||||||
<TooltipPrimitive.Portal>
|
appName: data?.appName || "",
|
||||||
<TooltipContent sideOffset={5} className="z-[60]">
|
})
|
||||||
<p>Downloads and sets up the MariaDB database</p>
|
.then(() => {
|
||||||
</TooltipContent>
|
toast.success("Mariadb reloaded successfully");
|
||||||
</TooltipPrimitive.Portal>
|
refetch();
|
||||||
</Tooltip>
|
})
|
||||||
</Button>
|
.catch(() => {
|
||||||
</DialogAction>
|
toast.error("Error reloading Mariadb");
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button variant="secondary" isLoading={isReloading}>
|
||||||
|
Reload
|
||||||
|
<RefreshCcw className="size-4" />
|
||||||
|
</Button>
|
||||||
|
</DialogAction>
|
||||||
|
{data?.applicationStatus === "idle" ? (
|
||||||
<DialogAction
|
<DialogAction
|
||||||
title="Reload Mariadb"
|
title="Start Mariadb"
|
||||||
description="Are you sure you want to reload this mariadb?"
|
description="Are you sure you want to start this mariadb?"
|
||||||
type="default"
|
type="default"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await reload({
|
await start({
|
||||||
mariadbId: mariadbId,
|
mariadbId: mariadbId,
|
||||||
appName: data?.appName || "",
|
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
toast.success("Mariadb reloaded successfully");
|
toast.success("Mariadb started successfully");
|
||||||
refetch();
|
refetch();
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
toast.error("Error reloading Mariadb");
|
toast.error("Error starting Mariadb");
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button
|
<Button variant="secondary" isLoading={isStarting}>
|
||||||
variant="secondary"
|
Start
|
||||||
isLoading={isReloading}
|
<CheckCircle2 className="size-4" />
|
||||||
className="flex items-center gap-1.5"
|
|
||||||
>
|
|
||||||
Reload
|
|
||||||
<RefreshCcw className="size-4" />
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipPrimitive.Portal>
|
|
||||||
<TooltipContent sideOffset={5} className="z-[60]">
|
|
||||||
<p>Restart the MariaDB service without rebuilding</p>
|
|
||||||
</TooltipContent>
|
|
||||||
</TooltipPrimitive.Portal>
|
|
||||||
</Tooltip>
|
|
||||||
</Button>
|
</Button>
|
||||||
</DialogAction>
|
</DialogAction>
|
||||||
{data?.applicationStatus === "idle" ? (
|
) : (
|
||||||
<DialogAction
|
<DialogAction
|
||||||
title="Start Mariadb"
|
title="Stop Mariadb"
|
||||||
description="Are you sure you want to start this mariadb?"
|
description="Are you sure you want to stop this mariadb?"
|
||||||
type="default"
|
onClick={async () => {
|
||||||
onClick={async () => {
|
await stop({
|
||||||
await start({
|
mariadbId: mariadbId,
|
||||||
mariadbId: mariadbId,
|
})
|
||||||
|
.then(() => {
|
||||||
|
toast.success("Mariadb stopped successfully");
|
||||||
|
refetch();
|
||||||
})
|
})
|
||||||
.then(() => {
|
.catch(() => {
|
||||||
toast.success("Mariadb started successfully");
|
toast.error("Error stopping Mariadb");
|
||||||
refetch();
|
});
|
||||||
})
|
}}
|
||||||
.catch(() => {
|
>
|
||||||
toast.error("Error starting Mariadb");
|
<Button variant="destructive" isLoading={isStopping}>
|
||||||
});
|
Stop
|
||||||
}}
|
<Ban className="size-4" />
|
||||||
>
|
</Button>
|
||||||
<Button
|
</DialogAction>
|
||||||
variant="secondary"
|
)}
|
||||||
isLoading={isStarting}
|
|
||||||
className="flex items-center gap-1.5"
|
|
||||||
>
|
|
||||||
Start
|
|
||||||
<CheckCircle2 className="size-4" />
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
|
||||||
</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>
|
|
||||||
) : (
|
|
||||||
<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"
|
|
||||||
>
|
|
||||||
Stop
|
|
||||||
<Ban className="size-4" />
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
|
||||||
</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
|
<DockerTerminalModal
|
||||||
appName={data?.appName || ""}
|
appName={data?.appName || ""}
|
||||||
serverId={data?.serverId || ""}
|
serverId={data?.serverId || ""}
|
||||||
|
|||||||
@@ -2,21 +2,8 @@ import { DialogAction } from "@/components/shared/dialog-action";
|
|||||||
import { DrawerLogs } from "@/components/shared/drawer-logs";
|
import { DrawerLogs } from "@/components/shared/drawer-logs";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import {
|
|
||||||
Tooltip,
|
|
||||||
TooltipContent,
|
|
||||||
TooltipProvider,
|
|
||||||
TooltipTrigger,
|
|
||||||
} from "@/components/ui/tooltip";
|
|
||||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
import {
|
import { Ban, CheckCircle2, RefreshCcw, Terminal } from "lucide-react";
|
||||||
Ban,
|
|
||||||
CheckCircle2,
|
|
||||||
HelpCircle,
|
|
||||||
RefreshCcw,
|
|
||||||
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";
|
||||||
@@ -77,150 +64,93 @@ export const ShowGeneralMongo = ({ mongoId }: Props) => {
|
|||||||
<CardTitle className="text-xl">Deploy Settings</CardTitle>
|
<CardTitle className="text-xl">Deploy Settings</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="flex flex-row gap-4 flex-wrap">
|
<CardContent className="flex flex-row gap-4 flex-wrap">
|
||||||
<TooltipProvider delayDuration={0}>
|
<DialogAction
|
||||||
<DialogAction
|
title="Deploy Mongo"
|
||||||
title="Deploy Mongo"
|
description="Are you sure you want to deploy this mongo?"
|
||||||
description="Are you sure you want to deploy this mongo?"
|
type="default"
|
||||||
type="default"
|
onClick={async () => {
|
||||||
onClick={async () => {
|
setIsDeploying(true);
|
||||||
setIsDeploying(true);
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
refetch();
|
||||||
refetch();
|
}}
|
||||||
}}
|
>
|
||||||
|
<Button
|
||||||
|
variant="default"
|
||||||
|
isLoading={data?.applicationStatus === "running"}
|
||||||
>
|
>
|
||||||
<Button
|
Deploy
|
||||||
variant="default"
|
</Button>
|
||||||
isLoading={data?.applicationStatus === "running"}
|
</DialogAction>
|
||||||
className="flex items-center gap-1.5"
|
<DialogAction
|
||||||
>
|
title="Reload Mongo"
|
||||||
Deploy
|
description="Are you sure you want to reload this mongo?"
|
||||||
<Tooltip>
|
type="default"
|
||||||
<TooltipTrigger asChild>
|
onClick={async () => {
|
||||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
await reload({
|
||||||
</TooltipTrigger>
|
mongoId: mongoId,
|
||||||
<TooltipPrimitive.Portal>
|
appName: data?.appName || "",
|
||||||
<TooltipContent sideOffset={5} className="z-[60]">
|
})
|
||||||
<p>Downloads and sets up the MongoDB database</p>
|
.then(() => {
|
||||||
</TooltipContent>
|
toast.success("Mongo reloaded successfully");
|
||||||
</TooltipPrimitive.Portal>
|
refetch();
|
||||||
</Tooltip>
|
})
|
||||||
</Button>
|
.catch(() => {
|
||||||
</DialogAction>
|
toast.error("Error reloading Mongo");
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button variant="secondary" isLoading={isReloading}>
|
||||||
|
Reload
|
||||||
|
<RefreshCcw className="size-4" />
|
||||||
|
</Button>
|
||||||
|
</DialogAction>
|
||||||
|
{data?.applicationStatus === "idle" ? (
|
||||||
<DialogAction
|
<DialogAction
|
||||||
title="Reload Mongo"
|
title="Start Mongo"
|
||||||
description="Are you sure you want to reload this mongo?"
|
description="Are you sure you want to start this mongo?"
|
||||||
type="default"
|
type="default"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await reload({
|
await start({
|
||||||
mongoId: mongoId,
|
mongoId: mongoId,
|
||||||
appName: data?.appName || "",
|
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
toast.success("Mongo reloaded successfully");
|
toast.success("Mongo started successfully");
|
||||||
refetch();
|
refetch();
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
toast.error("Error reloading Mongo");
|
toast.error("Error starting Mongo");
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button
|
<Button variant="secondary" isLoading={isStarting}>
|
||||||
variant="secondary"
|
Start
|
||||||
isLoading={isReloading}
|
<CheckCircle2 className="size-4" />
|
||||||
className="flex items-center gap-1.5"
|
|
||||||
>
|
|
||||||
Reload
|
|
||||||
<RefreshCcw className="size-4" />
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipPrimitive.Portal>
|
|
||||||
<TooltipContent sideOffset={5} className="z-[60]">
|
|
||||||
<p>Restart the MongoDB service without rebuilding</p>
|
|
||||||
</TooltipContent>
|
|
||||||
</TooltipPrimitive.Portal>
|
|
||||||
</Tooltip>
|
|
||||||
</Button>
|
</Button>
|
||||||
</DialogAction>
|
</DialogAction>
|
||||||
{data?.applicationStatus === "idle" ? (
|
) : (
|
||||||
<DialogAction
|
<DialogAction
|
||||||
title="Start Mongo"
|
title="Stop Mongo"
|
||||||
description="Are you sure you want to start this mongo?"
|
description="Are you sure you want to stop this mongo?"
|
||||||
type="default"
|
type="default"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await start({
|
await stop({
|
||||||
mongoId: mongoId,
|
mongoId: mongoId,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
toast.success("Mongo stopped successfully");
|
||||||
|
refetch();
|
||||||
})
|
})
|
||||||
.then(() => {
|
.catch(() => {
|
||||||
toast.success("Mongo started successfully");
|
toast.error("Error stopping Mongo");
|
||||||
refetch();
|
});
|
||||||
})
|
}}
|
||||||
.catch(() => {
|
>
|
||||||
toast.error("Error starting Mongo");
|
<Button variant="destructive" isLoading={isStopping}>
|
||||||
});
|
Stop
|
||||||
}}
|
<Ban className="size-4" />
|
||||||
>
|
</Button>
|
||||||
<Button
|
</DialogAction>
|
||||||
variant="secondary"
|
)}
|
||||||
isLoading={isStarting}
|
|
||||||
className="flex items-center gap-1.5"
|
|
||||||
>
|
|
||||||
Start
|
|
||||||
<CheckCircle2 className="size-4" />
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
|
||||||
</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"
|
|
||||||
>
|
|
||||||
Stop
|
|
||||||
<Ban className="size-4" />
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
|
||||||
</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
|
<DockerTerminalModal
|
||||||
appName={data?.appName || ""}
|
appName={data?.appName || ""}
|
||||||
serverId={data?.serverId || ""}
|
serverId={data?.serverId || ""}
|
||||||
|
|||||||
@@ -2,21 +2,8 @@ import { DialogAction } from "@/components/shared/dialog-action";
|
|||||||
import { DrawerLogs } from "@/components/shared/drawer-logs";
|
import { DrawerLogs } from "@/components/shared/drawer-logs";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import {
|
|
||||||
Tooltip,
|
|
||||||
TooltipContent,
|
|
||||||
TooltipProvider,
|
|
||||||
TooltipTrigger,
|
|
||||||
} from "@/components/ui/tooltip";
|
|
||||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
import {
|
import { Ban, CheckCircle2, RefreshCcw, Terminal } from "lucide-react";
|
||||||
Ban,
|
|
||||||
CheckCircle2,
|
|
||||||
HelpCircle,
|
|
||||||
RefreshCcw,
|
|
||||||
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";
|
||||||
@@ -75,150 +62,93 @@ export const ShowGeneralMysql = ({ mysqlId }: Props) => {
|
|||||||
<CardTitle className="text-xl">Deploy Settings</CardTitle>
|
<CardTitle className="text-xl">Deploy Settings</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="flex flex-row gap-4 flex-wrap">
|
<CardContent className="flex flex-row gap-4 flex-wrap">
|
||||||
<TooltipProvider delayDuration={0}>
|
<DialogAction
|
||||||
<DialogAction
|
title="Deploy Mysql"
|
||||||
title="Deploy Mysql"
|
description="Are you sure you want to deploy this mysql?"
|
||||||
description="Are you sure you want to deploy this mysql?"
|
type="default"
|
||||||
type="default"
|
onClick={async () => {
|
||||||
onClick={async () => {
|
setIsDeploying(true);
|
||||||
setIsDeploying(true);
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
refetch();
|
||||||
refetch();
|
}}
|
||||||
}}
|
>
|
||||||
|
<Button
|
||||||
|
variant="default"
|
||||||
|
isLoading={data?.applicationStatus === "running"}
|
||||||
>
|
>
|
||||||
<Button
|
Deploy
|
||||||
variant="default"
|
</Button>
|
||||||
isLoading={data?.applicationStatus === "running"}
|
</DialogAction>
|
||||||
className="flex items-center gap-1.5"
|
<DialogAction
|
||||||
>
|
title="Reload Mysql"
|
||||||
Deploy
|
description="Are you sure you want to reload this mysql?"
|
||||||
<Tooltip>
|
type="default"
|
||||||
<TooltipTrigger asChild>
|
onClick={async () => {
|
||||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
await reload({
|
||||||
</TooltipTrigger>
|
mysqlId: mysqlId,
|
||||||
<TooltipPrimitive.Portal>
|
appName: data?.appName || "",
|
||||||
<TooltipContent sideOffset={5} className="z-[60]">
|
})
|
||||||
<p>Downloads and sets up the MySQL database</p>
|
.then(() => {
|
||||||
</TooltipContent>
|
toast.success("Mysql reloaded successfully");
|
||||||
</TooltipPrimitive.Portal>
|
refetch();
|
||||||
</Tooltip>
|
})
|
||||||
</Button>
|
.catch(() => {
|
||||||
</DialogAction>
|
toast.error("Error reloading Mysql");
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button variant="secondary" isLoading={isReloading}>
|
||||||
|
Reload
|
||||||
|
<RefreshCcw className="size-4" />
|
||||||
|
</Button>
|
||||||
|
</DialogAction>
|
||||||
|
{data?.applicationStatus === "idle" ? (
|
||||||
<DialogAction
|
<DialogAction
|
||||||
title="Reload Mysql"
|
title="Start Mysql"
|
||||||
description="Are you sure you want to reload this mysql?"
|
description="Are you sure you want to start this mysql?"
|
||||||
type="default"
|
type="default"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await reload({
|
await start({
|
||||||
mysqlId: mysqlId,
|
mysqlId: mysqlId,
|
||||||
appName: data?.appName || "",
|
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
toast.success("Mysql reloaded successfully");
|
toast.success("Mysql started successfully");
|
||||||
refetch();
|
refetch();
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
toast.error("Error reloading Mysql");
|
toast.error("Error starting Mysql");
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button
|
<Button variant="secondary" isLoading={isStarting}>
|
||||||
variant="secondary"
|
Start
|
||||||
isLoading={isReloading}
|
<CheckCircle2 className="size-4" />
|
||||||
className="flex items-center gap-1.5"
|
|
||||||
>
|
|
||||||
Reload
|
|
||||||
<RefreshCcw className="size-4" />
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipPrimitive.Portal>
|
|
||||||
<TooltipContent sideOffset={5} className="z-[60]">
|
|
||||||
<p>Restart the MySQL service without rebuilding</p>
|
|
||||||
</TooltipContent>
|
|
||||||
</TooltipPrimitive.Portal>
|
|
||||||
</Tooltip>
|
|
||||||
</Button>
|
</Button>
|
||||||
</DialogAction>
|
</DialogAction>
|
||||||
{data?.applicationStatus === "idle" ? (
|
) : (
|
||||||
<DialogAction
|
<DialogAction
|
||||||
title="Start Mysql"
|
title="Stop Mysql"
|
||||||
description="Are you sure you want to start this mysql?"
|
description="Are you sure you want to stop this mysql?"
|
||||||
type="default"
|
onClick={async () => {
|
||||||
onClick={async () => {
|
await stop({
|
||||||
await start({
|
mysqlId: mysqlId,
|
||||||
mysqlId: mysqlId,
|
})
|
||||||
|
.then(() => {
|
||||||
|
toast.success("Mysql stopped successfully");
|
||||||
|
refetch();
|
||||||
})
|
})
|
||||||
.then(() => {
|
.catch(() => {
|
||||||
toast.success("Mysql started successfully");
|
toast.error("Error stopping Mysql");
|
||||||
refetch();
|
});
|
||||||
})
|
}}
|
||||||
.catch(() => {
|
>
|
||||||
toast.error("Error starting Mysql");
|
<Button variant="destructive" isLoading={isStopping}>
|
||||||
});
|
Stop
|
||||||
}}
|
<Ban className="size-4" />
|
||||||
>
|
</Button>
|
||||||
<Button
|
</DialogAction>
|
||||||
variant="secondary"
|
)}
|
||||||
isLoading={isStarting}
|
|
||||||
className="flex items-center gap-1.5"
|
|
||||||
>
|
|
||||||
Start
|
|
||||||
<CheckCircle2 className="size-4" />
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
|
||||||
</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"
|
|
||||||
>
|
|
||||||
Stop
|
|
||||||
<Ban className="size-4" />
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
|
||||||
</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
|
<DockerTerminalModal
|
||||||
appName={data?.appName || ""}
|
appName={data?.appName || ""}
|
||||||
serverId={data?.serverId || ""}
|
serverId={data?.serverId || ""}
|
||||||
|
|||||||
@@ -2,26 +2,12 @@ import { DialogAction } from "@/components/shared/dialog-action";
|
|||||||
import { DrawerLogs } from "@/components/shared/drawer-logs";
|
import { DrawerLogs } from "@/components/shared/drawer-logs";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import {
|
|
||||||
Tooltip,
|
|
||||||
TooltipContent,
|
|
||||||
TooltipProvider,
|
|
||||||
TooltipTrigger,
|
|
||||||
} from "@/components/ui/tooltip";
|
|
||||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
import {
|
import { Ban, CheckCircle2, RefreshCcw, Terminal } from "lucide-react";
|
||||||
Ban,
|
|
||||||
CheckCircle2,
|
|
||||||
HelpCircle,
|
|
||||||
RefreshCcw,
|
|
||||||
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";
|
||||||
import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal";
|
import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
postgresId: string;
|
postgresId: string;
|
||||||
}
|
}
|
||||||
@@ -71,179 +57,122 @@ export const ShowGeneralPostgres = ({ postgresId }: Props) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
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 className="pb-4">
|
||||||
<CardHeader>
|
<CardTitle className="text-xl">General</CardTitle>
|
||||||
<CardTitle className="text-xl">Deploy Settings</CardTitle>
|
</CardHeader>
|
||||||
</CardHeader>
|
<CardContent className="flex gap-4">
|
||||||
<CardContent className="flex flex-row gap-4 flex-wrap">
|
<DialogAction
|
||||||
<TooltipProvider delayDuration={0}>
|
title="Deploy Postgres"
|
||||||
<DialogAction
|
description="Are you sure you want to deploy this postgres?"
|
||||||
title="Deploy Postgres"
|
type="default"
|
||||||
description="Are you sure you want to deploy this postgres?"
|
onClick={async () => {
|
||||||
type="default"
|
setIsDeploying(true);
|
||||||
onClick={async () => {
|
|
||||||
setIsDeploying(true);
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
refetch();
|
||||||
refetch();
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<Button
|
||||||
<Button
|
variant="default"
|
||||||
variant="default"
|
isLoading={data?.applicationStatus === "running"}
|
||||||
isLoading={data?.applicationStatus === "running"}
|
|
||||||
className="flex items-center gap-1.5"
|
|
||||||
>
|
|
||||||
Deploy
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
|
||||||
</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"
|
|
||||||
description="Are you sure you want to reload this postgres?"
|
|
||||||
type="default"
|
|
||||||
onClick={async () => {
|
|
||||||
await reload({
|
|
||||||
postgresId: postgresId,
|
|
||||||
appName: data?.appName || "",
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
toast.success("Postgres reloaded successfully");
|
|
||||||
refetch();
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
toast.error("Error reloading Postgres");
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
variant="secondary"
|
|
||||||
isLoading={isReloading}
|
|
||||||
className="flex items-center gap-1.5"
|
|
||||||
>
|
|
||||||
Reload
|
|
||||||
<RefreshCcw className="size-4" />
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
|
||||||
</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"
|
|
||||||
description="Are you sure you want to start this postgres?"
|
|
||||||
type="default"
|
|
||||||
onClick={async () => {
|
|
||||||
await start({
|
|
||||||
postgresId: postgresId,
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
toast.success("Postgres started successfully");
|
|
||||||
refetch();
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
toast.error("Error starting Postgres");
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
variant="secondary"
|
|
||||||
isLoading={isStarting}
|
|
||||||
className="flex items-center gap-1.5"
|
|
||||||
>
|
|
||||||
Start
|
|
||||||
<CheckCircle2 className="size-4" />
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
|
||||||
</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"
|
|
||||||
description="Are you sure you want to stop this postgres?"
|
|
||||||
onClick={async () => {
|
|
||||||
await stop({
|
|
||||||
postgresId: postgresId,
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
toast.success("Postgres stopped successfully");
|
|
||||||
refetch();
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
toast.error("Error stopping Postgres");
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
variant="destructive"
|
|
||||||
isLoading={isStopping}
|
|
||||||
className="flex items-center gap-1.5"
|
|
||||||
>
|
|
||||||
Stop
|
|
||||||
<Ban className="size-4" />
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipPrimitive.Portal>
|
|
||||||
<TooltipContent sideOffset={5} className="z-[60]">
|
|
||||||
<p>Stop the currently running PostgreSQL database</p>
|
|
||||||
</TooltipContent>
|
|
||||||
</TooltipPrimitive.Portal>
|
|
||||||
</Tooltip>
|
|
||||||
</Button>
|
|
||||||
</DialogAction>
|
|
||||||
)}
|
|
||||||
</TooltipProvider>
|
|
||||||
<DockerTerminalModal
|
|
||||||
appName={data?.appName || ""}
|
|
||||||
serverId={data?.serverId || ""}
|
|
||||||
>
|
>
|
||||||
<Button variant="outline">
|
Deploy
|
||||||
<Terminal />
|
</Button>
|
||||||
Open Terminal
|
</DialogAction>
|
||||||
|
|
||||||
|
<DialogAction
|
||||||
|
title="Reload Postgres"
|
||||||
|
description="Are you sure you want to reload this postgres?"
|
||||||
|
type="default"
|
||||||
|
onClick={async () => {
|
||||||
|
await reload({
|
||||||
|
postgresId: postgresId,
|
||||||
|
appName: data?.appName || "",
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
toast.success("Postgres reloaded successfully");
|
||||||
|
refetch();
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
toast.error("Error reloading Postgres");
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button variant="secondary" isLoading={isReloading}>
|
||||||
|
Reload
|
||||||
|
<RefreshCcw className="size-4" />
|
||||||
|
</Button>
|
||||||
|
</DialogAction>
|
||||||
|
{data?.applicationStatus === "idle" ? (
|
||||||
|
<DialogAction
|
||||||
|
title="Start Postgres"
|
||||||
|
description="Are you sure you want to start this postgres?"
|
||||||
|
type="default"
|
||||||
|
onClick={async () => {
|
||||||
|
await start({
|
||||||
|
postgresId: postgresId,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
toast.success("Postgres started successfully");
|
||||||
|
refetch();
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
toast.error("Error starting Postgres");
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button variant="secondary" isLoading={isStarting}>
|
||||||
|
Start
|
||||||
|
<CheckCircle2 className="size-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</DockerTerminalModal>
|
</DialogAction>
|
||||||
</CardContent>
|
) : (
|
||||||
</Card>
|
<DialogAction
|
||||||
<DrawerLogs
|
title="Stop Postgres"
|
||||||
isOpen={isDrawerOpen}
|
description="Are you sure you want to stop this postgres?"
|
||||||
onClose={() => {
|
onClick={async () => {
|
||||||
setIsDrawerOpen(false);
|
await stop({
|
||||||
setFilteredLogs([]);
|
postgresId: postgresId,
|
||||||
setIsDeploying(false);
|
})
|
||||||
refetch();
|
.then(() => {
|
||||||
}}
|
toast.success("Postgres stopped successfully");
|
||||||
filteredLogs={filteredLogs}
|
refetch();
|
||||||
/>
|
})
|
||||||
</div>
|
.catch(() => {
|
||||||
</>
|
toast.error("Error stopping Postgres");
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button variant="destructive" isLoading={isStopping}>
|
||||||
|
Stop
|
||||||
|
<Ban className="size-4" />
|
||||||
|
</Button>
|
||||||
|
</DialogAction>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<DockerTerminalModal
|
||||||
|
appName={data?.appName || ""}
|
||||||
|
serverId={data?.serverId || ""}
|
||||||
|
>
|
||||||
|
<Button variant="outline">
|
||||||
|
<Terminal />
|
||||||
|
Open Terminal
|
||||||
|
</Button>
|
||||||
|
</DockerTerminalModal>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
<DrawerLogs
|
||||||
|
isOpen={isDrawerOpen}
|
||||||
|
onClose={() => {
|
||||||
|
setIsDrawerOpen(false);
|
||||||
|
setFilteredLogs([]);
|
||||||
|
setIsDeploying(false);
|
||||||
|
refetch();
|
||||||
|
}}
|
||||||
|
filteredLogs={filteredLogs}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,26 +2,12 @@ import { DialogAction } from "@/components/shared/dialog-action";
|
|||||||
import { DrawerLogs } from "@/components/shared/drawer-logs";
|
import { DrawerLogs } from "@/components/shared/drawer-logs";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import {
|
|
||||||
Tooltip,
|
|
||||||
TooltipContent,
|
|
||||||
TooltipProvider,
|
|
||||||
TooltipTrigger,
|
|
||||||
} from "@/components/ui/tooltip";
|
|
||||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
import {
|
import { Ban, CheckCircle2, RefreshCcw, Terminal } from "lucide-react";
|
||||||
Ban,
|
|
||||||
CheckCircle2,
|
|
||||||
HelpCircle,
|
|
||||||
RefreshCcw,
|
|
||||||
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";
|
||||||
import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal";
|
import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
redisId: string;
|
redisId: string;
|
||||||
}
|
}
|
||||||
@@ -77,150 +63,94 @@ export const ShowGeneralRedis = ({ redisId }: Props) => {
|
|||||||
<CardTitle className="text-xl">Deploy Settings</CardTitle>
|
<CardTitle className="text-xl">Deploy Settings</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="flex flex-row gap-4 flex-wrap">
|
<CardContent className="flex flex-row gap-4 flex-wrap">
|
||||||
<TooltipProvider delayDuration={0}>
|
<DialogAction
|
||||||
<DialogAction
|
title="Deploy Redis"
|
||||||
title="Deploy Redis"
|
description="Are you sure you want to deploy this redis?"
|
||||||
description="Are you sure you want to deploy this redis?"
|
type="default"
|
||||||
type="default"
|
onClick={async () => {
|
||||||
onClick={async () => {
|
setIsDeploying(true);
|
||||||
setIsDeploying(true);
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
refetch();
|
||||||
refetch();
|
}}
|
||||||
}}
|
>
|
||||||
|
<Button
|
||||||
|
variant="default"
|
||||||
|
isLoading={data?.applicationStatus === "running"}
|
||||||
>
|
>
|
||||||
<Button
|
Deploy
|
||||||
variant="default"
|
</Button>
|
||||||
isLoading={data?.applicationStatus === "running"}
|
</DialogAction>
|
||||||
className="flex items-center gap-1.5"
|
<DialogAction
|
||||||
>
|
title="Reload Redis"
|
||||||
Deploy
|
description="Are you sure you want to reload this redis?"
|
||||||
<Tooltip>
|
type="default"
|
||||||
<TooltipTrigger asChild>
|
onClick={async () => {
|
||||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
await reload({
|
||||||
</TooltipTrigger>
|
redisId: redisId,
|
||||||
<TooltipPrimitive.Portal>
|
appName: data?.appName || "",
|
||||||
<TooltipContent sideOffset={5} className="z-[60]">
|
})
|
||||||
<p>Downloads and sets up the Redis database</p>
|
.then(() => {
|
||||||
</TooltipContent>
|
toast.success("Redis reloaded successfully");
|
||||||
</TooltipPrimitive.Portal>
|
refetch();
|
||||||
</Tooltip>
|
})
|
||||||
</Button>
|
.catch(() => {
|
||||||
</DialogAction>
|
toast.error("Error reloading Redis");
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button variant="secondary" isLoading={isReloading}>
|
||||||
|
Reload
|
||||||
|
<RefreshCcw className="size-4" />
|
||||||
|
</Button>
|
||||||
|
</DialogAction>
|
||||||
|
{/* <ResetRedis redisId={redisId} appName={data?.appName || ""} /> */}
|
||||||
|
{data?.applicationStatus === "idle" ? (
|
||||||
<DialogAction
|
<DialogAction
|
||||||
title="Reload Redis"
|
title="Start Redis"
|
||||||
description="Are you sure you want to reload this redis?"
|
description="Are you sure you want to start this redis?"
|
||||||
type="default"
|
type="default"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await reload({
|
await start({
|
||||||
redisId: redisId,
|
redisId: redisId,
|
||||||
appName: data?.appName || "",
|
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
toast.success("Redis reloaded successfully");
|
toast.success("Redis started successfully");
|
||||||
refetch();
|
refetch();
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
toast.error("Error reloading Redis");
|
toast.error("Error starting Redis");
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button
|
<Button variant="secondary" isLoading={isStarting}>
|
||||||
variant="secondary"
|
Start
|
||||||
isLoading={isReloading}
|
<CheckCircle2 className="size-4" />
|
||||||
className="flex items-center gap-1.5"
|
|
||||||
>
|
|
||||||
Reload
|
|
||||||
<RefreshCcw className="size-4" />
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipPrimitive.Portal>
|
|
||||||
<TooltipContent sideOffset={5} className="z-[60]">
|
|
||||||
<p>Restart the Redis service without rebuilding</p>
|
|
||||||
</TooltipContent>
|
|
||||||
</TooltipPrimitive.Portal>
|
|
||||||
</Tooltip>
|
|
||||||
</Button>
|
</Button>
|
||||||
</DialogAction>
|
</DialogAction>
|
||||||
{data?.applicationStatus === "idle" ? (
|
) : (
|
||||||
<DialogAction
|
<DialogAction
|
||||||
title="Start Redis"
|
title="Stop Redis"
|
||||||
description="Are you sure you want to start this redis?"
|
description="Are you sure you want to stop this redis?"
|
||||||
type="default"
|
onClick={async () => {
|
||||||
onClick={async () => {
|
await stop({
|
||||||
await start({
|
redisId: redisId,
|
||||||
redisId: redisId,
|
})
|
||||||
|
.then(() => {
|
||||||
|
toast.success("Redis stopped successfully");
|
||||||
|
refetch();
|
||||||
})
|
})
|
||||||
.then(() => {
|
.catch(() => {
|
||||||
toast.success("Redis started successfully");
|
toast.error("Error stopping Redis");
|
||||||
refetch();
|
});
|
||||||
})
|
}}
|
||||||
.catch(() => {
|
>
|
||||||
toast.error("Error starting Redis");
|
<Button variant="destructive" isLoading={isStopping}>
|
||||||
});
|
Stop
|
||||||
}}
|
<Ban className="size-4" />
|
||||||
>
|
</Button>
|
||||||
<Button
|
</DialogAction>
|
||||||
variant="secondary"
|
)}
|
||||||
isLoading={isStarting}
|
|
||||||
className="flex items-center gap-1.5"
|
|
||||||
>
|
|
||||||
Start
|
|
||||||
<CheckCircle2 className="size-4" />
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
|
||||||
</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"
|
|
||||||
>
|
|
||||||
Stop
|
|
||||||
<Ban className="size-4" />
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
|
||||||
</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
|
<DockerTerminalModal
|
||||||
appName={data?.appName || ""}
|
appName={data?.appName || ""}
|
||||||
serverId={data?.serverId || ""}
|
serverId={data?.serverId || ""}
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
ALTER TABLE "application" ADD COLUMN "buildSecrets" text;--> statement-breakpoint
|
|
||||||
ALTER TABLE "user_temp" DROP COLUMN "enableLogRotation";
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -505,13 +505,6 @@
|
|||||||
"when": 1741460060541,
|
"when": 1741460060541,
|
||||||
"tag": "0071_flaky_black_queen",
|
"tag": "0071_flaky_black_queen",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
},
|
|
||||||
{
|
|
||||||
"idx": 72,
|
|
||||||
"version": "7",
|
|
||||||
"when": 1741481694393,
|
|
||||||
"tag": "0072_milky_lyja",
|
|
||||||
"breakpoints": true
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -65,7 +65,6 @@ import {
|
|||||||
PlusIcon,
|
PlusIcon,
|
||||||
Search,
|
Search,
|
||||||
X,
|
X,
|
||||||
Trash2,
|
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import type {
|
import type {
|
||||||
GetServerSidePropsContext,
|
GetServerSidePropsContext,
|
||||||
@@ -73,25 +72,9 @@ import type {
|
|||||||
} from "next";
|
} from "next";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { type ReactElement, useMemo, useState, useEffect } from "react";
|
import { type ReactElement, useMemo, useState } from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import superjson from "superjson";
|
import superjson from "superjson";
|
||||||
import {
|
|
||||||
Dialog,
|
|
||||||
DialogContent,
|
|
||||||
DialogDescription,
|
|
||||||
DialogFooter,
|
|
||||||
DialogHeader,
|
|
||||||
DialogTitle,
|
|
||||||
DialogTrigger,
|
|
||||||
} from "@/components/ui/dialog";
|
|
||||||
import {
|
|
||||||
Select,
|
|
||||||
SelectContent,
|
|
||||||
SelectItem,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectValue,
|
|
||||||
} from "@/components/ui/select";
|
|
||||||
|
|
||||||
export type Services = {
|
export type Services = {
|
||||||
appName: string;
|
appName: string;
|
||||||
@@ -220,47 +203,10 @@ const Project = (
|
|||||||
const [isBulkActionLoading, setIsBulkActionLoading] = useState(false);
|
const [isBulkActionLoading, setIsBulkActionLoading] = useState(false);
|
||||||
const { projectId } = props;
|
const { projectId } = props;
|
||||||
const { data: auth } = api.user.get.useQuery();
|
const { data: auth } = api.user.get.useQuery();
|
||||||
const [sortBy, setSortBy] = useState<string>(() => {
|
|
||||||
if (typeof window !== "undefined") {
|
|
||||||
return localStorage.getItem("servicesSort") || "createdAt-desc";
|
|
||||||
}
|
|
||||||
return "createdAt-desc";
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
localStorage.setItem("servicesSort", sortBy);
|
|
||||||
}, [sortBy]);
|
|
||||||
|
|
||||||
const sortServices = (services: Services[]) => {
|
|
||||||
const [field, direction] = sortBy.split("-");
|
|
||||||
return [...services].sort((a, b) => {
|
|
||||||
let comparison = 0;
|
|
||||||
switch (field) {
|
|
||||||
case "name":
|
|
||||||
comparison = a.name.localeCompare(b.name);
|
|
||||||
break;
|
|
||||||
case "type":
|
|
||||||
comparison = a.type.localeCompare(b.type);
|
|
||||||
break;
|
|
||||||
case "createdAt":
|
|
||||||
comparison =
|
|
||||||
new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
comparison = 0;
|
|
||||||
}
|
|
||||||
return direction === "asc" ? comparison : -comparison;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const { data, isLoading, refetch } = api.project.one.useQuery({ projectId });
|
const { data, isLoading, refetch } = api.project.one.useQuery({ projectId });
|
||||||
const { data: allProjects } = api.project.all.useQuery();
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const [isMoveDialogOpen, setIsMoveDialogOpen] = useState(false);
|
|
||||||
const [selectedTargetProject, setSelectedTargetProject] =
|
|
||||||
useState<string>("");
|
|
||||||
|
|
||||||
const emptyServices =
|
const emptyServices =
|
||||||
data?.mariadb?.length === 0 &&
|
data?.mariadb?.length === 0 &&
|
||||||
data?.mongo?.length === 0 &&
|
data?.mongo?.length === 0 &&
|
||||||
@@ -308,38 +254,6 @@ const Project = (
|
|||||||
const composeActions = {
|
const composeActions = {
|
||||||
start: api.compose.start.useMutation(),
|
start: api.compose.start.useMutation(),
|
||||||
stop: api.compose.stop.useMutation(),
|
stop: api.compose.stop.useMutation(),
|
||||||
move: api.compose.move.useMutation(),
|
|
||||||
delete: api.compose.delete.useMutation(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const applicationActions = {
|
|
||||||
move: api.application.move.useMutation(),
|
|
||||||
delete: api.application.delete.useMutation(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const postgresActions = {
|
|
||||||
move: api.postgres.move.useMutation(),
|
|
||||||
delete: api.postgres.remove.useMutation(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const mysqlActions = {
|
|
||||||
move: api.mysql.move.useMutation(),
|
|
||||||
delete: api.mysql.remove.useMutation(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const mariadbActions = {
|
|
||||||
move: api.mariadb.move.useMutation(),
|
|
||||||
delete: api.mariadb.remove.useMutation(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const redisActions = {
|
|
||||||
move: api.redis.move.useMutation(),
|
|
||||||
delete: api.redis.remove.useMutation(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const mongoActions = {
|
|
||||||
move: api.mongo.move.useMutation(),
|
|
||||||
delete: api.mongo.remove.useMutation(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleBulkStart = async () => {
|
const handleBulkStart = async () => {
|
||||||
@@ -382,145 +296,9 @@ const Project = (
|
|||||||
setIsBulkActionLoading(false);
|
setIsBulkActionLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleBulkMove = async () => {
|
|
||||||
if (!selectedTargetProject) {
|
|
||||||
toast.error("Please select a target project");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let success = 0;
|
|
||||||
setIsBulkActionLoading(true);
|
|
||||||
for (const serviceId of selectedServices) {
|
|
||||||
try {
|
|
||||||
const service = filteredServices.find((s) => s.id === serviceId);
|
|
||||||
if (!service) continue;
|
|
||||||
|
|
||||||
switch (service.type) {
|
|
||||||
case "application":
|
|
||||||
await applicationActions.move.mutateAsync({
|
|
||||||
applicationId: serviceId,
|
|
||||||
targetProjectId: selectedTargetProject,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case "compose":
|
|
||||||
await composeActions.move.mutateAsync({
|
|
||||||
composeId: serviceId,
|
|
||||||
targetProjectId: selectedTargetProject,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case "postgres":
|
|
||||||
await postgresActions.move.mutateAsync({
|
|
||||||
postgresId: serviceId,
|
|
||||||
targetProjectId: selectedTargetProject,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case "mysql":
|
|
||||||
await mysqlActions.move.mutateAsync({
|
|
||||||
mysqlId: serviceId,
|
|
||||||
targetProjectId: selectedTargetProject,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case "mariadb":
|
|
||||||
await mariadbActions.move.mutateAsync({
|
|
||||||
mariadbId: serviceId,
|
|
||||||
targetProjectId: selectedTargetProject,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case "redis":
|
|
||||||
await redisActions.move.mutateAsync({
|
|
||||||
redisId: serviceId,
|
|
||||||
targetProjectId: selectedTargetProject,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case "mongo":
|
|
||||||
await mongoActions.move.mutateAsync({
|
|
||||||
mongoId: serviceId,
|
|
||||||
targetProjectId: selectedTargetProject,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
success++;
|
|
||||||
} catch (error) {
|
|
||||||
toast.error(
|
|
||||||
`Error moving service ${serviceId}: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (success > 0) {
|
|
||||||
toast.success(`${success} services moved successfully`);
|
|
||||||
refetch();
|
|
||||||
}
|
|
||||||
setSelectedServices([]);
|
|
||||||
setIsDropdownOpen(false);
|
|
||||||
setIsMoveDialogOpen(false);
|
|
||||||
setIsBulkActionLoading(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleBulkDelete = async () => {
|
|
||||||
let success = 0;
|
|
||||||
setIsBulkActionLoading(true);
|
|
||||||
for (const serviceId of selectedServices) {
|
|
||||||
try {
|
|
||||||
const service = filteredServices.find((s) => s.id === serviceId);
|
|
||||||
if (!service) continue;
|
|
||||||
|
|
||||||
switch (service.type) {
|
|
||||||
case "application":
|
|
||||||
await applicationActions.delete.mutateAsync({
|
|
||||||
applicationId: serviceId,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case "compose":
|
|
||||||
await composeActions.delete.mutateAsync({
|
|
||||||
composeId: serviceId,
|
|
||||||
deleteVolumes: false,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case "postgres":
|
|
||||||
await postgresActions.delete.mutateAsync({
|
|
||||||
postgresId: serviceId,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case "mysql":
|
|
||||||
await mysqlActions.delete.mutateAsync({
|
|
||||||
mysqlId: serviceId,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case "mariadb":
|
|
||||||
await mariadbActions.delete.mutateAsync({
|
|
||||||
mariadbId: serviceId,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case "redis":
|
|
||||||
await redisActions.delete.mutateAsync({
|
|
||||||
redisId: serviceId,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case "mongo":
|
|
||||||
await mongoActions.delete.mutateAsync({
|
|
||||||
mongoId: serviceId,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
success++;
|
|
||||||
} catch (error) {
|
|
||||||
toast.error(
|
|
||||||
`Error deleting service ${serviceId}: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (success > 0) {
|
|
||||||
toast.success(`${success} services deleted successfully`);
|
|
||||||
refetch();
|
|
||||||
}
|
|
||||||
setSelectedServices([]);
|
|
||||||
setIsDropdownOpen(false);
|
|
||||||
setIsBulkActionLoading(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const filteredServices = useMemo(() => {
|
const filteredServices = useMemo(() => {
|
||||||
if (!applications) return [];
|
if (!applications) return [];
|
||||||
const filtered = applications.filter(
|
return applications.filter(
|
||||||
(service) =>
|
(service) =>
|
||||||
(service.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
(service.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
service.description
|
service.description
|
||||||
@@ -528,8 +306,7 @@ const Project = (
|
|||||||
.includes(searchQuery.toLowerCase())) &&
|
.includes(searchQuery.toLowerCase())) &&
|
||||||
(selectedTypes.length === 0 || selectedTypes.includes(service.type)),
|
(selectedTypes.length === 0 || selectedTypes.includes(service.type)),
|
||||||
);
|
);
|
||||||
return sortServices(filtered);
|
}, [applications, searchQuery, selectedTypes]);
|
||||||
}, [applications, searchQuery, selectedTypes, sortBy]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -603,7 +380,7 @@ const Project = (
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<div className="flex flex-col gap-4 xl:flex-row xl:items-center xl:justify-between">
|
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
@@ -668,107 +445,11 @@ const Project = (
|
|||||||
Stop
|
Stop
|
||||||
</Button>
|
</Button>
|
||||||
</DialogAction>
|
</DialogAction>
|
||||||
{(auth?.role === "owner" ||
|
|
||||||
auth?.canDeleteServices) && (
|
|
||||||
<DialogAction
|
|
||||||
title="Delete Services"
|
|
||||||
description={`Are you sure you want to delete ${selectedServices.length} services? This action cannot be undone.`}
|
|
||||||
type="destructive"
|
|
||||||
onClick={handleBulkDelete}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
className="w-full justify-start text-destructive"
|
|
||||||
>
|
|
||||||
<Trash2 className="mr-2 h-4 w-4" />
|
|
||||||
Delete
|
|
||||||
</Button>
|
|
||||||
</DialogAction>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Dialog
|
|
||||||
open={isMoveDialogOpen}
|
|
||||||
onOpenChange={setIsMoveDialogOpen}
|
|
||||||
>
|
|
||||||
<DialogTrigger asChild>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
className="w-full justify-start"
|
|
||||||
>
|
|
||||||
<FolderInput className="mr-2 h-4 w-4" />
|
|
||||||
Move
|
|
||||||
</Button>
|
|
||||||
</DialogTrigger>
|
|
||||||
<DialogContent>
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle>Move Services</DialogTitle>
|
|
||||||
<DialogDescription>
|
|
||||||
Select the target project to move{" "}
|
|
||||||
{selectedServices.length} services
|
|
||||||
</DialogDescription>
|
|
||||||
</DialogHeader>
|
|
||||||
<div className="flex flex-col gap-4">
|
|
||||||
{allProjects?.filter(
|
|
||||||
(p) => p.projectId !== projectId,
|
|
||||||
).length === 0 ? (
|
|
||||||
<div className="flex flex-col items-center justify-center gap-2 py-4">
|
|
||||||
<FolderInput className="h-8 w-8 text-muted-foreground" />
|
|
||||||
<p className="text-sm text-muted-foreground text-center">
|
|
||||||
No other projects available. Create a new
|
|
||||||
project first to move services.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<Select
|
|
||||||
value={selectedTargetProject}
|
|
||||||
onValueChange={setSelectedTargetProject}
|
|
||||||
>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue placeholder="Select target project" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
{allProjects
|
|
||||||
?.filter(
|
|
||||||
(p) => p.projectId !== projectId,
|
|
||||||
)
|
|
||||||
.map((project) => (
|
|
||||||
<SelectItem
|
|
||||||
key={project.projectId}
|
|
||||||
value={project.projectId}
|
|
||||||
>
|
|
||||||
{project.name}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<DialogFooter>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
onClick={() => setIsMoveDialogOpen(false)}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
onClick={handleBulkMove}
|
|
||||||
isLoading={isBulkActionLoading}
|
|
||||||
disabled={
|
|
||||||
allProjects?.filter(
|
|
||||||
(p) => p.projectId !== projectId,
|
|
||||||
).length === 0
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Move Services
|
|
||||||
</Button>
|
|
||||||
</DialogFooter>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col gap-2 lg:flex-row lg:gap-4 lg:items-center">
|
<div className="flex flex-col gap-2 sm:flex-row sm:gap-4 sm:items-center">
|
||||||
<div className="w-full relative">
|
<div className="w-full relative">
|
||||||
<Input
|
<Input
|
||||||
placeholder="Filter services..."
|
placeholder="Filter services..."
|
||||||
@@ -778,23 +459,6 @@ const Project = (
|
|||||||
/>
|
/>
|
||||||
<Search className="absolute right-3 top-1/2 -translate-y-1/2 size-4 text-muted-foreground" />
|
<Search className="absolute right-3 top-1/2 -translate-y-1/2 size-4 text-muted-foreground" />
|
||||||
</div>
|
</div>
|
||||||
<Select value={sortBy} onValueChange={setSortBy}>
|
|
||||||
<SelectTrigger className="lg:w-[280px]">
|
|
||||||
<SelectValue placeholder="Sort by..." />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="createdAt-desc">
|
|
||||||
Newest first
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value="createdAt-asc">
|
|
||||||
Oldest first
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value="name-asc">Name (A-Z)</SelectItem>
|
|
||||||
<SelectItem value="name-desc">Name (Z-A)</SelectItem>
|
|
||||||
<SelectItem value="type-asc">Type (A-Z)</SelectItem>
|
|
||||||
<SelectItem value="type-desc">Type (Z-A)</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
<Popover
|
<Popover
|
||||||
open={openCombobox}
|
open={openCombobox}
|
||||||
onOpenChange={setOpenCombobox}
|
onOpenChange={setOpenCombobox}
|
||||||
|
|||||||
@@ -298,7 +298,6 @@ export const applicationRouter = createTRPCRouter({
|
|||||||
await updateApplication(input.applicationId, {
|
await updateApplication(input.applicationId, {
|
||||||
env: input.env,
|
env: input.env,
|
||||||
buildArgs: input.buildArgs,
|
buildArgs: input.buildArgs,
|
||||||
buildSecrets: input.buildSecrets,
|
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}),
|
}),
|
||||||
@@ -669,49 +668,4 @@ export const applicationRouter = createTRPCRouter({
|
|||||||
|
|
||||||
return stats;
|
return stats;
|
||||||
}),
|
}),
|
||||||
move: protectedProcedure
|
|
||||||
.input(
|
|
||||||
z.object({
|
|
||||||
applicationId: z.string(),
|
|
||||||
targetProjectId: z.string(),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.mutation(async ({ input, ctx }) => {
|
|
||||||
const application = await findApplicationById(input.applicationId);
|
|
||||||
if (
|
|
||||||
application.project.organizationId !== ctx.session.activeOrganizationId
|
|
||||||
) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "UNAUTHORIZED",
|
|
||||||
message: "You are not authorized to move this application",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const targetProject = await findProjectById(input.targetProjectId);
|
|
||||||
if (targetProject.organizationId !== ctx.session.activeOrganizationId) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "UNAUTHORIZED",
|
|
||||||
message: "You are not authorized to move to this project",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the application's projectId
|
|
||||||
const updatedApplication = await db
|
|
||||||
.update(applications)
|
|
||||||
.set({
|
|
||||||
projectId: input.targetProjectId,
|
|
||||||
})
|
|
||||||
.where(eq(applications.applicationId, input.applicationId))
|
|
||||||
.returning()
|
|
||||||
.then((res) => res[0]);
|
|
||||||
|
|
||||||
if (!updatedApplication) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "INTERNAL_SERVER_ERROR",
|
|
||||||
message: "Failed to move application",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return updatedApplication;
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
apiFindCompose,
|
apiFindCompose,
|
||||||
apiRandomizeCompose,
|
apiRandomizeCompose,
|
||||||
apiUpdateCompose,
|
apiUpdateCompose,
|
||||||
compose as composeTable,
|
compose,
|
||||||
} from "@/server/db/schema";
|
} from "@/server/db/schema";
|
||||||
import { cleanQueuesByCompose, myQueue } from "@/server/queues/queueSetup";
|
import { cleanQueuesByCompose, myQueue } from "@/server/queues/queueSetup";
|
||||||
import { templates } from "@/templates/templates";
|
import { templates } from "@/templates/templates";
|
||||||
@@ -24,7 +24,6 @@ import { dump } from "js-yaml";
|
|||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
import { createTRPCRouter, protectedProcedure } from "../trpc";
|
import { createTRPCRouter, protectedProcedure } from "../trpc";
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
import type { DeploymentJob } from "@/server/queues/queue-types";
|
import type { DeploymentJob } from "@/server/queues/queue-types";
|
||||||
import { deploy } from "@/server/utils/deploy";
|
import { deploy } from "@/server/utils/deploy";
|
||||||
@@ -158,8 +157,8 @@ export const composeRouter = createTRPCRouter({
|
|||||||
4;
|
4;
|
||||||
|
|
||||||
const result = await db
|
const result = await db
|
||||||
.delete(composeTable)
|
.delete(compose)
|
||||||
.where(eq(composeTable.composeId, input.composeId))
|
.where(eq(compose.composeId, input.composeId))
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
const cleanupOperations = [
|
const cleanupOperations = [
|
||||||
@@ -502,48 +501,4 @@ export const composeRouter = createTRPCRouter({
|
|||||||
const uniqueTags = _.uniq(allTags);
|
const uniqueTags = _.uniq(allTags);
|
||||||
return uniqueTags;
|
return uniqueTags;
|
||||||
}),
|
}),
|
||||||
|
|
||||||
move: protectedProcedure
|
|
||||||
.input(
|
|
||||||
z.object({
|
|
||||||
composeId: z.string(),
|
|
||||||
targetProjectId: z.string(),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.mutation(async ({ input, ctx }) => {
|
|
||||||
const compose = await findComposeById(input.composeId);
|
|
||||||
if (compose.project.organizationId !== ctx.session.activeOrganizationId) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "UNAUTHORIZED",
|
|
||||||
message: "You are not authorized to move this compose",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const targetProject = await findProjectById(input.targetProjectId);
|
|
||||||
if (targetProject.organizationId !== ctx.session.activeOrganizationId) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "UNAUTHORIZED",
|
|
||||||
message: "You are not authorized to move to this project",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the compose's projectId
|
|
||||||
const updatedCompose = await db
|
|
||||||
.update(composeTable)
|
|
||||||
.set({
|
|
||||||
projectId: input.targetProjectId,
|
|
||||||
})
|
|
||||||
.where(eq(composeTable.composeId, input.composeId))
|
|
||||||
.returning()
|
|
||||||
.then((res) => res[0]);
|
|
||||||
|
|
||||||
if (!updatedCompose) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "INTERNAL_SERVER_ERROR",
|
|
||||||
message: "Failed to move compose",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return updatedCompose;
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import {
|
|||||||
apiSaveEnvironmentVariablesMariaDB,
|
apiSaveEnvironmentVariablesMariaDB,
|
||||||
apiSaveExternalPortMariaDB,
|
apiSaveExternalPortMariaDB,
|
||||||
apiUpdateMariaDB,
|
apiUpdateMariaDB,
|
||||||
mariadb as mariadbTable,
|
|
||||||
} from "@/server/db/schema";
|
} from "@/server/db/schema";
|
||||||
import { cancelJobs } from "@/server/utils/backup";
|
import { cancelJobs } from "@/server/utils/backup";
|
||||||
import {
|
import {
|
||||||
@@ -31,9 +30,6 @@ import {
|
|||||||
} from "@dokploy/server";
|
} from "@dokploy/server";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { observable } from "@trpc/server/observable";
|
import { observable } from "@trpc/server/observable";
|
||||||
import { z } from "zod";
|
|
||||||
import { eq } from "drizzle-orm";
|
|
||||||
import { db } from "@/server/db";
|
|
||||||
|
|
||||||
export const mariadbRouter = createTRPCRouter({
|
export const mariadbRouter = createTRPCRouter({
|
||||||
create: protectedProcedure
|
create: protectedProcedure
|
||||||
@@ -326,47 +322,4 @@ export const mariadbRouter = createTRPCRouter({
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}),
|
}),
|
||||||
move: protectedProcedure
|
|
||||||
.input(
|
|
||||||
z.object({
|
|
||||||
mariadbId: z.string(),
|
|
||||||
targetProjectId: z.string(),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.mutation(async ({ input, ctx }) => {
|
|
||||||
const mariadb = await findMariadbById(input.mariadbId);
|
|
||||||
if (mariadb.project.organizationId !== ctx.session.activeOrganizationId) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "UNAUTHORIZED",
|
|
||||||
message: "You are not authorized to move this mariadb",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const targetProject = await findProjectById(input.targetProjectId);
|
|
||||||
if (targetProject.organizationId !== ctx.session.activeOrganizationId) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "UNAUTHORIZED",
|
|
||||||
message: "You are not authorized to move to this project",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the mariadb's projectId
|
|
||||||
const updatedMariadb = await db
|
|
||||||
.update(mariadbTable)
|
|
||||||
.set({
|
|
||||||
projectId: input.targetProjectId,
|
|
||||||
})
|
|
||||||
.where(eq(mariadbTable.mariadbId, input.mariadbId))
|
|
||||||
.returning()
|
|
||||||
.then((res) => res[0]);
|
|
||||||
|
|
||||||
if (!updatedMariadb) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "INTERNAL_SERVER_ERROR",
|
|
||||||
message: "Failed to move mariadb",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return updatedMariadb;
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import {
|
|||||||
apiSaveEnvironmentVariablesMongo,
|
apiSaveEnvironmentVariablesMongo,
|
||||||
apiSaveExternalPortMongo,
|
apiSaveExternalPortMongo,
|
||||||
apiUpdateMongo,
|
apiUpdateMongo,
|
||||||
mongo as mongoTable,
|
|
||||||
} from "@/server/db/schema";
|
} from "@/server/db/schema";
|
||||||
import { cancelJobs } from "@/server/utils/backup";
|
import { cancelJobs } from "@/server/utils/backup";
|
||||||
import {
|
import {
|
||||||
@@ -31,9 +30,6 @@ import {
|
|||||||
} from "@dokploy/server";
|
} from "@dokploy/server";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { observable } from "@trpc/server/observable";
|
import { observable } from "@trpc/server/observable";
|
||||||
import { z } from "zod";
|
|
||||||
import { eq } from "drizzle-orm";
|
|
||||||
import { db } from "@/server/db";
|
|
||||||
|
|
||||||
export const mongoRouter = createTRPCRouter({
|
export const mongoRouter = createTRPCRouter({
|
||||||
create: protectedProcedure
|
create: protectedProcedure
|
||||||
@@ -340,47 +336,4 @@ export const mongoRouter = createTRPCRouter({
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}),
|
}),
|
||||||
move: protectedProcedure
|
|
||||||
.input(
|
|
||||||
z.object({
|
|
||||||
mongoId: z.string(),
|
|
||||||
targetProjectId: z.string(),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.mutation(async ({ input, ctx }) => {
|
|
||||||
const mongo = await findMongoById(input.mongoId);
|
|
||||||
if (mongo.project.organizationId !== ctx.session.activeOrganizationId) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "UNAUTHORIZED",
|
|
||||||
message: "You are not authorized to move this mongo",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const targetProject = await findProjectById(input.targetProjectId);
|
|
||||||
if (targetProject.organizationId !== ctx.session.activeOrganizationId) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "UNAUTHORIZED",
|
|
||||||
message: "You are not authorized to move to this project",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the mongo's projectId
|
|
||||||
const updatedMongo = await db
|
|
||||||
.update(mongoTable)
|
|
||||||
.set({
|
|
||||||
projectId: input.targetProjectId,
|
|
||||||
})
|
|
||||||
.where(eq(mongoTable.mongoId, input.mongoId))
|
|
||||||
.returning()
|
|
||||||
.then((res) => res[0]);
|
|
||||||
|
|
||||||
if (!updatedMongo) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "INTERNAL_SERVER_ERROR",
|
|
||||||
message: "Failed to move mongo",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return updatedMongo;
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import {
|
|||||||
apiSaveEnvironmentVariablesMySql,
|
apiSaveEnvironmentVariablesMySql,
|
||||||
apiSaveExternalPortMySql,
|
apiSaveExternalPortMySql,
|
||||||
apiUpdateMySql,
|
apiUpdateMySql,
|
||||||
mysql as mysqlTable,
|
|
||||||
} from "@/server/db/schema";
|
} from "@/server/db/schema";
|
||||||
|
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
@@ -33,9 +32,6 @@ import {
|
|||||||
updateMySqlById,
|
updateMySqlById,
|
||||||
} from "@dokploy/server";
|
} from "@dokploy/server";
|
||||||
import { observable } from "@trpc/server/observable";
|
import { observable } from "@trpc/server/observable";
|
||||||
import { eq } from "drizzle-orm";
|
|
||||||
import { db } from "@/server/db";
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
export const mysqlRouter = createTRPCRouter({
|
export const mysqlRouter = createTRPCRouter({
|
||||||
create: protectedProcedure
|
create: protectedProcedure
|
||||||
@@ -336,47 +332,4 @@ export const mysqlRouter = createTRPCRouter({
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}),
|
}),
|
||||||
move: protectedProcedure
|
|
||||||
.input(
|
|
||||||
z.object({
|
|
||||||
mysqlId: z.string(),
|
|
||||||
targetProjectId: z.string(),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.mutation(async ({ input, ctx }) => {
|
|
||||||
const mysql = await findMySqlById(input.mysqlId);
|
|
||||||
if (mysql.project.organizationId !== ctx.session.activeOrganizationId) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "UNAUTHORIZED",
|
|
||||||
message: "You are not authorized to move this mysql",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const targetProject = await findProjectById(input.targetProjectId);
|
|
||||||
if (targetProject.organizationId !== ctx.session.activeOrganizationId) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "UNAUTHORIZED",
|
|
||||||
message: "You are not authorized to move to this project",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the mysql's projectId
|
|
||||||
const updatedMysql = await db
|
|
||||||
.update(mysqlTable)
|
|
||||||
.set({
|
|
||||||
projectId: input.targetProjectId,
|
|
||||||
})
|
|
||||||
.where(eq(mysqlTable.mysqlId, input.mysqlId))
|
|
||||||
.returning()
|
|
||||||
.then((res) => res[0]);
|
|
||||||
|
|
||||||
if (!updatedMysql) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "INTERNAL_SERVER_ERROR",
|
|
||||||
message: "Failed to move mysql",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return updatedMysql;
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import {
|
|||||||
apiSaveEnvironmentVariablesPostgres,
|
apiSaveEnvironmentVariablesPostgres,
|
||||||
apiSaveExternalPortPostgres,
|
apiSaveExternalPortPostgres,
|
||||||
apiUpdatePostgres,
|
apiUpdatePostgres,
|
||||||
postgres as postgresTable,
|
|
||||||
} from "@/server/db/schema";
|
} from "@/server/db/schema";
|
||||||
import { cancelJobs } from "@/server/utils/backup";
|
import { cancelJobs } from "@/server/utils/backup";
|
||||||
import {
|
import {
|
||||||
@@ -31,9 +30,6 @@ import {
|
|||||||
} from "@dokploy/server";
|
} from "@dokploy/server";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { observable } from "@trpc/server/observable";
|
import { observable } from "@trpc/server/observable";
|
||||||
import { z } from "zod";
|
|
||||||
import { eq } from "drizzle-orm";
|
|
||||||
import { db } from "@/server/db";
|
|
||||||
|
|
||||||
export const postgresRouter = createTRPCRouter({
|
export const postgresRouter = createTRPCRouter({
|
||||||
create: protectedProcedure
|
create: protectedProcedure
|
||||||
@@ -356,49 +352,4 @@ export const postgresRouter = createTRPCRouter({
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}),
|
}),
|
||||||
move: protectedProcedure
|
|
||||||
.input(
|
|
||||||
z.object({
|
|
||||||
postgresId: z.string(),
|
|
||||||
targetProjectId: z.string(),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.mutation(async ({ input, ctx }) => {
|
|
||||||
const postgres = await findPostgresById(input.postgresId);
|
|
||||||
if (
|
|
||||||
postgres.project.organizationId !== ctx.session.activeOrganizationId
|
|
||||||
) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "UNAUTHORIZED",
|
|
||||||
message: "You are not authorized to move this postgres",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const targetProject = await findProjectById(input.targetProjectId);
|
|
||||||
if (targetProject.organizationId !== ctx.session.activeOrganizationId) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "UNAUTHORIZED",
|
|
||||||
message: "You are not authorized to move to this project",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the postgres's projectId
|
|
||||||
const updatedPostgres = await db
|
|
||||||
.update(postgresTable)
|
|
||||||
.set({
|
|
||||||
projectId: input.targetProjectId,
|
|
||||||
})
|
|
||||||
.where(eq(postgresTable.postgresId, input.postgresId))
|
|
||||||
.returning()
|
|
||||||
.then((res) => res[0]);
|
|
||||||
|
|
||||||
if (!updatedPostgres) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "INTERNAL_SERVER_ERROR",
|
|
||||||
message: "Failed to move postgres",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return updatedPostgres;
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,13 +1,23 @@
|
|||||||
import { apiFindAllByApplication } from "@/server/db/schema";
|
import { db } from "@/server/db";
|
||||||
|
import { apiFindAllByApplication, applications } from "@/server/db/schema";
|
||||||
import {
|
import {
|
||||||
|
createPreviewDeployment,
|
||||||
findApplicationById,
|
findApplicationById,
|
||||||
|
findPreviewDeploymentByApplicationId,
|
||||||
findPreviewDeploymentById,
|
findPreviewDeploymentById,
|
||||||
findPreviewDeploymentsByApplicationId,
|
findPreviewDeploymentsByApplicationId,
|
||||||
|
findPreviewDeploymentsByPullRequestId,
|
||||||
|
IS_CLOUD,
|
||||||
removePreviewDeployment,
|
removePreviewDeployment,
|
||||||
} from "@dokploy/server";
|
} from "@dokploy/server";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { createTRPCRouter, protectedProcedure } from "../trpc";
|
import { createTRPCRouter, protectedProcedure } from "../trpc";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
import { and } from "drizzle-orm";
|
||||||
|
import { myQueue } from "@/server/queues/queueSetup";
|
||||||
|
import { deploy } from "@/server/utils/deploy";
|
||||||
|
import type { DeploymentJob } from "@/server/queues/queue-types";
|
||||||
|
|
||||||
export const previewDeploymentRouter = createTRPCRouter({
|
export const previewDeploymentRouter = createTRPCRouter({
|
||||||
all: protectedProcedure
|
all: protectedProcedure
|
||||||
@@ -59,4 +69,142 @@ export const previewDeploymentRouter = createTRPCRouter({
|
|||||||
}
|
}
|
||||||
return previewDeployment;
|
return previewDeployment;
|
||||||
}),
|
}),
|
||||||
|
create: protectedProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
action: z.enum(["opened", "synchronize", "reopened", "closed"]),
|
||||||
|
pullRequestId: z.string(),
|
||||||
|
repository: z.string(),
|
||||||
|
owner: z.string(),
|
||||||
|
branch: z.string(),
|
||||||
|
deploymentHash: z.string(),
|
||||||
|
prBranch: z.string(),
|
||||||
|
prNumber: z.any(),
|
||||||
|
prTitle: z.string(),
|
||||||
|
prURL: z.string(),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
const organizationId = ctx.session.activeOrganizationId;
|
||||||
|
const action = input.action;
|
||||||
|
const prId = input.pullRequestId;
|
||||||
|
|
||||||
|
if (action === "closed") {
|
||||||
|
const previewDeploymentResult =
|
||||||
|
await findPreviewDeploymentsByPullRequestId(prId);
|
||||||
|
|
||||||
|
const filteredPreviewDeploymentResult = previewDeploymentResult.filter(
|
||||||
|
(previewDeployment) =>
|
||||||
|
previewDeployment.application.project.organizationId ===
|
||||||
|
organizationId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (filteredPreviewDeploymentResult.length > 0) {
|
||||||
|
for (const previewDeployment of filteredPreviewDeploymentResult) {
|
||||||
|
try {
|
||||||
|
await removePreviewDeployment(
|
||||||
|
previewDeployment.previewDeploymentId,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
message: "Preview Deployments Closed",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
action === "opened" ||
|
||||||
|
action === "synchronize" ||
|
||||||
|
action === "reopened"
|
||||||
|
) {
|
||||||
|
const deploymentHash = input.deploymentHash;
|
||||||
|
|
||||||
|
const prBranch = input.prBranch;
|
||||||
|
const prNumber = input.prNumber;
|
||||||
|
const prTitle = input.prTitle;
|
||||||
|
const prURL = input.prURL;
|
||||||
|
const apps = await db.query.applications.findMany({
|
||||||
|
where: and(
|
||||||
|
eq(applications.sourceType, "github"),
|
||||||
|
eq(applications.repository, input.repository),
|
||||||
|
eq(applications.branch, input.branch),
|
||||||
|
eq(applications.isPreviewDeploymentsActive, true),
|
||||||
|
eq(applications.owner, input.owner),
|
||||||
|
),
|
||||||
|
with: {
|
||||||
|
previewDeployments: true,
|
||||||
|
project: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const filteredApps = apps.filter(
|
||||||
|
(app) => app.project.organizationId === organizationId,
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(filteredApps);
|
||||||
|
|
||||||
|
for (const app of filteredApps) {
|
||||||
|
const previewLimit = app?.previewLimit || 0;
|
||||||
|
if (app?.previewDeployments?.length > previewLimit) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const previewDeploymentResult =
|
||||||
|
await findPreviewDeploymentByApplicationId(app.applicationId, prId);
|
||||||
|
|
||||||
|
let previewDeploymentId =
|
||||||
|
previewDeploymentResult?.previewDeploymentId || "";
|
||||||
|
|
||||||
|
if (!previewDeploymentResult) {
|
||||||
|
try {
|
||||||
|
const previewDeployment = await createPreviewDeployment({
|
||||||
|
applicationId: app.applicationId as string,
|
||||||
|
branch: prBranch,
|
||||||
|
pullRequestId: prId,
|
||||||
|
pullRequestNumber: prNumber,
|
||||||
|
pullRequestTitle: prTitle,
|
||||||
|
pullRequestURL: prURL,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(previewDeployment);
|
||||||
|
previewDeploymentId = previewDeployment.previewDeploymentId;
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const jobData: DeploymentJob = {
|
||||||
|
applicationId: app.applicationId as string,
|
||||||
|
titleLog: "Preview Deployment",
|
||||||
|
descriptionLog: `Hash: ${deploymentHash}`,
|
||||||
|
type: "deploy",
|
||||||
|
applicationType: "application-preview",
|
||||||
|
server: !!app.serverId,
|
||||||
|
previewDeploymentId,
|
||||||
|
isExternal: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (IS_CLOUD && app.serverId) {
|
||||||
|
jobData.serverId = app.serverId;
|
||||||
|
await deploy(jobData);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
await myQueue.add(
|
||||||
|
"deployments",
|
||||||
|
{ ...jobData },
|
||||||
|
{
|
||||||
|
removeOnComplete: true,
|
||||||
|
removeOnFail: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
message: "Preview Deployments Created",
|
||||||
|
};
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import {
|
|||||||
apiSaveEnvironmentVariablesRedis,
|
apiSaveEnvironmentVariablesRedis,
|
||||||
apiSaveExternalPortRedis,
|
apiSaveExternalPortRedis,
|
||||||
apiUpdateRedis,
|
apiUpdateRedis,
|
||||||
redis as redisTable,
|
|
||||||
} from "@/server/db/schema";
|
} from "@/server/db/schema";
|
||||||
|
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
@@ -31,9 +30,6 @@ import {
|
|||||||
updateRedisById,
|
updateRedisById,
|
||||||
} from "@dokploy/server";
|
} from "@dokploy/server";
|
||||||
import { observable } from "@trpc/server/observable";
|
import { observable } from "@trpc/server/observable";
|
||||||
import { eq } from "drizzle-orm";
|
|
||||||
import { db } from "@/server/db";
|
|
||||||
import { z } from "zod";
|
|
||||||
|
|
||||||
export const redisRouter = createTRPCRouter({
|
export const redisRouter = createTRPCRouter({
|
||||||
create: protectedProcedure
|
create: protectedProcedure
|
||||||
@@ -320,47 +316,4 @@ export const redisRouter = createTRPCRouter({
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}),
|
}),
|
||||||
move: protectedProcedure
|
|
||||||
.input(
|
|
||||||
z.object({
|
|
||||||
redisId: z.string(),
|
|
||||||
targetProjectId: z.string(),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.mutation(async ({ input, ctx }) => {
|
|
||||||
const redis = await findRedisById(input.redisId);
|
|
||||||
if (redis.project.organizationId !== ctx.session.activeOrganizationId) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "UNAUTHORIZED",
|
|
||||||
message: "You are not authorized to move this redis",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const targetProject = await findProjectById(input.targetProjectId);
|
|
||||||
if (targetProject.organizationId !== ctx.session.activeOrganizationId) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "UNAUTHORIZED",
|
|
||||||
message: "You are not authorized to move to this project",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the redis's projectId
|
|
||||||
const updatedRedis = await db
|
|
||||||
.update(redisTable)
|
|
||||||
.set({
|
|
||||||
projectId: input.targetProjectId,
|
|
||||||
})
|
|
||||||
.where(eq(redisTable.redisId, input.redisId))
|
|
||||||
.returning()
|
|
||||||
.then((res) => res[0]);
|
|
||||||
|
|
||||||
if (!updatedRedis) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "INTERNAL_SERVER_ERROR",
|
|
||||||
message: "Failed to move redis",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return updatedRedis;
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -596,25 +596,17 @@ export const settingsRouter = createTRPCRouter({
|
|||||||
}),
|
}),
|
||||||
readStats: adminProcedure
|
readStats: adminProcedure
|
||||||
.input(
|
.input(
|
||||||
z
|
z.object({
|
||||||
.object({
|
start: z.string().optional(),
|
||||||
dateRange: z
|
end: z.string().optional(),
|
||||||
.object({
|
}),
|
||||||
start: z.string().optional(),
|
|
||||||
end: z.string().optional(),
|
|
||||||
})
|
|
||||||
.optional(),
|
|
||||||
})
|
|
||||||
.optional(),
|
|
||||||
)
|
)
|
||||||
.query(({ input }) => {
|
.query(({ input }) => {
|
||||||
if (IS_CLOUD) {
|
if (IS_CLOUD) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const rawConfig = readMonitoringConfig(
|
const rawConfig = readMonitoringConfig(!!input?.start || !!input?.end);
|
||||||
!!input?.dateRange?.start || !!input?.dateRange?.end,
|
const processedLogs = processLogs(rawConfig as string, input);
|
||||||
);
|
|
||||||
const processedLogs = processLogs(rawConfig as string, input?.dateRange);
|
|
||||||
return processedLogs || [];
|
return processedLogs || [];
|
||||||
}),
|
}),
|
||||||
haveActivateRequests: adminProcedure.query(async () => {
|
haveActivateRequests: adminProcedure.query(async () => {
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ export const deploymentWorker = new Worker(
|
|||||||
titleLog: job.data.titleLog,
|
titleLog: job.data.titleLog,
|
||||||
descriptionLog: job.data.descriptionLog,
|
descriptionLog: job.data.descriptionLog,
|
||||||
previewDeploymentId: job.data.previewDeploymentId,
|
previewDeploymentId: job.data.previewDeploymentId,
|
||||||
|
isExternal: job.data.isExternal,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -107,6 +108,7 @@ export const deploymentWorker = new Worker(
|
|||||||
titleLog: job.data.titleLog,
|
titleLog: job.data.titleLog,
|
||||||
descriptionLog: job.data.descriptionLog,
|
descriptionLog: job.data.descriptionLog,
|
||||||
previewDeploymentId: job.data.previewDeploymentId,
|
previewDeploymentId: job.data.previewDeploymentId,
|
||||||
|
isExternal: job.data.isExternal,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ type DeployJob =
|
|||||||
applicationType: "application-preview";
|
applicationType: "application-preview";
|
||||||
previewDeploymentId: string;
|
previewDeploymentId: string;
|
||||||
serverId?: string;
|
serverId?: string;
|
||||||
|
isExternal?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DeploymentJob = DeployJob;
|
export type DeploymentJob = DeployJob;
|
||||||
|
|||||||
@@ -129,7 +129,6 @@ export const applications = pgTable("application", {
|
|||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
buildArgs: text("buildArgs"),
|
buildArgs: text("buildArgs"),
|
||||||
buildSecrets: json("buildSecrets").$type<Record<string, string>>(),
|
|
||||||
memoryReservation: text("memoryReservation"),
|
memoryReservation: text("memoryReservation"),
|
||||||
memoryLimit: text("memoryLimit"),
|
memoryLimit: text("memoryLimit"),
|
||||||
cpuReservation: text("cpuReservation"),
|
cpuReservation: text("cpuReservation"),
|
||||||
@@ -354,7 +353,6 @@ const createSchema = createInsertSchema(applications, {
|
|||||||
autoDeploy: z.boolean(),
|
autoDeploy: z.boolean(),
|
||||||
env: z.string().optional(),
|
env: z.string().optional(),
|
||||||
buildArgs: z.string().optional(),
|
buildArgs: z.string().optional(),
|
||||||
buildSecrets: z.record(z.string(), z.string()).optional(),
|
|
||||||
name: z.string().min(1),
|
name: z.string().min(1),
|
||||||
description: z.string().optional(),
|
description: z.string().optional(),
|
||||||
memoryReservation: z.string().optional(),
|
memoryReservation: z.string().optional(),
|
||||||
@@ -501,12 +499,11 @@ export const apiSaveGitProvider = createSchema
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
export const apiSaveEnvironmentVariables = z
|
export const apiSaveEnvironmentVariables = createSchema
|
||||||
.object({
|
.pick({
|
||||||
applicationId: z.string(),
|
applicationId: true,
|
||||||
env: z.string().optional(),
|
env: true,
|
||||||
buildArgs: z.string().optional(),
|
buildArgs: true,
|
||||||
buildSecrets: z.record(z.string(), z.string()).optional(),
|
|
||||||
})
|
})
|
||||||
.required();
|
.required();
|
||||||
|
|
||||||
|
|||||||
@@ -149,7 +149,6 @@ table application {
|
|||||||
previewLimit integer [default: 3]
|
previewLimit integer [default: 3]
|
||||||
isPreviewDeploymentsActive boolean [default: false]
|
isPreviewDeploymentsActive boolean [default: false]
|
||||||
buildArgs text
|
buildArgs text
|
||||||
buildSecrets json
|
|
||||||
memoryReservation text
|
memoryReservation text
|
||||||
memoryLimit text
|
memoryLimit text
|
||||||
cpuReservation text
|
cpuReservation text
|
||||||
|
|||||||
@@ -387,11 +387,13 @@ export const deployPreviewApplication = async ({
|
|||||||
titleLog = "Preview Deployment",
|
titleLog = "Preview Deployment",
|
||||||
descriptionLog = "",
|
descriptionLog = "",
|
||||||
previewDeploymentId,
|
previewDeploymentId,
|
||||||
|
isExternal = false,
|
||||||
}: {
|
}: {
|
||||||
applicationId: string;
|
applicationId: string;
|
||||||
titleLog: string;
|
titleLog: string;
|
||||||
descriptionLog: string;
|
descriptionLog: string;
|
||||||
previewDeploymentId: string;
|
previewDeploymentId: string;
|
||||||
|
isExternal?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const application = await findApplicationById(applicationId);
|
const application = await findApplicationById(applicationId);
|
||||||
|
|
||||||
@@ -417,46 +419,43 @@ export const deployPreviewApplication = async ({
|
|||||||
githubId: application?.githubId || "",
|
githubId: application?.githubId || "",
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
const commentExists = await issueCommentExists({
|
if (!isExternal) {
|
||||||
...issueParams,
|
const commentExists = await issueCommentExists({
|
||||||
});
|
|
||||||
if (!commentExists) {
|
|
||||||
const result = await createPreviewDeploymentComment({
|
|
||||||
...issueParams,
|
...issueParams,
|
||||||
previewDomain,
|
|
||||||
appName: previewDeployment.appName,
|
|
||||||
githubId: application?.githubId || "",
|
|
||||||
previewDeploymentId,
|
|
||||||
});
|
});
|
||||||
|
if (!commentExists) {
|
||||||
if (!result) {
|
const result = await createPreviewDeploymentComment({
|
||||||
throw new TRPCError({
|
...issueParams,
|
||||||
code: "NOT_FOUND",
|
previewDomain,
|
||||||
message: "Pull request comment not found",
|
appName: previewDeployment.appName,
|
||||||
|
githubId: application?.githubId || "",
|
||||||
|
previewDeploymentId,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
issueParams.comment_id = Number.parseInt(result?.pullRequestCommentId);
|
if (!result) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "NOT_FOUND",
|
||||||
|
message: "Pull request comment not found",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
issueParams.comment_id = Number.parseInt(result?.pullRequestCommentId);
|
||||||
|
}
|
||||||
|
const buildingComment = getIssueComment(
|
||||||
|
application.name,
|
||||||
|
"running",
|
||||||
|
previewDomain,
|
||||||
|
);
|
||||||
|
await updateIssueComment({
|
||||||
|
...issueParams,
|
||||||
|
body: `### Dokploy Preview Deployment\n\n${buildingComment}`,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
const buildingComment = getIssueComment(
|
|
||||||
application.name,
|
|
||||||
"running",
|
|
||||||
previewDomain,
|
|
||||||
);
|
|
||||||
await updateIssueComment({
|
|
||||||
...issueParams,
|
|
||||||
body: `### Dokploy Preview Deployment\n\n${buildingComment}`,
|
|
||||||
});
|
|
||||||
application.appName = previewDeployment.appName;
|
application.appName = previewDeployment.appName;
|
||||||
application.env = `${application.previewEnv}\nDOKPLOY_DEPLOY_URL=${previewDeployment?.domain}`;
|
application.env = `${application.previewEnv}\nDOKPLOY_DEPLOY_URL=${previewDeployment?.domain}`;
|
||||||
application.buildArgs = application.previewBuildArgs;
|
application.buildArgs = application.previewBuildArgs;
|
||||||
|
|
||||||
// const admin = await findUserById(application.project.userId);
|
|
||||||
|
|
||||||
// if (admin.cleanupCacheOnPreviews) {
|
|
||||||
// await cleanupFullDocker(application?.serverId);
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (application.sourceType === "github") {
|
if (application.sourceType === "github") {
|
||||||
await cloneGithubRepository({
|
await cloneGithubRepository({
|
||||||
...application,
|
...application,
|
||||||
@@ -466,25 +465,31 @@ export const deployPreviewApplication = async ({
|
|||||||
});
|
});
|
||||||
await buildApplication(application, deployment.logPath);
|
await buildApplication(application, deployment.logPath);
|
||||||
}
|
}
|
||||||
const successComment = getIssueComment(
|
|
||||||
application.name,
|
if (!isExternal) {
|
||||||
"success",
|
const successComment = getIssueComment(
|
||||||
previewDomain,
|
application.name,
|
||||||
);
|
"success",
|
||||||
await updateIssueComment({
|
previewDomain,
|
||||||
...issueParams,
|
);
|
||||||
body: `### Dokploy Preview Deployment\n\n${successComment}`,
|
await updateIssueComment({
|
||||||
});
|
...issueParams,
|
||||||
|
body: `### Dokploy Preview Deployment\n\n${successComment}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
await updateDeploymentStatus(deployment.deploymentId, "done");
|
await updateDeploymentStatus(deployment.deploymentId, "done");
|
||||||
await updatePreviewDeployment(previewDeploymentId, {
|
await updatePreviewDeployment(previewDeploymentId, {
|
||||||
previewStatus: "done",
|
previewStatus: "done",
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const comment = getIssueComment(application.name, "error", previewDomain);
|
if (!isExternal) {
|
||||||
await updateIssueComment({
|
const comment = getIssueComment(application.name, "error", previewDomain);
|
||||||
...issueParams,
|
await updateIssueComment({
|
||||||
body: `### Dokploy Preview Deployment\n\n${comment}`,
|
...issueParams,
|
||||||
});
|
body: `### Dokploy Preview Deployment\n\n${comment}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
await updateDeploymentStatus(deployment.deploymentId, "error");
|
await updateDeploymentStatus(deployment.deploymentId, "error");
|
||||||
await updatePreviewDeployment(previewDeploymentId, {
|
await updatePreviewDeployment(previewDeploymentId, {
|
||||||
previewStatus: "error",
|
previewStatus: "error",
|
||||||
@@ -500,11 +505,13 @@ export const deployRemotePreviewApplication = async ({
|
|||||||
titleLog = "Preview Deployment",
|
titleLog = "Preview Deployment",
|
||||||
descriptionLog = "",
|
descriptionLog = "",
|
||||||
previewDeploymentId,
|
previewDeploymentId,
|
||||||
|
isExternal = false,
|
||||||
}: {
|
}: {
|
||||||
applicationId: string;
|
applicationId: string;
|
||||||
titleLog: string;
|
titleLog: string;
|
||||||
descriptionLog: string;
|
descriptionLog: string;
|
||||||
previewDeploymentId: string;
|
previewDeploymentId: string;
|
||||||
|
isExternal?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const application = await findApplicationById(applicationId);
|
const application = await findApplicationById(applicationId);
|
||||||
|
|
||||||
@@ -530,36 +537,39 @@ export const deployRemotePreviewApplication = async ({
|
|||||||
githubId: application?.githubId || "",
|
githubId: application?.githubId || "",
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
const commentExists = await issueCommentExists({
|
if (!isExternal) {
|
||||||
...issueParams,
|
const commentExists = await issueCommentExists({
|
||||||
});
|
|
||||||
if (!commentExists) {
|
|
||||||
const result = await createPreviewDeploymentComment({
|
|
||||||
...issueParams,
|
...issueParams,
|
||||||
previewDomain,
|
|
||||||
appName: previewDeployment.appName,
|
|
||||||
githubId: application?.githubId || "",
|
|
||||||
previewDeploymentId,
|
|
||||||
});
|
});
|
||||||
|
if (!commentExists) {
|
||||||
if (!result) {
|
const result = await createPreviewDeploymentComment({
|
||||||
throw new TRPCError({
|
...issueParams,
|
||||||
code: "NOT_FOUND",
|
previewDomain,
|
||||||
message: "Pull request comment not found",
|
appName: previewDeployment.appName,
|
||||||
|
githubId: application?.githubId || "",
|
||||||
|
previewDeploymentId,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
issueParams.comment_id = Number.parseInt(result?.pullRequestCommentId);
|
if (!result) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "NOT_FOUND",
|
||||||
|
message: "Pull request comment not found",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
issueParams.comment_id = Number.parseInt(result?.pullRequestCommentId);
|
||||||
|
}
|
||||||
|
const buildingComment = getIssueComment(
|
||||||
|
application.name,
|
||||||
|
"running",
|
||||||
|
previewDomain,
|
||||||
|
);
|
||||||
|
await updateIssueComment({
|
||||||
|
...issueParams,
|
||||||
|
body: `### Dokploy Preview Deployment\n\n${buildingComment}`,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
const buildingComment = getIssueComment(
|
|
||||||
application.name,
|
|
||||||
"running",
|
|
||||||
previewDomain,
|
|
||||||
);
|
|
||||||
await updateIssueComment({
|
|
||||||
...issueParams,
|
|
||||||
body: `### Dokploy Preview Deployment\n\n${buildingComment}`,
|
|
||||||
});
|
|
||||||
application.appName = previewDeployment.appName;
|
application.appName = previewDeployment.appName;
|
||||||
application.env = `${application.previewEnv}\nDOKPLOY_DEPLOY_URL=${previewDeployment?.domain}`;
|
application.env = `${application.previewEnv}\nDOKPLOY_DEPLOY_URL=${previewDeployment?.domain}`;
|
||||||
application.buildArgs = application.previewBuildArgs;
|
application.buildArgs = application.previewBuildArgs;
|
||||||
@@ -586,25 +596,29 @@ export const deployRemotePreviewApplication = async ({
|
|||||||
await mechanizeDockerContainer(application);
|
await mechanizeDockerContainer(application);
|
||||||
}
|
}
|
||||||
|
|
||||||
const successComment = getIssueComment(
|
if (!isExternal) {
|
||||||
application.name,
|
const successComment = getIssueComment(
|
||||||
"success",
|
application.name,
|
||||||
previewDomain,
|
"success",
|
||||||
);
|
previewDomain,
|
||||||
await updateIssueComment({
|
);
|
||||||
...issueParams,
|
await updateIssueComment({
|
||||||
body: `### Dokploy Preview Deployment\n\n${successComment}`,
|
...issueParams,
|
||||||
});
|
body: `### Dokploy Preview Deployment\n\n${successComment}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
await updateDeploymentStatus(deployment.deploymentId, "done");
|
await updateDeploymentStatus(deployment.deploymentId, "done");
|
||||||
await updatePreviewDeployment(previewDeploymentId, {
|
await updatePreviewDeployment(previewDeploymentId, {
|
||||||
previewStatus: "done",
|
previewStatus: "done",
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const comment = getIssueComment(application.name, "error", previewDomain);
|
if (!isExternal) {
|
||||||
await updateIssueComment({
|
const comment = getIssueComment(application.name, "error", previewDomain);
|
||||||
...issueParams,
|
await updateIssueComment({
|
||||||
body: `### Dokploy Preview Deployment\n\n${comment}`,
|
...issueParams,
|
||||||
});
|
body: `### Dokploy Preview Deployment\n\n${comment}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
await updateDeploymentStatus(deployment.deploymentId, "error");
|
await updateDeploymentStatus(deployment.deploymentId, "error");
|
||||||
await updatePreviewDeployment(previewDeploymentId, {
|
await updatePreviewDeployment(previewDeploymentId, {
|
||||||
previewStatus: "error",
|
previewStatus: "error",
|
||||||
|
|||||||
@@ -136,26 +136,24 @@ export const getContainersByAppNameMatch = async (
|
|||||||
result = stdout.trim().split("\n");
|
result = stdout.trim().split("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
const containers = result
|
const containers = result.map((line) => {
|
||||||
.map((line) => {
|
const parts = line.split(" | ");
|
||||||
const parts = line.split(" | ");
|
const containerId = parts[0]
|
||||||
const containerId = parts[0]
|
? parts[0].replace("CONTAINER ID : ", "").trim()
|
||||||
? parts[0].replace("CONTAINER ID : ", "").trim()
|
: "No container id";
|
||||||
: "No container id";
|
const name = parts[1]
|
||||||
const name = parts[1]
|
? parts[1].replace("Name: ", "").trim()
|
||||||
? parts[1].replace("Name: ", "").trim()
|
: "No container name";
|
||||||
: "No container name";
|
|
||||||
|
|
||||||
const state = parts[2]
|
const state = parts[2]
|
||||||
? parts[2].replace("State: ", "").trim()
|
? parts[2].replace("State: ", "").trim()
|
||||||
: "No state";
|
: "No state";
|
||||||
return {
|
return {
|
||||||
containerId,
|
containerId,
|
||||||
name,
|
name,
|
||||||
state,
|
state,
|
||||||
};
|
};
|
||||||
})
|
});
|
||||||
.sort((a, b) => a.name.localeCompare(b.name));
|
|
||||||
|
|
||||||
return containers || [];
|
return containers || [];
|
||||||
} catch (_error) {}
|
} catch (_error) {}
|
||||||
@@ -192,30 +190,28 @@ export const getStackContainersByAppName = async (
|
|||||||
result = stdout.trim().split("\n");
|
result = stdout.trim().split("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
const containers = result
|
const containers = result.map((line) => {
|
||||||
.map((line) => {
|
const parts = line.split(" | ");
|
||||||
const parts = line.split(" | ");
|
const containerId = parts[0]
|
||||||
const containerId = parts[0]
|
? parts[0].replace("CONTAINER ID : ", "").trim()
|
||||||
? parts[0].replace("CONTAINER ID : ", "").trim()
|
: "No container id";
|
||||||
: "No container id";
|
const name = parts[1]
|
||||||
const name = parts[1]
|
? parts[1].replace("Name: ", "").trim()
|
||||||
? parts[1].replace("Name: ", "").trim()
|
: "No container name";
|
||||||
: "No container name";
|
|
||||||
|
|
||||||
const state = parts[2]
|
const state = parts[2]
|
||||||
? parts[2].replace("State: ", "").trim().toLowerCase()
|
? parts[2].replace("State: ", "").trim().toLowerCase()
|
||||||
: "No state";
|
: "No state";
|
||||||
const node = parts[3]
|
const node = parts[3]
|
||||||
? parts[3].replace("Node: ", "").trim()
|
? parts[3].replace("Node: ", "").trim()
|
||||||
: "No specific node";
|
: "No specific node";
|
||||||
return {
|
return {
|
||||||
containerId,
|
containerId,
|
||||||
name,
|
name,
|
||||||
state,
|
state,
|
||||||
node,
|
node,
|
||||||
};
|
};
|
||||||
})
|
});
|
||||||
.sort((a, b) => a.name.localeCompare(b.name));
|
|
||||||
|
|
||||||
return containers || [];
|
return containers || [];
|
||||||
} catch (_error) {}
|
} catch (_error) {}
|
||||||
@@ -253,31 +249,29 @@ export const getServiceContainersByAppName = async (
|
|||||||
result = stdout.trim().split("\n");
|
result = stdout.trim().split("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
const containers = result
|
const containers = result.map((line) => {
|
||||||
.map((line) => {
|
const parts = line.split(" | ");
|
||||||
const parts = line.split(" | ");
|
const containerId = parts[0]
|
||||||
const containerId = parts[0]
|
? parts[0].replace("CONTAINER ID : ", "").trim()
|
||||||
? parts[0].replace("CONTAINER ID : ", "").trim()
|
: "No container id";
|
||||||
: "No container id";
|
const name = parts[1]
|
||||||
const name = parts[1]
|
? parts[1].replace("Name: ", "").trim()
|
||||||
? parts[1].replace("Name: ", "").trim()
|
: "No container name";
|
||||||
: "No container name";
|
|
||||||
|
|
||||||
const state = parts[2]
|
const state = parts[2]
|
||||||
? parts[2].replace("State: ", "").trim().toLowerCase()
|
? parts[2].replace("State: ", "").trim().toLowerCase()
|
||||||
: "No state";
|
: "No state";
|
||||||
|
|
||||||
const node = parts[3]
|
const node = parts[3]
|
||||||
? parts[3].replace("Node: ", "").trim()
|
? parts[3].replace("Node: ", "").trim()
|
||||||
: "No specific node";
|
: "No specific node";
|
||||||
return {
|
return {
|
||||||
containerId,
|
containerId,
|
||||||
name,
|
name,
|
||||||
state,
|
state,
|
||||||
node,
|
node,
|
||||||
};
|
};
|
||||||
})
|
});
|
||||||
.sort((a, b) => a.name.localeCompare(b.name));
|
|
||||||
|
|
||||||
return containers || [];
|
return containers || [];
|
||||||
} catch (_error) {}
|
} catch (_error) {}
|
||||||
@@ -312,25 +306,23 @@ export const getContainersByAppLabel = async (
|
|||||||
|
|
||||||
const lines = stdout.trim().split("\n");
|
const lines = stdout.trim().split("\n");
|
||||||
|
|
||||||
const containers = lines
|
const containers = lines.map((line) => {
|
||||||
.map((line) => {
|
const parts = line.split(" | ");
|
||||||
const parts = line.split(" | ");
|
const containerId = parts[0]
|
||||||
const containerId = parts[0]
|
? parts[0].replace("CONTAINER ID : ", "").trim()
|
||||||
? parts[0].replace("CONTAINER ID : ", "").trim()
|
: "No container id";
|
||||||
: "No container id";
|
const name = parts[1]
|
||||||
const name = parts[1]
|
? parts[1].replace("Name: ", "").trim()
|
||||||
? parts[1].replace("Name: ", "").trim()
|
: "No container name";
|
||||||
: "No container name";
|
const state = parts[2]
|
||||||
const state = parts[2]
|
? parts[2].replace("State: ", "").trim()
|
||||||
? parts[2].replace("State: ", "").trim()
|
: "No state";
|
||||||
: "No state";
|
return {
|
||||||
return {
|
containerId,
|
||||||
containerId,
|
name,
|
||||||
name,
|
state,
|
||||||
state,
|
};
|
||||||
};
|
});
|
||||||
})
|
|
||||||
.sort((a, b) => a.name.localeCompare(b.name));
|
|
||||||
|
|
||||||
return containers || [];
|
return containers || [];
|
||||||
} catch (_error) {}
|
} catch (_error) {}
|
||||||
|
|||||||
@@ -151,6 +151,7 @@ export const findPreviewDeploymentsByApplicationId = async (
|
|||||||
|
|
||||||
export const createPreviewDeployment = async (
|
export const createPreviewDeployment = async (
|
||||||
schema: typeof apiCreatePreviewDeployment._type,
|
schema: typeof apiCreatePreviewDeployment._type,
|
||||||
|
isExternal = false,
|
||||||
) => {
|
) => {
|
||||||
const application = await findApplicationById(schema.applicationId);
|
const application = await findApplicationById(schema.applicationId);
|
||||||
const appName = `preview-${application.appName}-${generatePassword(6)}`;
|
const appName = `preview-${application.appName}-${generatePassword(6)}`;
|
||||||
@@ -165,27 +166,32 @@ export const createPreviewDeployment = async (
|
|||||||
org?.ownerId || "",
|
org?.ownerId || "",
|
||||||
);
|
);
|
||||||
|
|
||||||
const octokit = authGithub(application?.github as Github);
|
let issueId = "";
|
||||||
|
if (!isExternal) {
|
||||||
|
const octokit = authGithub(application?.github as Github);
|
||||||
|
|
||||||
const runningComment = getIssueComment(
|
const runningComment = getIssueComment(
|
||||||
application.name,
|
application.name,
|
||||||
"initializing",
|
"initializing",
|
||||||
generateDomain,
|
generateDomain,
|
||||||
);
|
);
|
||||||
|
|
||||||
const issue = await octokit.rest.issues.createComment({
|
const issue = await octokit.rest.issues.createComment({
|
||||||
owner: application?.owner || "",
|
owner: application?.owner || "",
|
||||||
repo: application?.repository || "",
|
repo: application?.repository || "",
|
||||||
issue_number: Number.parseInt(schema.pullRequestNumber),
|
issue_number: Number.parseInt(schema.pullRequestNumber),
|
||||||
body: `### Dokploy Preview Deployment\n\n${runningComment}`,
|
body: `### Dokploy Preview Deployment\n\n${runningComment}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
issueId = `${issue.data.id}`;
|
||||||
|
}
|
||||||
|
|
||||||
const previewDeployment = await db
|
const previewDeployment = await db
|
||||||
.insert(previewDeployments)
|
.insert(previewDeployments)
|
||||||
.values({
|
.values({
|
||||||
...schema,
|
...schema,
|
||||||
appName: appName,
|
appName: appName,
|
||||||
pullRequestCommentId: `${issue.data.id}`,
|
pullRequestCommentId: issueId,
|
||||||
})
|
})
|
||||||
.returning()
|
.returning()
|
||||||
.then((value) => value[0]);
|
.then((value) => value[0]);
|
||||||
@@ -231,6 +237,13 @@ export const findPreviewDeploymentsByPullRequestId = async (
|
|||||||
) => {
|
) => {
|
||||||
const previewDeploymentResult = await db.query.previewDeployments.findMany({
|
const previewDeploymentResult = await db.query.previewDeployments.findMany({
|
||||||
where: eq(previewDeployments.pullRequestId, pullRequestId),
|
where: eq(previewDeployments.pullRequestId, pullRequestId),
|
||||||
|
with: {
|
||||||
|
application: {
|
||||||
|
with: {
|
||||||
|
project: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return previewDeploymentResult;
|
return previewDeploymentResult;
|
||||||
|
|||||||
@@ -12,14 +12,8 @@ export const buildCustomDocker = async (
|
|||||||
application: ApplicationNested,
|
application: ApplicationNested,
|
||||||
writeStream: WriteStream,
|
writeStream: WriteStream,
|
||||||
) => {
|
) => {
|
||||||
const {
|
const { appName, env, publishDirectory, buildArgs, dockerBuildStage } =
|
||||||
appName,
|
application;
|
||||||
env,
|
|
||||||
publishDirectory,
|
|
||||||
buildArgs,
|
|
||||||
buildSecrets,
|
|
||||||
dockerBuildStage,
|
|
||||||
} = application;
|
|
||||||
const dockerFilePath = getBuildAppDirectory(application);
|
const dockerFilePath = getBuildAppDirectory(application);
|
||||||
try {
|
try {
|
||||||
const image = `${appName}`;
|
const image = `${appName}`;
|
||||||
@@ -31,10 +25,6 @@ export const buildCustomDocker = async (
|
|||||||
application.project.env,
|
application.project.env,
|
||||||
);
|
);
|
||||||
|
|
||||||
const secrets = buildSecrets
|
|
||||||
? Object.entries(buildSecrets).map(([key, value]) => `${key}=${value}`)
|
|
||||||
: [];
|
|
||||||
|
|
||||||
const dockerContextPath = getDockerContextPath(application);
|
const dockerContextPath = getDockerContextPath(application);
|
||||||
|
|
||||||
const commandArgs = ["build", "-t", image, "-f", dockerFilePath, "."];
|
const commandArgs = ["build", "-t", image, "-f", dockerFilePath, "."];
|
||||||
@@ -46,12 +36,6 @@ export const buildCustomDocker = async (
|
|||||||
for (const arg of args) {
|
for (const arg of args) {
|
||||||
commandArgs.push("--build-arg", arg);
|
commandArgs.push("--build-arg", arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const secret of secrets) {
|
|
||||||
const [key] = secret.split("=");
|
|
||||||
commandArgs.push("--secret", `id=${key},env=${key}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Do not generate an environment file when publishDirectory is specified,
|
Do not generate an environment file when publishDirectory is specified,
|
||||||
as it could be publicly exposed.
|
as it could be publicly exposed.
|
||||||
@@ -70,10 +54,6 @@ export const buildCustomDocker = async (
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
cwd: dockerContextPath || defaultContextPath,
|
cwd: dockerContextPath || defaultContextPath,
|
||||||
env: {
|
|
||||||
...process.env,
|
|
||||||
...Object.fromEntries(secrets.map((s) => s.split("="))),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -85,14 +65,8 @@ export const getDockerCommand = (
|
|||||||
application: ApplicationNested,
|
application: ApplicationNested,
|
||||||
logPath: string,
|
logPath: string,
|
||||||
) => {
|
) => {
|
||||||
const {
|
const { appName, env, publishDirectory, buildArgs, dockerBuildStage } =
|
||||||
appName,
|
application;
|
||||||
env,
|
|
||||||
publishDirectory,
|
|
||||||
buildArgs,
|
|
||||||
buildSecrets,
|
|
||||||
dockerBuildStage,
|
|
||||||
} = application;
|
|
||||||
const dockerFilePath = getBuildAppDirectory(application);
|
const dockerFilePath = getBuildAppDirectory(application);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -105,10 +79,6 @@ export const getDockerCommand = (
|
|||||||
application.project.env,
|
application.project.env,
|
||||||
);
|
);
|
||||||
|
|
||||||
const secrets = buildSecrets
|
|
||||||
? Object.entries(buildSecrets).map(([key, value]) => `${key}=${value}`)
|
|
||||||
: [];
|
|
||||||
|
|
||||||
const dockerContextPath =
|
const dockerContextPath =
|
||||||
getDockerContextPath(application) || defaultContextPath;
|
getDockerContextPath(application) || defaultContextPath;
|
||||||
|
|
||||||
@@ -122,11 +92,6 @@ export const getDockerCommand = (
|
|||||||
commandArgs.push("--build-arg", arg);
|
commandArgs.push("--build-arg", arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const secret of secrets) {
|
|
||||||
const [key] = secret.split("=");
|
|
||||||
commandArgs.push("--secret", `id=${key},env=${key}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Do not generate an environment file when publishDirectory is specified,
|
Do not generate an environment file when publishDirectory is specified,
|
||||||
as it could be publicly exposed.
|
as it could be publicly exposed.
|
||||||
@@ -140,14 +105,6 @@ export const getDockerCommand = (
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export secrets as environment variables
|
|
||||||
if (secrets.length > 0) {
|
|
||||||
command += "\n# Export build secrets\n";
|
|
||||||
for (const secret of secrets) {
|
|
||||||
command += `export ${secret}\n`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
command += `
|
command += `
|
||||||
echo "Building ${appName}" >> ${logPath};
|
echo "Building ${appName}" >> ${logPath};
|
||||||
cd ${dockerContextPath} >> ${logPath} 2>> ${logPath} || {
|
cd ${dockerContextPath} >> ${logPath} 2>> ${logPath} || {
|
||||||
|
|||||||
Reference in New Issue
Block a user