Files
dokploy/packages/server/src/services/docker-volume.ts
T

162 lines
4.2 KiB
TypeScript

import {
execAsync,
execAsyncRemote,
} from "@dokploy/server/utils/process/execAsync";
import { quote } from "shell-quote";
import { CONTAINER_FILE_SIZE_LIMIT } from "./docker";
export interface DockerVolume {
Name: string;
Driver: string;
Scope: string;
Mountpoint: string;
Labels: string;
Availability?: string;
Group?: string;
Links?: string;
Size?: string;
Status?: string;
}
const VOLUME_MOUNT = "/__volume";
export const getVolumes = async (serverId?: string) => {
try {
const command = "docker volume ls --format '{{json .}}'";
const { stdout } = serverId
? await execAsyncRemote(serverId, command)
: await execAsync(command);
return stdout
.trim()
.split("\n")
.filter(Boolean)
.map((line) => JSON.parse(line) as DockerVolume);
} catch (error) {
console.error(error);
return [];
}
};
export const getVolumesSize = async (serverId?: string) => {
try {
const command = "docker system df -v --format '{{json .}}'";
const { stdout } = serverId
? await execAsyncRemote(serverId, command)
: await execAsync(command);
const diskUsage = JSON.parse(stdout.trim());
return ((diskUsage?.Volumes ?? []) as Partial<DockerVolume>[])
.filter((volume) => volume?.Name)
.map((volume) => ({
name: volume.Name as string,
size: volume.Size ?? null,
}));
} catch (error) {
console.error(error);
return [];
}
};
export const getVolumeConfig = async (
volumeName: string,
serverId?: string,
) => {
const command = `docker volume inspect ${quote([String(volumeName ?? "")])}`;
const { stdout } = serverId
? await execAsyncRemote(serverId, command)
: await execAsync(command);
return JSON.parse(stdout.trim())[0];
};
export const removeVolume = async (volumeName: string, serverId?: string) => {
const command = `docker volume rm ${quote([String(volumeName ?? "")])}`;
if (serverId) {
await execAsyncRemote(serverId, command);
} else {
await execAsync(command);
}
};
export const listVolumeFiles = async (
volumeName: string,
path: string,
serverId?: string,
) => {
const command = `docker run --rm -v ${quote([`${volumeName}:${VOLUME_MOUNT}:ro`])} busybox ls -1Ap ${quote([`${VOLUME_MOUNT}${path}`])}`;
const { stdout } = serverId
? await execAsyncRemote(serverId, command)
: await execAsync(command);
return stdout
.split("\n")
.filter(Boolean)
.map((entry) => ({
name: entry.endsWith("/") ? entry.slice(0, -1) : entry,
isDirectory: entry.endsWith("/"),
}))
.sort((a, b) => {
if (a.isDirectory !== b.isDirectory) {
return a.isDirectory ? -1 : 1;
}
return a.name.localeCompare(b.name);
});
};
export const readVolumeFile = async (
volumeName: string,
filePath: string,
serverId?: string,
) => {
const command = `docker run --rm -v ${quote([`${volumeName}:${VOLUME_MOUNT}:ro`])} busybox cat ${quote([`${VOLUME_MOUNT}${filePath}`])} | head -c ${CONTAINER_FILE_SIZE_LIMIT + 1} | base64 | tr -d '\\n'`;
const { stdout, stderr } = serverId
? await execAsyncRemote(serverId, command)
: await execAsync(command);
if (stderr && !stdout) {
throw new Error(stderr);
}
const buffer = Buffer.from(stdout.trim(), "base64");
return {
content: buffer.subarray(0, CONTAINER_FILE_SIZE_LIMIT).toString("base64"),
truncated: buffer.byteLength > CONTAINER_FILE_SIZE_LIMIT,
};
};
export const writeVolumeFile = async (
volumeName: string,
filePath: string,
content: string,
serverId?: string,
) => {
const base64Content = Buffer.from(content, "utf8").toString("base64");
if (base64Content.length > CONTAINER_FILE_SIZE_LIMIT * 2) {
throw new Error("File is too large to save from the editor (max 512KB)");
}
const innerCommand = `printf '%s' ${base64Content} | base64 -d > ${quote([`${VOLUME_MOUNT}${filePath}`])}`;
const command = `docker run --rm -v ${quote([`${volumeName}:${VOLUME_MOUNT}`])} busybox sh -c ${quote([innerCommand])}`;
if (serverId) {
await execAsyncRemote(serverId, command);
} else {
await execAsync(command);
}
};
export const deleteVolumeFile = async (
volumeName: string,
path: string,
serverId?: string,
) => {
const command = `docker run --rm -v ${quote([`${volumeName}:${VOLUME_MOUNT}`])} busybox rm -rf ${quote([`${VOLUME_MOUNT}${path}`])}`;
if (serverId) {
await execAsyncRemote(serverId, command);
} else {
await execAsync(command);
}
};