diff --git a/README.md b/README.md index d192d6f75..e2969f76f 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,10 @@ For detailed documentation, visit [docs.dokploy.com](https://docs.dokploy.com). AmericanCloud + + Tolgee + + diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index ef14fd3b3..86aac4e4e 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -1,6 +1,6 @@ { "name": "dokploy", - "version": "v0.23.3", + "version": "v0.23.4", "private": true, "license": "Apache-2.0", "type": "module", diff --git a/apps/dokploy/server/api/routers/settings.ts b/apps/dokploy/server/api/routers/settings.ts index 149af7046..0d07bbea0 100644 --- a/apps/dokploy/server/api/routers/settings.ts +++ b/apps/dokploy/server/api/routers/settings.ts @@ -837,6 +837,14 @@ export const settingsRouter = createTRPCRouter({ getLogCleanupStatus: adminProcedure.query(async () => { return getLogCleanupStatus(); }), + + getDokployCloudIps: adminProcedure.query(async () => { + if (!IS_CLOUD) { + return []; + } + const ips = process.env.DOKPLOY_CLOUD_IPS?.split(","); + return ips; + }), }); export const getTraefikPorts = async (serverId?: string) => { diff --git a/packages/server/src/utils/backups/compose.ts b/packages/server/src/utils/backups/compose.ts index 82fb8828a..2fb808198 100644 --- a/packages/server/src/utils/backups/compose.ts +++ b/packages/server/src/utils/backups/compose.ts @@ -15,7 +15,7 @@ export const runComposeBackup = async ( ) => { const { projectId, name } = compose; const project = await findProjectById(projectId); - const { prefix } = backup; + const { prefix, databaseType } = backup; const destination = backup.destination; const backupFileName = `${new Date().toISOString()}.dump.gz`; const bucketDestination = `${normalizeS3Path(prefix)}${backupFileName}`; @@ -46,9 +46,10 @@ export const runComposeBackup = async ( await sendDatabaseBackupNotifications({ applicationName: name, projectName: project.name, - databaseType: "mongodb", + databaseType: getDatabaseType(databaseType), type: "success", organizationId: project.organizationId, + databaseName: backup.database, }); await updateDeploymentStatus(deployment.deploymentId, "done"); @@ -57,14 +58,31 @@ export const runComposeBackup = async ( await sendDatabaseBackupNotifications({ applicationName: name, projectName: project.name, - databaseType: "mongodb", + databaseType: getDatabaseType(databaseType), type: "error", // @ts-ignore errorMessage: error?.message || "Error message not provided", organizationId: project.organizationId, + databaseName: backup.database, }); await updateDeploymentStatus(deployment.deploymentId, "error"); throw error; } }; + +const getDatabaseType = (databaseType: BackupSchedule["databaseType"]) => { + if (databaseType === "mongo") { + return "mongodb"; + } + if (databaseType === "postgres") { + return "postgres"; + } + if (databaseType === "mariadb") { + return "mariadb"; + } + if (databaseType === "mysql") { + return "mysql"; + } + return "mongodb"; +}; diff --git a/packages/server/src/utils/backups/mariadb.ts b/packages/server/src/utils/backups/mariadb.ts index 70bad1698..8760095c8 100644 --- a/packages/server/src/utils/backups/mariadb.ts +++ b/packages/server/src/utils/backups/mariadb.ts @@ -48,6 +48,7 @@ export const runMariadbBackup = async ( databaseType: "mariadb", type: "success", organizationId: project.organizationId, + databaseName: backup.database, }); await updateDeploymentStatus(deployment.deploymentId, "done"); } catch (error) { @@ -60,6 +61,7 @@ export const runMariadbBackup = async ( // @ts-ignore errorMessage: error?.message || "Error message not provided", organizationId: project.organizationId, + databaseName: backup.database, }); await updateDeploymentStatus(deployment.deploymentId, "error"); throw error; diff --git a/packages/server/src/utils/backups/mongo.ts b/packages/server/src/utils/backups/mongo.ts index 806fd5c0f..e626efa01 100644 --- a/packages/server/src/utils/backups/mongo.ts +++ b/packages/server/src/utils/backups/mongo.ts @@ -46,6 +46,7 @@ export const runMongoBackup = async (mongo: Mongo, backup: BackupSchedule) => { databaseType: "mongodb", type: "success", organizationId: project.organizationId, + databaseName: backup.database, }); await updateDeploymentStatus(deployment.deploymentId, "done"); } catch (error) { @@ -58,6 +59,7 @@ export const runMongoBackup = async (mongo: Mongo, backup: BackupSchedule) => { // @ts-ignore errorMessage: error?.message || "Error message not provided", organizationId: project.organizationId, + databaseName: backup.database, }); await updateDeploymentStatus(deployment.deploymentId, "error"); throw error; diff --git a/packages/server/src/utils/backups/mysql.ts b/packages/server/src/utils/backups/mysql.ts index 2ba8a0e12..6f6678421 100644 --- a/packages/server/src/utils/backups/mysql.ts +++ b/packages/server/src/utils/backups/mysql.ts @@ -47,6 +47,7 @@ export const runMySqlBackup = async (mysql: MySql, backup: BackupSchedule) => { databaseType: "mysql", type: "success", organizationId: project.organizationId, + databaseName: backup.database, }); await updateDeploymentStatus(deployment.deploymentId, "done"); } catch (error) { @@ -59,6 +60,7 @@ export const runMySqlBackup = async (mysql: MySql, backup: BackupSchedule) => { // @ts-ignore errorMessage: error?.message || "Error message not provided", organizationId: project.organizationId, + databaseName: backup.database, }); await updateDeploymentStatus(deployment.deploymentId, "error"); throw error; diff --git a/packages/server/src/utils/backups/postgres.ts b/packages/server/src/utils/backups/postgres.ts index 9ce2025a9..1f7b6a2d2 100644 --- a/packages/server/src/utils/backups/postgres.ts +++ b/packages/server/src/utils/backups/postgres.ts @@ -50,6 +50,7 @@ export const runPostgresBackup = async ( databaseType: "postgres", type: "success", organizationId: project.organizationId, + databaseName: backup.database, }); await updateDeploymentStatus(deployment.deploymentId, "done"); @@ -62,6 +63,7 @@ export const runPostgresBackup = async ( // @ts-ignore errorMessage: error?.message || "Error message not provided", organizationId: project.organizationId, + databaseName: backup.database, }); await updateDeploymentStatus(deployment.deploymentId, "error"); diff --git a/packages/server/src/utils/builders/index.ts b/packages/server/src/utils/builders/index.ts index 198b953d7..4f8dfa2f3 100644 --- a/packages/server/src/utils/builders/index.ts +++ b/packages/server/src/utils/builders/index.ts @@ -1,5 +1,4 @@ import { createWriteStream } from "node:fs"; -import { join } from "node:path"; import type { InferResultType } from "@dokploy/server/types/with"; import type { CreateServiceOptions } from "dockerode"; import { uploadImage, uploadImageRemoteCommand } from "../cluster/upload"; @@ -211,16 +210,20 @@ export const mechanizeDockerContainer = async ( const getImageName = (application: ApplicationNested) => { const { appName, sourceType, dockerImage, registry } = application; - + const imageName = `${appName}:latest`; if (sourceType === "docker") { return dockerImage || "ERROR-NO-IMAGE-PROVIDED"; } if (registry) { - return join(registry.registryUrl, registry.imagePrefix || "", appName); + const { registryUrl, imagePrefix, username } = registry; + const registryTag = imagePrefix + ? `${registryUrl}/${imagePrefix}/${imageName}` + : `${registryUrl}/${username}/${imageName}`; + return registryTag; } - return `${appName}:latest`; + return imageName; }; const getAuthConfig = (application: ApplicationNested) => { diff --git a/packages/server/src/utils/cluster/upload.ts b/packages/server/src/utils/cluster/upload.ts index 980ace67c..f982d414b 100644 --- a/packages/server/src/utils/cluster/upload.ts +++ b/packages/server/src/utils/cluster/upload.ts @@ -1,5 +1,4 @@ import type { WriteStream } from "node:fs"; -import path, { join } from "node:path"; import type { ApplicationNested } from "../builders"; import { spawnAsync } from "../process/spawnAsync"; @@ -13,19 +12,22 @@ export const uploadImage = async ( throw new Error("Registry not found"); } - const { registryUrl, imagePrefix } = registry; + const { registryUrl, imagePrefix, username } = registry; const { appName } = application; const imageName = `${appName}:latest`; const finalURL = registryUrl; - const registryTag = path - .join(registryUrl, join(imagePrefix || "", imageName)) - .replace(/\/+/g, "/"); + // Build registry tag in correct format: registry.com/owner/image:tag + // For ghcr.io: ghcr.io/username/image:tag + // For docker.io: docker.io/username/image:tag + const registryTag = imagePrefix + ? `${registryUrl}/${imagePrefix}/${imageName}` + : `${registryUrl}/${username}/${imageName}`; try { writeStream.write( - `📦 [Enabled Registry] Uploading image to ${registry.registryType} | ${imageName} | ${finalURL}\n`, + `📦 [Enabled Registry] Uploading image to ${registry.registryType} | ${imageName} | ${finalURL} | ${registryTag}\n`, ); const loginCommand = spawnAsync( "docker", @@ -67,15 +69,16 @@ export const uploadImageRemoteCommand = ( throw new Error("Registry not found"); } - const { registryUrl, imagePrefix } = registry; + const { registryUrl, imagePrefix, username } = registry; const { appName } = application; const imageName = `${appName}:latest`; const finalURL = registryUrl; - const registryTag = path - .join(registryUrl, join(imagePrefix || "", imageName)) - .replace(/\/+/g, "/"); + // Build registry tag in correct format: registry.com/owner/image:tag + const registryTag = imagePrefix + ? `${registryUrl}/${imagePrefix}/${imageName}` + : `${registryUrl}/${username}/${imageName}`; try { const command = ` diff --git a/packages/server/src/utils/notifications/database-backup.ts b/packages/server/src/utils/notifications/database-backup.ts index 37a4a1ff2..044e3a0cf 100644 --- a/packages/server/src/utils/notifications/database-backup.ts +++ b/packages/server/src/utils/notifications/database-backup.ts @@ -19,6 +19,7 @@ export const sendDatabaseBackupNotifications = async ({ type, errorMessage, organizationId, + databaseName, }: { projectName: string; applicationName: string; @@ -26,6 +27,7 @@ export const sendDatabaseBackupNotifications = async ({ type: "error" | "success"; organizationId: string; errorMessage?: string; + databaseName: string; }) => { const date = new Date(); const unixDate = ~~(Number(date) / 1000); @@ -90,6 +92,11 @@ export const sendDatabaseBackupNotifications = async ({ value: databaseType, inline: true, }, + { + name: decorate("`📂`", "Database Name"), + value: databaseName, + inline: true, + }, { name: decorate("`📅`", "Date"), value: ``, @@ -136,6 +143,7 @@ export const sendDatabaseBackupNotifications = async ({ `${decorate("🛠️", `Project: ${projectName}`)}` + `${decorate("⚙️", `Application: ${applicationName}`)}` + `${decorate("❔", `Type: ${databaseType}`)}` + + `${decorate("📂", `Database Name: ${databaseName}`)}` + `${decorate("🕒", `Date: ${date.toLocaleString()}`)}` + `${type === "error" && errorMessage ? decorate("❌", `Error:\n${errorMessage}`) : ""}`, ); @@ -150,7 +158,7 @@ export const sendDatabaseBackupNotifications = async ({ ? `\n\nError:\n
${errorMessage}
` : ""; - const messageText = `${statusEmoji} Database Backup ${typeStatus}\n\nProject: ${projectName}\nApplication: ${applicationName}\nType: ${databaseType}\nDate: ${format(date, "PP")}\nTime: ${format(date, "pp")}${isError ? errorMsg : ""}`; + const messageText = `${statusEmoji} Database Backup ${typeStatus}\n\nProject: ${projectName}\nApplication: ${applicationName}\nType: ${databaseType}\nDatabase Name: ${databaseName}\nDate: ${format(date, "PP")}\nTime: ${format(date, "pp")}${isError ? errorMsg : ""}`; await sendTelegramNotification(telegram, messageText); } @@ -191,6 +199,10 @@ export const sendDatabaseBackupNotifications = async ({ value: databaseType, short: true, }, + { + title: "Database Name", + value: databaseName, + }, { title: "Time", value: date.toLocaleString(),