mirror of
https://github.com/dokploy/dokploy.git
synced 2026-06-14 03:19:49 +00:00
fix: strip credentials from service-level API responses (#4564)
* fix: strip credentials from service-level API responses
Registry passwords and S3 destination credentials were being returned
in service `.one` tRPC endpoints to any user with service-level read
access. Reported by Nihon Kohden Corporation security team.
- Strip registry `password` from `findApplicationById` via Drizzle `columns: { password: false }`
- Strip destination `accessKey`/`secretAccessKey` from all DB service finders (postgres, mysql, mariadb, mongo, libsql, compose, backup, volume-backups)
- Add `findRegistryByIdWithCredentials` for internal use only
- Builders and upload utils now load registry credentials by ID at execution time
- `createRollback` enriches `fullContext` with registry credentials before persisting to DB so rollback execution has what it needs
- Remove `findApplicationByIdWithCredentials` and `ApplicationNestedWithCredentials` — no longer needed
- Backup execution utils load full destination via `findDestinationById` at runtime instead of reading from the joined relation
* [autofix.ci] apply automated fixes
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
@@ -95,26 +95,22 @@ export const findApplicationById = async (applicationId: string) => {
|
|||||||
const application = await db.query.applications.findFirst({
|
const application = await db.query.applications.findFirst({
|
||||||
where: eq(applications.applicationId, applicationId),
|
where: eq(applications.applicationId, applicationId),
|
||||||
with: {
|
with: {
|
||||||
environment: {
|
environment: { with: { project: true } },
|
||||||
with: {
|
|
||||||
project: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
domains: true,
|
domains: true,
|
||||||
deployments: true,
|
deployments: true,
|
||||||
mounts: true,
|
mounts: true,
|
||||||
redirects: true,
|
redirects: true,
|
||||||
security: true,
|
security: true,
|
||||||
ports: true,
|
ports: true,
|
||||||
registry: true,
|
|
||||||
gitlab: true,
|
gitlab: true,
|
||||||
github: true,
|
github: true,
|
||||||
bitbucket: true,
|
bitbucket: true,
|
||||||
gitea: true,
|
gitea: true,
|
||||||
server: true,
|
server: true,
|
||||||
previewDeployments: true,
|
previewDeployments: true,
|
||||||
buildRegistry: true,
|
registry: { columns: { password: false } },
|
||||||
rollbackRegistry: true,
|
buildRegistry: { columns: { password: false } },
|
||||||
|
rollbackRegistry: { columns: { password: false } },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (!application) {
|
if (!application) {
|
||||||
|
|||||||
@@ -34,7 +34,12 @@ export const findBackupById = async (backupId: string) => {
|
|||||||
mariadb: true,
|
mariadb: true,
|
||||||
mongo: true,
|
mongo: true,
|
||||||
libsql: true,
|
libsql: true,
|
||||||
destination: true,
|
destination: {
|
||||||
|
columns: {
|
||||||
|
accessKey: false,
|
||||||
|
secretAccessKey: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
compose: true,
|
compose: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -83,7 +88,12 @@ export const findBackupsByDbId = async (
|
|||||||
mariadb: true,
|
mariadb: true,
|
||||||
mongo: true,
|
mongo: true,
|
||||||
libsql: true,
|
libsql: true,
|
||||||
destination: true,
|
destination: {
|
||||||
|
columns: {
|
||||||
|
accessKey: false,
|
||||||
|
secretAccessKey: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return result || [];
|
return result || [];
|
||||||
|
|||||||
@@ -131,7 +131,12 @@ export const findComposeById = async (composeId: string) => {
|
|||||||
server: true,
|
server: true,
|
||||||
backups: {
|
backups: {
|
||||||
with: {
|
with: {
|
||||||
destination: true,
|
destination: {
|
||||||
|
columns: {
|
||||||
|
accessKey: false,
|
||||||
|
secretAccessKey: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
deployments: true,
|
deployments: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -63,7 +63,12 @@ export const findLibsqlById = async (libsqlId: string) => {
|
|||||||
server: true,
|
server: true,
|
||||||
backups: {
|
backups: {
|
||||||
with: {
|
with: {
|
||||||
destination: true,
|
destination: {
|
||||||
|
columns: {
|
||||||
|
accessKey: false,
|
||||||
|
secretAccessKey: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
deployments: true,
|
deployments: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -68,7 +68,12 @@ export const findMariadbById = async (mariadbId: string) => {
|
|||||||
server: true,
|
server: true,
|
||||||
backups: {
|
backups: {
|
||||||
with: {
|
with: {
|
||||||
destination: true,
|
destination: {
|
||||||
|
columns: {
|
||||||
|
accessKey: false,
|
||||||
|
secretAccessKey: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
deployments: true,
|
deployments: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -63,7 +63,12 @@ export const findMongoById = async (mongoId: string) => {
|
|||||||
server: true,
|
server: true,
|
||||||
backups: {
|
backups: {
|
||||||
with: {
|
with: {
|
||||||
destination: true,
|
destination: {
|
||||||
|
columns: {
|
||||||
|
accessKey: false,
|
||||||
|
secretAccessKey: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
deployments: true,
|
deployments: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -66,7 +66,12 @@ export const findMySqlById = async (mysqlId: string) => {
|
|||||||
server: true,
|
server: true,
|
||||||
backups: {
|
backups: {
|
||||||
with: {
|
with: {
|
||||||
destination: true,
|
destination: {
|
||||||
|
columns: {
|
||||||
|
accessKey: false,
|
||||||
|
secretAccessKey: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
deployments: true,
|
deployments: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -76,7 +76,12 @@ export const findPostgresById = async (postgresId: string) => {
|
|||||||
server: true,
|
server: true,
|
||||||
backups: {
|
backups: {
|
||||||
with: {
|
with: {
|
||||||
destination: true,
|
destination: {
|
||||||
|
columns: {
|
||||||
|
accessKey: false,
|
||||||
|
secretAccessKey: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
deployments: true,
|
deployments: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -162,6 +162,19 @@ export const findRegistryById = async (registryId: string) => {
|
|||||||
return registryResponse;
|
return registryResponse;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const findRegistryByIdWithCredentials = async (registryId: string) => {
|
||||||
|
const registryResponse = await db.query.registry.findFirst({
|
||||||
|
where: eq(registry.registryId, registryId),
|
||||||
|
});
|
||||||
|
if (!registryResponse) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "NOT_FOUND",
|
||||||
|
message: "Registry not found",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return registryResponse;
|
||||||
|
};
|
||||||
|
|
||||||
export const findAllRegistryByOrganizationId = async (
|
export const findAllRegistryByOrganizationId = async (
|
||||||
organizationId: string,
|
organizationId: string,
|
||||||
) => {
|
) => {
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import {
|
|||||||
deployments as deploymentsSchema,
|
deployments as deploymentsSchema,
|
||||||
rollbacks,
|
rollbacks,
|
||||||
} from "../db/schema";
|
} from "../db/schema";
|
||||||
import type { ApplicationNested } from "../utils/builders";
|
|
||||||
import { getRegistryTag } from "../utils/cluster/upload";
|
import { getRegistryTag } from "../utils/cluster/upload";
|
||||||
import {
|
import {
|
||||||
calculateResources,
|
calculateResources,
|
||||||
@@ -23,7 +22,11 @@ import { findDeploymentById } from "./deployment";
|
|||||||
import type { Mount } from "./mount";
|
import type { Mount } from "./mount";
|
||||||
import type { Port } from "./port";
|
import type { Port } from "./port";
|
||||||
import type { Project } from "./project";
|
import type { Project } from "./project";
|
||||||
import { type Registry, safeDockerLoginCommand } from "./registry";
|
import {
|
||||||
|
findRegistryByIdWithCredentials,
|
||||||
|
type Registry,
|
||||||
|
safeDockerLoginCommand,
|
||||||
|
} from "./registry";
|
||||||
|
|
||||||
export const createRollback = async (
|
export const createRollback = async (
|
||||||
input: z.infer<typeof createRollbackSchema>,
|
input: z.infer<typeof createRollbackSchema>,
|
||||||
@@ -56,11 +59,29 @@ export const createRollback = async (
|
|||||||
...rest
|
...rest
|
||||||
} = await findApplicationById(deployment.applicationId);
|
} = await findApplicationById(deployment.applicationId);
|
||||||
|
|
||||||
|
const registry = rest.registryId
|
||||||
|
? await findRegistryByIdWithCredentials(rest.registryId)
|
||||||
|
: rest.registry;
|
||||||
|
const buildRegistry = rest.buildRegistryId
|
||||||
|
? await findRegistryByIdWithCredentials(rest.buildRegistryId)
|
||||||
|
: rest.buildRegistry;
|
||||||
|
const rollbackRegistry = rest.rollbackRegistryId
|
||||||
|
? await findRegistryByIdWithCredentials(rest.rollbackRegistryId)
|
||||||
|
: rest.rollbackRegistry;
|
||||||
|
|
||||||
|
const fullContextWithCredentials = {
|
||||||
|
...rest,
|
||||||
|
registry,
|
||||||
|
buildRegistry,
|
||||||
|
rollbackRegistry,
|
||||||
|
};
|
||||||
|
|
||||||
await tx
|
await tx
|
||||||
.update(rollbacks)
|
.update(rollbacks)
|
||||||
.set({
|
.set({
|
||||||
image: tagImage,
|
image: tagImage,
|
||||||
fullContext: rest,
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
fullContext: fullContextWithCredentials as any,
|
||||||
})
|
})
|
||||||
.where(eq(rollbacks.rollbackId, rollback.rollbackId));
|
.where(eq(rollbacks.rollbackId, rollback.rollbackId));
|
||||||
|
|
||||||
@@ -162,7 +183,6 @@ export const rollback = async (rollbackId: string) => {
|
|||||||
if (!result.fullContext) {
|
if (!result.fullContext) {
|
||||||
throw new Error("Rollback context not found");
|
throw new Error("Rollback context not found");
|
||||||
}
|
}
|
||||||
// Use the full context for rollback
|
|
||||||
await rollbackApplication(
|
await rollbackApplication(
|
||||||
application.appName,
|
application.appName,
|
||||||
result.image || "",
|
result.image || "",
|
||||||
@@ -198,24 +218,25 @@ const rollbackApplication = async (
|
|||||||
};
|
};
|
||||||
mounts: Mount[];
|
mounts: Mount[];
|
||||||
ports: Port[];
|
ports: Port[];
|
||||||
rollbackRegistry?: Registry;
|
rollbackRegistry?: Registry | null;
|
||||||
},
|
},
|
||||||
) => {
|
) => {
|
||||||
if (!fullContext) {
|
if (!fullContext) {
|
||||||
throw new Error("Full context is required for rollback");
|
throw new Error("Full context is required for rollback");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const rollbackRegistry = fullContext.rollbackRegistry ?? undefined;
|
||||||
|
|
||||||
// Ensure Docker daemon is authenticated with the rollback registry
|
// Ensure Docker daemon is authenticated with the rollback registry
|
||||||
// before updating the swarm service. The authconfig in CreateServiceOptions
|
// before updating the swarm service. The authconfig in CreateServiceOptions
|
||||||
// alone is not sufficient — Docker Swarm also relies on the daemon's
|
// alone is not sufficient — Docker Swarm also relies on the daemon's
|
||||||
// cached credentials (~/.docker/config.json) to distribute auth to nodes.
|
// cached credentials (~/.docker/config.json) to distribute auth to nodes.
|
||||||
if (fullContext.rollbackRegistry) {
|
if (rollbackRegistry) {
|
||||||
await dockerLoginForRegistry(fullContext.rollbackRegistry, serverId);
|
await dockerLoginForRegistry(rollbackRegistry, serverId);
|
||||||
}
|
}
|
||||||
|
|
||||||
const docker = await getRemoteDocker(serverId);
|
const docker = await getRemoteDocker(serverId);
|
||||||
|
|
||||||
// Use the same configuration as mechanizeDockerContainer
|
|
||||||
const {
|
const {
|
||||||
env,
|
env,
|
||||||
mounts,
|
mounts,
|
||||||
@@ -246,7 +267,9 @@ const rollbackApplication = async (
|
|||||||
UpdateConfig,
|
UpdateConfig,
|
||||||
Networks,
|
Networks,
|
||||||
Ulimits,
|
Ulimits,
|
||||||
} = generateConfigContainer(fullContext as ApplicationNested);
|
} = generateConfigContainer(
|
||||||
|
fullContext as Parameters<typeof generateConfigContainer>[0],
|
||||||
|
);
|
||||||
|
|
||||||
const bindsMount = generateBindMounts(mounts);
|
const bindsMount = generateBindMounts(mounts);
|
||||||
const envVariables = prepareEnvironmentVariables(
|
const envVariables = prepareEnvironmentVariables(
|
||||||
@@ -254,18 +277,16 @@ const rollbackApplication = async (
|
|||||||
fullContext.environment.project.env,
|
fullContext.environment.project.env,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Build the full registry image path if rollbackRegistry is available
|
|
||||||
// e.g., "appName:v5" -> "siumauricio/appName:v5" or "registry.com/prefix/appName:v5"
|
|
||||||
let rollbackImage = image;
|
let rollbackImage = image;
|
||||||
if (fullContext.rollbackRegistry) {
|
if (rollbackRegistry) {
|
||||||
rollbackImage = getRegistryTag(fullContext.rollbackRegistry, image);
|
rollbackImage = getRegistryTag(rollbackRegistry, image);
|
||||||
}
|
}
|
||||||
|
|
||||||
const settings: CreateServiceOptions = {
|
const settings: CreateServiceOptions = {
|
||||||
authconfig: {
|
authconfig: {
|
||||||
password: fullContext.rollbackRegistry?.password || "",
|
password: rollbackRegistry?.password || "",
|
||||||
username: fullContext.rollbackRegistry?.username || "",
|
username: rollbackRegistry?.username || "",
|
||||||
serveraddress: fullContext.rollbackRegistry?.registryUrl || "",
|
serveraddress: rollbackRegistry?.registryUrl || "",
|
||||||
},
|
},
|
||||||
Name: appName,
|
Name: appName,
|
||||||
TaskTemplate: {
|
TaskTemplate: {
|
||||||
|
|||||||
@@ -84,7 +84,12 @@ export const findVolumeBackupById = async (volumeBackupId: string) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
destination: true,
|
destination: {
|
||||||
|
columns: {
|
||||||
|
accessKey: false,
|
||||||
|
secretAccessKey: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
createDeploymentBackup,
|
createDeploymentBackup,
|
||||||
updateDeploymentStatus,
|
updateDeploymentStatus,
|
||||||
} from "@dokploy/server/services/deployment";
|
} from "@dokploy/server/services/deployment";
|
||||||
|
import { findDestinationById } from "@dokploy/server/services/destination";
|
||||||
import { findEnvironmentById } from "@dokploy/server/services/environment";
|
import { findEnvironmentById } from "@dokploy/server/services/environment";
|
||||||
import { findProjectById } from "@dokploy/server/services/project";
|
import { findProjectById } from "@dokploy/server/services/project";
|
||||||
import { sendDatabaseBackupNotifications } from "../notifications/database-backup";
|
import { sendDatabaseBackupNotifications } from "../notifications/database-backup";
|
||||||
@@ -23,7 +24,7 @@ export const runComposeBackup = async (
|
|||||||
const environment = await findEnvironmentById(environmentId);
|
const environment = await findEnvironmentById(environmentId);
|
||||||
const project = await findProjectById(environment.projectId);
|
const project = await findProjectById(environment.projectId);
|
||||||
const { prefix, databaseType, serviceName } = backup;
|
const { prefix, databaseType, serviceName } = backup;
|
||||||
const destination = backup.destination;
|
const destination = await findDestinationById(backup.destinationId);
|
||||||
const backupFileName = `${getBackupTimestamp()}.${databaseType === "mongo" ? "bson" : "sql"}.gz`;
|
const backupFileName = `${getBackupTimestamp()}.${databaseType === "mongo" ? "bson" : "sql"}.gz`;
|
||||||
const s3AppName = serviceName ? `${appName}_${serviceName}` : appName;
|
const s3AppName = serviceName ? `${appName}_${serviceName}` : appName;
|
||||||
const bucketDestination = `${s3AppName}/${normalizeS3Path(prefix)}${backupFileName}`;
|
const bucketDestination = `${s3AppName}/${normalizeS3Path(prefix)}${backupFileName}`;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { CLEANUP_CRON_JOB } from "@dokploy/server/constants";
|
import { CLEANUP_CRON_JOB } from "@dokploy/server/constants";
|
||||||
import { member } from "@dokploy/server/db/schema";
|
import { member } from "@dokploy/server/db/schema";
|
||||||
import type { BackupSchedule } from "@dokploy/server/services/backup";
|
import type { BackupSchedule } from "@dokploy/server/services/backup";
|
||||||
|
import { findDestinationById } from "@dokploy/server/services/destination";
|
||||||
import { getAllServers } from "@dokploy/server/services/server";
|
import { getAllServers } from "@dokploy/server/services/server";
|
||||||
import { getWebServerSettings } from "@dokploy/server/services/web-server-settings";
|
import { getWebServerSettings } from "@dokploy/server/services/web-server-settings";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
@@ -131,9 +132,10 @@ export const keepLatestNBackups = async (
|
|||||||
if (!backup.keepLatestCount) return;
|
if (!backup.keepLatestCount) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const rcloneFlags = getS3Credentials(backup.destination);
|
const destination = await findDestinationById(backup.destinationId);
|
||||||
|
const rcloneFlags = getS3Credentials(destination);
|
||||||
const appName = getServiceAppName(backup);
|
const appName = getServiceAppName(backup);
|
||||||
const backupFilesPath = `:s3:${backup.destination.bucket}/${appName}/${normalizeS3Path(backup.prefix)}`;
|
const backupFilesPath = `:s3:${destination.bucket}/${appName}/${normalizeS3Path(backup.prefix)}`;
|
||||||
|
|
||||||
// --include "*.bson.gz" or "*.sql.gz" or "*.zip" ensures nothing else other than the dokploy backup files are touched by rclone
|
// --include "*.bson.gz" or "*.sql.gz" or "*.zip" ensures nothing else other than the dokploy backup files are touched by rclone
|
||||||
const rcloneList = `rclone lsf ${rcloneFlags.join(" ")} --include "*${backup.databaseType === "web-server" ? ".zip" : ".{sql.gz,bson.gz}"}" ${backupFilesPath}`;
|
const rcloneList = `rclone lsf ${rcloneFlags.join(" ")} --include "*${backup.databaseType === "web-server" ? ".zip" : ".{sql.gz,bson.gz}"}" ${backupFilesPath}`;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
createDeploymentBackup,
|
createDeploymentBackup,
|
||||||
updateDeploymentStatus,
|
updateDeploymentStatus,
|
||||||
} from "@dokploy/server/services/deployment";
|
} from "@dokploy/server/services/deployment";
|
||||||
|
import { findDestinationById } from "@dokploy/server/services/destination";
|
||||||
import { findEnvironmentById } from "@dokploy/server/services/environment";
|
import { findEnvironmentById } from "@dokploy/server/services/environment";
|
||||||
import type { Libsql } from "@dokploy/server/services/libsql";
|
import type { Libsql } from "@dokploy/server/services/libsql";
|
||||||
import { findProjectById } from "@dokploy/server/services/project";
|
import { findProjectById } from "@dokploy/server/services/project";
|
||||||
@@ -29,7 +30,7 @@ export const runLibsqlBackup = async (
|
|||||||
description: "Initializing Backup",
|
description: "Initializing Backup",
|
||||||
});
|
});
|
||||||
const { prefix } = backup;
|
const { prefix } = backup;
|
||||||
const destination = backup.destination;
|
const destination = await findDestinationById(backup.destinationId);
|
||||||
const backupFileName = `${getBackupTimestamp()}.sql.gz`;
|
const backupFileName = `${getBackupTimestamp()}.sql.gz`;
|
||||||
const bucketDestination = `${appName}/${normalizeS3Path(prefix)}${backupFileName}`;
|
const bucketDestination = `${appName}/${normalizeS3Path(prefix)}${backupFileName}`;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
createDeploymentBackup,
|
createDeploymentBackup,
|
||||||
updateDeploymentStatus,
|
updateDeploymentStatus,
|
||||||
} from "@dokploy/server/services/deployment";
|
} from "@dokploy/server/services/deployment";
|
||||||
|
import { findDestinationById } from "@dokploy/server/services/destination";
|
||||||
import { findEnvironmentById } from "@dokploy/server/services/environment";
|
import { findEnvironmentById } from "@dokploy/server/services/environment";
|
||||||
import type { Mariadb } from "@dokploy/server/services/mariadb";
|
import type { Mariadb } from "@dokploy/server/services/mariadb";
|
||||||
import { findProjectById } from "@dokploy/server/services/project";
|
import { findProjectById } from "@dokploy/server/services/project";
|
||||||
@@ -23,7 +24,7 @@ export const runMariadbBackup = async (
|
|||||||
const environment = await findEnvironmentById(environmentId);
|
const environment = await findEnvironmentById(environmentId);
|
||||||
const project = await findProjectById(environment.projectId);
|
const project = await findProjectById(environment.projectId);
|
||||||
const { prefix } = backup;
|
const { prefix } = backup;
|
||||||
const destination = backup.destination;
|
const destination = await findDestinationById(backup.destinationId);
|
||||||
const backupFileName = `${getBackupTimestamp()}.sql.gz`;
|
const backupFileName = `${getBackupTimestamp()}.sql.gz`;
|
||||||
const bucketDestination = `${appName}/${normalizeS3Path(prefix)}${backupFileName}`;
|
const bucketDestination = `${appName}/${normalizeS3Path(prefix)}${backupFileName}`;
|
||||||
const deployment = await createDeploymentBackup({
|
const deployment = await createDeploymentBackup({
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
createDeploymentBackup,
|
createDeploymentBackup,
|
||||||
updateDeploymentStatus,
|
updateDeploymentStatus,
|
||||||
} from "@dokploy/server/services/deployment";
|
} from "@dokploy/server/services/deployment";
|
||||||
|
import { findDestinationById } from "@dokploy/server/services/destination";
|
||||||
import { findEnvironmentById } from "@dokploy/server/services/environment";
|
import { findEnvironmentById } from "@dokploy/server/services/environment";
|
||||||
import type { Mongo } from "@dokploy/server/services/mongo";
|
import type { Mongo } from "@dokploy/server/services/mongo";
|
||||||
import { findProjectById } from "@dokploy/server/services/project";
|
import { findProjectById } from "@dokploy/server/services/project";
|
||||||
@@ -20,7 +21,7 @@ export const runMongoBackup = async (mongo: Mongo, backup: BackupSchedule) => {
|
|||||||
const environment = await findEnvironmentById(environmentId);
|
const environment = await findEnvironmentById(environmentId);
|
||||||
const project = await findProjectById(environment.projectId);
|
const project = await findProjectById(environment.projectId);
|
||||||
const { prefix } = backup;
|
const { prefix } = backup;
|
||||||
const destination = backup.destination;
|
const destination = await findDestinationById(backup.destinationId);
|
||||||
const backupFileName = `${getBackupTimestamp()}.bson.gz`;
|
const backupFileName = `${getBackupTimestamp()}.bson.gz`;
|
||||||
const bucketDestination = `${appName}/${normalizeS3Path(prefix)}${backupFileName}`;
|
const bucketDestination = `${appName}/${normalizeS3Path(prefix)}${backupFileName}`;
|
||||||
const deployment = await createDeploymentBackup({
|
const deployment = await createDeploymentBackup({
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
createDeploymentBackup,
|
createDeploymentBackup,
|
||||||
updateDeploymentStatus,
|
updateDeploymentStatus,
|
||||||
} from "@dokploy/server/services/deployment";
|
} from "@dokploy/server/services/deployment";
|
||||||
|
import { findDestinationById } from "@dokploy/server/services/destination";
|
||||||
import { findEnvironmentById } from "@dokploy/server/services/environment";
|
import { findEnvironmentById } from "@dokploy/server/services/environment";
|
||||||
import type { MySql } from "@dokploy/server/services/mysql";
|
import type { MySql } from "@dokploy/server/services/mysql";
|
||||||
import { findProjectById } from "@dokploy/server/services/project";
|
import { findProjectById } from "@dokploy/server/services/project";
|
||||||
@@ -20,7 +21,7 @@ export const runMySqlBackup = async (mysql: MySql, backup: BackupSchedule) => {
|
|||||||
const environment = await findEnvironmentById(environmentId);
|
const environment = await findEnvironmentById(environmentId);
|
||||||
const project = await findProjectById(environment.projectId);
|
const project = await findProjectById(environment.projectId);
|
||||||
const { prefix } = backup;
|
const { prefix } = backup;
|
||||||
const destination = backup.destination;
|
const destination = await findDestinationById(backup.destinationId);
|
||||||
const backupFileName = `${getBackupTimestamp()}.sql.gz`;
|
const backupFileName = `${getBackupTimestamp()}.sql.gz`;
|
||||||
const bucketDestination = `${appName}/${normalizeS3Path(prefix)}${backupFileName}`;
|
const bucketDestination = `${appName}/${normalizeS3Path(prefix)}${backupFileName}`;
|
||||||
const deployment = await createDeploymentBackup({
|
const deployment = await createDeploymentBackup({
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
createDeploymentBackup,
|
createDeploymentBackup,
|
||||||
updateDeploymentStatus,
|
updateDeploymentStatus,
|
||||||
} from "@dokploy/server/services/deployment";
|
} from "@dokploy/server/services/deployment";
|
||||||
|
import { findDestinationById } from "@dokploy/server/services/destination";
|
||||||
import { findEnvironmentById } from "@dokploy/server/services/environment";
|
import { findEnvironmentById } from "@dokploy/server/services/environment";
|
||||||
import type { Postgres } from "@dokploy/server/services/postgres";
|
import type { Postgres } from "@dokploy/server/services/postgres";
|
||||||
import { findProjectById } from "@dokploy/server/services/project";
|
import { findProjectById } from "@dokploy/server/services/project";
|
||||||
@@ -29,7 +30,7 @@ export const runPostgresBackup = async (
|
|||||||
description: "Initializing Backup",
|
description: "Initializing Backup",
|
||||||
});
|
});
|
||||||
const { prefix } = backup;
|
const { prefix } = backup;
|
||||||
const destination = backup.destination;
|
const destination = await findDestinationById(backup.destinationId);
|
||||||
const backupFileName = `${getBackupTimestamp()}.sql.gz`;
|
const backupFileName = `${getBackupTimestamp()}.sql.gz`;
|
||||||
const bucketDestination = `${appName}/${normalizeS3Path(prefix)}${backupFileName}`;
|
const bucketDestination = `${appName}/${normalizeS3Path(prefix)}${backupFileName}`;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { findRegistryByIdWithCredentials } from "@dokploy/server/services/registry";
|
||||||
import type { InferResultType } from "@dokploy/server/types/with";
|
import type { InferResultType } from "@dokploy/server/types/with";
|
||||||
import type { CreateServiceOptions } from "dockerode";
|
import type { CreateServiceOptions } from "dockerode";
|
||||||
import { getRegistryTag, uploadImageRemoteCommand } from "../cluster/upload";
|
import { getRegistryTag, uploadImageRemoteCommand } from "../cluster/upload";
|
||||||
@@ -28,9 +29,9 @@ export type ApplicationNested = InferResultType<
|
|||||||
security: true;
|
security: true;
|
||||||
redirects: true;
|
redirects: true;
|
||||||
ports: true;
|
ports: true;
|
||||||
registry: true;
|
registry: { columns: { password: false } };
|
||||||
buildRegistry: true;
|
buildRegistry: { columns: { password: false } };
|
||||||
rollbackRegistry: true;
|
rollbackRegistry: { columns: { password: false } };
|
||||||
deployments: true;
|
deployments: true;
|
||||||
environment: { with: { project: true } };
|
environment: { with: { project: true } };
|
||||||
}
|
}
|
||||||
@@ -121,8 +122,8 @@ export const mechanizeDockerContainer = async (
|
|||||||
application.environment.env,
|
application.environment.env,
|
||||||
);
|
);
|
||||||
|
|
||||||
const image = getImageName(application);
|
const image = await getImageName(application);
|
||||||
const authConfig = getAuthConfig(application);
|
const authConfig = await getAuthConfig(application);
|
||||||
const docker = await getRemoteDocker(application.serverId);
|
const docker = await getRemoteDocker(application.serverId);
|
||||||
|
|
||||||
const settings: CreateServiceOptions = {
|
const settings: CreateServiceOptions = {
|
||||||
@@ -190,7 +191,7 @@ export const mechanizeDockerContainer = async (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getImageName = (application: ApplicationNested) => {
|
const getImageName = async (application: ApplicationNested) => {
|
||||||
const { appName, sourceType, dockerImage, registry, buildRegistry } =
|
const { appName, sourceType, dockerImage, registry, buildRegistry } =
|
||||||
application;
|
application;
|
||||||
const imageName = `${appName}:latest`;
|
const imageName = `${appName}:latest`;
|
||||||
@@ -199,18 +200,18 @@ const getImageName = (application: ApplicationNested) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (registry) {
|
if (registry) {
|
||||||
const registryTag = getRegistryTag(registry, imageName);
|
const r = await findRegistryByIdWithCredentials(registry.registryId);
|
||||||
return registryTag;
|
return getRegistryTag(r, imageName);
|
||||||
}
|
}
|
||||||
if (buildRegistry) {
|
if (buildRegistry) {
|
||||||
const registryTag = getRegistryTag(buildRegistry, imageName);
|
const r = await findRegistryByIdWithCredentials(buildRegistry.registryId);
|
||||||
return registryTag;
|
return getRegistryTag(r, imageName);
|
||||||
}
|
}
|
||||||
|
|
||||||
return imageName;
|
return imageName;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getAuthConfig = (application: ApplicationNested) => {
|
export const getAuthConfig = async (application: ApplicationNested) => {
|
||||||
const {
|
const {
|
||||||
registry,
|
registry,
|
||||||
buildRegistry,
|
buildRegistry,
|
||||||
@@ -222,23 +223,21 @@ export const getAuthConfig = (application: ApplicationNested) => {
|
|||||||
|
|
||||||
if (sourceType === "docker") {
|
if (sourceType === "docker") {
|
||||||
if (username && password) {
|
if (username && password) {
|
||||||
return {
|
return { password, username, serveraddress: registryUrl || "" };
|
||||||
password,
|
|
||||||
username,
|
|
||||||
serveraddress: registryUrl || "",
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
} else if (registry) {
|
} else if (registry) {
|
||||||
|
const r = await findRegistryByIdWithCredentials(registry.registryId);
|
||||||
return {
|
return {
|
||||||
password: registry.password,
|
password: r.password,
|
||||||
username: registry.username,
|
username: r.username,
|
||||||
serveraddress: registry.registryUrl,
|
serveraddress: r.registryUrl,
|
||||||
};
|
};
|
||||||
} else if (buildRegistry) {
|
} else if (buildRegistry) {
|
||||||
|
const r = await findRegistryByIdWithCredentials(buildRegistry.registryId);
|
||||||
return {
|
return {
|
||||||
password: buildRegistry.password,
|
password: r.password,
|
||||||
username: buildRegistry.username,
|
username: r.username,
|
||||||
serveraddress: buildRegistry.registryUrl,
|
serveraddress: r.registryUrl,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { findAllDeploymentsByApplicationId } from "@dokploy/server/services/deployment";
|
import { findAllDeploymentsByApplicationId } from "@dokploy/server/services/deployment";
|
||||||
import type { Registry } from "@dokploy/server/services/registry";
|
import {
|
||||||
|
findRegistryByIdWithCredentials,
|
||||||
|
type Registry,
|
||||||
|
} from "@dokploy/server/services/registry";
|
||||||
import { createRollback } from "@dokploy/server/services/rollbacks";
|
import { createRollback } from "@dokploy/server/services/rollbacks";
|
||||||
import type { ApplicationNested } from "../builders";
|
import type { ApplicationNested } from "../builders";
|
||||||
|
|
||||||
@@ -22,19 +25,19 @@ export const uploadImageRemoteCommand = async (
|
|||||||
|
|
||||||
const commands: string[] = [];
|
const commands: string[] = [];
|
||||||
if (registry) {
|
if (registry) {
|
||||||
const registryTag = getRegistryTag(registry, imageName);
|
const r = await findRegistryByIdWithCredentials(registry.registryId);
|
||||||
|
const registryTag = getRegistryTag(r, imageName);
|
||||||
if (registryTag) {
|
if (registryTag) {
|
||||||
commands.push(`echo "📦 [Enabled Registry Swarm]"`);
|
commands.push(`echo "📦 [Enabled Registry Swarm]"`);
|
||||||
commands.push(getRegistryCommands(registry, imageName, registryTag));
|
commands.push(getRegistryCommands(r, imageName, registryTag));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (buildRegistry) {
|
if (buildRegistry) {
|
||||||
const buildRegistryTag = getRegistryTag(buildRegistry, imageName);
|
const r = await findRegistryByIdWithCredentials(buildRegistry.registryId);
|
||||||
|
const buildRegistryTag = getRegistryTag(r, imageName);
|
||||||
if (buildRegistryTag) {
|
if (buildRegistryTag) {
|
||||||
commands.push(`echo "🔑 [Enabled Build Registry]"`);
|
commands.push(`echo "🔑 [Enabled Build Registry]"`);
|
||||||
commands.push(
|
commands.push(getRegistryCommands(r, imageName, buildRegistryTag));
|
||||||
getRegistryCommands(buildRegistry, imageName, buildRegistryTag),
|
|
||||||
);
|
|
||||||
commands.push(
|
commands.push(
|
||||||
`echo "⚠️ INFO: After the build is finished, you need to wait a few seconds for the server to download the image and run the container."`,
|
`echo "⚠️ INFO: After the build is finished, you need to wait a few seconds for the server to download the image and run the container."`,
|
||||||
);
|
);
|
||||||
@@ -57,15 +60,13 @@ export const uploadImageRemoteCommand = async (
|
|||||||
deploymentId: deploymentId,
|
deploymentId: deploymentId,
|
||||||
});
|
});
|
||||||
|
|
||||||
const rollbackRegistryTag = getRegistryTag(
|
const r = await findRegistryByIdWithCredentials(
|
||||||
rollbackRegistry,
|
rollbackRegistry.registryId,
|
||||||
rollback?.image || "",
|
|
||||||
);
|
);
|
||||||
|
const rollbackRegistryTag = getRegistryTag(r, rollback?.image || "");
|
||||||
if (rollbackRegistryTag) {
|
if (rollbackRegistryTag) {
|
||||||
commands.push(`echo "🔄 [Enabled Rollback Registry]"`);
|
commands.push(`echo "🔄 [Enabled Rollback Registry]"`);
|
||||||
commands.push(
|
commands.push(getRegistryCommands(r, imageName, rollbackRegistryTag));
|
||||||
getRegistryCommands(rollbackRegistry, imageName, rollbackRegistryTag),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -74,6 +75,7 @@ export const uploadImageRemoteCommand = async (
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract the repository name from imageName by taking the last part after '/'
|
* Extract the repository name from imageName by taking the last part after '/'
|
||||||
* Examples:
|
* Examples:
|
||||||
@@ -117,17 +119,17 @@ const getRegistryCommands = (
|
|||||||
): string => {
|
): string => {
|
||||||
return `
|
return `
|
||||||
echo "📦 [Enabled Registry] Uploading image to '${registry.registryType}' | '${registryTag}'" ;
|
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" ;
|
echo "❌ DockerHub Failed" ;
|
||||||
exit 1;
|
exit 1;
|
||||||
}
|
}
|
||||||
echo "✅ Registry Login Success" ;
|
echo "✅ Registry Login Success" ;
|
||||||
docker tag ${imageName} ${registryTag} || {
|
docker tag ${imageName} ${registryTag} || {
|
||||||
echo "❌ Error tagging image" ;
|
echo "❌ Error tagging image" ;
|
||||||
exit 1;
|
exit 1;
|
||||||
}
|
}
|
||||||
echo "✅ Image Tagged" ;
|
echo "✅ Image Tagged" ;
|
||||||
docker push ${registryTag} || {
|
docker push ${registryTag} || {
|
||||||
echo "❌ Error pushing image" ;
|
echo "❌ Error pushing image" ;
|
||||||
exit 1;
|
exit 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { paths } from "@dokploy/server/constants";
|
import { paths } from "@dokploy/server/constants";
|
||||||
import { findComposeById } from "@dokploy/server/services/compose";
|
import { findComposeById } from "@dokploy/server/services/compose";
|
||||||
|
import { findDestinationById } from "@dokploy/server/services/destination";
|
||||||
import type { findVolumeBackupById } from "@dokploy/server/services/volume-backups";
|
import type { findVolumeBackupById } from "@dokploy/server/services/volume-backups";
|
||||||
import {
|
import {
|
||||||
getBackupTimestamp,
|
getBackupTimestamp,
|
||||||
@@ -31,14 +32,14 @@ export const backupVolume = async (
|
|||||||
volumeBackup: Awaited<ReturnType<typeof findVolumeBackupById>>,
|
volumeBackup: Awaited<ReturnType<typeof findVolumeBackupById>>,
|
||||||
) => {
|
) => {
|
||||||
const { serviceType, volumeName, turnOff, prefix } = volumeBackup;
|
const { serviceType, volumeName, turnOff, prefix } = volumeBackup;
|
||||||
|
const destination = await findDestinationById(volumeBackup.destinationId);
|
||||||
const serverId =
|
const serverId =
|
||||||
volumeBackup.application?.serverId || volumeBackup.compose?.serverId;
|
volumeBackup.application?.serverId || volumeBackup.compose?.serverId;
|
||||||
const { VOLUME_BACKUPS_PATH, VOLUME_BACKUP_LOCK_PATH } = paths(!!serverId);
|
const { VOLUME_BACKUPS_PATH, VOLUME_BACKUP_LOCK_PATH } = paths(!!serverId);
|
||||||
const destination = volumeBackup.destination;
|
|
||||||
const s3AppName = getVolumeServiceAppName(volumeBackup);
|
const s3AppName = getVolumeServiceAppName(volumeBackup);
|
||||||
const backupFileName = `${volumeName}-${getBackupTimestamp()}.tar`;
|
const backupFileName = `${volumeName}-${getBackupTimestamp()}.tar`;
|
||||||
const bucketDestination = `${s3AppName}/${normalizeS3Path(prefix || "")}${backupFileName}`;
|
const bucketDestination = `${s3AppName}/${normalizeS3Path(prefix || "")}${backupFileName}`;
|
||||||
const rcloneFlags = getS3Credentials(volumeBackup.destination);
|
const rcloneFlags = getS3Credentials(destination);
|
||||||
const rcloneDestination = `:s3:${destination.bucket}/${bucketDestination}`;
|
const rcloneDestination = `:s3:${destination.bucket}/${bucketDestination}`;
|
||||||
const volumeBackupPath = path.join(VOLUME_BACKUPS_PATH, volumeBackup.appName);
|
const volumeBackupPath = path.join(VOLUME_BACKUPS_PATH, volumeBackup.appName);
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
createDeploymentVolumeBackup,
|
createDeploymentVolumeBackup,
|
||||||
updateDeploymentStatus,
|
updateDeploymentStatus,
|
||||||
} from "@dokploy/server/services/deployment";
|
} from "@dokploy/server/services/deployment";
|
||||||
|
import { findDestinationById } from "@dokploy/server/services/destination";
|
||||||
import { findVolumeBackupById } from "@dokploy/server/services/volume-backups";
|
import { findVolumeBackupById } from "@dokploy/server/services/volume-backups";
|
||||||
import {
|
import {
|
||||||
execAsync,
|
execAsync,
|
||||||
@@ -77,7 +78,8 @@ const cleanupOldVolumeBackups = async (
|
|||||||
volumeBackup: Awaited<ReturnType<typeof findVolumeBackupById>>,
|
volumeBackup: Awaited<ReturnType<typeof findVolumeBackupById>>,
|
||||||
serverId?: string | null,
|
serverId?: string | null,
|
||||||
) => {
|
) => {
|
||||||
const { keepLatestCount, destination, prefix, volumeName } = volumeBackup;
|
const { keepLatestCount, prefix, volumeName } = volumeBackup;
|
||||||
|
const destination = await findDestinationById(volumeBackup.destinationId);
|
||||||
|
|
||||||
if (!keepLatestCount) return;
|
if (!keepLatestCount) return;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user