Revert "Merge branch 'canary' into kucherenko/canary"

This reverts commit 819822f30b, reversing
changes made to bda9b05134.
This commit is contained in:
Mauricio Siu
2025-03-02 00:26:59 -06:00
parent 819822f30b
commit e6cb6454db
639 changed files with 17202 additions and 82902 deletions

View File

@@ -1,8 +1,8 @@
import { IS_CLOUD, paths } from "@dokploy/server/constants";
import { updateAdmin } from "@dokploy/server/services/admin";
import { type RotatingFileStream, createStream } from "rotating-file-stream";
import { db } from "../../db";
import { execAsync } from "../process/execAsync";
import { findAdmin } from "@dokploy/server/services/admin";
import { updateUser } from "@dokploy/server/services/user";
class LogRotationManager {
private static instance: LogRotationManager;
@@ -30,16 +30,17 @@ class LogRotationManager {
}
private async getStateFromDB(): Promise<boolean> {
const admin = await findAdmin();
return admin?.user.enableLogRotation ?? false;
const setting = await db.query.admins.findFirst({});
return setting?.enableLogRotation ?? false;
}
private async setStateInDB(active: boolean): Promise<void> {
const admin = await findAdmin();
const admin = await db.query.admins.findFirst({});
if (!admin) {
return;
}
await updateUser(admin.user.id, {
await updateAdmin(admin?.authId, {
enableLogRotation: active,
});
}

View File

@@ -1,3 +1,4 @@
import { findAdmin } from "@dokploy/server/services/admin";
import { getAllServers } from "@dokploy/server/services/server";
import { scheduleJob } from "node-schedule";
import { db } from "../../db/index";
@@ -11,14 +12,13 @@ import { runMariadbBackup } from "./mariadb";
import { runMongoBackup } from "./mongo";
import { runMySqlBackup } from "./mysql";
import { runPostgresBackup } from "./postgres";
import { findAdmin } from "../../services/admin";
export const initCronJobs = async () => {
console.log("Setting up cron jobs....");
const admin = await findAdmin();
if (admin?.user.enableDockerCleanup) {
if (admin?.enableDockerCleanup) {
scheduleJob("docker-cleanup", "0 0 * * *", async () => {
console.log(
`Docker Cleanup ${new Date().toLocaleString()}] Running docker cleanup`,
@@ -26,7 +26,7 @@ export const initCronJobs = async () => {
await cleanUpUnusedImages();
await cleanUpDockerBuilder();
await cleanUpSystemPrune();
await sendDockerCleanupNotifications(admin.user.id);
await sendDockerCleanupNotifications(admin.adminId);
});
}
@@ -43,7 +43,7 @@ export const initCronJobs = async () => {
await cleanUpDockerBuilder(serverId);
await cleanUpSystemPrune(serverId);
await sendDockerCleanupNotifications(
admin.user.id,
admin.adminId,
`Docker cleanup for Server ${name} (${serverId})`,
);
});

View File

@@ -49,7 +49,7 @@ export const runMariadbBackup = async (
projectName: project.name,
databaseType: "mariadb",
type: "success",
organizationId: project.organizationId,
adminId: project.adminId,
});
} catch (error) {
console.log(error);
@@ -60,7 +60,7 @@ export const runMariadbBackup = async (
type: "error",
// @ts-ignore
errorMessage: error?.message || "Error message not provided",
organizationId: project.organizationId,
adminId: project.adminId,
});
throw error;
}

View File

@@ -46,7 +46,7 @@ export const runMongoBackup = async (mongo: Mongo, backup: BackupSchedule) => {
projectName: project.name,
databaseType: "mongodb",
type: "success",
organizationId: project.organizationId,
adminId: project.adminId,
});
} catch (error) {
console.log(error);
@@ -57,7 +57,7 @@ export const runMongoBackup = async (mongo: Mongo, backup: BackupSchedule) => {
type: "error",
// @ts-ignore
errorMessage: error?.message || "Error message not provided",
organizationId: project.organizationId,
adminId: project.adminId,
});
throw error;
}

View File

@@ -1,3 +1,4 @@
import { unlink } from "node:fs/promises";
import path from "node:path";
import type { BackupSchedule } from "@dokploy/server/services/backup";
import type { MySql } from "@dokploy/server/services/mysql";
@@ -45,7 +46,7 @@ export const runMySqlBackup = async (mysql: MySql, backup: BackupSchedule) => {
projectName: project.name,
databaseType: "mysql",
type: "success",
organizationId: project.organizationId,
adminId: project.adminId,
});
} catch (error) {
console.log(error);
@@ -56,7 +57,7 @@ export const runMySqlBackup = async (mysql: MySql, backup: BackupSchedule) => {
type: "error",
// @ts-ignore
errorMessage: error?.message || "Error message not provided",
organizationId: project.organizationId,
adminId: project.adminId,
});
throw error;
}

