feat: новая функция

This commit is contained in:
2025-11-11 20:11:09 +03:00
parent 1466de718f
commit 44876c0bc2
9 changed files with 173 additions and 133 deletions

View File

@@ -9,14 +9,14 @@ const cleanupVdsServer = async (options) => {
// Остановка и удаление существующих Docker контейнеров
log.info('Остановка существующих Docker контейнеров...');
await execSshCommand('sudo docker stop $(sudo docker ps -aq) 2>/dev/null || true', options);
await execSshCommand('sudo docker rm $(sudo docker ps -aq) 2>/dev/null || true', options);
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 образов и системы...');
await execSshCommand('sudo docker system prune -af || true', options);
await execSshCommand('sudo docker volume prune -f || true', options);
await execSshCommand('sudo docker network prune -f || true', options);
await execSshCommand('docker system prune -af || true', options);
await execSshCommand('docker volume prune -f || true', options);
await execSshCommand('docker network prune -f || true', options);
// 🆕 Умная проверка и удаление системного nginx для избежания конфликтов портов
log.info('🔍 Проверка наличия системного nginx...');
@@ -26,12 +26,12 @@ const cleanupVdsServer = async (options) => {
log.info('⚠️ Обнаружен системный nginx - удаляем для освобождения портов 80/443...');
// Полная остановка и удаление системного nginx
await execSshCommand('sudo systemctl stop nginx || true', options);
await execSshCommand('sudo systemctl disable nginx || true', options);
await execSshCommand('sudo systemctl mask nginx || true', options);
await execSshCommand('sudo pkill -f nginx || true', options);
await execSshCommand('sudo apt-get purge -y nginx nginx-common nginx-full || true', options);
await execSshCommand('sudo apt-get autoremove -y || true', options);
await execSshCommand('systemctl stop nginx || true', options);
await execSshCommand('systemctl disable nginx || true', options);
await execSshCommand('systemctl mask nginx || true', options);
await execSshCommand('pkill -f nginx || true', options);
await execSshCommand('apt-get purge -y nginx nginx-common nginx-full || true', options);
await execSshCommand('apt-get autoremove -y || true', options);
log.success('✅ Системный nginx полностью удален, порты 80/443 освобождены для Docker nginx');
} else {
@@ -40,18 +40,18 @@ const cleanupVdsServer = async (options) => {
// Остановка других конфликтующих сервисов
log.info('Остановка других конфликтующих сервисов...');
await execSshCommand('sudo systemctl stop apache2 2>/dev/null || true', options);
await execSshCommand('sudo systemctl disable apache2 2>/dev/null || true', options);
await execSshCommand('sudo systemctl mask apache2 2>/dev/null || true', options);
await execSshCommand('systemctl stop apache2 2>/dev/null || true', options);
await execSshCommand('systemctl disable apache2 2>/dev/null || true', options);
await execSshCommand('systemctl mask apache2 2>/dev/null || true', options);
// Очистка старых пакетов
log.info('Очистка старых пакетов...');
await execSshCommand('sudo apt-get autoremove -y || true', options);
await execSshCommand('sudo apt-get autoclean || true', options);
await execSshCommand('apt-get autoremove -y || true', options);
await execSshCommand('apt-get autoclean || true', options);
// Очистка временных файлов
log.info('Очистка временных файлов...');
await execSshCommand('sudo rm -rf /tmp/* /var/tmp/* 2>/dev/null || true', options);
await execSshCommand('rm -rf /tmp/* /var/tmp/* 2>/dev/null || true', options);
log.success('VDS сервер очищен');
};
@@ -63,13 +63,13 @@ const setupRootSshKeys = async (publicKey, options) => {
log.info('Настройка SSH ключей...');
// Создание директории .ssh для root
await execSshCommand('sudo mkdir -p /root/.ssh', options);
await execSshCommand('sudo chmod 700 /root/.ssh', options);
await execSshCommand('mkdir -p /root/.ssh', options);
await execSshCommand('chmod 700 /root/.ssh', options);
// Добавление публичного ключа в authorized_keys
await execSshCommand(`echo "${publicKey}" | sudo tee -a /root/.ssh/authorized_keys`, options);
await execSshCommand('sudo chmod 600 /root/.ssh/authorized_keys', options);
await execSshCommand('sudo chown root:root /root/.ssh/authorized_keys', options);
await execSshCommand(`echo "${publicKey}" >> /root/.ssh/authorized_keys`, options);
await execSshCommand('chmod 600 /root/.ssh/authorized_keys', options);
await execSshCommand('chown root:root /root/.ssh/authorized_keys', options);
log.success('SSH ключи созданы и публичный ключ добавлен в authorized_keys');
};
@@ -79,9 +79,9 @@ const setupRootSshKeys = async (publicKey, options) => {
*/
const disablePasswordAuth = async (options) => {
log.info('Отключение парольной аутентификации...');
await execSshCommand('sudo sed -i "s/#PasswordAuthentication yes/PasswordAuthentication no/" /etc/ssh/sshd_config', options);
await execSshCommand('sudo sed -i "s/PasswordAuthentication yes/PasswordAuthentication no/" /etc/ssh/sshd_config', options);
await execSshCommand('sudo systemctl restart ssh', options);
await execSshCommand('sed -i "s/#PasswordAuthentication yes/PasswordAuthentication no/" /etc/ssh/sshd_config', options);
await execSshCommand('sed -i "s/PasswordAuthentication yes/PasswordAuthentication no/" /etc/ssh/sshd_config', options);
await execSshCommand('systemctl restart ssh', options);
log.success('Парольная аутентификация отключена, доступ только через SSH ключи');
};
@@ -90,10 +90,10 @@ const disablePasswordAuth = async (options) => {
*/
const setupFirewall = async (options) => {
log.info('Настройка firewall...');
await execSshCommand('sudo ufw --force enable', options);
await execSshCommand('sudo ufw allow ssh', options);
await execSshCommand('sudo ufw allow 80', options);
await execSshCommand('sudo ufw allow 443', options);
await execSshCommand('ufw --force enable', options);
await execSshCommand('ufw allow ssh', options);
await execSshCommand('ufw allow 80', options);
await execSshCommand('ufw allow 443', options);
log.success('Firewall настроен');
};

View File

@@ -29,6 +29,10 @@ const execDockerCommand = async (command) => {
return execAsync(command);
};
const execLocalCommand = async (command, options = {}) => {
return execAsync(command, { maxBuffer: options.maxBuffer || 1024 * 1024 * 50 });
};
/**
* Экспорт Docker образов и данных с локальной машины
*/
@@ -89,9 +93,8 @@ const exportDockerImages = async (sendWebSocketLog) => {
const tarFiles = images.map(img => img.file).join(' ');
const dataFiles = 'postgres_data.tar.gz ollama_data.tar.gz vector_search_data.tar.gz';
// Безопасное создание архива через CLI
const archiveCommand = `cd /tmp && tar -czf docker-images-and-data.tar.gz ${tarFiles} ${dataFiles}`;
await execDockerCommand(archiveCommand);
await execLocalCommand(archiveCommand);
sendWebSocketLog('success', '✅ Архив создан успешно', 'export_data', 80);
} catch (error) {
@@ -110,7 +113,7 @@ const exportVolumeData = async (volumeName, outputFile, sendWebSocketLog, progre
try {
// Безопасный экспорт через CLI с временным контейнером
const exportCommand = `docker run --rm -v ${volumeName}:/data:ro -v /tmp:/backup alpine tar czf /backup/${outputFile} -C /data .`;
await execDockerCommand(exportCommand);
await execLocalCommand(exportCommand);
sendWebSocketLog('success', `✅ Экспорт ${outputFile} завершен`, 'export_data', progress);
} catch (error) {
@@ -216,8 +219,8 @@ docker images | grep -E "digital_legal_entitydle|postgres"
echo "📋 Доступные volumes:"
docker volume ls | grep dapp_`;
await execSshCommand(`echo '${importScript}' | sudo tee /home/${dockerUser}/dapp/import-images-and-data.sh`, options);
await execSshCommand(`sudo chmod +x /home/${dockerUser}/dapp/import-images-and-data.sh`, options);
await execSshCommand(`echo '${importScript}' | tee /home/${dockerUser}/dapp/import-images-and-data.sh`, options);
await execSshCommand(`chmod +x /home/${dockerUser}/dapp/import-images-and-data.sh`, options);
// Импортируем образы и данные
log.info('Импорт Docker образов и данных...');
@@ -238,9 +241,7 @@ docker volume ls | grep dapp_`;
const cleanupLocalFiles = async () => {
log.info('Очистка временных файлов на хосте...');
try {
await fs.remove('/tmp/dapp-*.tar');
await fs.remove('/tmp/*_data.tar.gz');
await fs.remove('/tmp/docker-images-and-data.tar.gz');
await execLocalCommand("rm -f /tmp/dapp-*.tar /tmp/*_data.tar.gz /tmp/docker-images-and-data.tar.gz");
log.success('Временные файлы очищены');
} catch (error) {
log.error('Ошибка очистки файлов: ' + error.message);

View File

@@ -1,4 +1,5 @@
const { exec } = require('child_process');
const fs = require('fs-extra');
const os = require('os');
const path = require('path');
const log = require('./logger');
@@ -14,10 +15,10 @@ const sshConfigPath = path.join(sshDir, 'config');
const execLocalCommand = async (command) => {
return new Promise((resolve) => {
exec(command, (error, stdout, stderr) => {
resolve({
code: error ? error.code : 0,
stdout: stdout || '',
stderr: stderr || ''
resolve({
code: error ? error.code : 0,
stdout: stdout || '',
stderr: stderr || ''
});
});
});
@@ -28,35 +29,35 @@ const execLocalCommand = async (command) => {
*/
const createSshKeys = async (email) => {
log.info('Создание SSH ключей на хосте...');
return new Promise((resolve) => {
// Сначала исправляем права доступа к SSH конфигу
exec(`mkdir -p "${sshDir}" && chmod 700 "${sshDir}" && chmod 600 "${sshConfigPath}" 2>/dev/null || true`, (configError) => {
if (configError) {
log.warn('Не удалось исправить права доступа к SSH конфигу: ' + configError.message);
await fs.ensureDir(sshDir);
await execLocalCommand(`chmod 700 "${sshDir}" && chmod 600 "${sshConfigPath}" 2>/dev/null || true`);
const privateExists = await fs.pathExists(privateKeyPath);
const publicExists = await fs.pathExists(publicKeyPath);
if (privateExists && publicExists) {
log.info('SSH ключи уже существуют используем текущую пару');
await execLocalCommand(`chmod 600 "${privateKeyPath}" && chmod 644 "${publicKeyPath}"`);
return;
}
await new Promise((resolve) => {
exec(`ssh-keygen -q -t rsa -b 4096 -C "${email}" -f "${privateKeyPath}" -N ""`, (error) => {
if (error) {
log.error('Ошибка создания SSH ключей: ' + error.message);
return resolve();
}
// Создаем SSH ключи
exec(`ssh-keygen -t rsa -b 4096 -C "${email}" -f "${privateKeyPath}" -N ""`, (error, stdout, stderr) => {
if (error) {
log.error('Ошибка создания SSH ключей: ' + error.message);
log.success('SSH ключи успешно созданы на хосте');
exec(`chmod 600 "${privateKeyPath}" && chmod 644 "${publicKeyPath}"`, (permError) => {
if (permError) {
log.warn('Не удалось установить права доступа к SSH ключам: ' + permError.message);
} else {
log.success('SSH ключи успешно созданы на хосте');
// Устанавливаем правильные права доступа к созданным ключам
exec(`chmod 600 "${privateKeyPath}" && chmod 644 "${publicKeyPath}"`, (permError) => {
if (permError) {
log.warn('Не удалось установить права доступа к SSH ключам: ' + permError.message);
} else {
log.success('Права доступа к SSH ключам установлены');
}
resolve();
});
}
if (error) {
resolve();
log.success('Права доступа к SSH ключам установлены');
}
resolve();
});
});
});

View File

@@ -8,15 +8,15 @@ const createUserWithSshKeys = async (username, publicKey, options) => {
log.info(`Создание пользователя ${username}...`);
// Создание пользователя
await execSshCommand(`sudo useradd -m -s /bin/bash ${username} || true`, options);
await execSshCommand(`sudo usermod -aG sudo ${username}`, options);
await execSshCommand(`useradd -m -s /bin/bash ${username} || true`, options);
await execSshCommand(`usermod -aG sudo ${username}`, options);
// Настройка SSH ключей для пользователя
await execSshCommand(`sudo mkdir -p /home/${username}/.ssh`, options);
await execSshCommand(`echo "${publicKey}" | sudo tee /home/${username}/.ssh/authorized_keys`, options);
await execSshCommand(`sudo chown -R ${username}:${username} /home/${username}/.ssh`, options);
await execSshCommand(`sudo chmod 700 /home/${username}/.ssh`, options);
await execSshCommand(`sudo chmod 600 /home/${username}/.ssh/authorized_keys`, options);
await execSshCommand(`mkdir -p /home/${username}/.ssh`, options);
await execSshCommand(`echo "${publicKey}" > /home/${username}/.ssh/authorized_keys`, options);
await execSshCommand(`chown -R ${username}:${username} /home/${username}/.ssh`, options);
await execSshCommand(`chmod 700 /home/${username}/.ssh`, options);
await execSshCommand(`chmod 600 /home/${username}/.ssh/authorized_keys`, options);
log.success(`Пользователь ${username} создан с SSH ключами`);
};
@@ -32,11 +32,11 @@ const createAllUsers = async (ubuntuUser, dockerUser, publicKey, options) => {
await createUserWithSshKeys(dockerUser, publicKey, options);
// Добавление пользователя Docker в группу docker
await execSshCommand(`sudo usermod -aG docker ${dockerUser}`, options);
await execSshCommand(`usermod -aG docker ${dockerUser}`, options);
// Создание директории для приложения
await execSshCommand(`sudo mkdir -p /home/${dockerUser}/dapp`, options);
await execSshCommand(`sudo chown ${dockerUser}:${dockerUser} /home/${dockerUser}/dapp`, options);
await execSshCommand(`mkdir -p /home/${dockerUser}/dapp`, options);
await execSshCommand(`chown ${dockerUser}:${dockerUser} /home/${dockerUser}/dapp`, options);
log.success('Все пользователи созданы');
};