mirror of
https://github.com/andwati/dokploy.git
synced 2026-11-06 12:12:16 +00:00
feat(preview): ✨ add manual rebuild option for preview deployments
This commit is contained in:
@@ -4,6 +4,7 @@ import {
|
||||
deployPreviewApplication,
|
||||
rebuildApplication,
|
||||
rebuildCompose,
|
||||
rebuildPreviewApplication,
|
||||
updateApplicationStatus,
|
||||
updateCompose,
|
||||
updatePreviewDeployment,
|
||||
@@ -54,7 +55,14 @@ export const deploy = async (job: DeployJob) => {
|
||||
previewStatus: "running",
|
||||
});
|
||||
if (job.server) {
|
||||
if (job.type === "deploy") {
|
||||
if (job.type === "redeploy") {
|
||||
await rebuildPreviewApplication({
|
||||
applicationId: job.applicationId,
|
||||
titleLog: job.titleLog || "Rebuild Preview Deployment",
|
||||
descriptionLog: job.descriptionLog || "",
|
||||
previewDeploymentId: job.previewDeploymentId,
|
||||
});
|
||||
} else if (job.type === "deploy") {
|
||||
await deployPreviewApplication({
|
||||
applicationId: job.applicationId,
|
||||
titleLog: job.titleLog || "Preview Deployment",
|
||||
|
||||
+65
@@ -2,6 +2,7 @@ import {
|
||||
ExternalLink,
|
||||
FileText,
|
||||
GitPullRequest,
|
||||
Hammer,
|
||||
Loader2,
|
||||
PenSquare,
|
||||
RocketIcon,
|
||||
@@ -22,6 +23,13 @@ import {
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip";
|
||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
||||
import { api } from "@/utils/api";
|
||||
import { ShowModalLogs } from "../../settings/web-server/show-modal-logs";
|
||||
import { ShowDeploymentsModal } from "../deployments/show-deployments-modal";
|
||||
@@ -38,6 +46,9 @@ export const ShowPreviewDeployments = ({ applicationId }: Props) => {
|
||||
const { mutateAsync: deletePreviewDeployment, isLoading } =
|
||||
api.previewDeployment.delete.useMutation();
|
||||
|
||||
const { mutateAsync: redeployPreviewDeployment } =
|
||||
api.previewDeployment.redeploy.useMutation();
|
||||
|
||||
const {
|
||||
data: previewDeployments,
|
||||
refetch: refetchPreviewDeployments,
|
||||
@@ -46,6 +57,8 @@ export const ShowPreviewDeployments = ({ applicationId }: Props) => {
|
||||
{ applicationId },
|
||||
{
|
||||
enabled: !!applicationId,
|
||||
refetchInterval: (data) =>
|
||||
data?.some((d) => d.previewStatus === "running") ? 2000 : false,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -193,6 +206,58 @@ export const ShowPreviewDeployments = ({ applicationId }: Props) => {
|
||||
</Button>
|
||||
</ShowDeploymentsModal>
|
||||
|
||||
<DialogAction
|
||||
title="Rebuild Preview Deployment"
|
||||
description="Are you sure you want to rebuild this preview deployment?"
|
||||
type="default"
|
||||
onClick={async () => {
|
||||
await redeployPreviewDeployment({
|
||||
previewDeploymentId:
|
||||
deployment.previewDeploymentId,
|
||||
})
|
||||
.then(() => {
|
||||
toast.success(
|
||||
"Preview deployment rebuild started",
|
||||
);
|
||||
refetchPreviewDeployments();
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error(
|
||||
"Error rebuilding preview deployment",
|
||||
);
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
isLoading={status === "running"}
|
||||
className="gap-2"
|
||||
>
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div className="flex items-center gap-2">
|
||||
<Hammer className="size-4" />
|
||||
Rebuild
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent
|
||||
sideOffset={5}
|
||||
className="z-[60]"
|
||||
>
|
||||
<p>
|
||||
Rebuild the preview deployment without
|
||||
downloading new code
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</TooltipPrimitive.Portal>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</Button>
|
||||
</DialogAction>
|
||||
|
||||
<AddPreviewDomain
|
||||
previewDeploymentId={`${deployment.previewDeploymentId}`}
|
||||
domainId={deployment.domain?.domainId}
|
||||
|
||||
@@ -2,11 +2,15 @@ import {
|
||||
findApplicationById,
|
||||
findPreviewDeploymentById,
|
||||
findPreviewDeploymentsByApplicationId,
|
||||
IS_CLOUD,
|
||||
removePreviewDeployment,
|
||||
} from "@dokploy/server";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { z } from "zod";
|
||||
import { apiFindAllByApplication } from "@/server/db/schema";
|
||||
import type { DeploymentJob } from "@/server/queues/queue-types";
|
||||
import { myQueue } from "@/server/queues/queueSetup";
|
||||
import { deploy } from "@/server/utils/deploy";
|
||||
import { createTRPCRouter, protectedProcedure } from "../trpc";
|
||||
|
||||
export const previewDeploymentRouter = createTRPCRouter({
|
||||
@@ -60,4 +64,56 @@ export const previewDeploymentRouter = createTRPCRouter({
|
||||
}
|
||||
return previewDeployment;
|
||||
}),
|
||||
redeploy: protectedProcedure
|
||||
.input(
|
||||
z.object({
|
||||
previewDeploymentId: z.string(),
|
||||
title: z.string().optional(),
|
||||
description: z.string().optional(),
|
||||
}),
|
||||
)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const previewDeployment = await findPreviewDeploymentById(
|
||||
input.previewDeploymentId,
|
||||
);
|
||||
if (
|
||||
previewDeployment.application.environment.project.organizationId !==
|
||||
ctx.session.activeOrganizationId
|
||||
) {
|
||||
throw new TRPCError({
|
||||
code: "UNAUTHORIZED",
|
||||
message:
|
||||
"You are not authorized to redeploy this preview deployment",
|
||||
});
|
||||
}
|
||||
const application = await findApplicationById(
|
||||
previewDeployment.applicationId,
|
||||
);
|
||||
const jobData: DeploymentJob = {
|
||||
applicationId: previewDeployment.applicationId,
|
||||
titleLog: input.title || "Rebuild Preview Deployment",
|
||||
descriptionLog: input.description || "",
|
||||
type: "redeploy",
|
||||
applicationType: "application-preview",
|
||||
previewDeploymentId: input.previewDeploymentId,
|
||||
server: !!application.serverId,
|
||||
};
|
||||
|
||||
if (IS_CLOUD && application.serverId) {
|
||||
jobData.serverId = application.serverId;
|
||||
deploy(jobData).catch((error) => {
|
||||
console.error("Background deployment failed:", error);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
await myQueue.add(
|
||||
"deployments",
|
||||
{ ...jobData },
|
||||
{
|
||||
removeOnComplete: true,
|
||||
removeOnFail: true,
|
||||
},
|
||||
);
|
||||
return true;
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
deployPreviewApplication,
|
||||
rebuildApplication,
|
||||
rebuildCompose,
|
||||
rebuildPreviewApplication,
|
||||
updateApplicationStatus,
|
||||
updateCompose,
|
||||
updatePreviewDeployment,
|
||||
@@ -54,7 +55,14 @@ export const deploymentWorker = new Worker(
|
||||
previewStatus: "running",
|
||||
});
|
||||
|
||||
if (job.data.type === "deploy") {
|
||||
if (job.data.type === "redeploy") {
|
||||
await rebuildPreviewApplication({
|
||||
applicationId: job.data.applicationId,
|
||||
titleLog: job.data.titleLog,
|
||||
descriptionLog: job.data.descriptionLog,
|
||||
previewDeploymentId: job.data.previewDeploymentId,
|
||||
});
|
||||
} else if (job.data.type === "deploy") {
|
||||
await deployPreviewApplication({
|
||||
applicationId: job.data.applicationId,
|
||||
titleLog: job.data.titleLog,
|
||||
|
||||
@@ -22,7 +22,7 @@ type DeployJob =
|
||||
titleLog: string;
|
||||
descriptionLog: string;
|
||||
server?: boolean;
|
||||
type: "deploy";
|
||||
type: "deploy" | "redeploy";
|
||||
applicationType: "application-preview";
|
||||
previewDeploymentId: string;
|
||||
serverId?: string;
|
||||
|
||||
Reference in New Issue
Block a user