mirror of
https://github.com/andwati/dokploy.git
synced 2026-11-13 15:35:17 +00: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:
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE "volume_backup" ADD COLUMN "appName" text NOT NULL;
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -750,6 +750,13 @@
|
|||||||
"when": 1751260111986,
|
"when": 1751260111986,
|
||||||
"tag": "0106_furry_gargoyle",
|
"tag": "0106_furry_gargoyle",
|
||||||
"breakpoints": true
|
"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),
|
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
|
export const apiFindAllByApplication = schema
|
||||||
.pick({
|
.pick({
|
||||||
applicationId: true,
|
applicationId: true,
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { postgres } from "./postgres";
|
|||||||
import { mariadb } from "./mariadb";
|
import { mariadb } from "./mariadb";
|
||||||
import { destinations } from "./destination";
|
import { destinations } from "./destination";
|
||||||
import { deployments } from "./deployment";
|
import { deployments } from "./deployment";
|
||||||
|
import { generateAppName } from "./utils";
|
||||||
|
|
||||||
export const volumeBackups = pgTable("volume_backup", {
|
export const volumeBackups = pgTable("volume_backup", {
|
||||||
volumeBackupId: text("volumeBackupId")
|
volumeBackupId: text("volumeBackupId")
|
||||||
@@ -23,6 +24,9 @@ export const volumeBackups = pgTable("volume_backup", {
|
|||||||
volumeName: text("volumeName").notNull(),
|
volumeName: text("volumeName").notNull(),
|
||||||
prefix: text("prefix").notNull(),
|
prefix: text("prefix").notNull(),
|
||||||
serviceType: serviceType("serviceType").notNull().default("application"),
|
serviceType: serviceType("serviceType").notNull().default("application"),
|
||||||
|
appName: text("appName")
|
||||||
|
.notNull()
|
||||||
|
.$defaultFn(() => generateAppName("volumeBackup")),
|
||||||
serviceName: text("serviceName"),
|
serviceName: text("serviceName"),
|
||||||
turnOff: boolean("turnOff").notNull().default(false),
|
turnOff: boolean("turnOff").notNull().default(false),
|
||||||
cronExpression: text("cronExpression").notNull(),
|
cronExpression: text("cronExpression").notNull(),
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
type apiCreateDeploymentPreview,
|
type apiCreateDeploymentPreview,
|
||||||
type apiCreateDeploymentSchedule,
|
type apiCreateDeploymentSchedule,
|
||||||
type apiCreateDeploymentServer,
|
type apiCreateDeploymentServer,
|
||||||
|
type apiCreateDeploymentVolumeBackup,
|
||||||
deployments,
|
deployments,
|
||||||
} from "@dokploy/server/db/schema";
|
} from "@dokploy/server/db/schema";
|
||||||
import { removeDirectoryIfExistsContent } from "@dokploy/server/utils/filesystem/directory";
|
import { removeDirectoryIfExistsContent } from "@dokploy/server/utils/filesystem/directory";
|
||||||
@@ -32,6 +33,7 @@ import {
|
|||||||
} from "./preview-deployment";
|
} from "./preview-deployment";
|
||||||
import { findScheduleById } from "./schedule";
|
import { findScheduleById } from "./schedule";
|
||||||
import { removeRollbackById } from "./rollbacks";
|
import { removeRollbackById } from "./rollbacks";
|
||||||
|
import { findVolumeBackupById } from "./volume-backups";
|
||||||
|
|
||||||
export type Deployment = typeof deployments.$inferSelect;
|
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) => {
|
export const removeDeployment = async (deploymentId: string) => {
|
||||||
try {
|
try {
|
||||||
const deployment = await db
|
const deployment = await db
|
||||||
@@ -492,7 +576,8 @@ const getDeploymentsByType = async (
|
|||||||
| "server"
|
| "server"
|
||||||
| "schedule"
|
| "schedule"
|
||||||
| "previewDeployment"
|
| "previewDeployment"
|
||||||
| "backup",
|
| "backup"
|
||||||
|
| "volumeBackup",
|
||||||
) => {
|
) => {
|
||||||
const deploymentList = await db.query.deployments.findMany({
|
const deploymentList = await db.query.deployments.findMany({
|
||||||
where: eq(deployments[`${type}Id`], id),
|
where: eq(deployments[`${type}Id`], id),
|
||||||
@@ -524,7 +609,8 @@ const removeLastTenDeployments = async (
|
|||||||
| "server"
|
| "server"
|
||||||
| "schedule"
|
| "schedule"
|
||||||
| "previewDeployment"
|
| "previewDeployment"
|
||||||
| "backup",
|
| "backup"
|
||||||
|
| "volumeBackup",
|
||||||
serverId?: string | null,
|
serverId?: string | null,
|
||||||
) => {
|
) => {
|
||||||
const deploymentList = await getDeploymentsByType(id, type);
|
const deploymentList = await getDeploymentsByType(id, type);
|
||||||
|
|||||||
@@ -11,6 +11,15 @@ import type { z } from "zod";
|
|||||||
export const findVolumeBackupById = async (volumeBackupId: string) => {
|
export const findVolumeBackupById = async (volumeBackupId: string) => {
|
||||||
const volumeBackup = await db.query.volumeBackups.findFirst({
|
const volumeBackup = await db.query.volumeBackups.findFirst({
|
||||||
where: eq(volumeBackups.volumeBackupId, volumeBackupId),
|
where: eq(volumeBackups.volumeBackupId, volumeBackupId),
|
||||||
|
with: {
|
||||||
|
application: true,
|
||||||
|
postgres: true,
|
||||||
|
mysql: true,
|
||||||
|
mariadb: true,
|
||||||
|
mongo: true,
|
||||||
|
redis: true,
|
||||||
|
compose: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!volumeBackup) {
|
if (!volumeBackup) {
|
||||||
|
|||||||
Reference in New Issue
Block a user