mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-10-19 23:21:36 +00:00
ec202c8c6e
- Updated the version of @dokploy/trpc-openapi from 0.0.18 to 0.0.19 in pnpm-lock.yaml and package.json. - Added OpenAPI metadata to various procedures across multiple routers, improving API documentation and clarity for developers. - Enhanced descriptions and summaries for several API endpoints, ensuring better understanding of their functionality. These changes improve the overall API usability and documentation quality.
243 lines
6.9 KiB
TypeScript
243 lines
6.9 KiB
TypeScript
import {
|
|
execAsync,
|
|
execAsyncRemote,
|
|
findAllDeploymentsByApplicationId,
|
|
findAllDeploymentsByComposeId,
|
|
findAllDeploymentsByServerId,
|
|
findAllDeploymentsCentralized,
|
|
findDeploymentById,
|
|
IS_CLOUD,
|
|
removeDeployment,
|
|
resolveServicePath,
|
|
updateDeploymentStatus,
|
|
} from "@dokploy/server";
|
|
import { db } from "@dokploy/server/db";
|
|
import {
|
|
checkServicePermissionAndAccess,
|
|
findMemberByUserId,
|
|
} from "@dokploy/server/services/permission";
|
|
import { TRPCError } from "@trpc/server";
|
|
import { desc, eq } from "drizzle-orm";
|
|
import { z } from "zod";
|
|
import { audit } from "@/server/api/utils/audit";
|
|
import {
|
|
apiFindAllByApplication,
|
|
apiFindAllByCompose,
|
|
apiFindAllByServer,
|
|
apiFindAllByType,
|
|
deployments,
|
|
server,
|
|
} from "@/server/db/schema";
|
|
import { myQueue } from "@/server/queues/queueSetup";
|
|
import { fetchDeployApiJobs, type QueueJobRow } from "@/server/utils/deploy";
|
|
import { createTRPCRouter, protectedProcedure, withPermission } from "../trpc";
|
|
|
|
export const deploymentRouter = createTRPCRouter({
|
|
all: protectedProcedure
|
|
.meta({
|
|
openapi: {
|
|
summary: "List deployments by application",
|
|
description: "Returns all deployments associated with the given application, ordered by creation date.",
|
|
},
|
|
})
|
|
.input(apiFindAllByApplication)
|
|
.query(async ({ input, ctx }) => {
|
|
await checkServicePermissionAndAccess(ctx, input.applicationId, {
|
|
deployment: ["read"],
|
|
});
|
|
return await findAllDeploymentsByApplicationId(input.applicationId);
|
|
}),
|
|
|
|
allByCompose: protectedProcedure
|
|
.meta({
|
|
openapi: {
|
|
summary: "List deployments by compose",
|
|
description: "Returns all deployments associated with the given compose service.",
|
|
},
|
|
})
|
|
.input(apiFindAllByCompose)
|
|
.query(async ({ input, ctx }) => {
|
|
await checkServicePermissionAndAccess(ctx, input.composeId, {
|
|
deployment: ["read"],
|
|
});
|
|
return await findAllDeploymentsByComposeId(input.composeId);
|
|
}),
|
|
allByServer: withPermission("deployment", "read")
|
|
.meta({
|
|
openapi: {
|
|
summary: "List deployments by server",
|
|
description: "Returns all deployments associated with the given server.",
|
|
},
|
|
})
|
|
.input(apiFindAllByServer)
|
|
.query(async ({ input }) => {
|
|
return await findAllDeploymentsByServerId(input.serverId);
|
|
}),
|
|
allCentralized: withPermission("deployment", "read")
|
|
.meta({
|
|
openapi: {
|
|
summary: "List all deployments centralized",
|
|
description: "Returns all deployments across all services in the organization. Non-admin users only see deployments for their accessible services.",
|
|
},
|
|
})
|
|
.query(
|
|
async ({ ctx }) => {
|
|
const orgId = ctx.session.activeOrganizationId;
|
|
const accessedServices =
|
|
ctx.user.role !== "owner" && ctx.user.role !== "admin"
|
|
? (await findMemberByUserId(ctx.user.id, orgId)).accessedServices
|
|
: null;
|
|
if (accessedServices !== null && accessedServices.length === 0) {
|
|
return [];
|
|
}
|
|
return findAllDeploymentsCentralized(orgId, accessedServices);
|
|
},
|
|
),
|
|
|
|
queueList: withPermission("deployment", "read")
|
|
.meta({
|
|
openapi: {
|
|
summary: "List deployment queue jobs",
|
|
description: "Returns all jobs in the deployment queue with their current state, timestamps, and resolved service paths.",
|
|
},
|
|
})
|
|
.query(async ({ ctx }) => {
|
|
const orgId = ctx.session.activeOrganizationId;
|
|
let rows: QueueJobRow[];
|
|
|
|
if (IS_CLOUD) {
|
|
const servers = await db.query.server.findMany({
|
|
where: eq(server.organizationId, orgId),
|
|
columns: { serverId: true },
|
|
});
|
|
const serverRowsArrays = await Promise.all(
|
|
servers.map(({ serverId }) => fetchDeployApiJobs(serverId)),
|
|
);
|
|
rows = serverRowsArrays.flat();
|
|
rows.sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0));
|
|
} else {
|
|
const jobs = await myQueue.getJobs();
|
|
const jobRows = await Promise.all(
|
|
jobs.map(async (job) => {
|
|
const state = await job.getState();
|
|
return {
|
|
id: String(job.id),
|
|
name: job.name ?? undefined,
|
|
data: job.data as Record<string, unknown>,
|
|
timestamp: job.timestamp,
|
|
processedOn: job.processedOn,
|
|
finishedOn: job.finishedOn,
|
|
failedReason: job.failedReason ?? undefined,
|
|
state,
|
|
};
|
|
}),
|
|
);
|
|
jobRows.sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0));
|
|
rows = jobRows;
|
|
}
|
|
|
|
return Promise.all(
|
|
rows.map(async (row) => ({
|
|
...row,
|
|
servicePath: await resolveServicePath(
|
|
orgId,
|
|
(row.data ?? {}) as Record<string, unknown>,
|
|
),
|
|
})),
|
|
);
|
|
}),
|
|
|
|
allByType: protectedProcedure
|
|
.meta({
|
|
openapi: {
|
|
summary: "List deployments by service type",
|
|
description: "Returns all deployments for a given service ID and type (application, compose, etc.), including associated rollback information.",
|
|
},
|
|
})
|
|
.input(apiFindAllByType)
|
|
.query(async ({ input, ctx }) => {
|
|
await checkServicePermissionAndAccess(ctx, input.id, {
|
|
deployment: ["read"],
|
|
});
|
|
const deploymentsList = await db.query.deployments.findMany({
|
|
where: eq(deployments[`${input.type}Id`], input.id),
|
|
orderBy: desc(deployments.createdAt),
|
|
with: {
|
|
rollback: true,
|
|
},
|
|
});
|
|
return deploymentsList;
|
|
}),
|
|
killProcess: protectedProcedure
|
|
.meta({
|
|
openapi: {
|
|
summary: "Cancel a running deployment",
|
|
description: "Kills the running process of a deployment by sending SIGKILL to its PID. Updates the deployment status to error.",
|
|
},
|
|
})
|
|
.input(
|
|
z.object({
|
|
deploymentId: z.string().min(1),
|
|
}),
|
|
)
|
|
.mutation(async ({ input, ctx }) => {
|
|
const deployment = await findDeploymentById(input.deploymentId);
|
|
const serviceId = deployment.applicationId || deployment.composeId;
|
|
if (serviceId) {
|
|
await checkServicePermissionAndAccess(ctx, serviceId, {
|
|
deployment: ["cancel"],
|
|
});
|
|
}
|
|
|
|
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");
|
|
await audit(ctx, {
|
|
action: "cancel",
|
|
resourceType: "deployment",
|
|
resourceId: deployment.deploymentId,
|
|
});
|
|
}),
|
|
|
|
removeDeployment: protectedProcedure
|
|
.meta({
|
|
openapi: {
|
|
summary: "Delete a deployment",
|
|
description: "Permanently removes a deployment record and its associated data.",
|
|
},
|
|
})
|
|
.input(
|
|
z.object({
|
|
deploymentId: z.string().min(1),
|
|
}),
|
|
)
|
|
.mutation(async ({ input, ctx }) => {
|
|
const deployment = await findDeploymentById(input.deploymentId);
|
|
const serviceId = deployment.applicationId || deployment.composeId;
|
|
if (serviceId) {
|
|
await checkServicePermissionAndAccess(ctx, serviceId, {
|
|
deployment: ["cancel"],
|
|
});
|
|
}
|
|
const result = await removeDeployment(input.deploymentId);
|
|
await audit(ctx, {
|
|
action: "delete",
|
|
resourceType: "deployment",
|
|
resourceId: deployment.deploymentId,
|
|
});
|
|
return result;
|
|
}),
|
|
});
|