WeightTracker/backup-s3.js

150 lines
4.3 KiB
JavaScript

/**
* S3 Backup Script for Weight Tracker
*
* This script creates automated backups of the Weight Tracker data
* and uploads them to an S3-compatible storage (like Minio)
*/
const fs = require('fs');
const path = require('path');
const { S3Client, PutObjectCommand, ListObjectsV2Command, DeleteObjectCommand } = require('@aws-sdk/client-s3');
const cron = require('node-cron');
// Configuration (can be overridden by environment variables)
const config = {
// S3 Configuration
s3: {
endpoint: process.env.S3_ENDPOINT || 'https://your-minio-server.example.com',
region: process.env.S3_REGION || 'us-east-1', // Default region, can be any value for Minio
bucket: process.env.S3_BUCKET || 'weight-tracker-backups',
accessKey: process.env.S3_ACCESS_KEY || 'your-access-key',
secretKey: process.env.S3_SECRET_KEY || 'your-secret-key',
useSSL: process.env.S3_USE_SSL !== 'false' // Default to true unless explicitly set to 'false'
},
// Backup Configuration
backup: {
dataPath: process.env.DATA_PATH || '/data/weight-tracker-data.json',
schedule: process.env.BACKUP_SCHEDULE || '0 0 * * *', // Default: daily at midnight
retention: parseInt(process.env.BACKUP_RETENTION || '7') // Default: keep 7 backups
}
};
// Initialize S3 client
const s3Client = new S3Client({
endpoint: config.s3.endpoint,
region: config.s3.region,
credentials: {
accessKeyId: config.s3.accessKey,
secretAccessKey: config.s3.secretKey
},
forcePathStyle: true, // Required for Minio
tls: config.s3.useSSL
});
/**
* Create a backup and upload to S3
*/
async function createBackup() {
try {
// Check if data file exists
if (!fs.existsSync(config.backup.dataPath)) {
console.error(`Data file not found: ${config.backup.dataPath}`);
return;
}
// Read data file
const data = fs.readFileSync(config.backup.dataPath, 'utf8');
// Generate backup filename with timestamp
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const backupFilename = `weight-tracker-backup-${timestamp}.json`;
// Upload to S3
const uploadParams = {
Bucket: config.s3.bucket,
Key: backupFilename,
Body: data,
ContentType: 'application/json'
};
const uploadCommand = new PutObjectCommand(uploadParams);
await s3Client.send(uploadCommand);
console.log(`Backup created successfully: ${backupFilename}`);
// Cleanup old backups
await cleanupOldBackups();
} catch (error) {
console.error('Error creating backup:', error);
}
}
/**
* Clean up old backups based on retention policy
*/
async function cleanupOldBackups() {
try {
// List all backups
const listParams = {
Bucket: config.s3.bucket,
Prefix: 'weight-tracker-backup-'
};
const listCommand = new ListObjectsV2Command(listParams);
const response = await s3Client.send(listCommand);
if (!response.Contents || response.Contents.length <= config.backup.retention) {
return; // No cleanup needed
}
// Sort by date (oldest first)
const backups = response.Contents.sort((a, b) =>
new Date(a.LastModified) - new Date(b.LastModified)
);
// Delete oldest backups that exceed retention count
const backupsToDelete = backups.slice(0, backups.length - config.backup.retention);
for (const backup of backupsToDelete) {
const deleteParams = {
Bucket: config.s3.bucket,
Key: backup.Key
};
const deleteCommand = new DeleteObjectCommand(deleteParams);
await s3Client.send(deleteCommand);
console.log(`Deleted old backup: ${backup.Key}`);
}
} catch (error) {
console.error('Error cleaning up old backups:', error);
}
}
/**
* Initialize the backup system
*/
function init() {
console.log('Starting Weight Tracker S3 backup system');
console.log(`Backup schedule: ${config.backup.schedule}`);
console.log(`Backup retention: ${config.backup.retention} backups`);
console.log(`S3 endpoint: ${config.s3.endpoint}`);
console.log(`S3 bucket: ${config.s3.bucket}`);
// Schedule regular backups
cron.schedule(config.backup.schedule, () => {
console.log('Running scheduled backup...');
createBackup();
});
// Create initial backup
console.log('Creating initial backup...');
createBackup();
}
// Start the backup system
init();