claudes-office
Version:
CLI tool to initialize Claude's office in your project
261 lines • 9.23 kB
JavaScript
;
/**
* Backup system utilities for the update command
* These utilities help create, manage and restore backups
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createBackup = createBackup;
exports.restoreBackup = restoreBackup;
exports.listBackups = listBackups;
exports.deleteBackup = deleteBackup;
exports.cleanupOldBackups = cleanupOldBackups;
exports.formatBackupSize = formatBackupSize;
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
const crypto = __importStar(require("crypto"));
const archiver_1 = __importDefault(require("archiver"));
const extract_zip_1 = __importDefault(require("extract-zip"));
/**
* Create a backup of a directory
* @param sourceDir - Directory to backup
* @param options - Backup options
* @returns Information about the created backup
*/
async function createBackup(sourceDir, options = {}) {
// Create a unique ID for this backup
const timestamp = Date.now();
const randomPart = crypto.randomBytes(4).toString('hex');
const backupId = `backup-${timestamp}-${randomPart}`;
// Determine backup name
const backupName = options.name || `backup-${new Date().toISOString().replace(/:/g, '-')}`;
// Determine backup directory
const backupDir = options.backupDir || path.join(sourceDir, '.backups');
await fs.ensureDir(backupDir);
// Create backup file path
const backupPath = path.join(backupDir, `${backupName}.zip`);
// Create a zip archive
const output = fs.createWriteStream(backupPath);
const archive = (0, archiver_1.default)('zip', {
zlib: { level: 9 }, // Maximum compression
});
// Listen for archive events
await new Promise((resolve, reject) => {
output.on('close', () => {
resolve();
});
archive.on('error', (err) => {
reject(err);
});
// Pipe archive data to the file
archive.pipe(output);
// Add files to the archive
if (options.include && options.include.length > 0) {
// Only include specified files/directories
for (const includePath of options.include) {
const fullPath = path.join(sourceDir, includePath);
const isDir = fs.statSync(fullPath).isDirectory();
if (isDir) {
archive.directory(fullPath, includePath);
}
else {
archive.file(fullPath, { name: includePath });
}
}
}
else {
// Include everything except excluded files
archive.glob('**/*', {
cwd: sourceDir,
ignore: options.exclude || [],
dot: true, // Include hidden files/directories
}, { prefix: '' });
}
// Finalize the archive
archive.finalize();
});
// Get file size
const stats = await fs.stat(backupPath);
// Return backup info
return {
id: backupId,
name: backupName,
path: backupPath,
createdAt: new Date(timestamp),
size: stats.size,
};
}
/**
* Restore a backup to a directory
* @param backupPath - Path to the backup file
* @param destDir - Directory to restore to
* @param overwrite - Whether to overwrite existing files
* @returns Whether the restoration was successful
*/
async function restoreBackup(backupPath, destDir, overwrite = false) {
try {
// Check if backup file exists
if (!await fs.pathExists(backupPath)) {
throw new Error(`Backup file not found: ${backupPath}`);
}
// Check if destination directory exists
await fs.ensureDir(destDir);
// If not overwriting, check if destination has files
if (!overwrite) {
const entries = await fs.readdir(destDir);
if (entries.length > 0) {
throw new Error('Destination directory is not empty and overwrite is not enabled');
}
}
// Extract the zip archive
await (0, extract_zip_1.default)(backupPath, { dir: destDir });
return true;
}
catch (error) {
console.error('Error restoring backup:', error);
return false;
}
}
/**
* List all backups in a directory
* @param backupDir - Directory containing backups
* @returns Array of backup information
*/
async function listBackups(backupDir) {
try {
// Check if backup directory exists
if (!await fs.pathExists(backupDir)) {
return [];
}
// Get all zip files in the backup directory
const files = await fs.readdir(backupDir);
const backupFiles = files.filter(file => file.endsWith('.zip'));
// Get information about each backup
const backups = [];
for (const file of backupFiles) {
const backupPath = path.join(backupDir, file);
const stats = await fs.stat(backupPath);
// Extract backup ID and name from filename
const backupName = file.replace(/\.zip$/, '');
// Create unique ID from file path and timestamp
const fileHash = crypto.createHash('md5').update(backupPath).digest('hex').slice(0, 8);
const timestamp = stats.mtimeMs;
const backupId = `backup-${timestamp}-${fileHash}`;
backups.push({
id: backupId,
name: backupName,
path: backupPath,
createdAt: stats.mtime,
size: stats.size,
});
}
// Sort by creation time (newest first)
backups.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
return backups;
}
catch (error) {
console.error('Error listing backups:', error);
return [];
}
}
/**
* Delete a backup
* @param backupPath - Path to the backup file
* @returns Whether the deletion was successful
*/
async function deleteBackup(backupPath) {
try {
// Check if backup file exists
if (!await fs.pathExists(backupPath)) {
return false;
}
// Delete the backup file
await fs.unlink(backupPath);
return true;
}
catch (error) {
console.error('Error deleting backup:', error);
return false;
}
}
/**
* Delete old backups exceeding a limit
* @param backupDir - Directory containing backups
* @param limit - Maximum number of backups to keep
* @returns Number of backups deleted
*/
async function cleanupOldBackups(backupDir, limit) {
try {
// Get all backups
const backups = await listBackups(backupDir);
// If we're under the limit, do nothing
if (backups.length <= limit) {
return 0;
}
// Delete the oldest backups
const backupsToDelete = backups.slice(limit);
let deletedCount = 0;
for (const backup of backupsToDelete) {
const success = await deleteBackup(backup.path);
if (success) {
deletedCount++;
}
}
return deletedCount;
}
catch (error) {
console.error('Error cleaning up old backups:', error);
return 0;
}
}
/**
* Format backup size for display
* @param size - Size in bytes
* @returns Formatted size string
*/
function formatBackupSize(size) {
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
let index = 0;
let formattedSize = size;
while (formattedSize >= 1024 && index < units.length - 1) {
formattedSize /= 1024;
index++;
}
return `${formattedSize.toFixed(2)} ${units[index]}`;
}
//# sourceMappingURL=backupSystem.js.map