Update drizzle-zod dependency and enhance deployment tracking features

- Downgraded the drizzle-zod package from version 0.7.1 to 0.5.1 for compatibility.
- Added new fields `startedAt` and `finishedAt` to the deployment schema to track deployment timing.
- Enhanced deployment creation logic to set `startedAt` and `finishedAt` timestamps during deployment processes.
- Improved the ShowDeployments and ShowSchedulesLogs components to display deployment duration using a new Badge component.
- Refactored deployment removal logic to streamline the process of cleaning up old deployments.
This commit is contained in:
Mauricio Siu
2025-05-03 00:12:49 -06:00
parent 43ab1aa7b8
commit a5fb5532fd
12 changed files with 5763 additions and 159 deletions

View File

@@ -47,6 +47,8 @@ export const deployments = pgTable("deployment", {
createdAt: text("createdAt")
.notNull()
.$defaultFn(() => new Date().toISOString()),
startedAt: text("startedAt"),
finishedAt: text("finishedAt"),
errorMessage: text("errorMessage"),
scheduleId: text("scheduleId").references(
(): AnyPgColumn => schedules.scheduleId,

View File

@@ -1,6 +1,6 @@
import { relations } from "drizzle-orm";
import { boolean, pgEnum, pgTable, text } from "drizzle-orm/pg-core";
import { createInsertSchema, createUpdateSchema } from "drizzle-zod";
import { createInsertSchema } from "drizzle-zod";
import { nanoid } from "nanoid";
import { z } from "zod";
import { applications } from "./application";
@@ -78,6 +78,6 @@ export const schedulesRelations = relations(schedules, ({ one, many }) => ({
export const createScheduleSchema = createInsertSchema(schedules);
export const updateScheduleSchema = createUpdateSchema(schedules).extend({
export const updateScheduleSchema = createScheduleSchema.extend({
scheduleId: z.string().min(1),
});

View File

@@ -59,6 +59,7 @@ export const createDeployment = async (
try {
await removeLastTenDeployments(
deployment.applicationId,
"application",
application.serverId,
);
const { LOGS_PATH } = paths(!!application.serverId);
@@ -90,6 +91,7 @@ export const createDeployment = async (
status: "running",
logPath: logFilePath,
description: deployment.description || "",
startedAt: new Date().toISOString(),
})
.returning();
if (deploymentCreate.length === 0 || !deploymentCreate[0]) {
@@ -109,6 +111,8 @@ export const createDeployment = async (
logPath: "",
description: deployment.description || "",
errorMessage: `An error have occured: ${error instanceof Error ? error.message : error}`,
startedAt: new Date().toISOString(),
finishedAt: new Date().toISOString(),
})
.returning();
await updateApplicationStatus(application.applicationId, "error");
@@ -130,8 +134,9 @@ export const createDeploymentPreview = async (
deployment.previewDeploymentId,
);
try {
await removeLastTenPreviewDeploymenById(
await removeLastTenDeployments(
deployment.previewDeploymentId,
"previewDeployment",
previewDeployment?.application?.serverId,
);
@@ -167,6 +172,7 @@ export const createDeploymentPreview = async (
logPath: logFilePath,
description: deployment.description || "",
previewDeploymentId: deployment.previewDeploymentId,
startedAt: new Date().toISOString(),
})
.returning();
if (deploymentCreate.length === 0 || !deploymentCreate[0]) {
@@ -186,6 +192,8 @@ export const createDeploymentPreview = async (
logPath: "",
description: deployment.description || "",
errorMessage: `An error have occured: ${error instanceof Error ? error.message : error}`,
startedAt: new Date().toISOString(),
finishedAt: new Date().toISOString(),
})
.returning();
await updatePreviewDeployment(deployment.previewDeploymentId, {
@@ -207,8 +215,9 @@ export const createDeploymentCompose = async (
) => {
const compose = await findComposeById(deployment.composeId);
try {
await removeLastTenComposeDeployments(
await removeLastTenDeployments(
deployment.composeId,
"compose",
compose.serverId,
);
const { LOGS_PATH } = paths(!!compose.serverId);
@@ -240,6 +249,7 @@ echo "Initializing deployment" >> ${logFilePath};
description: deployment.description || "",
status: "running",
logPath: logFilePath,
startedAt: new Date().toISOString(),
})
.returning();
if (deploymentCreate.length === 0 || !deploymentCreate[0]) {
@@ -259,6 +269,8 @@ echo "Initializing deployment" >> ${logFilePath};
logPath: "",
description: deployment.description || "",
errorMessage: `An error have occured: ${error instanceof Error ? error.message : error}`,
startedAt: new Date().toISOString(),
finishedAt: new Date().toISOString(),
})
.returning();
await updateCompose(compose.composeId, {
@@ -285,14 +297,13 @@ export const createDeploymentSchedule = async (
schedule.application?.serverId ||
schedule.compose?.serverId ||
schedule.server?.serverId;
await removeDeploymentsSchedule(deployment.scheduleId, serverId);
await removeLastTenDeployments(deployment.scheduleId, "schedule", serverId);
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) {
console.log("serverId", serverId);
const server = await findServerById(serverId);
const command = `
@@ -316,6 +327,7 @@ export const createDeploymentSchedule = async (
status: "running",
logPath: logFilePath,
description: deployment.description || "",
startedAt: new Date().toISOString(),
})
.returning();
if (deploymentCreate.length === 0 || !deploymentCreate[0]) {
@@ -336,6 +348,8 @@ export const createDeploymentSchedule = async (
logPath: "",
description: deployment.description || "",
errorMessage: `An error have occured: ${error instanceof Error ? error.message : error}`,
startedAt: new Date().toISOString(),
finishedAt: new Date().toISOString(),
})
.returning();
@@ -372,144 +386,15 @@ export const removeDeploymentsByApplicationId = async (
.returning();
};
const removeLastTenDeployments = async (
applicationId: string,
serverId: string | null,
const getDeploymentsByType = async (
id: string,
type: "application" | "compose" | "server" | "schedule" | "previewDeployment",
) => {
const deploymentList = await db.query.deployments.findMany({
where: eq(deployments.applicationId, applicationId),
where: eq(deployments[`${type}Id`], id),
orderBy: desc(deployments.createdAt),
});
if (deploymentList.length > 10) {
const deploymentsToDelete = deploymentList.slice(9);
if (serverId) {
let command = "";
for (const oldDeployment of deploymentsToDelete) {
const logPath = path.join(oldDeployment.logPath);
command += `
rm -rf ${logPath};
`;
await removeDeployment(oldDeployment.deploymentId);
}
await execAsyncRemote(serverId, command);
} else {
for (const oldDeployment of deploymentsToDelete) {
const logPath = path.join(oldDeployment.logPath);
if (existsSync(logPath)) {
await fsPromises.unlink(logPath);
}
await removeDeployment(oldDeployment.deploymentId);
}
}
}
};
const removeLastTenComposeDeployments = async (
composeId: string,
serverId: string | null,
) => {
const deploymentList = await db.query.deployments.findMany({
where: eq(deployments.composeId, composeId),
orderBy: desc(deployments.createdAt),
});
if (deploymentList.length > 10) {
if (serverId) {
let command = "";
const deploymentsToDelete = deploymentList.slice(9);
for (const oldDeployment of deploymentsToDelete) {
const logPath = path.join(oldDeployment.logPath);
command += `
rm -rf ${logPath};
`;
await removeDeployment(oldDeployment.deploymentId);
}
await execAsyncRemote(serverId, command);
} else {
const deploymentsToDelete = deploymentList.slice(9);
for (const oldDeployment of deploymentsToDelete) {
const logPath = path.join(oldDeployment.logPath);
if (existsSync(logPath)) {
await fsPromises.unlink(logPath);
}
await removeDeployment(oldDeployment.deploymentId);
}
}
}
};
export const removeLastTenPreviewDeploymenById = async (
previewDeploymentId: string,
serverId: string | null,
) => {
const deploymentList = await db.query.deployments.findMany({
where: eq(deployments.previewDeploymentId, previewDeploymentId),
orderBy: desc(deployments.createdAt),
});
if (deploymentList.length > 10) {
const deploymentsToDelete = deploymentList.slice(9);
if (serverId) {
let command = "";
for (const oldDeployment of deploymentsToDelete) {
const logPath = path.join(oldDeployment.logPath);
command += `
rm -rf ${logPath};
`;
await removeDeployment(oldDeployment.deploymentId);
}
await execAsyncRemote(serverId, command);
} else {
for (const oldDeployment of deploymentsToDelete) {
const logPath = path.join(oldDeployment.logPath);
if (existsSync(logPath)) {
await fsPromises.unlink(logPath);
}
await removeDeployment(oldDeployment.deploymentId);
}
}
}
};
export const removeDeploymentsSchedule = async (
scheduleId: string,
serverId?: string | null,
) => {
const deploymentList = await db.query.deployments.findMany({
where: eq(deployments.scheduleId, scheduleId),
orderBy: desc(deployments.createdAt),
});
if (deploymentList.length > 10) {
const deploymentsToDelete = deploymentList.slice(9);
if (serverId) {
let command = "";
for (const oldDeployment of deploymentsToDelete) {
const logPath = path.join(oldDeployment.logPath);
command += `
rm -rf ${logPath};
`;
await removeDeployment(oldDeployment.deploymentId);
}
await execAsyncRemote(serverId, command);
} else {
for (const oldDeployment of deploymentsToDelete) {
const logPath = path.join(oldDeployment.logPath);
if (existsSync(logPath)) {
await fsPromises.unlink(logPath);
}
await removeDeployment(oldDeployment.deploymentId);
}
}
}
return deploymentList;
};
export const removeDeployments = async (application: Application) => {
@@ -524,6 +409,38 @@ export const removeDeployments = async (application: Application) => {
await removeDeploymentsByApplicationId(applicationId);
};
const removeLastTenDeployments = async (
id: string,
type: "application" | "compose" | "server" | "schedule" | "previewDeployment",
serverId?: string | null,
) => {
const deploymentList = await getDeploymentsByType(id, type);
if (deploymentList.length > 10) {
const deploymentsToDelete = deploymentList.slice(10);
if (serverId) {
let command = "";
for (const oldDeployment of deploymentsToDelete) {
const logPath = path.join(oldDeployment.logPath);
command += `
rm -rf ${logPath};
`;
await removeDeployment(oldDeployment.deploymentId);
}
await execAsyncRemote(serverId, command);
} else {
for (const oldDeployment of deploymentsToDelete) {
const logPath = path.join(oldDeployment.logPath);
if (existsSync(logPath)) {
await fsPromises.unlink(logPath);
}
await removeDeployment(oldDeployment.deploymentId);
}
}
}
};
export const removeDeploymentsByPreviewDeploymentId = async (
previewDeployment: PreviewDeployment,
serverId: string | null,
@@ -605,6 +522,10 @@ export const updateDeploymentStatus = async (
.update(deployments)
.set({
status: deploymentStatus,
finishedAt:
deploymentStatus === "done" || deploymentStatus === "error"
? new Date().toISOString()
: null,
})
.where(eq(deployments.deploymentId, deploymentId))
.returning();