20dd43c093
Mirror to GitHub / mirror (push) Successful in 28s
Test and Lint / backend-test (push) Successful in 1m26s
continuous-integration/drone/push Build is failing
Test and Lint / frontend-test (push) Failing after 2m18s
Version and Release / version-bump (push) Successful in 42s
Version and Release / trigger-drone (push) Successful in 3s
- Add S3/MinIO storage adapter with multipart upload support - Implement database backup service for SQLite and PostgreSQL - Create backup manifest generator for tracking backup contents - Enhance backup service with S3 integration and incremental backups - Add restore service with safety measures and rollback capability - Create comprehensive test suite for all backup functionality - Add admin API endpoints for backup/restore management - Implement frontend UI with dashboard, configuration, and restore wizard - Add roadmap section to README with implemented backup feature This implementation provides: - Multiple backup destinations (local, rsync, S3/MinIO) - Intelligent change detection to minimize backup frequency - Full database backups with compression - Manifest-based restore with integrity validation - Pre-restore safety backups with rollback - Comprehensive error handling and monitoring - User-friendly admin interface 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
192 lines
7.0 KiB
JavaScript
192 lines
7.0 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
/**
|
|
* Test script for backup manifest generator
|
|
* Demonstrates all features of the manifest generator
|
|
*/
|
|
|
|
const path = require('path');
|
|
const fs = require('fs').promises;
|
|
const backupManifest = require('../src/services/backupManifest');
|
|
const logger = require('../src/utils/logger');
|
|
|
|
async function testManifestGeneration() {
|
|
console.log('=== Testing Backup Manifest Generator ===\n');
|
|
|
|
try {
|
|
// 1. Generate a full backup manifest
|
|
console.log('1. Generating full backup manifest...');
|
|
|
|
const fullManifestOptions = {
|
|
backupType: 'full',
|
|
backupPath: '/backup/full/2025-01-21',
|
|
files: [
|
|
{
|
|
path: '/storage/events/active/wedding-smith-2025/DSC_001.jpg',
|
|
relativePath: 'events/active/wedding-smith-2025/DSC_001.jpg',
|
|
size: 2456789,
|
|
modified: new Date('2025-01-20T10:30:00Z'),
|
|
checksum: 'a1b2c3d4e5f6789012345678901234567890123456789012345678901234567890',
|
|
permissions: '644'
|
|
},
|
|
{
|
|
path: '/storage/events/active/wedding-smith-2025/DSC_002.jpg',
|
|
relativePath: 'events/active/wedding-smith-2025/DSC_002.jpg',
|
|
size: 2156789,
|
|
modified: new Date('2025-01-20T10:31:00Z'),
|
|
checksum: 'b2c3d4e5f67890123456789012345678901234567890123456789012345678901',
|
|
permissions: '644'
|
|
},
|
|
{
|
|
path: '/storage/thumbnails/wedding-smith-2025/thumb_DSC_001.jpg',
|
|
relativePath: 'thumbnails/wedding-smith-2025/thumb_DSC_001.jpg',
|
|
size: 45678,
|
|
modified: new Date('2025-01-20T10:35:00Z'),
|
|
checksum: 'c3d4e5f678901234567890123456789012345678901234567890123456789012',
|
|
permissions: '644'
|
|
}
|
|
],
|
|
databaseInfo: {
|
|
type: 'sqlite',
|
|
backupFile: 'database-backup-20250121-103000.sql.gz',
|
|
size: 1048576,
|
|
checksum: 'd4e5f6789012345678901234567890123456789012345678901234567890123',
|
|
tables: {
|
|
events: 156,
|
|
photos: 4523,
|
|
access_logs: 12456,
|
|
admin_users: 3
|
|
},
|
|
rowCounts: {
|
|
events: 156,
|
|
photos: 4523,
|
|
access_logs: 12456,
|
|
admin_users: 3
|
|
}
|
|
},
|
|
format: 'json',
|
|
customMetadata: {
|
|
operator: 'admin@example.com',
|
|
reason: 'Scheduled daily backup',
|
|
retentionDays: 30,
|
|
compressionType: 'gzip'
|
|
}
|
|
};
|
|
|
|
const fullManifest = await backupManifest.generateManifest(fullManifestOptions);
|
|
|
|
// Save in both formats
|
|
const jsonPath = path.join(__dirname, 'test-manifest-full.json');
|
|
const yamlPath = path.join(__dirname, 'test-manifest-full.yaml');
|
|
|
|
await backupManifest.saveManifest(fullManifest, jsonPath, 'json');
|
|
await backupManifest.saveManifest(fullManifest, yamlPath, 'yaml');
|
|
|
|
console.log('✓ Full backup manifest generated and saved\n');
|
|
|
|
// 2. Generate summary report
|
|
console.log('2. Generating summary report...');
|
|
const summaryReport = backupManifest.generateSummaryReport(fullManifest);
|
|
console.log(summaryReport);
|
|
console.log('\n');
|
|
|
|
// 3. Load and validate manifest
|
|
console.log('3. Loading and validating manifest...');
|
|
const loadedManifest = await backupManifest.loadManifest(jsonPath);
|
|
console.log('✓ Manifest loaded and validated successfully\n');
|
|
|
|
// 4. Generate incremental backup manifest
|
|
console.log('4. Generating incremental backup manifest...');
|
|
|
|
const incrementalOptions = {
|
|
backupType: 'incremental',
|
|
backupPath: '/backup/incremental/2025-01-22',
|
|
parentBackupId: fullManifest.backup.id,
|
|
files: [
|
|
// Original files with same checksums (unchanged)
|
|
fullManifestOptions.files[0],
|
|
fullManifestOptions.files[2],
|
|
// Modified file
|
|
{
|
|
...fullManifestOptions.files[1],
|
|
size: 2256789,
|
|
modified: new Date('2025-01-21T14:00:00Z'),
|
|
checksum: 'e5f678901234567890123456789012345678901234567890123456789012345'
|
|
},
|
|
// New file
|
|
{
|
|
path: '/storage/events/active/wedding-smith-2025/DSC_003.jpg',
|
|
relativePath: 'events/active/wedding-smith-2025/DSC_003.jpg',
|
|
size: 2356789,
|
|
modified: new Date('2025-01-21T14:30:00Z'),
|
|
checksum: 'f6789012345678901234567890123456789012345678901234567890123456',
|
|
permissions: '644'
|
|
}
|
|
],
|
|
databaseInfo: {
|
|
...fullManifestOptions.databaseInfo,
|
|
size: 1148576,
|
|
checksum: 'g7890123456789012345678901234567890123456789012345678901234567',
|
|
rowCounts: {
|
|
events: 158,
|
|
photos: 4567,
|
|
access_logs: 12789,
|
|
admin_users: 3
|
|
}
|
|
}
|
|
};
|
|
|
|
const incrementalManifest = await backupManifest.generateIncrementalManifest(
|
|
incrementalOptions,
|
|
fullManifest
|
|
);
|
|
|
|
const incrementalJsonPath = path.join(__dirname, 'test-manifest-incremental.json');
|
|
await backupManifest.saveManifest(incrementalManifest, incrementalJsonPath, 'json');
|
|
|
|
console.log('✓ Incremental backup manifest generated');
|
|
console.log(` - Added files: ${incrementalManifest.incremental.changes.added_files_count}`);
|
|
console.log(` - Modified files: ${incrementalManifest.incremental.changes.modified_files_count}`);
|
|
console.log(` - Deleted files: ${incrementalManifest.incremental.changes.deleted_files_count}`);
|
|
console.log(` - Size difference: ${(incrementalManifest.incremental.changes.size_difference / 1024).toFixed(2)} KB\n`);
|
|
|
|
// 5. Compare manifests
|
|
console.log('5. Comparing manifests...');
|
|
const comparison = backupManifest.compareManifests(incrementalManifest, fullManifest);
|
|
console.log('Comparison results:');
|
|
console.log(` - Added: ${comparison.added_files.length} files`);
|
|
console.log(` - Modified: ${comparison.modified_files.length} files`);
|
|
console.log(` - Deleted: ${comparison.deleted_files.length} files`);
|
|
console.log(` - Unchanged: ${comparison.unchanged_files.length} files`);
|
|
console.log(` - Database changed: ${comparison.database_changes.checksum_changed ? 'Yes' : 'No'}\n`);
|
|
|
|
// 6. Test manifest integrity
|
|
console.log('6. Testing manifest integrity...');
|
|
|
|
// Corrupt the manifest
|
|
const corruptedManifest = JSON.parse(JSON.stringify(incrementalManifest));
|
|
corruptedManifest.files.manifest[0].size = 9999999; // Change a file size
|
|
|
|
try {
|
|
backupManifest.validateManifest(corruptedManifest);
|
|
console.log('✗ Validation should have failed for corrupted manifest');
|
|
} catch (error) {
|
|
console.log('✓ Correctly detected corrupted manifest:', error.message);
|
|
}
|
|
|
|
console.log('\n=== All tests completed successfully! ===');
|
|
|
|
// Clean up test files
|
|
await fs.unlink(jsonPath).catch(() => {});
|
|
await fs.unlink(yamlPath).catch(() => {});
|
|
await fs.unlink(incrementalJsonPath).catch(() => {});
|
|
|
|
} catch (error) {
|
|
console.error('Test failed:', error);
|
|
logger.error('Manifest test failed:', error);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
// Run tests
|
|
testManifestGeneration().catch(console.error); |