diff --git a/packages/server/src/utils/builders/index.ts b/packages/server/src/utils/builders/index.ts index 1cdc9787..fe2ca0eb 100644 --- a/packages/server/src/utils/builders/index.ts +++ b/packages/server/src/utils/builders/index.ts @@ -4,12 +4,12 @@ import type { InferResultType } from "@dokploy/server/types/with"; import type { CreateServiceOptions } from "dockerode"; import { uploadImage, uploadImageRemoteCommand } from "../cluster/upload"; import { - calculateResources, - generateBindMounts, - generateConfigContainer, - generateFileMounts, - generateVolumeMounts, - prepareEnvironmentVariables, + calculateResources, + generateBindMounts, + generateConfigContainer, + generateFileMounts, + generateVolumeMounts, + prepareEnvironmentVariables, } from "../docker/utils"; import { getRemoteDocker } from "../servers/remote-docker"; import { buildCustomDocker, getDockerCommand } from "./docker-file"; @@ -24,217 +24,217 @@ import { nanoid } from "nanoid"; // PAKETO codeDirectory = where is the path of the code directory // DOCKERFILE codeDirectory = where is the exact path of the (Dockerfile) export type ApplicationNested = InferResultType< - "applications", - { - mounts: true; - security: true; - redirects: true; - ports: true; - registry: true; - project: true; - } + "applications", + { + mounts: true; + security: true; + redirects: true; + ports: true; + registry: true; + project: true; + } >; export const buildApplication = async ( - application: ApplicationNested, - logPath: string, + application: ApplicationNested, + logPath: string ) => { - const writeStream = createWriteStream(logPath, { flags: "a" }); - const { buildType, sourceType } = application; - try { - writeStream.write( - `\nBuild ${buildType}: ✅\nSource Type: ${sourceType}: ✅\n`, - ); - console.log(`Build ${buildType}: ✅`); - if (buildType === "nixpacks") { - await buildNixpacks(application, writeStream); - } else if (buildType === "heroku_buildpacks") { - await buildHeroku(application, writeStream); - } else if (buildType === "paketo_buildpacks") { - await buildPaketo(application, writeStream); - } else if (buildType === "dockerfile") { - await buildCustomDocker(application, writeStream); - } else if (buildType === "static") { - await buildStatic(application, writeStream); - } + const writeStream = createWriteStream(logPath, { flags: "a" }); + const { buildType, sourceType } = application; + try { + writeStream.write( + `\nBuild ${buildType}: ✅\nSource Type: ${sourceType}: ✅\n` + ); + console.log(`Build ${buildType}: ✅`); + if (buildType === "nixpacks") { + await buildNixpacks(application, writeStream); + } else if (buildType === "heroku_buildpacks") { + await buildHeroku(application, writeStream); + } else if (buildType === "paketo_buildpacks") { + await buildPaketo(application, writeStream); + } else if (buildType === "dockerfile") { + await buildCustomDocker(application, writeStream); + } else if (buildType === "static") { + await buildStatic(application, writeStream); + } - if (application.registryId) { - await uploadImage(application, writeStream); - } - await mechanizeDockerContainer(application); - writeStream.write("Docker Deployed: ✅"); - } catch (error) { - if (error instanceof Error) { - writeStream.write(`Error ❌\n${error?.message}`); - } else { - writeStream.write("Error ❌"); - } - throw error; - } finally { - writeStream.end(); - } + if (application.registryId) { + await uploadImage(application, writeStream); + } + await mechanizeDockerContainer(application); + writeStream.write("Docker Deployed: ✅"); + } catch (error) { + if (error instanceof Error) { + writeStream.write(`Error ❌\n${error?.message}`); + } else { + writeStream.write("Error ❌"); + } + throw error; + } finally { + writeStream.end(); + } }; export const getBuildCommand = ( - application: ApplicationNested, - logPath: string, + application: ApplicationNested, + logPath: string ) => { - let command = ""; - const { buildType, registry } = application; - switch (buildType) { - case "nixpacks": - command = getNixpacksCommand(application, logPath); - break; - case "heroku_buildpacks": - command = getHerokuCommand(application, logPath); - break; - case "paketo_buildpacks": - command = getPaketoCommand(application, logPath); - break; - case "static": - command = getStaticCommand(application, logPath); - break; - case "dockerfile": - command = getDockerCommand(application, logPath); - break; - } - if (registry) { - command += uploadImageRemoteCommand(application, logPath); - } + let command = ""; + const { buildType, registry } = application; + switch (buildType) { + case "nixpacks": + command = getNixpacksCommand(application, logPath); + break; + case "heroku_buildpacks": + command = getHerokuCommand(application, logPath); + break; + case "paketo_buildpacks": + command = getPaketoCommand(application, logPath); + break; + case "static": + command = getStaticCommand(application, logPath); + break; + case "dockerfile": + command = getDockerCommand(application, logPath); + break; + } + if (registry) { + command += uploadImageRemoteCommand(application, logPath); + } - return command; + return command; }; export const mechanizeDockerContainer = async ( - application: ApplicationNested, + application: ApplicationNested ) => { - const { - appName, - env, - mounts, - cpuLimit, - memoryLimit, - memoryReservation, - cpuReservation, - command, - ports, - } = application; + const { + appName, + env, + mounts, + cpuLimit, + memoryLimit, + memoryReservation, + cpuReservation, + command, + ports, + } = application; - const resources = calculateResources({ - memoryLimit, - memoryReservation, - cpuLimit, - cpuReservation, - }); + const resources = calculateResources({ + memoryLimit, + memoryReservation, + cpuLimit, + cpuReservation, + }); - const volumesMount = generateVolumeMounts(mounts); + const volumesMount = generateVolumeMounts(mounts); - const { - HealthCheck, - RestartPolicy, - Placement, - Labels, - Mode, - RollbackConfig, - UpdateConfig, - Networks, - } = generateConfigContainer(application); + const { + HealthCheck, + RestartPolicy, + Placement, + Labels, + Mode, + RollbackConfig, + UpdateConfig, + Networks, + } = generateConfigContainer(application); - const bindsMount = generateBindMounts(mounts); - const filesMount = generateFileMounts(appName, application); - const envVariables = prepareEnvironmentVariables( - env, - application.project.env, - ); + const bindsMount = generateBindMounts(mounts); + const filesMount = generateFileMounts(appName, application); + const envVariables = prepareEnvironmentVariables( + env, + application.project.env + ); - const image = getImageName(application); - const authConfig = getAuthConfig(application); - const docker = await getRemoteDocker(application.serverId); + const image = getImageName(application); + const authConfig = getAuthConfig(application); + const docker = await getRemoteDocker(application.serverId); - const settings: CreateServiceOptions = { - authconfig: authConfig, - Name: appName, - TaskTemplate: { - ContainerSpec: { - HealthCheck, - Image: image, - Env: envVariables, - Mounts: [...volumesMount, ...bindsMount, ...filesMount], - ...(command - ? { - Command: ["/bin/sh"], - Args: ["-c", command], - } - : {}), - Labels, - }, - Networks, - RestartPolicy, - Placement, - Resources: { - ...resources, - }, - }, - Mode, - RollbackConfig, - EndpointSpec: { - Ports: ports.map((port) => ({ - Protocol: port.protocol, - TargetPort: port.targetPort, - PublishedPort: port.publishedPort, - })), - }, - UpdateConfig, - }; + const settings: CreateServiceOptions = { + authconfig: authConfig, + Name: appName, + TaskTemplate: { + ContainerSpec: { + HealthCheck, + Image: image, + Env: envVariables, + Mounts: [...volumesMount, ...bindsMount, ...filesMount], + ...(command + ? { + Command: ["/bin/sh"], + Args: ["-c", command], + } + : {}), + Labels, + }, + Networks, + RestartPolicy, + Placement, + Resources: { + ...resources, + }, + }, + Mode, + RollbackConfig, + EndpointSpec: { + Ports: ports.map((port) => ({ + Protocol: port.protocol, + TargetPort: port.targetPort, + PublishedPort: port.publishedPort, + })), + }, + UpdateConfig, + }; - try { - const service = docker.getService(appName); - const inspect = await service.inspect(); - await service.update({ - version: Number.parseInt(inspect.Version.Index), - ...settings, - TaskTemplate: { - ...settings.TaskTemplate, - ForceUpdate: inspect.Spec.TaskTemplate.ForceUpdate + 1, - }, - }); - } catch (error) { - await docker.createService(settings); - } + try { + const service = docker.getService(appName); + const inspect = await service.inspect(); + await service.update({ + version: Number.parseInt(inspect.Version.Index), + ...settings, + TaskTemplate: { + ...settings.TaskTemplate, + ForceUpdate: inspect.Spec.TaskTemplate.ForceUpdate + 1, + }, + }); + } catch (error) { + await docker.createService(settings); + } }; const getImageName = (application: ApplicationNested) => { - const { appName, sourceType, dockerImage, registry } = application; + const { appName, sourceType, dockerImage, registry } = application; - if (sourceType === "docker") { - return dockerImage || "ERROR-NO-IMAGE-PROVIDED"; - } + if (sourceType === "docker") { + return dockerImage || "ERROR-NO-IMAGE-PROVIDED"; + } - if (registry) { - return join(registry.imagePrefix || "", appName); - } + if (registry) { + return join(registry.registryUrl, registry.imagePrefix || "", appName); + } - return `${appName}:latest`; + return `${appName}:latest`; }; const getAuthConfig = (application: ApplicationNested) => { - const { registry, username, password, sourceType } = application; + const { registry, username, password, sourceType, registryUrl } = application; - if (sourceType === "docker") { - if (username && password) { - return { - password, - username, - serveraddress: "https://index.docker.io/v1/", - }; - } - } else if (registry) { - return { - password: registry.password, - username: registry.username, - serveraddress: registry.registryUrl, - }; - } + if (sourceType === "docker") { + if (username && password) { + return { + password, + username, + serveraddress: registryUrl || "", + }; + } + } else if (registry) { + return { + password: registry.password, + username: registry.username, + serveraddress: registry.registryUrl, + }; + } - return undefined; + return undefined; }; diff --git a/packages/server/src/utils/cluster/upload.ts b/packages/server/src/utils/cluster/upload.ts index 0ecbda4f..e4143bb0 100644 --- a/packages/server/src/utils/cluster/upload.ts +++ b/packages/server/src/utils/cluster/upload.ts @@ -1,81 +1,84 @@ import type { WriteStream } from "node:fs"; -import { join } from "node:path"; +import path, { join } from "node:path"; import type { ApplicationNested } from "../builders"; import { spawnAsync } from "../process/spawnAsync"; export const uploadImage = async ( - application: ApplicationNested, - writeStream: WriteStream, + application: ApplicationNested, + writeStream: WriteStream ) => { - const registry = application.registry; + const registry = application.registry; - if (!registry) { - throw new Error("Registry not found"); - } + if (!registry) { + throw new Error("Registry not found"); + } - const { registryUrl, imagePrefix, registryType } = registry; - const { appName } = application; - const imageName = `${appName}:latest`; + const { registryUrl, imagePrefix } = registry; + const { appName } = application; + const imageName = `${appName}:latest`; - const finalURL = registryUrl; + const finalURL = registryUrl; - const registryTag = - `${registryUrl}/${join(imagePrefix || "", imageName)}`.replace(/\/+/g, "/"); + const registryTag = path + .join(registryUrl, join(imagePrefix || "", imageName)) + .replace(/\/+/g, "/"); - try { - writeStream.write( - `📦 [Enabled Registry] Uploading image to ${registry.registryType} | ${imageName} | ${finalURL}\n`, - ); - const loginCommand = spawnAsync( - "docker", - ["login", finalURL, "-u", registry.username, "--password-stdin"], - (data) => { - if (writeStream.writable) { - writeStream.write(data); - } - }, - ); - loginCommand.child?.stdin?.write(registry.password); - loginCommand.child?.stdin?.end(); - await loginCommand; + try { + writeStream.write( + `📦 [Enabled Registry] Uploading image to ${registry.registryType} | ${imageName} | ${finalURL}\n` + ); + const loginCommand = spawnAsync( + "docker", + ["login", finalURL, "-u", registry.username, "--password-stdin"], + (data) => { + if (writeStream.writable) { + writeStream.write(data); + } + } + ); + loginCommand.child?.stdin?.write(registry.password); + loginCommand.child?.stdin?.end(); + await loginCommand; - await spawnAsync("docker", ["tag", imageName, registryTag], (data) => { - if (writeStream.writable) { - writeStream.write(data); - } - }); + await spawnAsync("docker", ["tag", imageName, registryTag], (data) => { + if (writeStream.writable) { + writeStream.write(data); + } + }); - await spawnAsync("docker", ["push", registryTag], (data) => { - if (writeStream.writable) { - writeStream.write(data); - } - }); - } catch (error) { - console.log(error); - throw error; - } + await spawnAsync("docker", ["push", registryTag], (data) => { + if (writeStream.writable) { + writeStream.write(data); + } + }); + } catch (error) { + console.log(error); + throw error; + } }; export const uploadImageRemoteCommand = ( - application: ApplicationNested, - logPath: string, + application: ApplicationNested, + logPath: string ) => { - const registry = application.registry; + const registry = application.registry; - if (!registry) { - throw new Error("Registry not found"); - } + if (!registry) { + throw new Error("Registry not found"); + } - const { registryUrl, imagePrefix } = registry; - const { appName } = application; - const imageName = `${appName}:latest`; + const { registryUrl, imagePrefix } = registry; + const { appName } = application; + const imageName = `${appName}:latest`; - const finalURL = registryUrl; + const finalURL = registryUrl; - const registryTag = join(imagePrefix || "", imageName); + const registryTag = path + .join(registryUrl, join(imagePrefix || "", imageName)) + .replace(/\/+/g, "/"); - try { - const command = ` + try { + const command = ` echo "📦 [Enabled Registry] Uploading image to '${registry.registryType}' | '${registryTag}'" >> ${logPath}; echo "${registry.password}" | docker login ${finalURL} -u ${registry.username} --password-stdin >> ${logPath} 2>> ${logPath} || { echo "❌ DockerHub Failed" >> ${logPath}; @@ -93,9 +96,8 @@ export const uploadImageRemoteCommand = ( } echo "✅ Image Pushed" >> ${logPath}; `; - return command; - } catch (error) { - console.log(error); - throw error; - } + return command; + } catch (error) { + throw error; + } };