Merge branch 'canary' into feat/internal-path-routing

This commit is contained in:
Jhonatan Caldeira
2025-06-30 10:56:19 -03:00
18 changed files with 121 additions and 40 deletions

View File

@@ -62,6 +62,8 @@ export const ShowDeployments = ({
const { mutateAsync: rollback, isLoading: isRollingBack } =
api.rollback.rollback.useMutation();
const { mutateAsync: killProcess, isLoading: isKillingProcess } =
api.deployment.killProcess.useMutation();
const [url, setUrl] = React.useState("");
useEffect(() => {
@@ -170,6 +172,32 @@ export const ShowDeployments = ({
</div>
<div className="flex flex-row items-center gap-2">
{deployment.pid && deployment.status === "running" && (
<DialogAction
title="Kill Process"
description="Are you sure you want to kill the process?"
type="default"
onClick={async () => {
await killProcess({
deploymentId: deployment.deploymentId,
})
.then(() => {
toast.success("Process killed successfully");
})
.catch(() => {
toast.error("Error killing process");
});
}}
>
<Button
variant="destructive"
size="sm"
isLoading={isKillingProcess}
>
Kill Process
</Button>
</DialogAction>
)}
<Button
onClick={() => {
setActiveLog(deployment);

View File

@@ -166,12 +166,16 @@ export const ShowSchedules = ({ id, scheduleType = "application" }: Props) => {
await runManually({
scheduleId: schedule.scheduleId,
}).then(async () => {
await new Promise((resolve) =>
setTimeout(resolve, 1500),
);
refetchSchedules();
});
})
.then(async () => {
await new Promise((resolve) =>
setTimeout(resolve, 1500),
);
refetchSchedules();
})
.catch(() => {
toast.error("Error running schedule");
});
}}
>
<Play className="size-4 transition-colors" />

View File

@@ -20,7 +20,7 @@ export const ShowNodesModal = ({ serverId }: Props) => {
Show Swarm Nodes
</DropdownMenuItem>
</DialogTrigger>
<DialogContent className="sm:max-w-5xl overflow-y-auto max-h-screen ">
<DialogContent className="min-w-[70vw] overflow-y-auto max-h-screen">
<div className="grid w-full gap-1">
<ShowNodes serverId={serverId} />
</div>

View File

@@ -87,7 +87,7 @@ export const ShowNodes = ({ serverId }: Props) => {
</TableCaption>
<TableHeader>
<TableRow>
<TableHead className="w-[100px]">Hostname</TableHead>
<TableHead className="text-left">Hostname</TableHead>
<TableHead className="text-right">Status</TableHead>
<TableHead className="text-right">Role</TableHead>
<TableHead className="text-right">Availability</TableHead>
@@ -104,7 +104,7 @@ export const ShowNodes = ({ serverId }: Props) => {
const isManager = node.Spec.Role === "manager";
return (
<TableRow key={node.ID}>
<TableCell className="w-[100px]">
<TableCell className="text-left">
{node.Description.Hostname}
</TableCell>
<TableCell className="text-right">

View File

@@ -141,7 +141,7 @@ export const ShowServers = () => {
</TableCaption>
<TableHeader>
<TableRow>
<TableHead className="w-[100px]">Name</TableHead>
<TableHead className="text-left">Name</TableHead>
{isCloud && (
<TableHead className="text-center">
Status
@@ -173,7 +173,7 @@ export const ShowServers = () => {
const isActive = server.serverStatus === "active";
return (
<TableRow key={server.serverId}>
<TableCell className="w-[100px]">
<TableCell className="text-left">
{server.name}
</TableCell>
{isCloud && (

View File

@@ -4,7 +4,7 @@ import type * as React from "react";
import { cn } from "@/lib/utils";
const badgeVariants = cva(
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold whitespace-nowrap transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
{
variants: {
variant: {

View File

@@ -0,0 +1 @@
ALTER TABLE "deployment" ADD COLUMN "pid" text;

View File

@@ -1,5 +1,5 @@
{
"id": "6903c6d7-6a8f-4f52-9e29-52251005d1e8",
"id": "edde8c54-b715-4db6-bc3a-85d435226083",
"prevId": "fdc95bd1-866c-4efd-9dce-20ed08f5b0b5",
"version": "7",
"dialect": "postgresql",
@@ -1209,20 +1209,6 @@
"primaryKey": false,
"notNull": true,
"default": "'none'"
},
"internalPath": {
"name": "internalPath",
"type": "text",
"primaryKey": false,
"notNull": false,
"default": "'/'"
},
"stripPath": {
"name": "stripPath",
"type": "boolean",
"primaryKey": false,
"notNull": true,
"default": false
}
},
"indexes": {},
@@ -2126,6 +2112,12 @@
"primaryKey": false,
"notNull": true
},
"pid": {
"name": "pid",
"type": "text",
"primaryKey": false,
"notNull": false
},
"applicationId": {
"name": "applicationId",
"type": "text",

View File

@@ -691,8 +691,8 @@
{
"idx": 98,
"version": "7",
"when": 1750570751159,
"tag": "0098_warm_tiger_shark",
"when": 1751233265357,
"tag": "0098_conscious_chat",
"breakpoints": true
}
]

View File

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

View File

@@ -7,16 +7,21 @@ import {
deployments,
} from "@/server/db/schema";
import {
execAsync,
execAsyncRemote,
findAllDeploymentsByApplicationId,
findAllDeploymentsByComposeId,
findAllDeploymentsByServerId,
findApplicationById,
findComposeById,
findDeploymentById,
findServerById,
updateDeploymentStatus,
} from "@dokploy/server";
import { TRPCError } from "@trpc/server";
import { desc, eq } from "drizzle-orm";
import { createTRPCRouter, protectedProcedure } from "../trpc";
import { z } from "zod";
export const deploymentRouter = createTRPCRouter({
all: protectedProcedure
@@ -72,4 +77,30 @@ export const deploymentRouter = createTRPCRouter({
return deploymentsList;
}),
killProcess: protectedProcedure
.input(
z.object({
deploymentId: z.string().min(1),
}),
)
.mutation(async ({ input }) => {
const deployment = await findDeploymentById(input.deploymentId);
if (!deployment.pid) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Deployment is not running",
});
}
const command = `kill -9 ${deployment.pid}`;
if (deployment.schedule?.serverId) {
await execAsyncRemote(deployment.schedule.serverId, command);
} else {
await execAsync(command);
}
await updateDeploymentStatus(deployment.deploymentId, "error");
}),
});