View File

@@ -49,7 +49,7 @@ export const runPostgresBackup = async (
projectName: project.name,
databaseType: "postgres",
type: "success",
organizationId: project.organizationId,
adminId: project.adminId,
});
} catch (error) {
await sendDatabaseBackupNotifications({
@@ -59,7 +59,7 @@ export const runPostgresBackup = async (
type: "error",
// @ts-ignore
errorMessage: error?.message || "Error message not provided",
organizationId: project.organizationId,
adminId: project.adminId,
});
throw error;

View File

@@ -28,7 +28,7 @@ export const removeScheduleBackup = (backupId: string) => {
};
export const getS3Credentials = (destination: Destination) => {
const { accessKey, secretAccessKey, region, endpoint, provider } =
const { accessKey, secretAccessKey, bucket, region, endpoint, provider } =
destination;
const rcloneFlags = [
`--s3-access-key-id=${accessKey}`,

View File

@@ -12,12 +12,8 @@ import {
writeDomainsToCompose,
writeDomainsToComposeRemote,
} from "../docker/domain";
import {
encodeBase64,
getEnviromentVariablesObject,
prepareEnvironmentVariables,
} from "../docker/utils";
import { execAsync, execAsyncRemote } from "../process/execAsync";
import { encodeBase64, prepareEnvironmentVariables } from "../docker/utils";
import { execAsyncRemote } from "../process/execAsync";
import { spawnAsync } from "../process/spawnAsync";
export type ComposeNested = InferResultType<
@@ -33,19 +29,13 @@ export const buildCompose = async (compose: ComposeNested, logPath: string) => {
await writeDomainsToCompose(compose, domains);
createEnvFile(compose);
if (compose.isolatedDeployment) {
await execAsync(
`docker network inspect ${compose.appName} >/dev/null 2>&1 || docker network create --attachable ${compose.appName}`,
);
}
const logContent = `
App Name: ${appName}
Build Compose 🐳
Detected: ${mounts.length} mounts 📂
Command: docker ${command}
Source Type: docker ${sourceType}
Compose Type: ${composeType}`;
App Name: ${appName}
Build Compose 🐳
Detected: ${mounts.length} mounts 📂
Command: docker ${command}
Source Type: docker ${sourceType}
Compose Type: ${composeType}`;
const logBox = boxen(logContent, {
padding: {
left: 1,
@@ -56,6 +46,7 @@ export const buildCompose = async (compose: ComposeNested, logPath: string) => {
borderStyle: "double",
});
writeStream.write(`\n${logBox}\n`);
const projectPath = join(COMPOSE_PATH, compose.appName, "code");
await spawnAsync(
@@ -71,19 +62,10 @@ export const buildCompose = async (compose: ComposeNested, logPath: string) => {
env: {
NODE_ENV: process.env.NODE_ENV,
PATH: process.env.PATH,
...(composeType === "stack" && {
...getEnviromentVariablesObject(compose.env, compose.project.env),
}),
},
},
);
if (compose.isolatedDeployment) {
await execAsync(
`docker network connect ${compose.appName} $(docker ps --filter "name=dokploy-traefik" -q) >/dev/null 2>&1`,
).catch(() => {});
}
writeStream.write("Docker Compose Deployed: ✅");
} catch (error) {
writeStream.write(`Error ❌ ${(error as Error).message}`);
@@ -98,11 +80,11 @@ export const getBuildComposeCommand = async (
logPath: string,
) => {
const { COMPOSE_PATH } = paths(true);
const { sourceType, appName, mounts, composeType, domains } = compose;
const { sourceType, appName, mounts, composeType, domains, composePath } =
compose;
const command = createCommand(compose);
const envCommand = getCreateEnvFileCommand(compose);
const projectPath = join(COMPOSE_PATH, compose.appName, "code");
const exportEnvCommand = getExportEnvCommand(compose);
const newCompose = await writeDomainsToComposeRemote(
compose,
@@ -138,10 +120,7 @@ Compose Type: ${composeType} ✅`;
cd "${projectPath}";
${exportEnvCommand}
${compose.isolatedDeployment ? `docker network inspect ${compose.appName} >/dev/null 2>&1 || docker network create --attachable ${compose.appName}` : ""}
docker ${command.split(" ").join(" ")} >> "${logPath}" 2>&1 || { echo "Error: ❌ Docker command failed" >> "${logPath}"; exit 1; }
${compose.isolatedDeployment ? `docker network connect ${compose.appName} $(docker ps --filter "name=dokploy-traefik" -q) >/dev/null 2>&1` : ""}
echo "Docker Compose Deployed: ✅" >> "${logPath}"
} || {
@@ -165,6 +144,7 @@ const sanitizeCommand = (command: string) => {
export const createCommand = (compose: ComposeNested) => {
const { composeType, appName, sourceType } = compose;
if (compose.command) {
return `${sanitizeCommand(compose.command)}`;
}
@@ -239,17 +219,3 @@ touch ${envFilePath};
echo "${encodedContent}" | base64 -d > "${envFilePath}";
`;
};
const getExportEnvCommand = (compose: ComposeNested) => {
if (compose.composeType !== "stack") return "";
const envVars = getEnviromentVariablesObject(
compose.env,
compose.project.env,
);
const exports = Object.entries(envVars)
.map(([key, value]) => `export ${key}=${JSON.stringify(value)}`)
.join("\n");
return exports ? `\n# Export environment variables\n${exports}\n` : "";
};

View File

@@ -197,7 +197,7 @@ export const mechanizeDockerContainer = async (
ForceUpdate: inspect.Spec.TaskTemplate.ForceUpdate + 1,
},
});
} catch (_error) {
} catch (error) {
await docker.createService(settings);
}
};

View File

@@ -91,7 +91,7 @@ export const getNixpacksCommand = (
application: ApplicationNested,
logPath: string,
) => {
const { env, appName, publishDirectory } = application;
const { env, appName, publishDirectory, serverId } = application;
const buildAppDirectory = getBuildAppDirectory(application);
const buildContainerId = `${appName}-${nanoid(10)}`;

View File

@@ -98,7 +98,7 @@ export const buildMariadb = async (mariadb: MariadbNested) => {
version: Number.parseInt(inspect.Version.Index),
...settings,
});
} catch (_error) {
} catch (error) {
await docker.createService(settings);
}
};

