mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
- Updated fullContext type in rollbacks schema to include Application and Project types. - Refactored createRollback function to separate fullContext from input and handle it more efficiently. - Integrated environment variable preparation into the rollback process.
202 lines
4.6 KiB
TypeScript
202 lines
4.6 KiB
TypeScript
import { eq } from "drizzle-orm";
|
|
import { db } from "../db";
|
|
import {
|
|
type createRollbackSchema,
|
|
rollbacks,
|
|
deployments as deploymentsSchema,
|
|
} from "../db/schema";
|
|
import type { z } from "zod";
|
|
import { findApplicationById } from "./application";
|
|
import { getRemoteDocker } from "../utils/servers/remote-docker";
|
|
import type { ApplicationNested } from "../utils/builders";
|
|
import { execAsync, execAsyncRemote } from "../utils/process/execAsync";
|
|
import type { CreateServiceOptions } from "dockerode";
|
|
import { findDeploymentById } from "./deployment";
|
|
import { prepareEnvironmentVariables } from "../utils/docker/utils";
|
|
|
|
export const createRollback = async (
|
|
input: z.infer<typeof createRollbackSchema>,
|
|
) => {
|
|
await db.transaction(async (tx) => {
|
|
const { fullContext, ...other } = input;
|
|
const rollback = await tx
|
|
.insert(rollbacks)
|
|
.values(other)
|
|
.returning()
|
|
.then((res) => res[0]);
|
|
|
|
if (!rollback) {
|
|
throw new Error("Failed to create rollback");
|
|
}
|
|
|
|
const tagImage = `${input.appName}:v${rollback.version}`;
|
|
const deployment = await findDeploymentById(rollback.deploymentId);
|
|
|
|
if (!deployment?.applicationId) {
|
|
throw new Error("Deployment not found");
|
|
}
|
|
|
|
const {
|
|
deployments: _,
|
|
bitbucket,
|
|
github,
|
|
gitlab,
|
|
gitea,
|
|
...rest
|
|
} = await findApplicationById(deployment.applicationId);
|
|
|
|
await tx
|
|
.update(rollbacks)
|
|
.set({
|
|
image: tagImage,
|
|
fullContext: rest,
|
|
})
|
|
.where(eq(rollbacks.rollbackId, rollback.rollbackId));
|
|
|
|
// Update the deployment to reference this rollback
|
|
await tx
|
|
.update(deploymentsSchema)
|
|
.set({
|
|
rollbackId: rollback.rollbackId,
|
|
})
|
|
.where(eq(deploymentsSchema.deploymentId, rollback.deploymentId));
|
|
|
|
await createRollbackImage(rest, tagImage);
|
|
|
|
return rollback;
|
|
});
|
|
};
|
|
|
|
const findRollbackById = async (rollbackId: string) => {
|
|
const result = await db.query.rollbacks.findFirst({
|
|
where: eq(rollbacks.rollbackId, rollbackId),
|
|
});
|
|
|
|
if (!result) {
|
|
throw new Error("Rollback not found");
|
|
}
|
|
|
|
return result;
|
|
};
|
|
|
|
const createRollbackImage = async (
|
|
application: ApplicationNested,
|
|
tagImage: string,
|
|
) => {
|
|
const docker = await getRemoteDocker(application.serverId);
|
|
|
|
const appTagName =
|
|
application.sourceType === "docker"
|
|
? application.dockerImage
|
|
: `${application.appName}:latest`;
|
|
|
|
const result = docker.getImage(appTagName || "");
|
|
|
|
const [repo, version] = tagImage.split(":");
|
|
|
|
await result.tag({
|
|
repo,
|
|
tag: version,
|
|
});
|
|
};
|
|
|
|
const deleteRollbackImage = async (image: string, serverId?: string | null) => {
|
|
const command = `docker image rm ${image} --force`;
|
|
|
|
if (serverId) {
|
|
await execAsyncRemote(command, serverId);
|
|
} else {
|
|
await execAsync(command);
|
|
}
|
|
};
|
|
|
|
export const removeRollbackById = async (rollbackId: string) => {
|
|
const rollback = await findRollbackById(rollbackId);
|
|
|
|
if (!rollback) {
|
|
throw new Error("Rollback not found");
|
|
}
|
|
|
|
if (rollback?.image) {
|
|
try {
|
|
const deployment = await findDeploymentById(rollback.deploymentId);
|
|
|
|
if (!deployment?.applicationId) {
|
|
throw new Error("Deployment not found");
|
|
}
|
|
|
|
const application = await findApplicationById(deployment.applicationId);
|
|
await deleteRollbackImage(rollback.image, application.serverId);
|
|
|
|
await db
|
|
.delete(rollbacks)
|
|
.where(eq(rollbacks.rollbackId, rollbackId))
|
|
.returning()
|
|
.then((res) => res[0]);
|
|
} catch (error) {
|
|
console.error(error);
|
|
}
|
|
}
|
|
|
|
return rollback;
|
|
};
|
|
|
|
export const rollback = async (rollbackId: string) => {
|
|
const result = await findRollbackById(rollbackId);
|
|
|
|
const deployment = await findDeploymentById(result.deploymentId);
|
|
|
|
if (!deployment?.applicationId) {
|
|
throw new Error("Deployment not found");
|
|
}
|
|
|
|
const application = await findApplicationById(deployment.applicationId);
|
|
|
|
const envVariables = prepareEnvironmentVariables(
|
|
result?.fullContext?.env || "",
|
|
result.fullContext?.project?.env || "",
|
|
);
|
|
|
|
await rollbackApplication(
|
|
application.appName,
|
|
result.image || "",
|
|
application.serverId,
|
|
envVariables,
|
|
);
|
|
};
|
|
|
|
const rollbackApplication = async (
|
|
appName: string,
|
|
image: string,
|
|
serverId?: string | null,
|
|
env: string[] = [],
|
|
) => {
|
|
const docker = await getRemoteDocker(serverId);
|
|
|
|
const settings: CreateServiceOptions = {
|
|
Name: appName,
|
|
TaskTemplate: {
|
|
ContainerSpec: {
|
|
Image: image,
|
|
Env: env,
|
|
},
|
|
},
|
|
};
|
|
|
|
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: unknown) {
|
|
await docker.createService(settings);
|
|
}
|
|
};
|