Merge branch 'canary' of github.com:amirhmoradi/dokploy into claude/update-dockerfile-deps-WD7Lw

This commit is contained in:
Amir Moradi
2026-01-08 11:53:55 +01:00
72 changed files with 9076 additions and 746 deletions

View File

@@ -35,3 +35,4 @@ export * from "./ssh-key";
export * from "./user";
export * from "./utils";
export * from "./volume-backups";
export * from "./web-server-settings";

View File

@@ -80,6 +80,14 @@ export const apiTestRegistry = createSchema.pick({}).extend({
serverId: z.string().optional(),
});
export const apiTestRegistryById = createSchema
.pick({
registryId: true,
})
.extend({
serverId: z.string().optional(),
});
export const apiRemoveRegistry = createSchema
.pick({
registryId: true,

View File

@@ -3,7 +3,6 @@ import { relations } from "drizzle-orm";
import {
boolean,
integer,
jsonb,
pgTable,
text,
timestamp,
@@ -15,7 +14,6 @@ import { account, apikey, organization } from "./account";
import { backups } from "./backups";
import { projects } from "./project";
import { schedules } from "./schedule";
import { certificateType } from "./shared";
/**
* This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same
* database instance for multiple projects.
@@ -51,73 +49,10 @@ export const user = pgTable("user", {
banExpires: timestamp("ban_expires"),
updatedAt: timestamp("updated_at").notNull(),
// Admin
serverIp: text("serverIp"),
certificateType: certificateType("certificateType").notNull().default("none"),
https: boolean("https").notNull().default(false),
host: text("host"),
letsEncryptEmail: text("letsEncryptEmail"),
sshPrivateKey: text("sshPrivateKey"),
enableDockerCleanup: boolean("enableDockerCleanup").notNull().default(true),
logCleanupCron: text("logCleanupCron").default("0 0 * * *"),
role: text("role").notNull().default("user"),
// Metrics
enablePaidFeatures: boolean("enablePaidFeatures").notNull().default(false),
allowImpersonation: boolean("allowImpersonation").notNull().default(false),
metricsConfig: jsonb("metricsConfig")
.$type<{
server: {
type: "Dokploy" | "Remote";
refreshRate: number;
port: number;
token: string;
urlCallback: string;
retentionDays: number;
cronJob: string;
thresholds: {
cpu: number;
memory: number;
};
};
containers: {
refreshRate: number;
services: {
include: string[];
exclude: string[];
};
};
}>()
.notNull()
.default({
server: {
type: "Dokploy",
refreshRate: 60,
port: 4500,
token: "",
retentionDays: 2,
cronJob: "",
urlCallback: "",
thresholds: {
cpu: 0,
memory: 0,
},
},
containers: {
refreshRate: 60,
services: {
include: [],
exclude: [],
},
},
}),
cleanupCacheApplications: boolean("cleanupCacheApplications")
.notNull()
.default(false),
cleanupCacheOnPreviews: boolean("cleanupCacheOnPreviews")
.notNull()
.default(false),
cleanupCacheOnCompose: boolean("cleanupCacheOnCompose")
.notNull()
.default(false),
stripeCustomerId: text("stripeCustomerId"),
stripeSubscriptionId: text("stripeSubscriptionId"),
serversQuantity: integer("serversQuantity").notNull().default(0),
@@ -203,33 +138,6 @@ export const apiFindOneUserByAuth = createSchema
// authId: true,
})
.required();
export const apiSaveSSHKey = createSchema
.pick({
sshPrivateKey: true,
})
.required();
export const apiAssignDomain = createSchema
.pick({
host: true,
certificateType: true,
letsEncryptEmail: true,
https: true,
})
.required()
.partial({
letsEncryptEmail: true,
https: true,
});
export const apiUpdateDockerCleanup = createSchema
.pick({
enableDockerCleanup: true,
})
.required()
.extend({
serverId: z.string().optional(),
});
export const apiTraefikConfig = z.object({
traefikConfig: z.string().min(1),
@@ -298,32 +206,6 @@ export const apiReadStatsLogs = z.object({
.optional(),
});
export const apiUpdateWebServerMonitoring = z.object({
metricsConfig: z
.object({
server: z.object({
refreshRate: z.number().min(2),
port: z.number().min(1),
token: z.string(),
urlCallback: z.string().url(),
retentionDays: z.number().min(1),
cronJob: z.string().min(1),
thresholds: z.object({
cpu: z.number().min(0),
memory: z.number().min(0),
}),
}),
containers: z.object({
refreshRate: z.number().min(2),
services: z.object({
include: z.array(z.string()).optional(),
exclude: z.array(z.string()).optional(),
}),
}),
})
.required(),
});
export const apiUpdateUser = createSchema.partial().extend({
email: z
.string()
@@ -334,29 +216,4 @@ export const apiUpdateUser = createSchema.partial().extend({
currentPassword: z.string().optional(),
name: z.string().optional(),
lastName: z.string().optional(),
metricsConfig: z
.object({
server: z.object({
type: z.enum(["Dokploy", "Remote"]),
refreshRate: z.number(),
port: z.number(),
token: z.string(),
urlCallback: z.string(),
retentionDays: z.number(),
cronJob: z.string(),
thresholds: z.object({
cpu: z.number(),
memory: z.number(),
}),
}),
containers: z.object({
refreshRate: z.number(),
services: z.object({
include: z.array(z.string()),
exclude: z.array(z.string()),
}),
}),
})
.optional(),
logCleanupCron: z.string().optional().nullable(),
});

View File

@@ -0,0 +1,178 @@
import { relations } from "drizzle-orm";
import { boolean, jsonb, pgTable, text, timestamp } from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod";
import { nanoid } from "nanoid";
import { z } from "zod";
import { certificateType } from "./shared";
export const webServerSettings = pgTable("webServerSettings", {
id: text("id")
.notNull()
.primaryKey()
.$defaultFn(() => nanoid()),
// Web Server Configuration
serverIp: text("serverIp"),
certificateType: certificateType("certificateType").notNull().default("none"),
https: boolean("https").notNull().default(false),
host: text("host"),
letsEncryptEmail: text("letsEncryptEmail"),
sshPrivateKey: text("sshPrivateKey"),
enableDockerCleanup: boolean("enableDockerCleanup").notNull().default(true),
logCleanupCron: text("logCleanupCron").default("0 0 * * *"),
// Metrics Configuration
metricsConfig: jsonb("metricsConfig")
.$type<{
server: {
type: "Dokploy" | "Remote";
refreshRate: number;
port: number;
token: string;
urlCallback: string;
retentionDays: number;
cronJob: string;
thresholds: {
cpu: number;
memory: number;
};
};
containers: {
refreshRate: number;
services: {
include: string[];
exclude: string[];
};
};
}>()
.notNull()
.default({
server: {
type: "Dokploy",
refreshRate: 60,
port: 4500,
token: "",
retentionDays: 2,
cronJob: "",
urlCallback: "",
thresholds: {
cpu: 0,
memory: 0,
},
},
containers: {
refreshRate: 60,
services: {
include: [],
exclude: [],
},
},
}),
// Cache Cleanup Configuration
cleanupCacheApplications: boolean("cleanupCacheApplications")
.notNull()
.default(false),
cleanupCacheOnPreviews: boolean("cleanupCacheOnPreviews")
.notNull()
.default(false),
cleanupCacheOnCompose: boolean("cleanupCacheOnCompose")
.notNull()
.default(false),
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").notNull().defaultNow(),
});
export const webServerSettingsRelations = relations(
webServerSettings,
() => ({}),
);
const createSchema = createInsertSchema(webServerSettings, {
id: z.string().min(1),
});
export const apiUpdateWebServerSettings = createSchema.partial().extend({
serverIp: z.string().optional(),
certificateType: z.enum(["letsencrypt", "none", "custom"]).optional(),
https: z.boolean().optional(),
host: z.string().optional(),
letsEncryptEmail: z.string().email().optional().nullable(),
sshPrivateKey: z.string().optional(),
enableDockerCleanup: z.boolean().optional(),
logCleanupCron: z.string().optional().nullable(),
metricsConfig: z
.object({
server: z.object({
type: z.enum(["Dokploy", "Remote"]),
refreshRate: z.number(),
port: z.number(),
token: z.string(),
urlCallback: z.string(),
retentionDays: z.number(),
cronJob: z.string(),
thresholds: z.object({
cpu: z.number(),
memory: z.number(),
}),
}),
containers: z.object({
refreshRate: z.number(),
services: z.object({
include: z.array(z.string()),
exclude: z.array(z.string()),
}),
}),
})
.optional(),
cleanupCacheApplications: z.boolean().optional(),
cleanupCacheOnPreviews: z.boolean().optional(),
cleanupCacheOnCompose: z.boolean().optional(),
});
export const apiAssignDomain = z
.object({
host: z.string(),
certificateType: z.enum(["letsencrypt", "none", "custom"]),
letsEncryptEmail: z.string().email().optional().nullable(),
https: z.boolean().optional(),
})
.required()
.partial({
letsEncryptEmail: true,
https: true,
});
export const apiSaveSSHKey = z
.object({
sshPrivateKey: z.string(),
})
.required();
export const apiUpdateDockerCleanup = z.object({
enableDockerCleanup: z.boolean(),
serverId: z.string().optional(),
});
export const apiUpdateWebServerMonitoring = z.object({
metricsConfig: z
.object({
server: z.object({
refreshRate: z.number().min(2),
port: z.number().min(1),
token: z.string(),
urlCallback: z.string().url(),
retentionDays: z.number().min(1),
cronJob: z.string().min(1),
thresholds: z.object({
cpu: z.number().min(0),
memory: z.number().min(0),
}),
}),
containers: z.object({
refreshRate: z.number().min(2),
services: z.object({
include: z.array(z.string()).optional(),
exclude: z.array(z.string()).optional(),
}),
}),
})
.required(),
});

View File

@@ -41,6 +41,7 @@ export * from "./services/settings";
export * from "./services/ssh-key";
export * from "./services/user";
export * from "./services/volume-backups";
export * from "./services/web-server-settings";
export * from "./setup/config-paths";
export * from "./setup/monitoring-setup";
export * from "./setup/postgres-setup";

View File

@@ -9,7 +9,10 @@ import { IS_CLOUD } from "../constants";
import { db } from "../db";
import * as schema from "../db/schema";
import { getUserByToken } from "../services/admin";
import { updateUser } from "../services/user";
import {
getWebServerSettings,
updateWebServerSettings,
} from "../services/web-server-settings";
import { getHubSpotUTK, submitToHubSpot } from "../utils/tracking/hubspot";
import { sendEmail } from "../verification/send-verification-email";
import { getPublicIpWithFallback } from "../wss/utils";
@@ -35,22 +38,14 @@ const { handler, api } = betterAuth({
},
...(!IS_CLOUD && {
async trustedOrigins() {
const admin = await db.query.member.findFirst({
where: eq(schema.member.role, "owner"),
with: {
user: true,
},
});
if (admin?.user) {
return [
...(admin.user.serverIp
? [`http://${admin.user.serverIp}:3000`]
: []),
...(admin.user.host ? [`https://${admin.user.host}`] : []),
];
const settings = await getWebServerSettings();
if (!settings) {
return [];
}
return [];
return [
...(settings?.serverIp ? [`http://${settings?.serverIp}:3000`] : []),
...(settings?.host ? [`https://${settings?.host}`] : []),
];
},
}),
emailVerification: {
@@ -122,7 +117,7 @@ const { handler, api } = betterAuth({
});
if (!IS_CLOUD) {
await updateUser(user.id, {
await updateWebServerSettings({
serverIp: await getPublicIpWithFallback(),
});
}

View File

@@ -8,6 +8,7 @@ import {
import { TRPCError } from "@trpc/server";
import { eq } from "drizzle-orm";
import { IS_CLOUD } from "../constants";
import { getWebServerSettings } from "./web-server-settings";
export const findUserById = async (userId: string) => {
const userResult = await db.query.user.findFirst({
@@ -107,11 +108,11 @@ export const getDokployUrl = async () => {
if (IS_CLOUD) {
return "https://app.dokploy.com";
}
const owner = await findOwner();
const settings = await getWebServerSettings();
if (owner.user.host) {
const protocol = owner.user.https ? "https" : "http";
return `${protocol}://${owner.user.host}`;
if (settings?.host) {
const protocol = settings?.https ? "https" : "http";
return `${protocol}://${settings?.host}`;
}
return `http://${owner.user.serverIp}:${process.env.PORT}`;
return `http://${settings?.serverIp}:${process.env.PORT}`;
};

View File

@@ -6,8 +6,8 @@ import { generateObject } from "ai";
import { desc, eq } from "drizzle-orm";
import { z } from "zod";
import { IS_CLOUD } from "../constants";
import { findOrganizationById } from "./admin";
import { findServerById } from "./server";
import { getWebServerSettings } from "./web-server-settings";
export const getAiSettingsByOrganizationId = async (organizationId: string) => {
const aiSettings = await db.query.ai.findMany({
@@ -79,8 +79,8 @@ export const suggestVariants = async ({
let ip = "";
if (!IS_CLOUD) {
const organization = await findOrganizationById(organizationId);
ip = organization?.owner.serverIp || "";
const settings = await getWebServerSettings();
ip = settings?.serverIp || "";
}
if (serverId) {

View File

@@ -3,10 +3,10 @@ import { promisify } from "node:util";
import { db } from "@dokploy/server/db";
import { generateRandomDomain } from "@dokploy/server/templates";
import { manageDomain } from "@dokploy/server/utils/traefik/domain";
import { getWebServerSettings } from "@dokploy/server/services/web-server-settings";
import { TRPCError } from "@trpc/server";
import { eq } from "drizzle-orm";
import { type apiCreateDomain, domains } from "../db/schema";
import { findUserById } from "./admin";
import { findApplicationById } from "./application";
import { detectCDNProvider } from "./cdn";
import { findServerById } from "./server";
@@ -61,9 +61,9 @@ export const generateTraefikMeDomain = async (
projectName: appName,
});
}
const admin = await findUserById(userId);
const settings = await getWebServerSettings();
return generateRandomDomain({
serverIp: admin?.serverIp || "",
serverIp: settings?.serverIp || "",
projectName: appName,
});
};

View File

@@ -13,11 +13,11 @@ import { removeDirectoryCode } from "../utils/filesystem/directory";
import { authGithub } from "../utils/providers/github";
import { removeTraefikConfig } from "../utils/traefik/application";
import { manageDomain } from "../utils/traefik/domain";
import { findUserById } from "./admin";
import { findApplicationById } from "./application";
import { removeDeploymentsByPreviewDeploymentId } from "./deployment";
import { createDomain } from "./domain";
import { type Github, getIssueComment } from "./github";
import { getWebServerSettings } from "./web-server-settings";
export type PreviewDeployment = typeof previewDeployments.$inferSelect;
@@ -253,8 +253,8 @@ const generateWildcardDomain = async (
}
if (!ip) {
const admin = await findUserById(userId);
ip = admin?.serverIp || "";
const settings = await getWebServerSettings();
ip = settings?.serverIp || "";
}
const slugIp = ip.replaceAll(".", "-");

View File

@@ -0,0 +1,44 @@
import { db } from "@dokploy/server/db";
import { webServerSettings } from "@dokploy/server/db/schema";
import { eq } from "drizzle-orm";
/**
* Get the web server settings (singleton - only one row should exist)
*/
export const getWebServerSettings = async () => {
const settings = await db.query.webServerSettings.findFirst({
orderBy: (settings, { asc }) => [asc(settings.createdAt)],
});
if (!settings) {
// Create default settings if none exist
const [newSettings] = await db
.insert(webServerSettings)
.values({})
.returning();
return newSettings;
}
return settings;
};
/**
* Update web server settings
*/
export const updateWebServerSettings = async (
updates: Partial<typeof webServerSettings.$inferInsert>,
) => {
const current = await getWebServerSettings();
const [updated] = await db
.update(webServerSettings)
.set({
...updates,
updatedAt: new Date(),
})
.where(eq(webServerSettings.id, current?.id ?? ""))
.returning();
return updated;
};

View File

@@ -1,7 +1,7 @@
import { findServerById } from "@dokploy/server/services/server";
import { getWebServerSettings } from "@dokploy/server/services/web-server-settings";
import type { ContainerCreateOptions } from "dockerode";
import { IS_CLOUD } from "../constants";
import { findUserById } from "../services/admin";
import { getDokployImageTag } from "../services/settings";
import { pullImage, pullRemoteImage } from "../utils/docker/utils";
import { execAsync, execAsyncRemote } from "../utils/process/execAsync";
@@ -83,8 +83,8 @@ export const setupMonitoring = async (serverId: string) => {
}
};
export const setupWebMonitoring = async (userId: string) => {
const user = await findUserById(userId);
export const setupWebMonitoring = async () => {
const webServerSettings = await getWebServerSettings();
const containerName = "dokploy-monitoring";
let imageName = "dokploy/monitoring:latest";
@@ -99,7 +99,7 @@ export const setupWebMonitoring = async (userId: string) => {
const settings: ContainerCreateOptions = {
name: containerName,
Env: [`METRICS_CONFIG=${JSON.stringify(user?.metricsConfig)}`],
Env: [`METRICS_CONFIG=${JSON.stringify(webServerSettings?.metricsConfig)}`],
Image: imageName,
HostConfig: {
// Memory: 100 * 1024 * 1024, // 100MB en bytes
@@ -110,9 +110,9 @@ export const setupWebMonitoring = async (userId: string) => {
Name: "always",
},
PortBindings: {
[`${user?.metricsConfig?.server?.port}/tcp`]: [
[`${webServerSettings?.metricsConfig?.server?.port}/tcp`]: [
{
HostPort: user?.metricsConfig?.server?.port.toString(),
HostPort: webServerSettings?.metricsConfig?.server?.port.toString(),
},
],
},
@@ -126,7 +126,7 @@ export const setupWebMonitoring = async (userId: string) => {
// NetworkMode: "host",
},
ExposedPorts: {
[`${user?.metricsConfig?.server?.port}/tcp`]: {},
[`${webServerSettings?.metricsConfig?.server?.port}/tcp`]: {},
},
};
const docker = await getRemoteDocker();

View File

@@ -1,6 +1,8 @@
import { paths } from "@dokploy/server/constants";
import { findOwner } from "@dokploy/server/services/admin";
import { updateUser } from "@dokploy/server/services/user";
import {
getWebServerSettings,
updateWebServerSettings,
} from "@dokploy/server/services/web-server-settings";
import { scheduledJobs, scheduleJob } from "node-schedule";
import { execAsync } from "../process/execAsync";
@@ -29,12 +31,9 @@ export const startLogCleanup = async (
}
});
const owner = await findOwner();
if (owner) {
await updateUser(owner.user.id, {
logCleanupCron: cronExpression,
});
}
await updateWebServerSettings({
logCleanupCron: cronExpression,
});
return true;
} catch (error) {
@@ -51,12 +50,9 @@ export const stopLogCleanup = async (): Promise<boolean> => {
}
// Update database
const owner = await findOwner();
if (owner) {
await updateUser(owner.user.id, {
logCleanupCron: null,
});
}
await updateWebServerSettings({
logCleanupCron: null,
});
return true;
} catch (error) {
@@ -69,8 +65,8 @@ export const getLogCleanupStatus = async (): Promise<{
enabled: boolean;
cronExpression: string | null;
}> => {
const owner = await findOwner();
const cronExpression = owner?.user.logCleanupCron ?? null;
const settings = await getWebServerSettings();
const cronExpression = settings?.logCleanupCron ?? null;
return {
enabled: cronExpression !== null,
cronExpression,

View File

@@ -2,6 +2,7 @@ import path from "node:path";
import { member } from "@dokploy/server/db/schema";
import type { BackupSchedule } from "@dokploy/server/services/backup";
import { getAllServers } from "@dokploy/server/services/server";
import { getWebServerSettings } from "@dokploy/server/services/web-server-settings";
import { eq } from "drizzle-orm";
import { scheduleJob } from "node-schedule";
import { db } from "../../db/index";
@@ -25,7 +26,9 @@ export const initCronJobs = async () => {
return;
}
if (admin?.user?.enableDockerCleanup) {
const webServerSettings = await getWebServerSettings();
if (webServerSettings?.enableDockerCleanup) {
scheduleJob("docker-cleanup", "0 0 * * *", async () => {
console.log(
`Docker Cleanup ${new Date().toLocaleString()}] Running docker cleanup`,
@@ -82,9 +85,12 @@ export const initCronJobs = async () => {
}
}
if (admin?.user?.logCleanupCron) {
console.log("Starting log requests cleanup", admin.user.logCleanupCron);
await startLogCleanup(admin.user.logCleanupCron);
if (webServerSettings?.logCleanupCron) {
console.log(
"Starting log requests cleanup",
webServerSettings.logCleanupCron,
);
await startLogCleanup(webServerSettings.logCleanupCron);
}
};

View File

@@ -90,7 +90,7 @@ export const createCommand = (compose: ComposeNested) => {
if (composeType === "docker-compose") {
command = `compose -p ${appName} -f ${path} up -d --build --remove-orphans`;
} else if (composeType === "stack") {
command = `stack deploy -c ${path} ${appName} --prune`;
command = `stack deploy -c ${path} ${appName} --prune --with-registry-auth`;
}
return command;

View File

@@ -117,7 +117,7 @@ const getRegistryCommands = (
): string => {
return `
echo "📦 [Enabled Registry] Uploading image to '${registry.registryType}' | '${registryTag}'" ;
echo "${registry.password}" | docker login ${registry.registryUrl} -u ${registry.username} --password-stdin || {
echo "${registry.password}" | docker login ${registry.registryUrl} -u '${registry.username}' --password-stdin || {
echo "❌ DockerHub Failed" ;
exit 1;
}

View File

@@ -54,7 +54,7 @@ if [ "$REPLICA_STATUS" != "1" ]; then
mongosh --eval '
rs.initiate({
_id: "rs0",
members: [{ _id: 0, host: "localhost:27017", priority: 1 }]
members: [{ _id: 0, host: "${appName}:27017", priority: 1 }]
});
// Wait for the replica set to initialize

View File

@@ -146,17 +146,17 @@ export const getContainerByName = (name: string): Promise<ContainerInfo> => {
};
/**
* Docker commands passed through this method are held during Docker's build or pull process.
* Docker commands sent using this method are held in a hold when Docker is busy.
*
* https://github.com/Dokploy/dokploy/pull/3064
* https://github.com/fir4tozden
*/
export const dockerSafeExec = (exec: string) => `CHECK_INTERVAL=10
export const dockerSafeExec = (exec: string) => `
CHECK_INTERVAL=10
echo "Preparing for execution..."
while true; do
PROCESSES=$(ps aux | grep -E "docker build|docker pull" | grep -v grep)
PROCESSES=$(ps aux | grep -E "^.*docker [A-Za-z]" | grep -v grep)
if [ -z "$PROCESSES" ]; then
echo "Docker is idle. Starting execution..."
@@ -169,14 +169,15 @@ done
${exec}
echo "Execution completed."`;
echo "Execution completed."
`;
const cleanupCommands = {
containers: "docker container prune --force",
images: "docker image prune --all --force",
volumes: "docker volume prune --all --force",
builders: "docker builder prune --all --force",
system: "docker system prune --all --force",
volumes: "docker volume prune --all --force",
};
export const cleanupContainers = async (serverId?: string) => {
@@ -257,24 +258,48 @@ export const cleanupSystem = async (serverId?: string) => {
}
};
/**
* Volume cleanup should always be performed manually by the user. The reason is that during automatic cleanup, a volume may be deleted due to a stopped container, which is a dangerous situation.
*
* https://github.com/Dokploy/dokploy/pull/3267
*/
const excludedCleanupAllCommands: (keyof typeof cleanupCommands)[] = [
"volumes",
];
export const cleanupAll = async (serverId?: string) => {
await cleanupContainers(serverId);
await cleanupImages(serverId);
await cleanupBuilders(serverId);
await cleanupSystem(serverId);
for (const [key, command] of Object.entries(cleanupCommands) as [
keyof typeof cleanupCommands,
string,
][]) {
if (excludedCleanupAllCommands.includes(key)) continue;
try {
if (serverId) {
await execAsyncRemote(serverId, dockerSafeExec(command));
} else {
await execAsync(dockerSafeExec(command));
}
} catch {}
}
};
export const cleanupAllBackground = async (serverId?: string) => {
Promise.allSettled(
Object.values(cleanupCommands).map(async (command) => {
try {
(
Object.entries(cleanupCommands) as [
keyof typeof cleanupCommands,
string,
][]
)
.filter(([key]) => !excludedCleanupAllCommands.includes(key))
.map(async ([, command]) => {
if (serverId) {
await execAsyncRemote(serverId, dockerSafeExec(command));
} else {
await execAsync(dockerSafeExec(command));
}
} catch (error) {}
}),
}),
)
.then((results) => {
const failed = results.filter((r) => r.status === "rejected");

View File

@@ -1,7 +1,7 @@
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 { webServerSettings } from "@dokploy/server/db/schema/web-server-settings";
import { parse, stringify } from "yaml";
import {
loadOrCreateConfig,
@@ -12,10 +12,10 @@ import type { FileConfig } from "./file-types";
import type { MainTraefikConfig } from "./types";
export const updateServerTraefik = (
user: User | null,
settings: typeof webServerSettings.$inferSelect | null,
newHost: string | null,
) => {
const { https, certificateType } = user || {};
const { https, certificateType } = settings || {};
const appName = "dokploy";
const config: FileConfig = loadOrCreateConfig(appName);