View File

@@ -152,7 +152,7 @@ ${command ?? "wait $MONGOD_PID"}`;
version: Number.parseInt(inspect.Version.Index),
...settings,
});
} catch (_error) {
} catch (error) {
await docker.createService(settings);
}
};

View File

@@ -104,7 +104,7 @@ export const buildMysql = async (mysql: MysqlNested) => {
version: Number.parseInt(inspect.Version.Index),
...settings,
});
} catch (_error) {
} catch (error) {
await docker.createService(settings);
}
};

View File

@@ -95,7 +95,7 @@ export const buildRedis = async (redis: RedisNested) => {
version: Number.parseInt(inspect.Version.Index),
...settings,
});
} catch (_error) {
} catch (error) {
await docker.createService(settings);
}
};

View File

@@ -1,46 +0,0 @@
import { findComposeById } from "@dokploy/server/services/compose";
import { dump, load } from "js-yaml";
import { addAppNameToAllServiceNames } from "./collision/root-network";
import { generateRandomHash } from "./compose";
import { addSuffixToAllVolumes } from "./compose/volume";
import type { ComposeSpecification } from "./types";
export const addAppNameToPreventCollision = (
composeData: ComposeSpecification,
appName: string,
): ComposeSpecification => {
let updatedComposeData = { ...composeData };
updatedComposeData = addAppNameToAllServiceNames(updatedComposeData, appName);
updatedComposeData = addSuffixToAllVolumes(updatedComposeData, appName);
return updatedComposeData;
};
export const randomizeIsolatedDeploymentComposeFile = async (
composeId: string,
suffix?: string,
) => {
const compose = await findComposeById(composeId);
const composeFile = compose.composeFile;
const composeData = load(composeFile) as ComposeSpecification;
const randomSuffix = suffix || compose.appName || generateRandomHash();
const newComposeFile = addAppNameToPreventCollision(
composeData,
randomSuffix,
);
return dump(newComposeFile);
};
export const randomizeDeployableSpecificationFile = (
composeSpec: ComposeSpecification,
suffix?: string,
) => {
if (!suffix) {
return composeSpec;
}
const newComposeFile = addAppNameToPreventCollision(composeSpec, suffix);
return newComposeFile;
};

View File

@@ -1,62 +0,0 @@
import _ from "lodash";
import type { ComposeSpecification, DefinitionsService } from "../types";
export const addAppNameToRootNetwork = (
composeData: ComposeSpecification,
appName: string,
): ComposeSpecification => {
const updatedComposeData = { ...composeData };
// Initialize networks if it doesn't exist
if (!updatedComposeData.networks) {
updatedComposeData.networks = {};
}
// Add the new network with the app name
updatedComposeData.networks[appName] = {
name: appName,
external: true,
};
return updatedComposeData;
};
export const addAppNameToServiceNetworks = (
services: { [key: string]: DefinitionsService },
appName: string,
): { [key: string]: DefinitionsService } => {
return _.mapValues(services, (service) => {
if (!service.networks) {
service.networks = [appName];
return service;
}
if (Array.isArray(service.networks)) {
if (!service.networks.includes(appName)) {
service.networks.push(appName);
}
} else {
service.networks[appName] = {};
}
return service;
});
};
export const addAppNameToAllServiceNames = (
composeData: ComposeSpecification,
appName: string,
): ComposeSpecification => {
let updatedComposeData = { ...composeData };
updatedComposeData = addAppNameToRootNetwork(updatedComposeData, appName);
if (updatedComposeData.services) {
updatedComposeData.services = addAppNameToServiceNetworks(
updatedComposeData.services,
appName,
);
}
return updatedComposeData;
};

View File

@@ -26,7 +26,6 @@ import {
createComposeFileRaw,
createComposeFileRawRemote,
} from "../providers/raw";
import { randomizeDeployableSpecificationFile } from "./collision";
import { randomizeSpecificationFile } from "./compose";
import type {
ComposeSpecification,
@@ -109,7 +108,7 @@ export const loadDockerComposeRemote = async (
if (!stdout) return null;
const parsedConfig = load(stdout) as ComposeSpecification;
return parsedConfig;
} catch (_err) {
} catch (err) {
return null;
}
};
@@ -191,13 +190,7 @@ export const addDomainToCompose = async (
return null;
}
if (compose.isolatedDeployment) {
const randomized = randomizeDeployableSpecificationFile(
result,
compose.suffix || compose.appName,
);
result = randomized;
} else if (compose.randomize) {
if (compose.randomize) {
const randomized = randomizeSpecificationFile(result, compose.suffix);
result = randomized;
}
@@ -210,6 +203,9 @@ export const addDomainToCompose = async (
if (!result?.services?.[serviceName]) {
throw new Error(`The service ${serviceName} not found in the compose`);
}
if (!result.services[serviceName].labels) {
result.services[serviceName].labels = [];
}
const httpLabels = await createDomainLabels(appName, domain, "web");
if (https) {
@@ -221,24 +217,7 @@ export const addDomainToCompose = async (
httpLabels.push(...httpsLabels);
}
let labels: DefinitionsService["labels"] = [];
if (compose.composeType === "docker-compose") {
if (!result.services[serviceName].labels) {
result.services[serviceName].labels = [];
}
labels = result.services[serviceName].labels;
} else {
// Stack Case
if (!result.services[serviceName].deploy) {
result.services[serviceName].deploy = {};
}
if (!result.services[serviceName].deploy.labels) {
result.services[serviceName].deploy.labels = [];
}
labels = result.services[serviceName].deploy.labels;
}
const labels = result.services[serviceName].labels;
if (Array.isArray(labels)) {
if (!labels.includes("traefik.enable=true")) {
@@ -247,18 +226,14 @@ export const addDomainToCompose = async (
labels.push(...httpLabels);
}
if (!compose.isolatedDeployment) {
// Add the dokploy-network to the service
result.services[serviceName].networks = addDokployNetworkToService(
result.services[serviceName].networks,
);
}
// Add the dokploy-network to the service
result.services[serviceName].networks = addDokployNetworkToService(
result.services[serviceName].networks,
);
}
// Add dokploy-network to the root of the compose file
if (!compose.isolatedDeployment) {
result.networks = addDokployNetworkToRoot(result.networks);
}
result.networks = addDokployNetworkToRoot(result.networks);
return result;
};

View File

@@ -100,7 +100,7 @@ export const containerExists = async (containerName: string) => {
try {
await container.inspect();
return true;
} catch (_error) {
} catch (error) {
return false;
}
};
@@ -144,11 +144,10 @@ export const getContainerByName = (name: string): Promise<ContainerInfo> => {
};
export const cleanUpUnusedImages = async (serverId?: string) => {
try {
const command = "docker image prune --force";
if (serverId) {
await execAsyncRemote(serverId, command);
await execAsyncRemote(serverId, "docker image prune --all --force");
} else {
await execAsync(command);
await execAsync("docker image prune --all --force");
}
} catch (error) {
console.error(error);
@@ -158,11 +157,10 @@ export const cleanUpUnusedImages = async (serverId?: string) => {
export const cleanStoppedContainers = async (serverId?: string) => {
try {
const command = "docker container prune --force";
if (serverId) {
await execAsyncRemote(serverId, command);
await execAsyncRemote(serverId, "docker container prune --force");
} else {
await execAsync(command);
await execAsync("docker container prune --force");
}
} catch (error) {
console.error(error);
@@ -172,11 +170,10 @@ export const cleanStoppedContainers = async (serverId?: string) => {
export const cleanUpUnusedVolumes = async (serverId?: string) => {
try {
const command = "docker volume prune --force";
if (serverId) {
await execAsyncRemote(serverId, command);
await execAsyncRemote(serverId, "docker volume prune --all --force");
} else {
await execAsync(command);
await execAsync("docker volume prune --all --force");
}
} catch (error) {
console.error(error);
@@ -202,20 +199,21 @@ export const cleanUpInactiveContainers = async () => {
};
export const cleanUpDockerBuilder = async (serverId?: string) => {
const command = "docker builder prune --all --force";
if (serverId) {
await execAsyncRemote(serverId, command);
await execAsyncRemote(serverId, "docker builder prune --all --force");
} else {
await execAsync(command);
await execAsync("docker builder prune --all --force");
}
};
export const cleanUpSystemPrune = async (serverId?: string) => {
const command = "docker system prune --all --force --volumes";
if (serverId) {
await execAsyncRemote(serverId, command);
await execAsyncRemote(
serverId,
"docker system prune --all --force --volumes",
);
} else {
await execAsync(command);
await execAsync("docker system prune --all --force --volumes");
}
};
@@ -240,7 +238,7 @@ export const startServiceRemote = async (serverId: string, appName: string) => {
export const removeService = async (
appName: string,
serverId?: string | null,
_deleteVolumes = false,
deleteVolumes = false,
) => {
try {
const command = `docker service rm ${appName}`;
@@ -278,15 +276,12 @@ export const prepareEnvironmentVariables = (
return resolvedVars;
};
export const getEnviromentVariablesObject = (
input: string | null,
projectEnv?: string | null,
) => {
const envs = prepareEnvironmentVariables(input, projectEnv);
export const prepareBuildArgs = (input: string | null) => {
const pairs = (input ?? "").split("\n");
const jsonObject: Record<string, string> = {};
for (const pair of envs) {
for (const pair of pairs) {
const [key, value] = pair.split("=");
if (key && value) {
jsonObject[key] = value;

View File

@@ -34,7 +34,7 @@ export async function checkGPUStatus(serverId?: string): Promise<GPUInfo> {
...gpuInfo,
...cudaInfo,
};
} catch (_error) {
} catch (error) {
return {
driverInstalled: false,
driverVersion: undefined,
@@ -315,7 +315,7 @@ const setupLocalServer = async (daemonConfig: any) => {
try {
await execAsync(setupCommands);
} catch (_error) {
} catch (error) {
throw new Error(
"Failed to configure GPU support. Please ensure you have sudo privileges and try again.",
);

View File

@@ -18,7 +18,7 @@ interface Props {
applicationType: string;
errorMessage: string;
buildLink: string;
organizationId: string;
adminId: string;
}
export const sendBuildErrorNotifications = async ({
@@ -27,14 +27,14 @@ export const sendBuildErrorNotifications = async ({
applicationType,
errorMessage,
buildLink,
organizationId,
adminId,
}: Props) => {
const date = new Date();
const unixDate = ~~(Number(date) / 1000);
const notificationList = await db.query.notifications.findMany({
where: and(
eq(notifications.appBuildError, true),
eq(notifications.organizationId, organizationId),
eq(notifications.adminId, adminId),
),
with: {
email: true,

View File

@@ -18,7 +18,7 @@ interface Props {
applicationName: string;
applicationType: string;
buildLink: string;
organizationId: string;
adminId: string;
domains: Domain[];
}
@@ -27,7 +27,7 @@ export const sendBuildSuccessNotifications = async ({
applicationName,
applicationType,
buildLink,
organizationId,
adminId,
domains,
}: Props) => {
const date = new Date();
@@ -35,7 +35,7 @@ export const sendBuildSuccessNotifications = async ({
const notificationList = await db.query.notifications.findMany({
where: and(
eq(notifications.appDeploy, true),
eq(notifications.organizationId, organizationId),
eq(notifications.adminId, adminId),
),
with: {
email: true,

View File

@@ -1,3 +1,4 @@
import { error } from "node:console";
import { db } from "@dokploy/server/db";
import { notifications } from "@dokploy/server/db/schema";
import DatabaseBackupEmail from "@dokploy/server/emails/emails/database-backup";
@@ -18,13 +19,13 @@ export const sendDatabaseBackupNotifications = async ({
databaseType,
type,
errorMessage,
organizationId,
adminId,
}: {
projectName: string;
applicationName: string;
databaseType: "postgres" | "mysql" | "mongodb" | "mariadb";
type: "error" | "success";
organizationId: string;
adminId: string;
errorMessage?: string;
}) => {
const date = new Date();
@@ -32,7 +33,7 @@ export const sendDatabaseBackupNotifications = async ({
const notificationList = await db.query.notifications.findMany({
where: and(
eq(notifications.databaseBackup, true),
eq(notifications.organizationId, organizationId),
eq(notifications.adminId, adminId),
),
with: {
email: true,

View File

@@ -13,7 +13,7 @@ import {
} from "./utils";
export const sendDockerCleanupNotifications = async (
organizationId: string,
adminId: string,
message = "Docker cleanup for dokploy",
) => {
const date = new Date();
@@ -21,7 +21,7 @@ export const sendDockerCleanupNotifications = async (
const notificationList = await db.query.notifications.findMany({
where: and(
eq(notifications.dockerCleanup, true),
eq(notifications.organizationId, organizationId),
eq(notifications.adminId, adminId),
),
with: {
email: true,

View File

@@ -1,155 +0,0 @@
import { and, eq } from "drizzle-orm";
import { db } from "../../db";
import { notifications } from "../../db/schema";
import {
sendDiscordNotification,
sendSlackNotification,
sendTelegramNotification,
} from "./utils";
interface ServerThresholdPayload {
Type: "CPU" | "Memory";
Value: number;
Threshold: number;
Message: string;
Timestamp: string;
Token: string;
ServerName: string;
}
export const sendServerThresholdNotifications = async (
organizationId: string,
payload: ServerThresholdPayload,
) => {
const date = new Date(payload.Timestamp);
const unixDate = ~~(Number(date) / 1000);
const notificationList = await db.query.notifications.findMany({
where: and(
eq(notifications.serverThreshold, true),
eq(notifications.organizationId, organizationId),
),
with: {
email: true,
discord: true,
telegram: true,
slack: true,
},
});
const typeEmoji = payload.Type === "CPU" ? "🔲" : "💾";
const typeColor = 0xff0000; // Rojo para indicar alerta
for (const notification of notificationList) {
const { discord, telegram, slack } = notification;
if (discord) {
const decorate = (decoration: string, text: string) =>
`${discord.decoration ? decoration : ""} ${text}`.trim();
await sendDiscordNotification(discord, {
title: decorate(">", `\`⚠️\` Server ${payload.Type} Alert`),
color: typeColor,
fields: [
{
name: decorate("`🏷️`", "Server Name"),
value: payload.ServerName,
inline: true,
},
{
name: decorate("`📅`", "Date"),
value: `<t:${unixDate}:D>`,
inline: true,
},
{
name: decorate("`⌚`", "Time"),
value: `<t:${unixDate}:t>`,
inline: true,
},
{
name: decorate(typeEmoji, "Type"),
value: payload.Type,
inline: true,
},
{
name: decorate("📊", "Current Value"),
value: `${payload.Value.toFixed(2)}%`,
inline: true,
},
{
name: decorate("⚠️", "Threshold"),
value: `${payload.Threshold.toFixed(2)}%`,
inline: true,
},
{
name: decorate("`📜`", "Message"),
value: `\`\`\`${payload.Message}\`\`\``,
},
],
timestamp: date.toISOString(),
footer: {
text: "Dokploy Server Monitoring Alert",
},
});
}
if (telegram) {
await sendTelegramNotification(
telegram,
`
<b>⚠️ Server ${payload.Type} Alert</b>
<b>Server Name:</b> ${payload.ServerName}
<b>Type:</b> ${payload.Type}
<b>Current Value:</b> ${payload.Value.toFixed(2)}%
<b>Threshold:</b> ${payload.Threshold.toFixed(2)}%
<b>Message:</b> ${payload.Message}
<b>Time:</b> ${date.toLocaleString()}
`,
);
}
if (slack) {
const { channel } = slack;
await sendSlackNotification(slack, {
channel: channel,
attachments: [
{
color: "#FF0000",
pretext: `:warning: *Server ${payload.Type} Alert*`,
fields: [
{
title: "Server Name",
value: payload.ServerName,
short: true,
},
{
title: "Type",
value: payload.Type,
short: true,
},
{
title: "Current Value",
value: `${payload.Value.toFixed(2)}%`,
short: true,
},
{
title: "Threshold",
value: `${payload.Threshold.toFixed(2)}%`,
short: true,
},
{
title: "Message",
value: payload.Message,
},
{
title: "Time",
value: date.toLocaleString(),
short: true,
},
],
},
],
});
}
}
};

