1052 lines · 28.5 KB · TypeScript
RawDownload
import { existsSync, promises as fsPromises } from "node:fs";import path from "node:path";import { paths } from "@dokploy/server/constants";import { db } from "@dokploy/server/db";import {	type apiCreateDeployment,	type apiCreateDeploymentBackup,	type apiCreateDeploymentCompose,	type apiCreateDeploymentPreview,	type apiCreateDeploymentSchedule,	type apiCreateDeploymentServer,	type apiCreateDeploymentVolumeBackup,	applications,	compose,	deployments,	environments,	projects,} from "@dokploy/server/db/schema";import { removeDirectoryIfExistsContent } from "@dokploy/server/utils/filesystem/directory";import {	execAsync,	execAsyncRemote,} from "@dokploy/server/utils/process/execAsync";import { TRPCError } from "@trpc/server";import { format } from "date-fns";import { and, desc, eq, inArray, or, sql } from "drizzle-orm";import type { z } from "zod";import {	type Application,	findApplicationById,	updateApplicationStatus,} from "./application";import { findBackupById } from "./backup";import { type Compose, findComposeById, updateCompose } from "./compose";import {	findPreviewDeploymentById,	type PreviewDeployment,	updatePreviewDeployment,} from "./preview-deployment";import { removeRollbackById } from "./rollbacks";import { findScheduleById } from "./schedule";import { findServerById, type Server } from "./server";import { findVolumeBackupById } from "./volume-backups"; export type ServicePath = { href: string | null; label: string }; export async function resolveServicePath(	orgId: string,	data: Record<string, unknown>,): Promise<ServicePath> {	try {		const applicationId = data?.applicationId as string | undefined;		const composeId = data?.composeId as string | undefined;		if (applicationId) {			const app = await findApplicationById(applicationId);			if (app.environment.project.organizationId !== orgId) {				return { href: null, label: "Application" };			}			return {				href: `/dashboard/project/${app.environment.project.projectId}/environment/${app.environment.environmentId}/services/application/${app.applicationId}`,				label: "Application",			};		}		if (composeId) {			const comp = await findComposeById(composeId);			if (comp.environment.project.organizationId !== orgId) {				return { href: null, label: "Compose" };			}			return {				href: `/dashboard/project/${comp.environment.project.projectId}/environment/${comp.environment.environmentId}/services/compose/${comp.composeId}`,				label: "Compose",			};		}	} catch {		// not found or unauthorized	}	return { href: null, label: "—" };} export type Deployment = typeof deployments.$inferSelect; export const findDeploymentById = async (deploymentId: string) => {	const deployment = await db.query.deployments.findFirst({		where: eq(deployments.deploymentId, deploymentId),		with: {			application: true,			compose: true,			schedule: true,		},	});	if (!deployment) {		throw new TRPCError({			code: "NOT_FOUND",			message: "Deployment not found",		});	}	return deployment;}; export const findDeploymentByApplicationId = async (applicationId: string) => {	const deployment = await db.query.deployments.findFirst({		where: eq(deployments.applicationId, applicationId),	}); 	if (!deployment) {		throw new TRPCError({			code: "NOT_FOUND",			message: "Deployment not found",		});	}	return deployment;}; export const createDeployment = async (	deployment: Omit<		z.infer<typeof apiCreateDeployment>,		"deploymentId" | "createdAt" | "status" | "logPath"	>,) => {	const application = await findApplicationById(deployment.applicationId);	await removeLastTenDeployments(		deployment.applicationId,		"application",		application.serverId,	);	try {		const serverId = application.buildServerId || application.serverId; 		const { LOGS_PATH } = paths(!!serverId);		const formattedDateTime = format(new Date(), "yyyy-MM-dd:HH:mm:ss");		const fileName = `${application.appName}-${formattedDateTime}.log`;		const logFilePath = path.join(LOGS_PATH, application.appName, fileName); 		if (serverId) {			const server = await findServerById(serverId); 			const command = `				mkdir -p ${LOGS_PATH}/${application.appName};            	echo "Initializing deployment" >> ${logFilePath};			    echo "Building on ${serverId ? "Build Server" : "Dokploy Server"}" >> ${logFilePath};			`; 			await execAsyncRemote(server.serverId, command);		} else {			await fsPromises.mkdir(path.join(LOGS_PATH, application.appName), {				recursive: true,			});			await fsPromises.writeFile(logFilePath, "Initializing deployment\n");		} 		const deploymentCreate = await db			.insert(deployments)			.values({				applicationId: deployment.applicationId,				title: deployment.title || "Deployment",				status: "running",				logPath: logFilePath,				description: deployment.description || "",				startedAt: new Date().toISOString(),				...(application.buildServerId && {					buildServerId: application.buildServerId,				}),			})			.returning();		if (deploymentCreate.length === 0 || !deploymentCreate[0]) {			throw new TRPCError({				code: "BAD_REQUEST",				message: "Error creating the deployment",			});		}		return deploymentCreate[0];	} catch (error) {		await db			.insert(deployments)			.values({				applicationId: deployment.applicationId,				title: deployment.title || "Deployment",				status: "error",				logPath: "",				description: deployment.description || "",				errorMessage: `An error have occurred: ${error instanceof Error ? error.message : error}`,				startedAt: new Date().toISOString(),				finishedAt: new Date().toISOString(),			})			.returning();		await updateApplicationStatus(application.applicationId, "error");		console.log(error);		throw new TRPCError({			code: "BAD_REQUEST",			message: "Error creating the deployment",		});	}}; export const createDeploymentPreview = async (	deployment: Omit<		z.infer<typeof apiCreateDeploymentPreview>,		"deploymentId" | "createdAt" | "status" | "logPath"	>,) => {	const previewDeployment = await findPreviewDeploymentById(		deployment.previewDeploymentId,	);	await removeLastTenDeployments(		deployment.previewDeploymentId,		"previewDeployment",		previewDeployment?.application?.serverId,	);	try {		const appName = `${previewDeployment.appName}`;		const { LOGS_PATH } = paths(!!previewDeployment?.application?.serverId);		const formattedDateTime = format(new Date(), "yyyy-MM-dd:HH:mm:ss");		const fileName = `${appName}-${formattedDateTime}.log`;		const logFilePath = path.join(LOGS_PATH, appName, fileName); 		if (previewDeployment?.application?.serverId) {			const server = await findServerById(				previewDeployment?.application?.serverId,			); 			const command = `				mkdir -p ${LOGS_PATH}/${appName};            	echo "Initializing deployment" >> ${logFilePath};			`; 			await execAsyncRemote(server.serverId, command);		} else {			await fsPromises.mkdir(path.join(LOGS_PATH, appName), {				recursive: true,			});			await fsPromises.writeFile(logFilePath, "Initializing deployment");		} 		const deploymentCreate = await db			.insert(deployments)			.values({				title: deployment.title || "Deployment",				status: "running",				logPath: logFilePath,				description: deployment.description || "",				previewDeploymentId: deployment.previewDeploymentId,				startedAt: new Date().toISOString(),			})			.returning();		if (deploymentCreate.length === 0 || !deploymentCreate[0]) {			throw new TRPCError({				code: "BAD_REQUEST",				message: "Error creating the deployment",			});		}		return deploymentCreate[0];	} catch (error) {		await db			.insert(deployments)			.values({				previewDeploymentId: deployment.previewDeploymentId,				title: deployment.title || "Deployment",				status: "error",				logPath: "",				description: deployment.description || "",				errorMessage: `An error have occurred: ${error instanceof Error ? error.message : error}`,				startedAt: new Date().toISOString(),				finishedAt: new Date().toISOString(),			})			.returning();		await updatePreviewDeployment(deployment.previewDeploymentId, {			previewStatus: "error",		});		console.log(error);		throw new TRPCError({			code: "BAD_REQUEST",			message: "Error creating the deployment",		});	}}; export const createDeploymentCompose = async (	deployment: Omit<		z.infer<typeof apiCreateDeploymentCompose>,		"deploymentId" | "createdAt" | "status" | "logPath"	>,) => {	const compose = await findComposeById(deployment.composeId);	await removeLastTenDeployments(		deployment.composeId,		"compose",		compose.serverId,	);	try {		const { LOGS_PATH } = paths(!!compose.serverId);		const formattedDateTime = format(new Date(), "yyyy-MM-dd:HH:mm:ss");		const fileName = `${compose.appName}-${formattedDateTime}.log`;		const logFilePath = path.join(LOGS_PATH, compose.appName, fileName); 		if (compose.serverId) {			const server = await findServerById(compose.serverId); 			const command = `mkdir -p ${LOGS_PATH}/${compose.appName};echo "Initializing deployment\n" >> ${logFilePath};`; 			await execAsyncRemote(server.serverId, command);		} else {			await fsPromises.mkdir(path.join(LOGS_PATH, compose.appName), {				recursive: true,			});			await fsPromises.writeFile(logFilePath, "Initializing deployment\n");		} 		const deploymentCreate = await db			.insert(deployments)			.values({				composeId: deployment.composeId,				title: deployment.title || "Deployment",				description: deployment.description || "",				status: "running",				logPath: logFilePath,				startedAt: new Date().toISOString(),			})			.returning();		if (deploymentCreate.length === 0 || !deploymentCreate[0]) {			throw new TRPCError({				code: "BAD_REQUEST",				message: "Error creating the deployment",			});		}		return deploymentCreate[0];	} catch (error) {		await db			.insert(deployments)			.values({				composeId: deployment.composeId,				title: deployment.title || "Deployment",				status: "error",				logPath: "",				description: deployment.description || "",				errorMessage: `An error have occurred: ${error instanceof Error ? error.message : error}`,				startedAt: new Date().toISOString(),				finishedAt: new Date().toISOString(),			})			.returning();		await updateCompose(compose.composeId, {			composeStatus: "error",		});		console.log(error);		throw new TRPCError({			code: "BAD_REQUEST",			message: "Error creating the deployment",		});	}}; export const createDeploymentBackup = async (	deployment: Omit<		z.infer<typeof apiCreateDeploymentBackup>,		"deploymentId" | "createdAt" | "status" | "logPath"	>,) => {	const backup = await findBackupById(deployment.backupId); 	let serverId: string | null | undefined;	if (backup.backupType === "database") {		serverId =			backup.postgres?.serverId ||			backup.mariadb?.serverId ||			backup.mysql?.serverId ||			backup.mongo?.serverId;	} else if (backup.backupType === "compose") {		serverId = backup.compose?.serverId;	}	await removeLastTenDeployments(deployment.backupId, "backup", serverId);	try {		const { LOGS_PATH } = paths(!!serverId);		const formattedDateTime = format(new Date(), "yyyy-MM-dd:HH:mm:ss");		const fileName = `${backup.appName}-${formattedDateTime}.log`;		const logFilePath = path.join(LOGS_PATH, backup.appName, fileName); 		if (serverId) {			const server = await findServerById(serverId); 			const command = `mkdir -p ${LOGS_PATH}/${backup.appName};echo "Initializing backup\n" >> ${logFilePath};`; 			await execAsyncRemote(server.serverId, command);		} else {			await fsPromises.mkdir(path.join(LOGS_PATH, backup.appName), {				recursive: true,			});			await fsPromises.writeFile(logFilePath, "Initializing backup\n");		} 		const deploymentCreate = await db			.insert(deployments)			.values({				backupId: deployment.backupId,				title: deployment.title || "Backup",				description: deployment.description || "",				status: "running",				logPath: logFilePath,				startedAt: new Date().toISOString(),			})			.returning();		if (deploymentCreate.length === 0 || !deploymentCreate[0]) {			throw new TRPCError({				code: "BAD_REQUEST",				message: "Error creating the backup",			});		}		return deploymentCreate[0];	} catch (error) {		await db			.insert(deployments)			.values({				backupId: deployment.backupId,				title: deployment.title || "Backup",				status: "error",				logPath: "",				description: deployment.description || "",				errorMessage: `An error have occurred: ${error instanceof Error ? error.message : error}`,				startedAt: new Date().toISOString(),				finishedAt: new Date().toISOString(),			})			.returning();		throw new TRPCError({			code: "BAD_REQUEST",			message: "Error creating the backup",		});	}}; export const createDeploymentSchedule = async (	deployment: Omit<		z.infer<typeof apiCreateDeploymentSchedule>,		"deploymentId" | "createdAt" | "status" | "logPath"	>,) => {	const schedule = await findScheduleById(deployment.scheduleId); 	const serverId =		schedule.application?.serverId ||		schedule.compose?.serverId ||		schedule.server?.serverId;	await removeLastTenDeployments(deployment.scheduleId, "schedule", serverId);	try {		const { SCHEDULES_PATH } = paths(!!serverId);		const formattedDateTime = format(new Date(), "yyyy-MM-dd:HH:mm:ss");		const fileName = `${schedule.appName}-${formattedDateTime}.log`;		const logFilePath = path.join(SCHEDULES_PATH, schedule.appName, fileName); 		if (serverId) {			const server = await findServerById(serverId); 			const command = `				mkdir -p ${SCHEDULES_PATH}/${schedule.appName};            	echo "Initializing schedule" >> ${logFilePath};			`; 			await execAsyncRemote(server.serverId, command);		} else {			await fsPromises.mkdir(path.join(SCHEDULES_PATH, schedule.appName), {				recursive: true,			});			await fsPromises.writeFile(logFilePath, "Initializing schedule\n");		} 		const deploymentCreate = await db			.insert(deployments)			.values({				scheduleId: deployment.scheduleId,				title: deployment.title || "Deployment",				status: "running",				logPath: logFilePath,				description: deployment.description || "",				startedAt: new Date().toISOString(),			})			.returning();		if (deploymentCreate.length === 0 || !deploymentCreate[0]) {			throw new TRPCError({				code: "BAD_REQUEST",				message: "Error creating the deployment",			});		}		return deploymentCreate[0];	} catch (error) {		console.log(error);		await db			.insert(deployments)			.values({				scheduleId: deployment.scheduleId,				title: deployment.title || "Deployment",				status: "error",				logPath: "",				description: deployment.description || "",				errorMessage: `An error have occurred: ${error instanceof Error ? error.message : error}`,				startedAt: new Date().toISOString(),				finishedAt: new Date().toISOString(),			})

Lines 1–500 of 1052.