feat: новая функция
This commit is contained in:
@@ -23,11 +23,12 @@ RUN apt-get update && apt-get install -y \
|
|||||||
python3 \
|
python3 \
|
||||||
make \
|
make \
|
||||||
g++ \
|
g++ \
|
||||||
docker.io \
|
|
||||||
curl \
|
curl \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Docker CLI НЕ устанавливаем - используем Docker Socket + dockerode SDK
|
||||||
|
|
||||||
COPY package.json yarn.lock ./
|
COPY package.json yarn.lock ./
|
||||||
RUN yarn install --frozen-lockfile
|
RUN yarn install --frozen-lockfile
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
# This software is proprietary and confidential.
|
# This software is proprietary and confidential.
|
||||||
# For licensing inquiries: info@hb3-accelerator.com
|
# For licensing inquiries: info@hb3-accelerator.com
|
||||||
|
|
||||||
FROM node:20-alpine
|
FROM node:20-slim
|
||||||
|
|
||||||
# Добавляем метки для авторских прав
|
# Добавляем метки для авторских прав
|
||||||
LABEL maintainer="Тарабанов Александр Викторович <info@hb3-accelerator.com>"
|
LABEL maintainer="Тарабанов Александр Викторович <info@hb3-accelerator.com>"
|
||||||
@@ -13,8 +13,13 @@ LABEL website="https://hb3-accelerator.com"
|
|||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Устанавливаем только docker-cli (без демона) для Alpine Linux
|
# Устанавливаем системные зависимости для Debian
|
||||||
RUN apk update && apk add --no-cache docker-cli curl ca-certificates
|
RUN apt-get update && apt-get install -y \
|
||||||
|
curl \
|
||||||
|
ca-certificates \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Docker CLI НЕ устанавливаем - используем Docker Socket + dockerode SDK
|
||||||
|
|
||||||
COPY package.json yarn.lock ./
|
COPY package.json yarn.lock ./
|
||||||
RUN yarn install --frozen-lockfile --production
|
RUN yarn install --frozen-lockfile --production
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:16-alpine
|
image: postgres:16
|
||||||
container_name: dapp-postgres
|
container_name: dapp-postgres
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
logging:
|
logging:
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Этап 1: Сборка frontend
|
# Этап 1: Сборка frontend
|
||||||
FROM node:18-alpine AS frontend-builder
|
FROM node:20-slim AS frontend-builder
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Копируем файлы зависимостей
|
# Копируем файлы зависимостей
|
||||||
@@ -23,8 +23,8 @@ RUN apk add --no-cache curl
|
|||||||
# Копируем собранный frontend из первого этапа
|
# Копируем собранный frontend из первого этапа
|
||||||
COPY --from=frontend-builder /app/dist/ /usr/share/nginx/html/
|
COPY --from=frontend-builder /app/dist/ /usr/share/nginx/html/
|
||||||
|
|
||||||
# Копируем конфигурацию nginx (используем dev версию для локальной разработки)
|
# Копируем конфигурацию nginx
|
||||||
COPY nginx-dev.conf /etc/nginx/nginx.conf.template
|
COPY nginx-simple.conf /etc/nginx/nginx.conf.template
|
||||||
|
|
||||||
# Копируем скрипт запуска
|
# Копируем скрипт запуска
|
||||||
COPY docker-entrypoint.sh /docker-entrypoint.sh
|
COPY docker-entrypoint.sh /docker-entrypoint.sh
|
||||||
|
|||||||
51
setup.sh
51
setup.sh
@@ -31,21 +31,62 @@ check_docker() {
|
|||||||
curl -fsSL https://get.docker.com -o get-docker.sh
|
curl -fsSL https://get.docker.com -o get-docker.sh
|
||||||
sudo sh get-docker.sh
|
sudo sh get-docker.sh
|
||||||
rm get-docker.sh
|
rm get-docker.sh
|
||||||
print_green "Docker установлен. Перезапустите терминал или выполните: newgrp docker"
|
|
||||||
|
# Добавляем текущего пользователя в группу docker
|
||||||
|
print_blue "Добавление пользователя в группу docker..."
|
||||||
|
sudo usermod -aG docker $USER
|
||||||
|
|
||||||
|
print_green "Docker установлен!"
|
||||||
|
print_yellow "⚠️ ВАЖНО: Для применения изменений выполните одну из команд:"
|
||||||
|
print_yellow " 1. newgrp docker (применить в текущем терминале)"
|
||||||
|
print_yellow " 2. Перезапустите терминал"
|
||||||
|
print_yellow " 3. Перезайдите в систему"
|
||||||
|
print_blue "Нажмите Enter для продолжения после выполнения команды..."
|
||||||
|
read
|
||||||
else
|
else
|
||||||
print_yellow "Пожалуйста, установите Docker вручную: https://docs.docker.com/get-docker/"
|
print_yellow "Пожалуйста, установите Docker вручную: https://docs.docker.com/get-docker/"
|
||||||
print_yellow "Для Windows/Mac: скачайте и установите Docker Desktop."
|
print_yellow "Для Windows/Mac: скачайте и установите Docker Desktop."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
print_green "Docker установлен."
|
|
||||||
|
# Проверка прав доступа к Docker
|
||||||
|
if ! docker ps &> /dev/null; then
|
||||||
|
print_yellow "⚠️ Нет прав для запуска Docker команд."
|
||||||
|
print_blue "Добавление пользователя в группу docker..."
|
||||||
|
|
||||||
|
# Проверяем, есть ли пользователь в группе docker
|
||||||
|
if ! groups $USER | grep -q docker; then
|
||||||
|
sudo usermod -aG docker $USER
|
||||||
|
print_yellow "Пользователь добавлен в группу docker."
|
||||||
|
print_yellow "Выполните команду для применения изменений: newgrp docker"
|
||||||
|
print_yellow "Или перезапустите терминал и запустите скрипт снова."
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
print_red "Пользователь уже в группе docker, но права не работают."
|
||||||
|
print_yellow "Попробуйте:"
|
||||||
|
print_yellow " 1. newgrp docker"
|
||||||
|
print_yellow " 2. Перезайдите в систему"
|
||||||
|
print_yellow " 3. Перезапустите Docker: sudo systemctl restart docker"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_green "Docker установлен и доступен."
|
||||||
|
|
||||||
print_blue "Проверка Docker Compose..."
|
print_blue "Проверка Docker Compose..."
|
||||||
if ! docker compose version &> /dev/null; then
|
if ! docker compose version &> /dev/null; then
|
||||||
print_yellow "Docker Compose не установлен или требуется обновление."
|
print_yellow "Docker Compose не установлен или требуется обновление."
|
||||||
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
||||||
print_blue "Установка Docker Compose (входит в новые версии Docker)..."
|
print_blue "Установка Docker Compose плагина..."
|
||||||
print_yellow "Если после установки Docker Compose не работает, обновите Docker или следуйте инструкции: https://docs.docker.com/compose/install/"
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y docker-compose-plugin
|
||||||
|
|
||||||
|
if ! docker compose version &> /dev/null; then
|
||||||
|
print_red "Не удалось установить Docker Compose плагин."
|
||||||
|
print_yellow "Попробуйте обновить Docker: https://docs.docker.com/compose/install/"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
print_yellow "Пожалуйста, установите Docker Compose вручную: https://docs.docker.com/compose/install/"
|
print_yellow "Пожалуйста, установите Docker Compose вручную: https://docs.docker.com/compose/install/"
|
||||||
exit 1
|
exit 1
|
||||||
@@ -98,7 +139,7 @@ create_encryption_key() {
|
|||||||
pull_images() {
|
pull_images() {
|
||||||
print_blue "Предварительная загрузка образов Docker..."
|
print_blue "Предварительная загрузка образов Docker..."
|
||||||
|
|
||||||
images=("node:20-alpine" "postgres:16-alpine" "ollama/ollama:latest" "curlimages/curl:latest")
|
images=("node:20-slim" "postgres:16" "ollama/ollama:latest")
|
||||||
|
|
||||||
for img in "${images[@]}"; do
|
for img in "${images[@]}"; do
|
||||||
print_blue "Загрузка образа: $img"
|
print_blue "Загрузка образа: $img"
|
||||||
|
|||||||
@@ -6,13 +6,14 @@ RUN apt-get update && apt-get install -y \
|
|||||||
sshpass \
|
sshpass \
|
||||||
curl \
|
curl \
|
||||||
wget \
|
wget \
|
||||||
docker.io \
|
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
python3 \
|
python3 \
|
||||||
make \
|
make \
|
||||||
g++ \
|
g++ \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Docker CLI НЕ устанавливаем - используем Docker Socket + dockerode SDK
|
||||||
|
|
||||||
# Создаем рабочую директорию
|
# Создаем рабочую директорию
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,8 @@
|
|||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"fs-extra": "^11.1.1",
|
"fs-extra": "^11.1.1",
|
||||||
"chalk": "^4.1.2",
|
"chalk": "^4.1.2",
|
||||||
"ws": "^8.14.2"
|
"ws": "^8.14.2",
|
||||||
|
"dockerode": "^4.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"nodemon": "^3.0.1"
|
"nodemon": "^3.0.1"
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
const { exec } = require('child_process');
|
const Docker = require('dockerode');
|
||||||
|
const fs = require('fs-extra');
|
||||||
const { execSshCommand, execScpCommand } = require('./sshUtils');
|
const { execSshCommand, execScpCommand } = require('./sshUtils');
|
||||||
const log = require('./logger');
|
const log = require('./logger');
|
||||||
|
|
||||||
|
// Инициализируем Docker клиент через socket
|
||||||
|
const docker = new Docker({ socketPath: '/var/run/docker.sock' });
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Экспорт Docker образов и данных с локальной машины
|
* Экспорт Docker образов и данных с локальной машины
|
||||||
*/
|
*/
|
||||||
@@ -10,7 +14,7 @@ const exportDockerImages = async (sendWebSocketLog) => {
|
|||||||
sendWebSocketLog('info', '📦 Начинаем экспорт Docker образов...', 'export_images', 60);
|
sendWebSocketLog('info', '📦 Начинаем экспорт Docker образов...', 'export_images', 60);
|
||||||
|
|
||||||
const images = [
|
const images = [
|
||||||
{ name: 'postgres:16-alpine', file: 'dapp-postgres.tar' },
|
{ name: 'postgres:16', file: 'dapp-postgres.tar' },
|
||||||
{ name: 'digital_legal_entitydle-ollama:latest', file: 'dapp-ollama.tar' },
|
{ name: 'digital_legal_entitydle-ollama:latest', file: 'dapp-ollama.tar' },
|
||||||
{ name: 'digital_legal_entitydle-vector-search:latest', file: 'dapp-vector-search.tar' },
|
{ name: 'digital_legal_entitydle-vector-search:latest', file: 'dapp-vector-search.tar' },
|
||||||
{ name: 'digital_legal_entitydle-backend:latest', file: 'dapp-backend.tar' },
|
{ name: 'digital_legal_entitydle-backend:latest', file: 'dapp-backend.tar' },
|
||||||
@@ -25,121 +29,102 @@ const exportDockerImages = async (sendWebSocketLog) => {
|
|||||||
const progress = 60 + Math.floor((i / images.length) * 10); // 60-70%
|
const progress = 60 + Math.floor((i / images.length) * 10); // 60-70%
|
||||||
sendWebSocketLog('info', `📦 Экспорт образа: ${image.name}`, 'export_images', progress);
|
sendWebSocketLog('info', `📦 Экспорт образа: ${image.name}`, 'export_images', progress);
|
||||||
|
|
||||||
await new Promise((resolve) => {
|
try {
|
||||||
exec(`docker save ${image.name} -o /tmp/${image.file}`, (error, stdout, stderr) => {
|
const dockerImage = docker.getImage(image.name);
|
||||||
if (error) {
|
const stream = await dockerImage.get();
|
||||||
log.error(`Ошибка экспорта ${image.name}: ${error.message}`);
|
const outputPath = `/tmp/${image.file}`;
|
||||||
sendWebSocketLog('error', `❌ Ошибка экспорта ${image.name}`, 'export_images', progress);
|
|
||||||
} else {
|
// Сохраняем stream в файл
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
const writeStream = fs.createWriteStream(outputPath);
|
||||||
|
stream.pipe(writeStream);
|
||||||
|
stream.on('end', () => {
|
||||||
sendWebSocketLog('success', `✅ Экспорт ${image.name} завершен`, 'export_images', progress);
|
sendWebSocketLog('success', `✅ Экспорт ${image.name} завершен`, 'export_images', progress);
|
||||||
}
|
resolve();
|
||||||
resolve();
|
});
|
||||||
|
stream.on('error', reject);
|
||||||
|
writeStream.on('error', reject);
|
||||||
});
|
});
|
||||||
});
|
} catch (error) {
|
||||||
|
log.error(`Ошибка экспорта ${image.name}: ${error.message}`);
|
||||||
|
sendWebSocketLog('error', `❌ Ошибка экспорта ${image.name}`, 'export_images', progress);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Экспортируем данные из volumes
|
// Экспортируем данные из volumes
|
||||||
log.info('Экспорт данных из Docker volumes...');
|
log.info('Экспорт данных из Docker volumes...');
|
||||||
sendWebSocketLog('info', '📦 Экспорт данных из Docker volumes...', 'export_data', 70);
|
sendWebSocketLog('info', '📦 Экспорт данных из Docker volumes...', 'export_data', 70);
|
||||||
|
|
||||||
// PostgreSQL данные - проверяем наличие данных перед экспортом
|
// PostgreSQL данные
|
||||||
sendWebSocketLog('info', '📦 Экспорт данных PostgreSQL...', 'export_data', 72);
|
sendWebSocketLog('info', '📦 Экспорт данных PostgreSQL...', 'export_data', 72);
|
||||||
await new Promise((resolve) => {
|
await exportVolumeData('digital_legal_entitydle_postgres_data', 'postgres_data.tar.gz', sendWebSocketLog, 72);
|
||||||
// Сначала проверим, есть ли данные в volume
|
|
||||||
exec('docker run --rm -v digital_legal_entitydle_postgres_data:/data alpine ls -la /data/base', (checkError, checkStdout, checkStderr) => {
|
|
||||||
if (checkError) {
|
|
||||||
log.error(`Ошибка проверки данных PostgreSQL: ${checkError.message}`);
|
|
||||||
sendWebSocketLog('error', `❌ Ошибка проверки данных PostgreSQL`, 'export_data', 72);
|
|
||||||
resolve();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Если данные есть, экспортируем их
|
|
||||||
exec('docker run --rm -v digital_legal_entitydle_postgres_data:/data -v /tmp:/backup alpine tar czf /backup/postgres_data.tar.gz -C /data .', (error, stdout, stderr) => {
|
|
||||||
if (error) {
|
|
||||||
log.error(`Ошибка экспорта данных PostgreSQL: ${error.message}`);
|
|
||||||
sendWebSocketLog('error', `❌ Ошибка экспорта данных PostgreSQL`, 'export_data', 72);
|
|
||||||
} else {
|
|
||||||
log.info(`Данные PostgreSQL экспортированы: ${checkStdout.trim()}`);
|
|
||||||
sendWebSocketLog('success', `✅ Экспорт данных PostgreSQL завершен (содержит базы данных)`, 'export_data', 72);
|
|
||||||
}
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Ollama данные
|
// Ollama данные
|
||||||
sendWebSocketLog('info', '📦 Экспорт данных Ollama...', 'export_data', 75);
|
sendWebSocketLog('info', '📦 Экспорт данных Ollama...', 'export_data', 75);
|
||||||
await new Promise((resolve) => {
|
await exportVolumeData('digital_legal_entitydle_ollama_data', 'ollama_data.tar.gz', sendWebSocketLog, 75);
|
||||||
exec('docker run --rm -v digital_legal_entitydle_ollama_data:/data -v /tmp:/backup alpine tar czf /backup/ollama_data.tar.gz -C /data .', (error, stdout, stderr) => {
|
|
||||||
if (error) {
|
|
||||||
log.error(`Ошибка экспорта данных Ollama: ${error.message}`);
|
|
||||||
sendWebSocketLog('error', `❌ Ошибка экспорта данных Ollama`, 'export_data', 75);
|
|
||||||
} else {
|
|
||||||
sendWebSocketLog('success', `✅ Экспорт данных Ollama завершен`, 'export_data', 75);
|
|
||||||
}
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Vector Search данные
|
// Vector Search данные
|
||||||
sendWebSocketLog('info', '📦 Экспорт данных Vector Search...', 'export_data', 78);
|
sendWebSocketLog('info', '📦 Экспорт данных Vector Search...', 'export_data', 78);
|
||||||
await new Promise((resolve) => {
|
await exportVolumeData('digital_legal_entitydle_vector_search_data', 'vector_search_data.tar.gz', sendWebSocketLog, 78);
|
||||||
exec('docker run --rm -v digital_legal_entitydle_vector_search_data:/data -v /tmp:/backup alpine tar czf /backup/vector_search_data.tar.gz -C /data .', (error, stdout, stderr) => {
|
|
||||||
if (error) {
|
|
||||||
log.error(`Ошибка экспорта данных Vector Search: ${error.message}`);
|
|
||||||
sendWebSocketLog('error', `❌ Ошибка экспорта данных Vector Search`, 'export_data', 78);
|
|
||||||
} else {
|
|
||||||
sendWebSocketLog('success', `✅ Экспорт данных Vector Search завершен`, 'export_data', 78);
|
|
||||||
}
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Проверяем размеры экспортированных данных
|
|
||||||
log.info('Проверка размеров экспортированных данных...');
|
|
||||||
sendWebSocketLog('info', '📊 Проверка размеров экспортированных данных...', 'export_data', 78);
|
|
||||||
|
|
||||||
await new Promise((resolve) => {
|
|
||||||
exec('ls -lh /tmp/postgres_data.tar.gz /tmp/ollama_data.tar.gz /tmp/vector_search_data.tar.gz 2>/dev/null || echo "Некоторые файлы не найдены"', (error, stdout, stderr) => {
|
|
||||||
if (stdout && stdout.trim()) {
|
|
||||||
log.info(`Размеры экспортированных данных:\n${stdout.trim()}`);
|
|
||||||
sendWebSocketLog('info', `📊 Размеры данных:\n${stdout.trim()}`, 'export_data', 78);
|
|
||||||
}
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Создаем архив с ВСЕМИ образами и данными приложения
|
// Создаем архив с ВСЕМИ образами и данными приложения
|
||||||
log.info('Создание архива Docker образов и данных на хосте...');
|
log.info('Создание архива Docker образов и данных на хосте...');
|
||||||
sendWebSocketLog('info', '📦 Создание архива всех данных...', 'export_data', 80);
|
sendWebSocketLog('info', '📦 Создание архива всех данных...', 'export_data', 80);
|
||||||
|
|
||||||
const tarFiles = images.map(img => img.file).join(' ');
|
try {
|
||||||
const dataFiles = 'postgres_data.tar.gz ollama_data.tar.gz vector_search_data.tar.gz';
|
const tarFiles = images.map(img => img.file).join(' ');
|
||||||
await new Promise((resolve) => {
|
const dataFiles = 'postgres_data.tar.gz ollama_data.tar.gz vector_search_data.tar.gz';
|
||||||
exec(`chmod 644 /tmp/dapp-*.tar /tmp/*_data.tar.gz && cd /tmp && tar -czf docker-images-and-data.tar.gz ${tarFiles} ${dataFiles}`, (error, stdout, stderr) => {
|
|
||||||
if (error) {
|
// Создаем архив через временный контейнер
|
||||||
log.error('Ошибка создания архива: ' + error.message);
|
const container = await docker.createContainer({
|
||||||
sendWebSocketLog('error', '❌ Ошибка создания архива', 'export_data', 80);
|
Image: 'alpine',
|
||||||
} else {
|
Cmd: ['sh', '-c', `cd /tmp && tar -czf docker-images-and-data.tar.gz ${tarFiles} ${dataFiles}`],
|
||||||
// Проверяем размер финального архива
|
HostConfig: {
|
||||||
exec('ls -lh /tmp/docker-images-and-data.tar.gz', (sizeError, sizeStdout, sizeStderr) => {
|
Binds: ['/tmp:/tmp'],
|
||||||
if (sizeStdout && sizeStdout.trim()) {
|
AutoRemove: true
|
||||||
log.info(`Финальный архив создан: ${sizeStdout.trim()}`);
|
|
||||||
sendWebSocketLog('success', `✅ Архив создан успешно (${sizeStdout.trim()})`, 'export_data', 80);
|
|
||||||
} else {
|
|
||||||
sendWebSocketLog('success', '✅ Архив создан успешно', 'export_data', 80);
|
|
||||||
}
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
await container.start();
|
||||||
|
await container.wait();
|
||||||
|
|
||||||
|
sendWebSocketLog('success', '✅ Архив создан успешно', 'export_data', 80);
|
||||||
|
} catch (error) {
|
||||||
|
log.error('Ошибка создания архива: ' + error.message);
|
||||||
|
sendWebSocketLog('error', '❌ Ошибка создания архива', 'export_data', 80);
|
||||||
|
}
|
||||||
|
|
||||||
log.success('Docker образы и данные успешно экспортированы');
|
log.success('Docker образы и данные успешно экспортированы');
|
||||||
sendWebSocketLog('success', '✅ Экспорт данных завершен', 'export_data', 80);
|
sendWebSocketLog('success', '✅ Экспорт данных завершен', 'export_data', 80);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Вспомогательная функция для экспорта данных volume
|
||||||
|
*/
|
||||||
|
const exportVolumeData = async (volumeName, outputFile, sendWebSocketLog, progress) => {
|
||||||
|
try {
|
||||||
|
const container = await docker.createContainer({
|
||||||
|
Image: 'alpine',
|
||||||
|
Cmd: ['tar', 'czf', `/backup/${outputFile}`, '-C', '/data', '.'],
|
||||||
|
HostConfig: {
|
||||||
|
Binds: [
|
||||||
|
`${volumeName}:/data:ro`,
|
||||||
|
'/tmp:/backup'
|
||||||
|
],
|
||||||
|
AutoRemove: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await container.start();
|
||||||
|
await container.wait();
|
||||||
|
|
||||||
|
sendWebSocketLog('success', `✅ Экспорт ${outputFile} завершен`, 'export_data', progress);
|
||||||
|
} catch (error) {
|
||||||
|
log.error(`Ошибка экспорта ${volumeName}: ${error.message}`);
|
||||||
|
sendWebSocketLog('error', `❌ Ошибка экспорта ${volumeName}`, 'export_data', progress);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Передача Docker образов и данных на VDS
|
* Передача Docker образов и данных на VDS
|
||||||
*/
|
*/
|
||||||
@@ -258,13 +243,14 @@ docker volume ls | grep dapp_`;
|
|||||||
*/
|
*/
|
||||||
const cleanupLocalFiles = async () => {
|
const cleanupLocalFiles = async () => {
|
||||||
log.info('Очистка временных файлов на хосте...');
|
log.info('Очистка временных файлов на хосте...');
|
||||||
await new Promise((resolve) => {
|
try {
|
||||||
exec('rm -f /tmp/dapp-*.tar /tmp/*_data.tar.gz /tmp/docker-images-and-data.tar.gz', (error, stdout, stderr) => {
|
await fs.remove('/tmp/dapp-*.tar');
|
||||||
if (error) log.error('Ошибка очистки файлов: ' + error.message);
|
await fs.remove('/tmp/*_data.tar.gz');
|
||||||
resolve();
|
await fs.remove('/tmp/docker-images-and-data.tar.gz');
|
||||||
});
|
log.success('Временные файлы очищены');
|
||||||
});
|
} catch (error) {
|
||||||
log.success('Временные файлы очищены (SSH ключи сохранены на хосте)');
|
log.error('Ошибка очистки файлов: ' + error.message);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|||||||
Reference in New Issue
Block a user