mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-06-19 06:05:25 +02:00
feat: add appName field to volume_backup schema and enhance deployment volume backup functionality
- Introduced a new appName column in the volume_backup table to improve application identification. - Updated the volume backup schema to ensure appName is a required field. - Enhanced the deployment service to support volume backup creation, integrating appName into the deployment process. - Added validation for volume backup creation to ensure proper handling of appName and related data.
This commit is contained in:
1
apps/dokploy/drizzle/0107_brief_the_fallen.sql
Normal file
1
apps/dokploy/drizzle/0107_brief_the_fallen.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE "volume_backup" ADD COLUMN "appName" text NOT NULL;
|
||||
6092
apps/dokploy/drizzle/meta/0107_snapshot.json
Normal file
6092
apps/dokploy/drizzle/meta/0107_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -750,6 +750,13 @@
|
||||
"when": 1751260111986,
|
||||
"tag": "0106_furry_gargoyle",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 107,
|
||||
"version": "7",
|
||||
"when": 1751260608863,
|
||||
"tag": "0107_brief_the_fallen",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -188,6 +188,17 @@ export const apiCreateDeploymentSchedule = schema
|
||||
scheduleId: z.string().min(1),
|
||||
});
|
||||
|
||||
export const apiCreateDeploymentVolumeBackup = schema
|
||||
.pick({
|
||||
title: true,
|
||||
status: true,
|
||||
logPath: true,
|
||||
description: true,
|
||||
})
|
||||
.extend({
|
||||
volumeBackupId: z.string().min(1),
|
||||
});
|
||||
|
||||
export const apiFindAllByApplication = schema
|
||||
.pick({
|
||||
applicationId: true,
|
||||
|
||||
@@ -13,6 +13,7 @@ import { postgres } from "./postgres";
|
||||
import { mariadb } from "./mariadb";
|
||||
import { destinations } from "./destination";
|
||||
import { deployments } from "./deployment";
|
||||
import { generateAppName } from "./utils";
|
||||
|
||||
export const volumeBackups = pgTable("volume_backup", {
|
||||
volumeBackupId: text("volumeBackupId")
|
||||
@@ -23,6 +24,9 @@ export const volumeBackups = pgTable("volume_backup", {
|
||||
volumeName: text("volumeName").notNull(),
|
||||
prefix: text("prefix").notNull(),
|
||||
serviceType: serviceType("serviceType").notNull().default("application"),
|
||||
appName: text("appName")
|
||||
.notNull()
|
||||
.$defaultFn(() => generateAppName("volumeBackup")),
|
||||
serviceName: text("serviceName"),
|
||||
turnOff: boolean("turnOff").notNull().default(false),
|
||||
cronExpression: text("cronExpression").notNull(),
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
type apiCreateDeploymentPreview,
|
||||
type apiCreateDeploymentSchedule,
|
||||
type apiCreateDeploymentServer,
|
||||
type apiCreateDeploymentVolumeBackup,
|
||||
deployments,
|
||||
} from "@dokploy/server/db/schema";
|
||||
import { removeDirectoryIfExistsContent } from "@dokploy/server/utils/filesystem/directory";
|
||||
@@ -32,6 +33,7 @@ import {
|
||||
} from "./preview-deployment";
|
||||
import { findScheduleById } from "./schedule";
|
||||
import { removeRollbackById } from "./rollbacks";
|
||||
import { findVolumeBackupById } from "./volume-backups";
|
||||
|
||||
export type Deployment = typeof deployments.$inferSelect;
|
||||
|
||||
@@ -458,6 +460,88 @@ export const createDeploymentSchedule = async (
|
||||
}
|
||||
};
|
||||
|
||||
export const createDeploymentVolumeBackup = async (
|
||||
deployment: Omit<
|
||||
typeof apiCreateDeploymentVolumeBackup._type,
|
||||
"deploymentId" | "createdAt" | "status" | "logPath"
|
||||
>,
|
||||
) => {
|
||||
const volumeBackup = await findVolumeBackupById(deployment.volumeBackupId);
|
||||
|
||||
try {
|
||||
const serverId =
|
||||
volumeBackup.application?.serverId || volumeBackup.compose?.serverId;
|
||||
await removeLastTenDeployments(
|
||||
deployment.volumeBackupId,
|
||||
"volumeBackup",
|
||||
serverId,
|
||||
);
|
||||
const { SCHEDULES_PATH } = paths(!!serverId);
|
||||
const formattedDateTime = format(new Date(), "yyyy-MM-dd:HH:mm:ss");
|
||||
const fileName = `${volumeBackup.appName}-${formattedDateTime}.log`;
|
||||
const logFilePath = path.join(
|
||||
SCHEDULES_PATH,
|
||||
volumeBackup.appName,
|
||||
fileName,
|
||||
);
|
||||
|
||||
if (serverId) {
|
||||
const server = await findServerById(serverId);
|
||||
|
||||
const command = `
|
||||
mkdir -p ${SCHEDULES_PATH}/${volumeBackup.appName};
|
||||
echo "Initializing volume backup" >> ${logFilePath};
|
||||
`;
|
||||
|
||||
await execAsyncRemote(server.serverId, command);
|
||||
} else {
|
||||
await fsPromises.mkdir(path.join(SCHEDULES_PATH, volumeBackup.appName), {
|
||||
recursive: true,
|
||||
});
|
||||
await fsPromises.writeFile(logFilePath, "Initializing volume backup\n");
|
||||
}
|
||||
|
||||
const deploymentCreate = await db
|
||||
.insert(deployments)
|
||||
.values({
|
||||
volumeBackupId: deployment.volumeBackupId,
|
||||
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({
|
||||
volumeBackupId: deployment.volumeBackupId,
|
||||
title: deployment.title || "Deployment",
|
||||
status: "error",
|
||||
logPath: "",
|
||||
description: deployment.description || "",
|
||||
errorMessage: `An error have occured: ${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 deployment",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const removeDeployment = async (deploymentId: string) => {
|
||||
try {
|
||||
const deployment = await db
|
||||
@@ -492,7 +576,8 @@ const getDeploymentsByType = async (
|
||||
| "server"
|
||||
| "schedule"
|
||||
| "previewDeployment"
|
||||
| "backup",
|
||||
| "backup"
|
||||
| "volumeBackup",
|
||||
) => {
|
||||
const deploymentList = await db.query.deployments.findMany({
|
||||
where: eq(deployments[`${type}Id`], id),
|
||||
@@ -524,7 +609,8 @@ const removeLastTenDeployments = async (
|
||||
| "server"
|
||||
| "schedule"
|
||||
| "previewDeployment"
|
||||
| "backup",
|
||||
| "backup"
|
||||
| "volumeBackup",
|
||||
serverId?: string | null,
|
||||
) => {
|
||||
const deploymentList = await getDeploymentsByType(id, type);
|
||||
|
||||
@@ -11,6 +11,15 @@ import type { z } from "zod";
|
||||
export const findVolumeBackupById = async (volumeBackupId: string) => {
|
||||
const volumeBackup = await db.query.volumeBackups.findFirst({
|
||||
where: eq(volumeBackups.volumeBackupId, volumeBackupId),
|
||||
with: {
|
||||
application: true,
|
||||
postgres: true,
|
||||
mysql: true,
|
||||
mariadb: true,
|
||||
mongo: true,
|
||||
redis: true,
|
||||
compose: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!volumeBackup) {
|
||||
|
||||
Reference in New Issue
Block a user