UNPKG

yemot-recordings-backup

Version:

Automatic backup tool for Yemot HaMashiach call recordings to Amazon S3

334 lines (287 loc) 10.9 kB
const cron = require('node-cron'); const { Command } = require('commander'); const logger = require('./logger'); const config = require('./config'); const BackupService = require('./backupService'); // Load system configuration config.loadSystems(); /** * Run backup for all enabled systems */ async function runBackup(yemotPath) { try { logger.info('Starting backup job'); // Filter enabled systems const enabledSystems = config.systems.filter(system => system.enabled); if (enabledSystems.length === 0) { logger.warn('No enabled systems found. Please check your configuration.'); return; } logger.info(`Found ${enabledSystems.length} enabled systems to backup`); // Run backup for each system for (const system of enabledSystems) { const backupService = new BackupService(system); await backupService.runBackup(yemotPath); } logger.info('Backup job completed'); } catch (error) { logger.error(`Backup job failed: ${error.message}`); } } // Create CLI const program = new Command(); program .name('yemot-backup') .description('Backup Yemot HaMashiach recordings to Amazon S3') .version('1.0.0'); program .command('run') .description('Run backup job once immediately') .option('-p, --yemot-path <path>', 'Specific path to backup from Yemot (default: root "/")') .action(async (options) => { try { await runBackup(options.yemotPath); process.exit(0); } catch (error) { logger.error(`Error: ${error.message}`); process.exit(1); } }); program .command('schedule') .description('Schedule backup job to run at specified time') .option('-s, --schedule <cron>', 'Cron schedule expression (default: 0 0 * * *)') .option('-p, --yemot-path <path>', 'Specific path to backup from Yemot (default: root "/")') .action((options) => { const schedule = options.schedule || config.schedule; const yemotPath = options.yemotPath; logger.info(`Scheduling backup job with cron: ${schedule}`); cron.schedule(schedule, () => { runBackup(yemotPath).catch(error => { logger.error(`Scheduled backup failed: ${error.message}`); }); }); logger.info('Backup scheduler started. Press Ctrl+C to exit.'); }); program .command('test-connection') .description('Test connection to Yemot API for all systems') .action(async () => { try { logger.info('Testing system connections...'); for (const system of config.systems) { try { const backupService = new BackupService(system); const directoryContents = await backupService.yemotApi.getDirectoryContents('/'); logger.info(`✓ System ${system.id} (${system.username}): Connection successful`); } catch (error) { logger.error(`✗ System ${system.id} (${system.username}): Connection failed - ${error.message}`); } } process.exit(0); } catch (error) { logger.error(`Error: ${error.message}`); process.exit(1); } }); // Add system management commands program .command('systems:list') .description('List all configured systems') .action(() => { try { if (config.systems.length === 0) { console.log('No systems configured.'); return; } console.log('Configured systems:'); config.systems.forEach(system => { const status = system.enabled ? 'Enabled' : 'Disabled'; console.log(`- ${system.id} (${system.username}): ${status}`); }); } catch (error) { logger.error(`Error: ${error.message}`); process.exit(1); } }); program .command('systems:add') .description('Add a new Yemot system') .requiredOption('-i, --id <id>', 'System ID') .requiredOption('-u, --username <username>', 'Yemot username/system number') .requiredOption('-p, --password <password>', 'Yemot password') .option('-e, --enabled <boolean>', 'Enable this system', 'true') .action(async (options) => { try { const newSystem = { id: options.id, username: options.username, password: options.password, enabled: options.enabled === 'true' }; // Add to existing systems const systems = [...config.systems]; // Check if system with same ID already exists const existingIndex = systems.findIndex(s => s.id === newSystem.id); if (existingIndex >= 0) { logger.error(`System with ID "${newSystem.id}" already exists. Use systems:update instead.`); process.exit(1); } systems.push(newSystem); // Save to file const fs = require('fs'); const path = require('path'); const systemsPath = path.join(__dirname, 'systems.json'); fs.writeFileSync(systemsPath, JSON.stringify(systems, null, 2)); logger.info(`System "${newSystem.id}" added successfully.`); } catch (error) { logger.error(`Error: ${error.message}`); process.exit(1); } }); program .command('systems:update') .description('Update an existing Yemot system') .requiredOption('-i, --id <id>', 'System ID to update') .option('-u, --username <username>', 'Yemot username/system number') .option('-p, --password <password>', 'Yemot password') .option('-e, --enabled <boolean>', 'Enable or disable this system') .action(async (options) => { try { // Get existing systems const systems = [...config.systems]; // Find system to update const systemIndex = systems.findIndex(s => s.id === options.id); if (systemIndex < 0) { logger.error(`System with ID "${options.id}" not found.`); process.exit(1); } // Update fields if (options.username) systems[systemIndex].username = options.username; if (options.password) systems[systemIndex].password = options.password; if (options.enabled !== undefined) systems[systemIndex].enabled = options.enabled === 'true'; // Save to file const fs = require('fs'); const path = require('path'); const systemsPath = path.join(__dirname, 'systems.json'); fs.writeFileSync(systemsPath, JSON.stringify(systems, null, 2)); logger.info(`System "${options.id}" updated successfully.`); } catch (error) { logger.error(`Error: ${error.message}`); process.exit(1); } }); program .command('systems:remove') .description('Remove a Yemot system') .requiredOption('-i, --id <id>', 'System ID to remove') .action(async (options) => { try { // Get existing systems const systems = [...config.systems]; // Find system to remove const systemIndex = systems.findIndex(s => s.id === options.id); if (systemIndex < 0) { logger.error(`System with ID "${options.id}" not found.`); process.exit(1); } // Remove system systems.splice(systemIndex, 1); // Save to file const fs = require('fs'); const path = require('path'); const systemsPath = path.join(__dirname, 'systems.json'); fs.writeFileSync(systemsPath, JSON.stringify(systems, null, 2)); logger.info(`System "${options.id}" removed successfully.`); } catch (error) { logger.error(`Error: ${error.message}`); process.exit(1); } }); program .command('systems:show') .description('Show details for a specific system') .requiredOption('-i, --id <id>', 'System ID to show') .action(async (options) => { try { // Find system const system = config.systems.find(s => s.id === options.id); if (!system) { logger.error(`System with ID "${options.id}" not found.`); process.exit(1); } console.log('System details:'); console.log(`- ID: ${system.id}`); console.log(`- Username: ${system.username}`); console.log(`- Password: ${system.password.substring(0, 3)}${'*'.repeat(system.password.length - 3)}`); console.log(`- Enabled: ${system.enabled ? 'Yes' : 'No'}`); } catch (error) { logger.error(`Error: ${error.message}`); process.exit(1); } }); // AWS configuration commands program .command('aws:configure') .description('Configure AWS settings') .requiredOption('-k, --key <key>', 'AWS access key ID') .requiredOption('-s, --secret <secret>', 'AWS secret access key') .requiredOption('-r, --region <region>', 'AWS region') .requiredOption('-b, --bucket <bucket>', 'S3 bucket name') .action(async (options) => { try { // Update .env file const fs = require('fs'); const path = require('path'); const dotenv = require('dotenv'); const envPath = path.join(process.cwd(), '.env'); // Read existing .env if it exists let envConfig = {}; try { const existingEnv = fs.readFileSync(envPath, 'utf8'); envConfig = dotenv.parse(existingEnv); } catch (error) { // File doesn't exist, start with empty config } // Update values envConfig.AWS_ACCESS_KEY_ID = options.key; envConfig.AWS_SECRET_ACCESS_KEY = options.secret; envConfig.AWS_REGION = options.region; envConfig.AWS_S3_BUCKET = options.bucket; // Write back to .env const envContent = Object.entries(envConfig) .map(([key, value]) => `${key}=${value}`) .join('\n'); fs.writeFileSync(envPath, envContent); logger.info('AWS credentials saved successfully.'); // Update current config config.aws = { accessKeyId: options.key, secretAccessKey: options.secret, region: options.region, bucket: options.bucket }; } catch (error) { logger.error(`Error: ${error.message}`); process.exit(1); } }); program .command('aws:status') .description('Show current AWS configuration') .action(() => { console.log('Current AWS Configuration:'); if (!config.aws.accessKeyId) { console.log('AWS is not configured. Use aws:configure to set up AWS credentials.'); return; } console.log(`- Access Key: ${config.aws.accessKeyId.substring(0, 4)}${'*'.repeat(16)}`); console.log(`- Secret Key: ${'*'.repeat(8)}`); console.log(`- Region: ${config.aws.region}`); console.log(`- S3 Bucket: ${config.aws.bucket}`); }); program.parse(process.argv); // If no command specified, show help if (!process.argv.slice(2).length) { program.outputHelp(); }