UNPKG

yemot-recordings-backup

Version:

Automatic backup tool for Yemot HaMashiach call recordings to Amazon S3

182 lines (150 loc) 6.04 kB
const path = require('path'); const logger = require('./logger'); const YemotApi = require('./yemotApi'); const S3Client = require('./s3Client'); class BackupService { constructor(system) { this.system = system; this.yemotApi = new YemotApi(system.username, system.password); this.s3Client = new S3Client(); } /** * Run backup for previous day's recordings * @param {string} customPath - Optional custom path to backup instead of entire system */ async runBackup(customPath) { try { logger.info(`Starting backup for system ${this.system.id} (${this.system.username})`); // Get yesterday's date in YYYY-MM-DD format const yesterday = this.getYesterdayDate(); logger.info(`Backing up recordings from ${yesterday}`); // Start from custom path or root directory const startPath = customPath || '/'; await this.processDirectory(startPath, yesterday); logger.info(`Backup completed for system ${this.system.id}`); } catch (error) { logger.error(`Backup failed for system ${this.system.id}: ${error.message}`); } } /** * Process directory and its subdirectories * @param {string} dirPath - Directory path * @param {string} targetDate - Date to filter recordings (YYYY-MM-DD) */ async processDirectory(dirPath, targetDate) { try { logger.debug(`Processing directory: ${dirPath}`); const directoryContents = await this.yemotApi.getDirectoryContents(dirPath); // Process files in this directory if (directoryContents.files && directoryContents.files.length > 0) { await this.processFiles(dirPath, directoryContents.files, targetDate); } // Process subdirectories recursively if (directoryContents.dirs && directoryContents.dirs.length > 0) { for (const dir of directoryContents.dirs) { // Skip if not a valid directory object if (!dir.what) continue; await this.processDirectory(dir.what, targetDate); } } } catch (error) { logger.error(`Failed to process directory ${dirPath}: ${error.message}`); } } /** * Process files in a directory * @param {string} dirPath - Directory path * @param {Array} files - List of files * @param {string} targetDate - Date to filter recordings (YYYY-MM-DD) */ async processFiles(dirPath, files, targetDate) { // Filter for audio recordings from the target date const recordingsToBackup = files.filter(file => { // Check if it's a recording file if (!this.yemotApi.isRecording(file.name)) return false; // Check if the file was created on the target date if (!file.date) return false; // Extract date part only (remove time) const fileDate = file.date.split(' ')[0]; // dd/mm/yyyy const [day, month, year] = fileDate.split('/'); const fileIsoDate = `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`; return fileIsoDate === targetDate; }); // Log found recordings logger.info(`Found ${recordingsToBackup.length} recordings from ${targetDate} in ${dirPath}`); // Download and upload each recording for (const file of recordingsToBackup) { await this.backupFile(file, dirPath); } } /** * Download and upload a single file * @param {object} file - File object * @param {string} dirPath - Directory path */ async backupFile(file, dirPath) { try { const filePath = this.joinPaths(dirPath, file.name); logger.debug(`Processing file: ${filePath}`); // Create S3 key const s3Key = this.createS3Key(filePath); // Check if file already exists in S3 const exists = await this.s3Client.fileExists(s3Key); if (exists) { logger.info(`File already exists in S3: ${s3Key}`); return; } // Download file const fileContent = await this.yemotApi.downloadFile(filePath); // Upload to S3 await this.s3Client.uploadFile(fileContent, s3Key); logger.info(`Successfully backed up ${filePath} to ${s3Key}`); } catch (error) { logger.error(`Failed to backup file ${file.name}: ${error.message}`); } } /** * Join paths safely * @param {string} dirPath - Directory path * @param {string} fileName - File name * @returns {string} Joined path */ joinPaths(dirPath, fileName) { // If dirPath is root (/), don't add another slash if (dirPath === '/') { return `/${fileName}`; } // Otherwise join with slash return `${dirPath}/${fileName}`; } /** * Create S3 key (path) for a file * @param {string} filePath - File path on Yemot system * @returns {string} S3 key */ createS3Key(filePath) { // Remove leading slash if present const cleanPath = filePath.startsWith('/') ? filePath.substring(1) : filePath; // Extract date from file path or metadata - for now, use today's date const date = this.getYesterdayDate(); // Extract filename const fileName = cleanPath.split('/').pop(); // Extract directory path (without filename) const dirPath = cleanPath.split('/').slice(0, -1).join('/'); // Format: system ID > yemot path > date > filename return `${this.system.id}/${dirPath}/${date}/${fileName}`; } /** * Get yesterday's date in YYYY-MM-DD format * @returns {string} Yesterday's date */ getYesterdayDate() { const date = new Date(); date.setDate(date.getDate() - 1); const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); return `${year}-${month}-${day}`; } } module.exports = BackupService;