mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-07-07 06:55:23 +02:00
Merge branch 'canary' into feat/quick-service-switcher
This commit is contained in:
@@ -73,23 +73,25 @@ const { handler, api } = betterAuth({
|
||||
disabled: process.env.NODE_ENV === "production",
|
||||
},
|
||||
async trustedOrigins() {
|
||||
const trustedOrigins = await getTrustedOrigins();
|
||||
if (IS_CLOUD) {
|
||||
return trustedOrigins;
|
||||
return getTrustedOrigins();
|
||||
}
|
||||
const settings = await getWebServerSettings();
|
||||
if (!settings) {
|
||||
return [];
|
||||
}
|
||||
return [
|
||||
...(settings?.serverIp ? [`http://${settings?.serverIp}:3000`] : []),
|
||||
...(settings?.host ? [`https://${settings?.host}`] : []),
|
||||
...(process.env.NODE_ENV === "development"
|
||||
const [trustedOrigins, settings] = await Promise.all([
|
||||
getTrustedOrigins(),
|
||||
getWebServerSettings(),
|
||||
]);
|
||||
if (!settings) return [];
|
||||
const devOrigins =
|
||||
process.env.NODE_ENV === "development"
|
||||
? [
|
||||
"http://localhost:3000",
|
||||
"https://absolutely-handy-falcon.ngrok-free.app",
|
||||
]
|
||||
: []),
|
||||
: [];
|
||||
return [
|
||||
...(settings?.serverIp ? [`http://${settings?.serverIp}:3000`] : []),
|
||||
...(settings?.host ? [`https://${settings?.host}`] : []),
|
||||
...devOrigins,
|
||||
...trustedOrigins,
|
||||
];
|
||||
},
|
||||
|
||||
@@ -117,23 +117,33 @@ export const getDokployUrl = async () => {
|
||||
return `http://${settings?.serverIp}:${process.env.PORT}`;
|
||||
};
|
||||
|
||||
export const getTrustedOrigins = async () => {
|
||||
const members = await db.query.member.findMany({
|
||||
where: eq(member.role, "owner"),
|
||||
with: {
|
||||
user: true,
|
||||
},
|
||||
});
|
||||
const TRUSTED_ORIGINS_CACHE_TTL_MS = 30 * 60_000;
|
||||
let trustedOriginsCache: { data: string[]; expiresAt: number } | null = null;
|
||||
|
||||
if (members.length === 0) {
|
||||
return [];
|
||||
export const getTrustedOrigins = async () => {
|
||||
const runQuery = async () => {
|
||||
const rows = await db
|
||||
.select({ trustedOrigins: user.trustedOrigins })
|
||||
.from(member)
|
||||
.innerJoin(user, eq(member.userId, user.id))
|
||||
.where(eq(member.role, "owner"));
|
||||
return Array.from(new Set(rows.flatMap((r) => r.trustedOrigins ?? [])));
|
||||
};
|
||||
|
||||
if (IS_CLOUD) {
|
||||
const now = Date.now();
|
||||
if (trustedOriginsCache && now < trustedOriginsCache.expiresAt) {
|
||||
return trustedOriginsCache.data;
|
||||
}
|
||||
const trustedOrigins = await runQuery();
|
||||
trustedOriginsCache = {
|
||||
data: trustedOrigins,
|
||||
expiresAt: now + TRUSTED_ORIGINS_CACHE_TTL_MS,
|
||||
};
|
||||
return trustedOrigins;
|
||||
}
|
||||
|
||||
const trustedOrigins = members.flatMap(
|
||||
(member) => member.user.trustedOrigins || [],
|
||||
);
|
||||
|
||||
return Array.from(new Set(trustedOrigins));
|
||||
return runQuery();
|
||||
};
|
||||
|
||||
export const getTrustedProviders = async () => {
|
||||
|
||||
@@ -34,42 +34,139 @@ export const createEnvironment = async (
|
||||
export const findEnvironmentById = async (environmentId: string) => {
|
||||
const environment = await db.query.environments.findFirst({
|
||||
where: eq(environments.environmentId, environmentId),
|
||||
columns: {
|
||||
name: true,
|
||||
description: true,
|
||||
environmentId: true,
|
||||
isDefault: true,
|
||||
projectId: true,
|
||||
env: true,
|
||||
},
|
||||
with: {
|
||||
applications: {
|
||||
with: {
|
||||
deployments: true,
|
||||
server: true,
|
||||
server: {
|
||||
columns: {
|
||||
name: true,
|
||||
serverId: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
columns: {
|
||||
name: true,
|
||||
applicationId: true,
|
||||
createdAt: true,
|
||||
applicationStatus: true,
|
||||
description: true,
|
||||
serverId: true,
|
||||
},
|
||||
},
|
||||
mariadb: {
|
||||
with: {
|
||||
server: true,
|
||||
server: {
|
||||
columns: {
|
||||
name: true,
|
||||
serverId: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
columns: {
|
||||
mariadbId: true,
|
||||
name: true,
|
||||
createdAt: true,
|
||||
applicationStatus: true,
|
||||
description: true,
|
||||
serverId: true,
|
||||
},
|
||||
},
|
||||
mongo: {
|
||||
with: {
|
||||
server: true,
|
||||
server: {
|
||||
columns: {
|
||||
name: true,
|
||||
serverId: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
columns: {
|
||||
mongoId: true,
|
||||
name: true,
|
||||
createdAt: true,
|
||||
applicationStatus: true,
|
||||
description: true,
|
||||
serverId: true,
|
||||
},
|
||||
},
|
||||
mysql: {
|
||||
with: {
|
||||
server: true,
|
||||
server: {
|
||||
columns: {
|
||||
name: true,
|
||||
serverId: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
columns: {
|
||||
mysqlId: true,
|
||||
name: true,
|
||||
createdAt: true,
|
||||
applicationStatus: true,
|
||||
description: true,
|
||||
serverId: true,
|
||||
},
|
||||
},
|
||||
postgres: {
|
||||
with: {
|
||||
server: true,
|
||||
server: {
|
||||
columns: {
|
||||
name: true,
|
||||
serverId: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
columns: {
|
||||
postgresId: true,
|
||||
name: true,
|
||||
description: true,
|
||||
createdAt: true,
|
||||
applicationStatus: true,
|
||||
serverId: true,
|
||||
},
|
||||
},
|
||||
redis: {
|
||||
with: {
|
||||
server: true,
|
||||
server: {
|
||||
columns: {
|
||||
name: true,
|
||||
serverId: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
columns: {
|
||||
redisId: true,
|
||||
name: true,
|
||||
createdAt: true,
|
||||
applicationStatus: true,
|
||||
description: true,
|
||||
serverId: true,
|
||||
},
|
||||
},
|
||||
compose: {
|
||||
with: {
|
||||
deployments: true,
|
||||
server: true,
|
||||
server: {
|
||||
columns: {
|
||||
name: true,
|
||||
serverId: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
columns: {
|
||||
composeId: true,
|
||||
name: true,
|
||||
createdAt: true,
|
||||
composeStatus: true,
|
||||
description: true,
|
||||
serverId: true,
|
||||
},
|
||||
},
|
||||
project: true,
|
||||
@@ -98,6 +195,12 @@ export const findEnvironmentsByProjectId = async (projectId: string) => {
|
||||
compose: true,
|
||||
project: true,
|
||||
},
|
||||
columns: {
|
||||
name: true,
|
||||
description: true,
|
||||
environmentId: true,
|
||||
isDefault: true,
|
||||
},
|
||||
});
|
||||
return projectEnvironments;
|
||||
};
|
||||
@@ -169,6 +272,7 @@ export const duplicateEnvironment = async (
|
||||
name: input.name,
|
||||
description: input.description || originalEnvironment.description,
|
||||
projectId: originalEnvironment.projectId,
|
||||
env: originalEnvironment.env,
|
||||
})
|
||||
.returning()
|
||||
.then((value) => value[0]);
|
||||
|
||||
@@ -281,17 +281,43 @@ const installRequirements = async (
|
||||
.on("error", (err) => {
|
||||
client.end();
|
||||
if (err.level === "client-authentication") {
|
||||
onData?.(
|
||||
`Authentication failed: Invalid SSH private key. ❌ Error: ${err.message} ${err.level}`,
|
||||
);
|
||||
const technicalDetail = `Error: ${err.message} ${err.level}`;
|
||||
const friendlyMessage = [
|
||||
"",
|
||||
"❌ Couldn't connect to your server — the SSH key was not accepted.",
|
||||
"",
|
||||
"This usually means the key doesn't match what's on the server, or the key format is invalid.",
|
||||
"",
|
||||
`Technical details: ${technicalDetail}`,
|
||||
"",
|
||||
"💡 Hints:",
|
||||
" • Check that the SSH key you added in Dokploy is the same one installed on the server (e.g. in ~/.ssh/authorized_keys).",
|
||||
" • Try generating a new SSH key in Dokploy and add only the public key to the server, then try again.",
|
||||
" • Make sure to follow the instructions on the Setup Server Button on the SSH Keys tab",
|
||||
].join("\n");
|
||||
onData?.(friendlyMessage);
|
||||
reject(
|
||||
new Error(
|
||||
`Authentication failed: Invalid SSH private key. ❌ Error: ${err.message} ${err.level}`,
|
||||
`Authentication failed: Invalid SSH private key. ${technicalDetail}`,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
onData?.(`SSH connection error: ${err.message} ${err.level}`);
|
||||
reject(new Error(`SSH connection error: ${err.message}`));
|
||||
const technicalDetail = `${err.message} ${err.level ?? ""}`.trim();
|
||||
const friendlyMessage = [
|
||||
"",
|
||||
"❌ Couldn't connect to your server.",
|
||||
"",
|
||||
"The connection failed before setup could run. Common causes: wrong IP or port, firewall blocking access, or the server is offline.",
|
||||
"",
|
||||
`Technical details: ${technicalDetail}`,
|
||||
"",
|
||||
"💡 Hints:",
|
||||
" • Check that the server IP address and SSH port are correct and the server is powered on.",
|
||||
" • If the server is in a private network, ensure Dokploy can reach it (VPN, firewall rules, or correct security groups).",
|
||||
" • Make sure the SSH port (usually 22) is open and the SSH service is running on the server.",
|
||||
].join("\n");
|
||||
onData?.(friendlyMessage);
|
||||
reject(new Error(`SSH connection error: ${technicalDetail}`));
|
||||
}
|
||||
})
|
||||
.connect({
|
||||
|
||||
@@ -106,10 +106,6 @@ export const writeDomainsToCompose = async (
|
||||
compose: Compose,
|
||||
domains: Domain[],
|
||||
) => {
|
||||
if (!domains.length) {
|
||||
return "";
|
||||
}
|
||||
|
||||
try {
|
||||
const composeConverted = await addDomainToCompose(compose, domains);
|
||||
const path = getComposePath(compose);
|
||||
@@ -145,7 +141,7 @@ export const addDomainToCompose = async (
|
||||
result = await loadDockerCompose(compose);
|
||||
}
|
||||
|
||||
if (!result || domains.length === 0) {
|
||||
if (!result) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -201,14 +201,31 @@ export const execAsyncRemote = async (
|
||||
.on("error", (err) => {
|
||||
conn.end();
|
||||
if (err.level === "client-authentication") {
|
||||
const technicalDetail = `Error: ${err.message} ${err.level}`;
|
||||
const friendlyMessage = [
|
||||
"",
|
||||
"❌ Couldn't connect to your server — the SSH key was not accepted.",
|
||||
"",
|
||||
"This usually means the key doesn't match what's on the server, or the key format is invalid.",
|
||||
"",
|
||||
`Technical details: ${technicalDetail}`,
|
||||
"",
|
||||
"💡 Hints:",
|
||||
" • Check that the SSH key you added in Dokploy is the same one installed on the server (e.g. in ~/.ssh/authorized_keys).",
|
||||
" • Try generating a new SSH key in Dokploy and add only the public key to the server, then try again.",
|
||||
" • Make sure to follow the instructions on the Setup Server Button on the SSH Keys tab and then click on deployments tab and check the logs for more details.",
|
||||
].join("\n");
|
||||
const errorMsg = `Authentication failed: Invalid SSH private key. ❌ Error: ${err.message} ${err.level}`;
|
||||
onData?.(errorMsg);
|
||||
onData?.(friendlyMessage);
|
||||
reject(
|
||||
new ExecError(errorMsg, {
|
||||
command,
|
||||
serverId,
|
||||
originalError: err,
|
||||
}),
|
||||
new ExecError(
|
||||
`Authentication failed: Invalid SSH private key. ${friendlyMessage}`,
|
||||
{
|
||||
command,
|
||||
serverId,
|
||||
originalError: err,
|
||||
},
|
||||
),
|
||||
);
|
||||
} else {
|
||||
const errorMsg = `SSH connection error: ${err.message}`;
|
||||
|
||||
Reference in New Issue
Block a user