TransHub_backend / comprehensive-backup.js
linguabot's picture
Upload folder using huggingface_hub
da819ac verified
const mongoose = require('mongoose');
const fs = require('fs').promises;
const path = require('path');
const { exec } = require('child_process');
const { promisify } = require('util');
const execAsync = promisify(exec);
// Atlas MongoDB connection string
const MONGODB_URI = 'mongodb+srv://nothingyu:wSg3lbO1PkHiRMq9@sandbox.ecysggv.mongodb.net/test?retryWrites=true&w=majority&appName=sandbox';
// Connect to MongoDB Atlas
const connectDB = async () => {
try {
await mongoose.connect(MONGODB_URI);
console.log('βœ… Connected to MongoDB Atlas');
} catch (error) {
console.error('❌ MongoDB connection error:', error);
process.exit(1);
}
};
// Comprehensive Backup System (Data + Code)
const comprehensiveBackup = {
// Create comprehensive backup
async createComprehensiveBackup() {
try {
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const backupName = `comprehensive-backup-${timestamp}`;
console.log(`πŸš€ Creating comprehensive backup: ${backupName}`);
// Create backup directory
const backupDir = path.join(__dirname, 'backups', backupName);
await fs.mkdir(backupDir, { recursive: true });
// 1. BACKUP DATABASE DATA
console.log('πŸ“Š Backing up database data...');
const dbBackup = await this.backupDatabase(backupDir);
// 2. BACKUP CODE FILES
console.log('πŸ’» Backing up code files...');
const codeBackup = await this.backupCodeFiles(backupDir);
// 3. BACKUP CONFIGURATION
console.log('βš™οΈ Backing up configuration...');
const configBackup = await this.backupConfiguration(backupDir);
// 4. CREATE BACKUP MANIFEST
console.log('πŸ“‹ Creating backup manifest...');
const manifest = {
backupName,
timestamp: new Date(),
type: 'comprehensive',
data: {
database: dbBackup,
code: codeBackup,
configuration: configBackup
},
totalSize: await this.calculateBackupSize(backupDir),
version: '1.0'
};
await fs.writeFile(path.join(backupDir, 'manifest.json'), JSON.stringify(manifest, null, 2));
// 5. SAVE BACKUP RECORD TO DATABASE
const backupRecord = {
backupName,
timestamp: new Date(),
type: 'comprehensive',
location: backupDir,
size: manifest.totalSize,
status: 'created'
};
await mongoose.connection.db.collection('backups').insertOne(backupRecord);
console.log(`βœ… Comprehensive backup created: ${backupName}`);
console.log(`πŸ“Š Database records: ${dbBackup.totalRecords}`);
console.log(`πŸ’» Code files: ${codeBackup.fileCount}`);
console.log(`πŸ’Ύ Total size: ${(manifest.totalSize / 1024 / 1024).toFixed(2)} MB`);
return backupName;
} catch (error) {
console.error('❌ Error creating comprehensive backup:', error);
throw error;
}
},
// Backup database data
async backupDatabase(backupDir) {
const collections = ['subtitles', 'sourcetexts', 'submissions', 'users', 'backups'];
const dbData = {};
let totalRecords = 0;
for (const collection of collections) {
try {
const data = await mongoose.connection.db.collection(collection).find({}).toArray();
dbData[collection] = data;
totalRecords += data.length;
console.log(` πŸ“¦ Exported ${data.length} records from ${collection}`);
} catch (error) {
console.warn(` ⚠️ Could not export ${collection}:`, error.message);
}
}
const dbBackupPath = path.join(backupDir, 'database.json');
await fs.writeFile(dbBackupPath, JSON.stringify(dbData, null, 2));
return {
totalRecords,
collections: Object.keys(dbData),
filePath: dbBackupPath
};
},
// Backup code files
async backupCodeFiles(backupDir) {
const codeDir = path.join(backupDir, 'code');
await fs.mkdir(codeDir, { recursive: true });
// Define important code directories and files
const codePaths = [
// Backend code
{ src: path.join(__dirname), dest: 'backend' },
// Frontend code (relative to backend)
{ src: path.join(__dirname, '../frontend'), dest: 'frontend' },
// Root configuration
{ src: path.join(__dirname, '../../'), dest: 'root' }
];
let fileCount = 0;
for (const codePath of codePaths) {
try {
if (await this.pathExists(codePath.src)) {
await this.copyDirectory(codePath.src, path.join(codeDir, codePath.dest));
const count = await this.countFiles(codePath.src);
fileCount += count;
console.log(` πŸ’» Copied ${count} files from ${codePath.dest}`);
}
} catch (error) {
console.warn(` ⚠️ Could not backup ${codePath.dest}:`, error.message);
}
}
return {
fileCount,
directories: codePaths.map(p => p.dest),
location: codeDir
};
},
// Backup configuration
async backupConfiguration(backupDir) {
const configDir = path.join(backupDir, 'config');
await fs.mkdir(configDir, { recursive: true });
const configFiles = [
'package.json',
'package-lock.json',
'Dockerfile',
'docker-compose.yml',
'nginx.conf',
'.gitignore'
];
let configCount = 0;
for (const configFile of configFiles) {
try {
const srcPath = path.join(__dirname, configFile);
if (await this.pathExists(srcPath)) {
const destPath = path.join(configDir, configFile);
await fs.copyFile(srcPath, destPath);
configCount++;
console.log(` βš™οΈ Copied ${configFile}`);
}
} catch (error) {
console.warn(` ⚠️ Could not copy ${configFile}:`, error.message);
}
}
return {
fileCount: configCount,
files: configFiles,
location: configDir
};
},
// Helper functions
async pathExists(path) {
try {
await fs.access(path);
return true;
} catch {
return false;
}
},
async copyDirectory(src, dest) {
await fs.mkdir(dest, { recursive: true });
const entries = await fs.readdir(src, { withFileTypes: true });
for (const entry of entries) {
const srcPath = path.join(src, entry.name);
const destPath = path.join(dest, entry.name);
if (entry.isDirectory()) {
await this.copyDirectory(srcPath, destPath);
} else {
await fs.copyFile(srcPath, destPath);
}
}
},
async countFiles(dir) {
let count = 0;
const entries = await fs.readdir(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
count += await this.countFiles(fullPath);
} else {
count++;
}
}
return count;
},
async calculateBackupSize(backupDir) {
const { stdout } = await execAsync(`du -sb "${backupDir}" | cut -f1`);
return parseInt(stdout.trim());
},
// List comprehensive backups
async listComprehensiveBackups() {
try {
console.log('πŸ“‹ Available comprehensive backups:');
const backupCollection = mongoose.connection.db.collection('backups');
const backups = await backupCollection.find({ type: 'comprehensive' }).sort({ timestamp: -1 }).toArray();
if (backups.length === 0) {
console.log(' No comprehensive backups found');
} else {
backups.forEach(backup => {
const date = new Date(backup.timestamp).toLocaleString();
const size = (backup.size / 1024 / 1024).toFixed(2);
console.log(` πŸ“¦ ${backup.backupName} (${size} MB, ${date})`);
});
}
} catch (error) {
console.error('❌ Error listing backups:', error);
}
},
// Restore from comprehensive backup
async restoreFromBackup(backupName) {
try {
console.log(`πŸ”„ Restoring from comprehensive backup: ${backupName}`);
const backupDir = path.join(__dirname, 'backups', backupName);
const manifestPath = path.join(backupDir, 'manifest.json');
if (!await this.pathExists(manifestPath)) {
throw new Error('Backup manifest not found');
}
const manifest = JSON.parse(await fs.readFile(manifestPath, 'utf8'));
console.log('πŸ“‹ Backup manifest:', manifest);
// Restore database
console.log('πŸ“Š Restoring database...');
await this.restoreDatabase(backupDir);
// Restore code (optional - user confirmation)
console.log('πŸ’» Code restoration available');
console.log('⚠️ Code restoration will overwrite existing files');
console.log(' Run: node restore-code.js <backup-name> to restore code');
console.log(`βœ… Database restoration completed: ${backupName}`);
} catch (error) {
console.error('❌ Error restoring from backup:', error);
}
},
// Restore database only
async restoreDatabase(backupDir) {
const dbBackupPath = path.join(backupDir, 'database.json');
const dbData = JSON.parse(await fs.readFile(dbBackupPath, 'utf8'));
for (const [collection, data] of Object.entries(dbData)) {
try {
// Clear existing data
await mongoose.connection.db.collection(collection).deleteMany({});
// Insert backup data
if (data.length > 0) {
await mongoose.connection.db.collection(collection).insertMany(data);
}
console.log(` βœ… Restored ${data.length} records to ${collection}`);
} catch (error) {
console.error(` ❌ Error restoring ${collection}:`, error.message);
}
}
}
};
// Main function
const main = async () => {
try {
console.log('πŸš€ Starting comprehensive backup system...');
// Create comprehensive backup
const backupName = await comprehensiveBackup.createComprehensiveBackup();
// List backups
await comprehensiveBackup.listComprehensiveBackups();
console.log('\nπŸŽ‰ Comprehensive backup system ready!');
console.log('\nπŸ“‹ Available functions:');
console.log(' - createComprehensiveBackup(): Backup data + code');
console.log(' - listComprehensiveBackups(): List all backups');
console.log(' - restoreFromBackup(name): Restore database');
console.log(' - restoreCode(name): Restore code files (separate script)');
console.log('\n⏰ To set up automated comprehensive backups:');
console.log(' 1. Add to crontab: 0 2 * * * cd /path/to/backend && node comprehensive-backup.js');
console.log(' 2. Or run manually: node comprehensive-backup.js');
} catch (error) {
console.error('❌ Error in comprehensive backup system:', error);
} finally {
await mongoose.disconnect();
console.log('πŸ”Œ Disconnected from MongoDB');
}
};
// Run the system
connectDB().then(() => {
main();
});