yemot-recordings-backup
Version:
Automatic backup tool for Yemot HaMashiach call recordings to Amazon S3
334 lines (287 loc) • 10.9 kB
JavaScript
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();
}