mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-06-19 14:15:21 +02:00
Merge branch 'canary' into feat/internal-path-routing
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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 && (
|
||||
|
||||
@@ -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: {
|
||||
|
||||
1
apps/dokploy/drizzle/0098_conscious_chat.sql
Normal file
1
apps/dokploy/drizzle/0098_conscious_chat.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE "deployment" ADD COLUMN "pid" text;
|
||||
@@ -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",
|
||||
|
||||
@@ -691,8 +691,8 @@
|
||||
{
|
||||
"idx": 98,
|
||||
"version": "7",
|
||||
"when": 1750570751159,
|
||||
"tag": "0098_warm_tiger_shark",
|
||||
"when": 1751233265357,
|
||||
"tag": "0098_conscious_chat",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "dokploy",
|
||||
"version": "v0.23.4",
|
||||
"version": "v0.23.5",
|
||||
"private": true,
|
||||
"license": "Apache-2.0",
|
||||
"type": "module",
|
||||
|
||||
@@ -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");
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -31,6 +31,7 @@ export const deployments = pgTable("deployment", {
|
||||
description: text("description"),
|
||||
status: deploymentStatus("status").default("running"),
|
||||
logPath: text("logPath").notNull(),
|
||||
pid: text("pid"),
|
||||
applicationId: text("applicationId").references(
|
||||
() => applications.applicationId,
|
||||
{ onDelete: "cascade" },
|
||||
|
||||
@@ -40,6 +40,7 @@ export const findDeploymentById = async (deploymentId: string) => {
|
||||
where: eq(deployments.deploymentId, deploymentId),
|
||||
with: {
|
||||
application: true,
|
||||
schedule: true,
|
||||
},
|
||||
});
|
||||
if (!deployment) {
|
||||
|
||||
@@ -171,7 +171,7 @@ export const createPreviewDeployment = async (
|
||||
const runningComment = getIssueComment(
|
||||
application.name,
|
||||
"initializing",
|
||||
generateDomain,
|
||||
`${application.previewHttps ? "https" : "http"}://${generateDomain}`,
|
||||
);
|
||||
|
||||
const issue = await octokit.rest.issues.createComment({
|
||||
|
||||
@@ -109,7 +109,12 @@ export const updateSchedule = async (
|
||||
const handleScript = async (schedule: Schedule) => {
|
||||
const { SCHEDULES_PATH } = paths(!!schedule?.serverId);
|
||||
const fullPath = path.join(SCHEDULES_PATH, schedule?.appName || "");
|
||||
const encodedContent = encodeBase64(schedule?.script || "");
|
||||
|
||||
// Add PID and Schedule ID echo by default to all scripts
|
||||
const scriptWithPid = `echo "PID: $$ | Schedule ID: ${schedule.scheduleId}"
|
||||
${schedule?.script || ""}`;
|
||||
|
||||
const encodedContent = encodeBase64(scriptWithPid);
|
||||
const script = `
|
||||
mkdir -p ${fullPath}
|
||||
rm -f ${fullPath}/script.sh
|
||||
|
||||
@@ -193,7 +193,7 @@ const createEnvFile = (compose: ComposeNested) => {
|
||||
let envContent = `APP_NAME=${appName}\n`;
|
||||
envContent += env || "";
|
||||
if (!envContent.includes("DOCKER_CONFIG")) {
|
||||
envContent += "\nDOCKER_CONFIG=/root/.docker/config.json";
|
||||
envContent += "\nDOCKER_CONFIG=/root/.docker";
|
||||
}
|
||||
|
||||
if (compose.randomize) {
|
||||
@@ -223,7 +223,7 @@ export const getCreateEnvFileCommand = (compose: ComposeNested) => {
|
||||
let envContent = `APP_NAME=${appName}\n`;
|
||||
envContent += env || "";
|
||||
if (!envContent.includes("DOCKER_CONFIG")) {
|
||||
envContent += "\nDOCKER_CONFIG=/root/.docker/config.json";
|
||||
envContent += "\nDOCKER_CONFIG=/root/.docker";
|
||||
}
|
||||
|
||||
if (compose.randomize) {
|
||||
|
||||
@@ -2,7 +2,10 @@ import { createWriteStream } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { paths } from "@dokploy/server/constants";
|
||||
import type { Schedule } from "@dokploy/server/db/schema/schedule";
|
||||
import { createDeploymentSchedule } from "@dokploy/server/services/deployment";
|
||||
import {
|
||||
createDeploymentSchedule,
|
||||
updateDeployment,
|
||||
} from "@dokploy/server/services/deployment";
|
||||
import { updateDeploymentStatus } from "@dokploy/server/services/deployment";
|
||||
import { findScheduleById } from "@dokploy/server/services/schedule";
|
||||
import { scheduleJob as scheduleJobNode, scheduledJobs } from "node-schedule";
|
||||
@@ -113,8 +116,16 @@ export const runCommand = async (scheduleId: string) => {
|
||||
await spawnAsync(
|
||||
"bash",
|
||||
["-c", "./script.sh"],
|
||||
(data) => {
|
||||
async (data) => {
|
||||
if (writeStream.writable) {
|
||||
// we need to extract the PID and Schedule ID from the data
|
||||
const pid = data?.match(/PID: (\d+)/)?.[1];
|
||||
|
||||
if (pid) {
|
||||
await updateDeployment(deployment.deploymentId, {
|
||||
pid,
|
||||
});
|
||||
}
|
||||
writeStream.write(data);
|
||||
}
|
||||
},
|
||||
@@ -133,13 +144,21 @@ export const runCommand = async (scheduleId: string) => {
|
||||
const command = `
|
||||
set -e
|
||||
echo "Running script" >> ${deployment.logPath};
|
||||
bash -c ${fullPath}/script.sh >> ${deployment.logPath} 2>> ${deployment.logPath} || {
|
||||
bash -c ${fullPath}/script.sh 2>&1 | tee -a ${deployment.logPath} || {
|
||||
echo "❌ Command failed" >> ${deployment.logPath};
|
||||
exit 1;
|
||||
}
|
||||
echo "✅ Command executed successfully" >> ${deployment.logPath};
|
||||
`;
|
||||
await execAsyncRemote(serverId, command);
|
||||
await execAsyncRemote(serverId, command, async (data) => {
|
||||
// we need to extract the PID and Schedule ID from the data
|
||||
const pid = data?.match(/PID: (\d+)/)?.[1];
|
||||
if (pid) {
|
||||
await updateDeployment(deployment.deploymentId, {
|
||||
pid,
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
await updateDeploymentStatus(deployment.deploymentId, "error");
|
||||
throw error;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
packages:
|
||||
- "apps/dokploy"
|
||||
- "apps/api"
|
||||
- "apps/dokploy"
|
||||
- "apps/monitoring"
|
||||
- "apps/schedules"
|
||||
- "apps/models"
|
||||
- "packages/server"
|
||||
|
||||
Reference in New Issue
Block a user