105 lines
3.6 KiB
JavaScript
105 lines
3.6 KiB
JavaScript
|
|
#!/usr/bin/env node
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Скрипт для автоматического бэкапа MongoDB
|
|||
|
|
* Использование: node scripts/backup.js
|
|||
|
|
* Или добавить в cron: 0 2 * * * node /path/to/scripts/backup.js
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
const { execSync } = require('child_process');
|
|||
|
|
const fs = require('fs');
|
|||
|
|
const path = require('path');
|
|||
|
|
const dotenv = require('dotenv');
|
|||
|
|
|
|||
|
|
// Загрузить переменные окружения
|
|||
|
|
dotenv.config({ path: path.join(__dirname, '../.env') });
|
|||
|
|
|
|||
|
|
const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017/nakama';
|
|||
|
|
const BACKUP_DIR = process.env.BACKUP_DIR || path.join(__dirname, '../backups');
|
|||
|
|
const MAX_BACKUPS = parseInt(process.env.MAX_BACKUPS || '30'); // Хранить 30 бэкапов
|
|||
|
|
|
|||
|
|
// Парсинг MongoDB URI для получения имени базы данных
|
|||
|
|
function getDatabaseName(uri) {
|
|||
|
|
try {
|
|||
|
|
const match = uri.match(/\/([^/?]+)/);
|
|||
|
|
return match ? match[1] : 'nakama';
|
|||
|
|
} catch {
|
|||
|
|
return 'nakama';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Парсинг MongoDB URI для получения хоста и порта
|
|||
|
|
function getMongoHost(uri) {
|
|||
|
|
try {
|
|||
|
|
const url = new URL(uri);
|
|||
|
|
return `${url.hostname}:${url.port || 27017}`;
|
|||
|
|
} catch {
|
|||
|
|
return 'localhost:27017';
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Создать директорию для бэкапов
|
|||
|
|
if (!fs.existsSync(BACKUP_DIR)) {
|
|||
|
|
fs.mkdirSync(BACKUP_DIR, { recursive: true });
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Имя базы данных
|
|||
|
|
const dbName = getDatabaseName(MONGODB_URI);
|
|||
|
|
const mongoHost = getMongoHost(MONGODB_URI);
|
|||
|
|
|
|||
|
|
// Имя файла бэкапа
|
|||
|
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|||
|
|
const backupName = `backup-${dbName}-${timestamp}`;
|
|||
|
|
const backupPath = path.join(BACKUP_DIR, backupName);
|
|||
|
|
|
|||
|
|
console.log(`📦 Создание бэкапа базы данных: ${dbName}`);
|
|||
|
|
console.log(`📁 Путь: ${backupPath}`);
|
|||
|
|
console.log(`🖥️ Хост: ${mongoHost}`);
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// Создать бэкап с помощью mongodump
|
|||
|
|
const command = `mongodump --host ${mongoHost} --db ${dbName} --out ${backupPath}`;
|
|||
|
|
|
|||
|
|
console.log(`🔄 Выполнение команды: ${command}`);
|
|||
|
|
execSync(command, { stdio: 'inherit' });
|
|||
|
|
|
|||
|
|
console.log(`✅ Бэкап создан успешно: ${backupPath}`);
|
|||
|
|
|
|||
|
|
// Архивировать бэкап
|
|||
|
|
const archivePath = `${backupPath}.tar.gz`;
|
|||
|
|
console.log(`📦 Архивирование бэкапа...`);
|
|||
|
|
execSync(`tar -czf ${archivePath} -C ${BACKUP_DIR} ${backupName}`, { stdio: 'inherit' });
|
|||
|
|
|
|||
|
|
// Удалить неархивированную директорию
|
|||
|
|
execSync(`rm -rf ${backupPath}`, { stdio: 'inherit' });
|
|||
|
|
|
|||
|
|
console.log(`✅ Архив создан: ${archivePath}`);
|
|||
|
|
|
|||
|
|
// Удалить старые бэкапы
|
|||
|
|
const backups = fs.readdirSync(BACKUP_DIR)
|
|||
|
|
.filter(file => file.startsWith(`backup-${dbName}-`) && file.endsWith('.tar.gz'))
|
|||
|
|
.map(file => ({
|
|||
|
|
name: file,
|
|||
|
|
path: path.join(BACKUP_DIR, file),
|
|||
|
|
time: fs.statSync(path.join(BACKUP_DIR, file)).mtime
|
|||
|
|
}))
|
|||
|
|
.sort((a, b) => b.time - a.time);
|
|||
|
|
|
|||
|
|
if (backups.length > MAX_BACKUPS) {
|
|||
|
|
const toDelete = backups.slice(MAX_BACKUPS);
|
|||
|
|
console.log(`🗑️ Удаление старых бэкапов (${toDelete.length} файлов)...`);
|
|||
|
|
toDelete.forEach(backup => {
|
|||
|
|
fs.unlinkSync(backup.path);
|
|||
|
|
console.log(` Удален: ${backup.name}`);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log(`✅ Бэкап завершен успешно`);
|
|||
|
|
console.log(`📊 Всего бэкапов: ${backups.length}`);
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error(`❌ Ошибка создания бэкапа:`, error.message);
|
|||
|
|
process.exit(1);
|
|||
|
|
}
|
|||
|
|
|