View File

@@ -68,7 +68,6 @@ export const sendTelegramNotification = async (
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
chat_id: connection.chatId,
message_thread_id: connection.messageThreadId,
text: messageText,
parse_mode: "HTML",
disable_web_page_preview: true,

View File

@@ -27,7 +27,7 @@ export const execAsyncRemote = async (
throw err;
}
stream
.on("close", (code: number, _signal: string) => {
.on("close", (code: number, signal: string) => {
conn.end();
if (code === 0) {
resolve({ stdout, stderr });

View File

@@ -176,6 +176,7 @@ export const getBitbucketCloneCommand = async (
bitbucketBranch,
bitbucketId,
serverId,
bitbucket,
} = entity;
if (!serverId) {

View File

@@ -69,7 +69,6 @@ export const cloneGitRepository = async (
});
}
const { port } = sanitizeRepoPathSSH(customGitUrl);
await spawnAsync(
"git",
[
@@ -92,7 +91,7 @@ export const cloneGitRepository = async (
env: {
...process.env,
...(customGitSSHKeyId && {
GIT_SSH_COMMAND: `ssh -i ${temporalKeyPath}${port ? ` -p ${port}` : ""} -o UserKnownHostsFile=${knownHostsPath}`,
GIT_SSH_COMMAND: `ssh -i ${temporalKeyPath} -o UserKnownHostsFile=${knownHostsPath}`,
}),
},
},
@@ -169,8 +168,7 @@ export const getCustomGitCloneCommand = async (
);
if (customGitSSHKeyId) {
const sshKey = await findSSHKeyById(customGitSSHKeyId);
const { port } = sanitizeRepoPathSSH(customGitUrl);
const gitSshCommand = `ssh -i /tmp/id_rsa${port ? ` -p ${port}` : ""} -o UserKnownHostsFile=${knownHostsPath}`;
const gitSshCommand = `ssh -i /tmp/id_rsa -o UserKnownHostsFile=${knownHostsPath}`;
command.push(
`
echo "${sshKey.privateKey}" > /tmp/id_rsa
@@ -306,7 +304,6 @@ export const cloneGitRawRepository = async (entity: {
});
}
const { port } = sanitizeRepoPathSSH(customGitUrl);
await spawnAsync(
"git",
[
@@ -320,12 +317,12 @@ export const cloneGitRawRepository = async (entity: {
outputPath,
"--progress",
],
(_data) => {},
(data) => {},
{
env: {
...process.env,
...(customGitSSHKeyId && {
GIT_SSH_COMMAND: `ssh -i ${temporalKeyPath}${port ? ` -p ${port}` : ""} -o UserKnownHostsFile=${knownHostsPath}`,
GIT_SSH_COMMAND: `ssh -i ${temporalKeyPath} -o UserKnownHostsFile=${knownHostsPath}`,
}),
},
},
@@ -384,8 +381,7 @@ export const cloneRawGitRepositoryRemote = async (compose: Compose) => {
command.push(`mkdir -p ${outputPath};`);
if (customGitSSHKeyId) {
const sshKey = await findSSHKeyById(customGitSSHKeyId);
const { port } = sanitizeRepoPathSSH(customGitUrl);
const gitSshCommand = `ssh -i /tmp/id_rsa${port ? ` -p ${port}` : ""} -o UserKnownHostsFile=${knownHostsPath}`;
const gitSshCommand = `ssh -i /tmp/id_rsa -o UserKnownHostsFile=${knownHostsPath}`;
command.push(
`
echo "${sshKey.privateKey}" > /tmp/id_rsa

View File

@@ -162,6 +162,8 @@ export const getGitlabCloneCommand = async (
) => {
const {
appName,
gitlabRepository,
gitlabOwner,
gitlabPathNamespace,
gitlabBranch,
gitlabId,
@@ -266,7 +268,7 @@ export const getGitlabRepositories = async (gitlabId?: string) => {
if (groupName) {
return full_path.toLowerCase().includes(groupName) && kind === "group";
}
return kind === "member";
return kind === "user";
});
const mappedRepositories = filteredRepos.map((repo: any) => {
return {
@@ -326,7 +328,14 @@ export const getGitlabBranches = async (input: {
};
export const cloneRawGitlabRepository = async (entity: Compose) => {
const { appName, gitlabBranch, gitlabId, gitlabPathNamespace } = entity;
const {
appName,
gitlabRepository,
gitlabOwner,
gitlabBranch,
gitlabId,
gitlabPathNamespace,
} = entity;
if (!gitlabId) {
throw new TRPCError({
@@ -433,7 +442,7 @@ export const testGitlabConnection = async (
if (groupName) {
return full_path.toLowerCase().includes(groupName) && kind === "group";
}
return kind === "member";
return kind === "user";
});
return filteredRepos.length;

View File

@@ -67,7 +67,7 @@ export const removeTraefikConfig = async (
if (fs.existsSync(configPath)) {
await fs.promises.unlink(configPath);
}
} catch (_error) {}
} catch (error) {}
};
export const removeTraefikConfigRemote = async (
@@ -78,7 +78,7 @@ export const removeTraefikConfigRemote = async (
const { DYNAMIC_TRAEFIK_PATH } = paths(true);
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
await execAsyncRemote(serverId, `rm ${configPath}`);
} catch (_error) {}
} catch (error) {}
};
export const loadOrCreateConfig = (appName: string): FileConfig => {
@@ -110,7 +110,7 @@ export const loadOrCreateConfigRemote = async (
http: { routers: {}, services: {} },
};
return parsedConfig;
} catch (_err) {
} catch (err) {
return fileConfig;
}
};
@@ -132,7 +132,7 @@ export const readRemoteConfig = async (serverId: string, appName: string) => {
const { stdout } = await execAsyncRemote(serverId, `cat ${configPath}`);
if (!stdout) return null;
return stdout;
} catch (_err) {
} catch (err) {
return null;
}
};

View File

@@ -122,25 +122,13 @@ export const createRouterConfig = async (
if ((entryPoint === "websecure" && https) || !https) {
// redirects
for (const redirect of redirects) {
let middlewareName = `redirect-${appName}-${redirect.uniqueConfigKey}`;
if (domain.domainType === "preview") {
middlewareName = `redirect-${appName.replace(
/^preview-(.+)-[^-]+$/,
"$1",
)}-${redirect.uniqueConfigKey}`;
}
const middlewareName = `redirect-${appName}-${redirect.uniqueConfigKey}`;
routerConfig.middlewares?.push(middlewareName);
}
// security
if (security.length > 0) {
let middlewareName = `auth-${appName}`;
if (domain.domainType === "preview") {
middlewareName = `auth-${appName.replace(
/^preview-(.+)-[^-]+$/,
"$1",
)}`;
}
const middlewareName = `auth-${appName}`;
routerConfig.middlewares?.push(middlewareName);
}
}

View File

@@ -95,7 +95,7 @@ export const loadRemoteMiddlewares = async (serverId: string) => {
}
const config = load(stdout) as FileConfig;
return config;
} catch (_) {
} catch (error) {
throw new Error(`File not found: ${configPath}`);
}
};

View File

@@ -1,14 +1,14 @@
import { existsSync, readFileSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { paths } from "@dokploy/server/constants";
import type { User } from "@dokploy/server/services/user";
import type { Admin } from "@dokploy/server/services/admin";
import { dump, load } from "js-yaml";
import { loadOrCreateConfig, writeTraefikConfig } from "./application";
import type { FileConfig } from "./file-types";
import type { MainTraefikConfig } from "./types";
export const updateServerTraefik = (
user: User | null,
admin: Admin | null,
newHost: string | null,
) => {
const appName = "dokploy";
@@ -22,7 +22,7 @@ export const updateServerTraefik = (
if (currentRouterConfig && newHost) {
currentRouterConfig.rule = `Host(\`${newHost}\`)`;
if (user?.certificateType === "letsencrypt") {
if (admin?.certificateType === "letsencrypt") {
config.http.routers[`${appName}-router-app-secure`] = {
...currentRouterConfig,
entryPoints: ["websecure"],