ваше сообщение коммита
This commit is contained in:
@@ -139,27 +139,34 @@ app.post('/vds/check-requirements', logRequest, async (req, res) => {
|
||||
const {
|
||||
vdsIp,
|
||||
ubuntuUser,
|
||||
sshUser,
|
||||
sshHost,
|
||||
sshPort = 22,
|
||||
sshConnectUser,
|
||||
sshConnectPassword
|
||||
} = req.body;
|
||||
|
||||
if (!vdsIp || !ubuntuUser || !sshConnectUser || !sshConnectPassword) {
|
||||
// Нормализуем значения (удаляем пробелы)
|
||||
const normalizedVdsIp = String(vdsIp || '').trim();
|
||||
const normalizedSshHost = sshHost ? String(sshHost).trim() : undefined;
|
||||
const normalizedSshConnectUser = String(sshConnectUser || sshUser || 'root').trim();
|
||||
const normalizedSshConnectPassword = sshConnectPassword ? String(sshConnectPassword).trim() : undefined;
|
||||
|
||||
if (!normalizedVdsIp || !ubuntuUser || !normalizedSshConnectUser || !normalizedSshConnectPassword) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Необходимы параметры: vdsIp, ubuntuUser, sshConnectUser, sshConnectPassword'
|
||||
});
|
||||
}
|
||||
|
||||
log.info(`Проверка системных требований VDS: ${vdsIp}`);
|
||||
log.info(`Проверка системных требований VDS: ${normalizedVdsIp}`);
|
||||
|
||||
const options = {
|
||||
vdsIp,
|
||||
sshHost,
|
||||
vdsIp: normalizedVdsIp,
|
||||
sshHost: normalizedSshHost,
|
||||
sshPort,
|
||||
sshConnectUser,
|
||||
sshConnectPassword
|
||||
sshConnectUser: normalizedSshConnectUser,
|
||||
sshConnectPassword: normalizedSshConnectPassword
|
||||
};
|
||||
|
||||
const result = await checkSystemRequirements(options);
|
||||
@@ -199,21 +206,27 @@ app.post('/vds/transfer-encryption-key', logRequest, async (req, res) => {
|
||||
sshConnectPassword
|
||||
} = req.body;
|
||||
|
||||
if (!vdsIp || !dockerUser || !sshConnectUser || !sshConnectPassword) {
|
||||
// Нормализуем значения (удаляем пробелы)
|
||||
const normalizedVdsIp = String(vdsIp || '').trim();
|
||||
const normalizedSshHost = sshHost ? String(sshHost).trim() : undefined;
|
||||
const normalizedSshConnectUser = String(sshConnectUser || sshUser || 'root').trim();
|
||||
const normalizedSshConnectPassword = sshConnectPassword ? String(sshConnectPassword).trim() : undefined;
|
||||
|
||||
if (!normalizedVdsIp || !dockerUser || !normalizedSshConnectUser || !normalizedSshConnectPassword) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Необходимы параметры: vdsIp, dockerUser, sshConnectUser, sshConnectPassword'
|
||||
});
|
||||
}
|
||||
|
||||
log.info(`🔐 Передача ключа шифрования на VDS: ${vdsIp}`);
|
||||
log.info(`🔐 Передача ключа шифрования на VDS: ${normalizedVdsIp}`);
|
||||
|
||||
const options = {
|
||||
vdsIp,
|
||||
sshHost,
|
||||
vdsIp: normalizedVdsIp,
|
||||
sshHost: normalizedSshHost,
|
||||
sshPort,
|
||||
sshConnectUser,
|
||||
sshConnectPassword
|
||||
sshConnectUser: normalizedSshConnectUser,
|
||||
sshConnectPassword: normalizedSshConnectPassword
|
||||
};
|
||||
|
||||
// 1. Убеждаемся, что директория для ключа существует на VDS
|
||||
@@ -312,18 +325,24 @@ app.post('/vds/setup', logRequest, async (req, res) => {
|
||||
sshConnectPassword
|
||||
} = req.body;
|
||||
|
||||
log.info(`Настройка VDS: ${vdsIp} для домена: ${domain}`);
|
||||
// Нормализуем значения (удаляем пробелы)
|
||||
const normalizedVdsIp = String(vdsIp || '').trim();
|
||||
const normalizedSshHost = sshHost ? String(sshHost).trim() : undefined;
|
||||
const normalizedSshConnectUser = String(sshConnectUser || sshUser || 'root').trim();
|
||||
const normalizedSshConnectPassword = sshConnectPassword ? String(sshConnectPassword).trim() : undefined;
|
||||
|
||||
log.info(`Настройка VDS: ${normalizedVdsIp} для домена: ${domain}`);
|
||||
|
||||
// Отправляем начальный статус через WebSocket
|
||||
sendWebSocketStatus(false, 'Начинаем настройку VDS...');
|
||||
sendWebSocketLog('info', `🚀 Начинаем настройку VDS: ${vdsIp} для домена: ${domain}`, 'init', 0);
|
||||
sendWebSocketLog('info', `🚀 Начинаем настройку VDS: ${normalizedVdsIp} для домена: ${domain}`, 'init', 0);
|
||||
|
||||
const options = {
|
||||
vdsIp,
|
||||
sshHost,
|
||||
vdsIp: normalizedVdsIp,
|
||||
sshHost: normalizedSshHost,
|
||||
sshPort,
|
||||
sshConnectUser,
|
||||
sshConnectPassword
|
||||
sshConnectUser: normalizedSshConnectUser,
|
||||
sshConnectPassword: normalizedSshConnectPassword
|
||||
};
|
||||
|
||||
// 0. Проверка системных требований
|
||||
@@ -404,6 +423,52 @@ findtime = 3600
|
||||
await execSshCommand(`chown ${dockerUser}:${dockerUser} /home/${dockerUser}/dapp/ssl/keys`, options);
|
||||
log.success('Директория для ключа шифрования подготовлена');
|
||||
|
||||
// 9.1. Передача ключа шифрования на VDS
|
||||
sendWebSocketLog('info', '🔐 Передача ключа шифрования на VDS...', 'encryption_key', 36);
|
||||
log.info('🔐 Передача ключа шифрования на VDS...');
|
||||
|
||||
try {
|
||||
// Читаем ключ шифрования с локальной машины
|
||||
const encryptionKeyPath = process.env.ENCRYPTION_KEY_PATH
|
||||
|| path.resolve(__dirname, '..', 'ssl', 'keys', 'full_db_encryption.key');
|
||||
|
||||
const encryptionKeyContent = await fs.readFile(encryptionKeyPath, 'utf8');
|
||||
log.success('✅ Ключ шифрования прочитан с локальной машины');
|
||||
|
||||
// Создаем временный файл с ключом
|
||||
const tempKeyPath = `/tmp/encryption_key_${Date.now()}.key`;
|
||||
await fs.writeFile(tempKeyPath, encryptionKeyContent);
|
||||
|
||||
// Передаем файл на VDS через SCP
|
||||
await execScpCommand(
|
||||
tempKeyPath,
|
||||
`/home/${dockerUser}/dapp/ssl/keys/full_db_encryption.key`,
|
||||
options
|
||||
);
|
||||
|
||||
// Удаляем временный файл
|
||||
await fs.remove(tempKeyPath);
|
||||
|
||||
// Устанавливаем правильные права доступа к ключу на VDS
|
||||
await execSshCommand(`chown ${dockerUser}:${dockerUser} /home/${dockerUser}/dapp/ssl/keys/full_db_encryption.key`, options);
|
||||
await execSshCommand(`chmod 600 /home/${dockerUser}/dapp/ssl/keys/full_db_encryption.key`, options);
|
||||
|
||||
// Проверяем, что ключ успешно передан
|
||||
const verifyResult = await execSshCommand(`ls -la /home/${dockerUser}/dapp/ssl/keys/full_db_encryption.key`, options);
|
||||
|
||||
if (verifyResult.code === 0) {
|
||||
log.success('✅ Ключ шифрования успешно передан на VDS');
|
||||
sendWebSocketLog('success', '✅ Ключ шифрования передан на VDS', 'encryption_key', 37);
|
||||
} else {
|
||||
throw new Error('Не удалось проверить передачу ключа шифрования');
|
||||
}
|
||||
} catch (error) {
|
||||
log.error('❌ Ошибка передачи ключа шифрования: ' + error.message);
|
||||
sendWebSocketLog('error', '❌ Ошибка передачи ключа шифрования: ' + error.message, 'encryption_key', 37);
|
||||
// Продолжаем установку, но предупреждаем пользователя
|
||||
log.warn('⚠️ Внимание: ключ шифрования не передан. Backend может не запуститься без ключа.');
|
||||
}
|
||||
|
||||
// 10. Проверка и удаление системного nginx для избежания конфликтов портов
|
||||
log.info('🔍 Проверка наличия системного nginx...');
|
||||
const nginxCheck = await execSshCommand('systemctl list-units --type=service --state=active,inactive | grep nginx || echo "nginx not found"', options);
|
||||
@@ -677,27 +742,34 @@ app.post('/vds/diagnostics', logRequest, async (req, res) => {
|
||||
try {
|
||||
const {
|
||||
vdsIp,
|
||||
sshUser,
|
||||
sshHost,
|
||||
sshPort = 22,
|
||||
sshConnectUser,
|
||||
sshConnectPassword
|
||||
} = req.body;
|
||||
|
||||
if (!vdsIp || !sshConnectUser || !sshConnectPassword) {
|
||||
// Нормализуем значения (удаляем пробелы)
|
||||
const normalizedVdsIp = String(vdsIp || '').trim();
|
||||
const normalizedSshHost = sshHost ? String(sshHost).trim() : undefined;
|
||||
const normalizedSshConnectUser = String(sshConnectUser || sshUser || 'root').trim();
|
||||
const normalizedSshConnectPassword = sshConnectPassword ? String(sshConnectPassword).trim() : undefined;
|
||||
|
||||
if (!normalizedVdsIp || !normalizedSshConnectUser || !normalizedSshConnectPassword) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Необходимы параметры: vdsIp, sshConnectUser, sshConnectPassword'
|
||||
});
|
||||
}
|
||||
|
||||
log.info(`Диагностика VDS: ${vdsIp}`);
|
||||
log.info(`Диагностика VDS: ${normalizedVdsIp}`);
|
||||
|
||||
const options = {
|
||||
vdsIp,
|
||||
sshHost,
|
||||
vdsIp: normalizedVdsIp,
|
||||
sshHost: normalizedSshHost,
|
||||
sshPort,
|
||||
sshConnectUser,
|
||||
sshConnectPassword
|
||||
sshConnectUser: normalizedSshConnectUser,
|
||||
sshConnectPassword: normalizedSshConnectPassword
|
||||
};
|
||||
|
||||
// 1. Проверка статуса системы
|
||||
|
||||
@@ -137,6 +137,7 @@ services:
|
||||
# Также монтируем в стандартные пути для совместимости (read-only для безопасности)
|
||||
- /proc:/proc:ro
|
||||
- /sys:/sys:ro
|
||||
- ~/.ssh:/root/.ssh:ro # SSH ключи для подключения к VDS
|
||||
# Добавляем необходимые capabilities для управления системой
|
||||
cap_add:
|
||||
- SYS_ADMIN # Для управления системой (reboot, shutdown, useradd и т.д.)
|
||||
|
||||
@@ -7,16 +7,114 @@ const log = require('./logger');
|
||||
const cleanupVdsServer = async (options) => {
|
||||
log.info('Очистка VDS сервера...');
|
||||
|
||||
// Остановка и удаление существующих Docker контейнеров
|
||||
log.info('Остановка существующих Docker контейнеров...');
|
||||
await execSshCommand('docker ps -aq | xargs -r docker stop 2>/dev/null || true', options);
|
||||
await execSshCommand('docker ps -aq | xargs -r docker rm 2>/dev/null || true', options);
|
||||
// Проверяем наличие Docker перед попыткой очистки
|
||||
log.info('🔍 Проверка наличия Docker...');
|
||||
const dockerCheck = await execSshCommand('command -v docker >/dev/null 2>&1 && echo "docker installed" || echo "docker not installed"', options);
|
||||
|
||||
// Удаление Docker образов и очистка системы
|
||||
log.info('Очистка Docker образов и системы...');
|
||||
await execSshCommand('docker system prune -af || true', options);
|
||||
await execSshCommand('docker volume prune -f || true', options);
|
||||
await execSshCommand('docker network prune -f || true', options);
|
||||
// Если ошибка SSH подключения (код 255), пропускаем очистку Docker
|
||||
if (dockerCheck.code === 255) {
|
||||
log.warn('⚠️ Ошибка SSH подключения при проверке Docker, пропускаем очистку Docker');
|
||||
log.info('Продолжаем настройку сервера...');
|
||||
} else {
|
||||
const hasDocker = dockerCheck.stdout.trim().includes('docker installed');
|
||||
|
||||
if (hasDocker) {
|
||||
log.info('Docker обнаружен, выполняем полную очистку...');
|
||||
|
||||
// 1. Остановка всех Docker контейнеров (включая запущенные)
|
||||
log.info('🛑 Остановка всех Docker контейнеров...');
|
||||
const stopResult = await execSshCommand('docker ps -aq | xargs -r docker stop 2>/dev/null || true', options);
|
||||
if (stopResult.code !== 0 && stopResult.code !== 255) {
|
||||
log.warn(`Предупреждение при остановке контейнеров: ${stopResult.stderr}`);
|
||||
}
|
||||
|
||||
// 2. Удаление всех контейнеров (включая остановленные)
|
||||
log.info('🗑️ Удаление всех Docker контейнеров...');
|
||||
// Удаляем все контейнеры (запущенные и остановленные)
|
||||
const rmResult = await execSshCommand('docker container ls -aq | xargs -r docker rm -f 2>/dev/null || true', options);
|
||||
if (rmResult.code !== 0 && rmResult.code !== 255) {
|
||||
log.warn(`Предупреждение при удалении контейнеров: ${rmResult.stderr}`);
|
||||
}
|
||||
// Дополнительная проверка и удаление любых оставшихся контейнеров
|
||||
await execSshCommand('docker ps -aq 2>/dev/null | xargs -r docker rm -f 2>/dev/null || true', options);
|
||||
|
||||
// 3. Удаление всех volumes (включая именованные)
|
||||
log.info('🗑️ Удаление всех Docker volumes...');
|
||||
// Сначала получаем список всех volumes
|
||||
const allVolumesList = await execSshCommand('docker volume ls -q 2>/dev/null || true', options);
|
||||
if (allVolumesList.stdout.trim()) {
|
||||
const volumes = allVolumesList.stdout.trim().split('\n').filter(v => v);
|
||||
log.info(`Найдено ${volumes.length} volumes для удаления`);
|
||||
for (const volume of volumes) {
|
||||
// Принудительно удаляем каждый volume
|
||||
await execSshCommand(`docker volume rm -f "${volume}" 2>/dev/null || true`, options);
|
||||
}
|
||||
log.info(`Удалено ${volumes.length} volumes`);
|
||||
}
|
||||
|
||||
// 4. Дополнительная очистка неиспользуемых volumes (на случай если что-то осталось)
|
||||
log.info('🧹 Финальная очистка volumes...');
|
||||
const volumePruneResult = await execSshCommand('docker volume prune -f 2>/dev/null || true', options);
|
||||
if (volumePruneResult.code !== 0 && volumePruneResult.code !== 255) {
|
||||
log.warn(`Предупреждение при финальной очистке volumes: ${volumePruneResult.stderr}`);
|
||||
}
|
||||
|
||||
// 5. Удаление всех Docker образов приложения
|
||||
log.info('🗑️ Удаление старых Docker образов приложения...');
|
||||
const imagesList = await execSshCommand('docker images -q | xargs -r docker rmi -f 2>/dev/null || true', options);
|
||||
// Удаляем все образы, связанные с приложением
|
||||
await execSshCommand('docker images --format "{{.Repository}}:{{.Tag}}" | grep -E "digital_legal_entity|dapp-" | xargs -r docker rmi -f 2>/dev/null || true', options);
|
||||
|
||||
// 6. Полная очистка Docker системы (все образы, кэш, сети)
|
||||
log.info('🧹 Полная очистка Docker системы...');
|
||||
const pruneResult = await execSshCommand('docker system prune -af --volumes 2>/dev/null || true', options);
|
||||
if (pruneResult.code !== 0 && pruneResult.code !== 255) {
|
||||
log.warn(`Предупреждение при очистке Docker: ${pruneResult.stderr}`);
|
||||
}
|
||||
|
||||
// 7. Удаление всех Docker сетей
|
||||
log.info('🗑️ Удаление всех Docker сетей...');
|
||||
const networkPruneResult = await execSshCommand('docker network prune -f 2>/dev/null || true', options);
|
||||
if (networkPruneResult.code !== 0 && networkPruneResult.code !== 255) {
|
||||
log.warn(`Предупреждение при очистке сетей: ${networkPruneResult.stderr}`);
|
||||
}
|
||||
|
||||
log.success('✅ Docker полностью очищен');
|
||||
} else {
|
||||
log.info('ℹ️ Docker не установлен, пропускаем очистку Docker');
|
||||
}
|
||||
}
|
||||
|
||||
// Удаление старых директорий приложения
|
||||
log.info('🗑️ Удаление старых директорий приложения...');
|
||||
await execSshCommand('find /home -maxdepth 3 -type d -name "dapp" -exec rm -rf {} + 2>/dev/null || true', options);
|
||||
await execSshCommand('find /home -maxdepth 3 -type d -name "digital_legal_entity" -exec rm -rf {} + 2>/dev/null || true', options);
|
||||
|
||||
// Удаление старых docker-compose файлов
|
||||
log.info('🗑️ Удаление старых конфигурационных файлов...');
|
||||
await execSshCommand('find /home -name "docker-compose*.yml" -type f -delete 2>/dev/null || true', options);
|
||||
await execSshCommand('find /home -name ".env" -path "*/dapp/*" -type f -delete 2>/dev/null || true', options);
|
||||
await execSshCommand('find /home -name "import-images-and-data.sh" -type f -delete 2>/dev/null || true', options);
|
||||
await execSshCommand('find /home -name "renew-ssl.sh" -type f -delete 2>/dev/null || true', options);
|
||||
|
||||
// Очистка старых cron задач, связанных с приложением
|
||||
log.info('🗑️ Очистка старых cron задач приложения...');
|
||||
const crontabBackup = await execSshCommand('crontab -l 2>/dev/null || echo ""', options);
|
||||
if (crontabBackup.stdout.trim()) {
|
||||
const cleanedCrontab = crontabBackup.stdout
|
||||
.split('\n')
|
||||
.filter(line => !line.includes('renew-ssl.sh') && !line.includes('dapp') && !line.includes('digital_legal_entity'))
|
||||
.join('\n');
|
||||
if (cleanedCrontab.trim()) {
|
||||
await execSshCommand(`echo '${cleanedCrontab.replace(/'/g, "'\\''")}' | crontab -`, options);
|
||||
} else {
|
||||
await execSshCommand('crontab -r 2>/dev/null || true', options);
|
||||
}
|
||||
}
|
||||
|
||||
// Очистка старых SSL сертификатов (опционально, можно оставить для повторного использования)
|
||||
log.info('🧹 Проверка старых SSL сертификатов...');
|
||||
await execSshCommand('rm -rf /var/www/certbot/.well-known 2>/dev/null || true', options);
|
||||
|
||||
// 🆕 Умная проверка и удаление системного nginx для избежания конфликтов портов
|
||||
log.info('🔍 Проверка наличия системного nginx...');
|
||||
|
||||
@@ -69,21 +69,26 @@ const exportDockerImages = async (sendWebSocketLog) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Экспортируем данные из volumes
|
||||
// Экспортируем данные из volumes (динамически определяем все volumes приложения)
|
||||
log.info('Экспорт данных из Docker volumes...');
|
||||
sendWebSocketLog('info', '📦 Экспорт данных из Docker volumes...', 'export_data', 70);
|
||||
|
||||
// PostgreSQL данные
|
||||
sendWebSocketLog('info', '📦 Экспорт данных PostgreSQL...', 'export_data', 72);
|
||||
await exportVolumeData('digital_legal_entitydle_postgres_data', 'postgres_data.tar.gz', sendWebSocketLog, 72);
|
||||
// Получаем список всех volumes приложения (без node_modules)
|
||||
const volumesList = await execLocalCommand('docker volume ls -q | grep -E "digital_legal_entitydle_|dapp_" | grep -v node_modules || true');
|
||||
const volumes = volumesList.stdout.trim().split('\n').filter(v => v && v.endsWith('_data'));
|
||||
|
||||
// Ollama данные
|
||||
sendWebSocketLog('info', '📦 Экспорт данных Ollama...', 'export_data', 75);
|
||||
await exportVolumeData('digital_legal_entitydle_ollama_data', 'ollama_data.tar.gz', sendWebSocketLog, 75);
|
||||
let progress = 72;
|
||||
const progressStep = Math.floor(8 / Math.max(volumes.length, 1));
|
||||
|
||||
// Vector Search данные
|
||||
sendWebSocketLog('info', '📦 Экспорт данных Vector Search...', 'export_data', 78);
|
||||
await exportVolumeData('digital_legal_entitydle_vector_search_data', 'vector_search_data.tar.gz', sendWebSocketLog, 78);
|
||||
for (const volumeName of volumes) {
|
||||
// Извлекаем имя файла из имени volume (например, digital_legal_entitydle_postgres_data -> postgres_data.tar.gz)
|
||||
const volumeBaseName = volumeName.replace(/^(digital_legal_entitydle_|dapp_)/, '').replace(/_data$/, '_data');
|
||||
const outputFile = `${volumeBaseName}.tar.gz`;
|
||||
|
||||
sendWebSocketLog('info', `📦 Экспорт данных: ${volumeName}`, 'export_data', progress);
|
||||
await exportVolumeData(volumeName, outputFile, sendWebSocketLog, progress);
|
||||
progress += progressStep;
|
||||
}
|
||||
|
||||
// Создаем архив с ВСЕМИ образами и данными приложения
|
||||
log.info('Создание архива Docker образов и данных на хосте...');
|
||||
@@ -91,9 +96,11 @@ const exportDockerImages = async (sendWebSocketLog) => {
|
||||
|
||||
try {
|
||||
const tarFiles = images.map(img => img.file).join(' ');
|
||||
const dataFiles = 'postgres_data.tar.gz ollama_data.tar.gz vector_search_data.tar.gz';
|
||||
// Динамически собираем список файлов данных из экспортированных volumes
|
||||
const dataFilesList = await execLocalCommand('ls /tmp/*_data.tar.gz 2>/dev/null | xargs -r basename -a || echo ""');
|
||||
const dataFiles = dataFilesList.stdout.trim().split('\n').filter(f => f).join(' ');
|
||||
|
||||
const archiveCommand = `cd /tmp && tar -czf docker-images-and-data.tar.gz ${tarFiles} ${dataFiles}`;
|
||||
const archiveCommand = `cd /tmp && tar -czf docker-images-and-data.tar.gz ${tarFiles} ${dataFiles || ''}`.trim();
|
||||
await execLocalCommand(archiveCommand);
|
||||
|
||||
sendWebSocketLog('success', '✅ Архив создан успешно', 'export_data', 80);
|
||||
@@ -170,45 +177,33 @@ echo "📦 Распаковка архива..."
|
||||
tar -xzf ./docker-images-and-data.tar.gz -C ./temp-import
|
||||
|
||||
# Импортируем ВСЕ образы приложения
|
||||
echo "📦 Импорт образа postgres..."
|
||||
docker load -i ./temp-import/dapp-postgres.tar
|
||||
echo "📦 Импорт образов..."
|
||||
for image_file in ./temp-import/dapp-*.tar; do
|
||||
if [ -f "$image_file" ]; then
|
||||
echo "📦 Импорт образа: $(basename $image_file)"
|
||||
docker load -i "$image_file"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "📦 Импорт образа ollama..."
|
||||
docker load -i ./temp-import/dapp-ollama.tar
|
||||
|
||||
echo "📦 Импорт образа vector-search..."
|
||||
docker load -i ./temp-import/dapp-vector-search.tar
|
||||
|
||||
echo "📦 Импорт образа backend..."
|
||||
docker load -i ./temp-import/dapp-backend.tar
|
||||
|
||||
echo "📦 Импорт образа frontend..."
|
||||
docker load -i ./temp-import/dapp-frontend.tar
|
||||
|
||||
echo "📦 Импорт образа frontend-nginx..."
|
||||
docker load -i ./temp-import/dapp-frontend-nginx.tar
|
||||
|
||||
echo "📦 Импорт образа webssh-agent..."
|
||||
docker load -i ./temp-import/dapp-webssh-agent.tar
|
||||
|
||||
# 🆕 Импортируем данные в volumes с правильными именами для соответствия docker-compose
|
||||
echo "📦 Импорт данных PostgreSQL..."
|
||||
# Удаляем старый volume если существует
|
||||
docker volume rm dapp_postgres_data 2>/dev/null || true
|
||||
docker volume create dapp_postgres_data
|
||||
docker run --rm -v dapp_postgres_data:/data -v ./temp-import:/backup alpine tar xzf /backup/postgres_data.tar.gz -C /data
|
||||
|
||||
echo "📦 Импорт данных Ollama..."
|
||||
# Удаляем старый volume если существует
|
||||
docker volume rm dapp_ollama_data 2>/dev/null || true
|
||||
docker volume create dapp_ollama_data
|
||||
docker run --rm -v dapp_ollama_data:/data -v ./temp-import:/backup alpine tar xzf /backup/ollama_data.tar.gz -C /data
|
||||
|
||||
echo "📦 Импорт данных Vector Search..."
|
||||
# Удаляем старый volume если существует
|
||||
docker volume rm dapp_vector_search_data 2>/dev/null || true
|
||||
docker volume create dapp_vector_search_data
|
||||
docker run --rm -v dapp_vector_search_data:/data -v ./temp-import:/backup alpine tar xzf /backup/vector_search_data.tar.gz -C /data
|
||||
# 🆕 Динамически определяем volumes для импорта из имен файлов в архиве
|
||||
echo "📦 Импорт данных в volumes..."
|
||||
for data_file in ./temp-import/*_data.tar.gz; do
|
||||
if [ -f "$data_file" ]; then
|
||||
# Извлекаем имя volume из имени файла (например, postgres_data.tar.gz -> postgres_data)
|
||||
volume_name=$(basename "$data_file" .tar.gz)
|
||||
|
||||
# Используем префикс dapp_ для соответствия docker-compose.prod.yml
|
||||
full_volume_name="dapp_${volume_name}"
|
||||
|
||||
echo "📦 Импорт данных: $full_volume_name"
|
||||
# Удаляем старый volume если существует
|
||||
docker volume rm -f "$full_volume_name" 2>/dev/null || true
|
||||
# Создаем новый volume
|
||||
docker volume create "$full_volume_name"
|
||||
# Импортируем данные
|
||||
docker run --rm -v "$full_volume_name:/data" -v ./temp-import:/backup alpine tar xzf "/backup/$(basename $data_file)" -C /data
|
||||
fi
|
||||
done
|
||||
|
||||
# Очищаем временные файлы
|
||||
rm -rf ./temp-import
|
||||
|
||||
@@ -35,10 +35,22 @@ const execSshCommand = async (command, options = {}) => {
|
||||
const privateKeyExists = await fs.pathExists(privateKeyPath);
|
||||
const escapedCommand = command.replace(/"/g, '\\"');
|
||||
|
||||
let sshCommand = `ssh -p ${sshPort} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${sshConnectUser}@${sshHost || vdsIp} "${escapedCommand}"`;
|
||||
// Удаляем пробелы и проверяем, что значения не пустые
|
||||
const user = String(sshConnectUser || 'root').trim();
|
||||
const host = String((sshHost || vdsIp || '')).trim();
|
||||
|
||||
if (!host) {
|
||||
throw new Error('Не указан хост для SSH подключения (sshHost или vdsIp)');
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
throw new Error('Не указан пользователь для SSH подключения (sshConnectUser)');
|
||||
}
|
||||
|
||||
let sshCommand = `ssh -p ${sshPort} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR ${user}@${host} "${escapedCommand}"`;
|
||||
|
||||
if (privateKeyExists) {
|
||||
sshCommand = `ssh -i "${privateKeyPath}" -p ${sshPort} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${sshConnectUser}@${sshHost || vdsIp} "${escapedCommand}"`;
|
||||
sshCommand = `ssh -i "${privateKeyPath}" -p ${sshPort} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR ${user}@${host} "${escapedCommand}"`;
|
||||
}
|
||||
|
||||
log.info(`🔍 Выполняем SSH команду: ${sshCommand}`);
|
||||
@@ -49,7 +61,7 @@ const execSshCommand = async (command, options = {}) => {
|
||||
|
||||
if (error && error.code === 255 && sshConnectPassword) {
|
||||
log.info('SSH ключи не сработали, пробуем с паролем...');
|
||||
const passwordCommand = `sshpass -p "${sshConnectPassword}" ssh -p ${sshPort} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${sshConnectUser}@${sshHost || vdsIp} "${escapedCommand}"`;
|
||||
const passwordCommand = `sshpass -p "${String(sshConnectPassword || '').trim()}" ssh -p ${sshPort} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR ${user}@${host} "${escapedCommand}"`;
|
||||
|
||||
exec(passwordCommand, (passwordError, passwordStdout, passwordStderr) => {
|
||||
log.info(`📤 SSH с паролем результат - код: ${passwordError ? passwordError.code : 0}, stdout: "${passwordStdout}", stderr: "${passwordStderr}"`);
|
||||
@@ -83,17 +95,29 @@ const execScpCommand = async (sourcePath, targetPath, options = {}) => {
|
||||
|
||||
const privateKeyExists = await fs.pathExists(privateKeyPath);
|
||||
|
||||
let scpCommand = `scp -P ${sshPort} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${sourcePath} ${sshConnectUser}@${sshHost || vdsIp}:${targetPath}`;
|
||||
// Удаляем пробелы и проверяем, что значения не пустые
|
||||
const user = String(sshConnectUser || 'root').trim();
|
||||
const host = String((sshHost || vdsIp || '')).trim();
|
||||
|
||||
if (!host) {
|
||||
throw new Error('Не указан хост для SCP подключения (sshHost или vdsIp)');
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
throw new Error('Не указан пользователь для SCP подключения (sshConnectUser)');
|
||||
}
|
||||
|
||||
let scpCommand = `scp -P ${sshPort} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR ${sourcePath} ${user}@${host}:${targetPath}`;
|
||||
|
||||
if (privateKeyExists) {
|
||||
scpCommand = `scp -i "${privateKeyPath}" -P ${sshPort} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${sourcePath} ${sshConnectUser}@${sshHost || vdsIp}:${targetPath}`;
|
||||
scpCommand = `scp -i "${privateKeyPath}" -P ${sshPort} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR ${sourcePath} ${user}@${host}:${targetPath}`;
|
||||
}
|
||||
|
||||
return new Promise((resolve) => {
|
||||
exec(scpCommand, (error, stdout, stderr) => {
|
||||
if (error && error.code === 255 && sshConnectPassword) {
|
||||
log.info('SCP с ключами не сработал, пробуем с паролем...');
|
||||
const passwordScpCommand = `sshpass -p "${sshConnectPassword}" scp -P ${sshPort} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${sourcePath} ${sshConnectUser}@${sshHost || vdsIp}:${targetPath}`;
|
||||
const passwordScpCommand = `sshpass -p "${String(sshConnectPassword || '').trim()}" scp -P ${sshPort} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR ${sourcePath} ${user}@${host}:${targetPath}`;
|
||||
|
||||
exec(passwordScpCommand, (passwordError, passwordStdout, passwordStderr) => {
|
||||
if (passwordError) {
|
||||
|
||||
Reference in New Issue
Block a user