polyv-live-cli
Version:
CLI tool for managing PolyV live streaming services.
593 lines • 25 kB
JavaScript
;
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;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.defaultConfigRecoveryManager = exports.ConfigRecoveryManager = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const config_version_manager_1 = require("./config-version-manager");
const file_permission_manager_1 = require("./file-permission-manager");
class ConfigRecoveryManager {
constructor() {
this.versionManager = new config_version_manager_1.ConfigVersionManager();
this.permissionManager = new file_permission_manager_1.FilePermissionManager();
}
checkIntegrity(configPath) {
const issues = [];
const validationDetails = {
fileAccessible: false,
validJson: false,
validStructure: false,
validEncryption: false,
securePermissions: false
};
try {
if (!fs.existsSync(configPath)) {
issues.push({
type: 'file_access',
severity: 'critical',
description: 'Configuration file does not exist',
suggestedFix: 'Create a new configuration file or restore from backup',
autoFixable: false
});
}
else {
validationDetails.fileAccessible = true;
try {
const fileContent = fs.readFileSync(configPath, 'utf8');
let config;
try {
config = JSON.parse(fileContent);
validationDetails.validJson = true;
}
catch (jsonError) {
issues.push({
type: 'json_format',
severity: 'critical',
description: 'Configuration file contains invalid JSON',
suggestedFix: 'Restore from backup or recreate configuration file',
autoFixable: false
});
throw jsonError;
}
if (this.validateConfigStructure(config)) {
validationDetails.validStructure = true;
}
else {
issues.push({
type: 'structure',
severity: 'critical',
description: 'Configuration file has invalid structure',
suggestedFix: 'Restore from backup or recreate with correct structure',
autoFixable: false
});
}
if (this.validateEncryptionData(config)) {
validationDetails.validEncryption = true;
}
else {
issues.push({
type: 'encryption',
severity: 'critical',
description: 'Encrypted data in configuration is corrupted or invalid',
suggestedFix: 'Check POLYV_MASTER_KEY environment variable or restore from backup',
autoFixable: false
});
}
try {
const versionValidation = this.versionManager.validateVersion(config);
if (!versionValidation.isValid && versionValidation.compatibility === 'incompatible') {
issues.push({
type: 'version',
severity: 'warning',
description: `Configuration version is incompatible: ${versionValidation.detectedVersion}`,
suggestedFix: 'Update CLI or migrate configuration to compatible version',
autoFixable: false
});
}
}
catch (versionError) {
issues.push({
type: 'version',
severity: 'warning',
description: 'Unable to determine configuration version',
suggestedFix: 'Check configuration format and structure',
autoFixable: false
});
}
}
catch (readError) {
issues.push({
type: 'file_access',
severity: 'critical',
description: 'Cannot read configuration file (permission denied or file corrupted)',
suggestedFix: 'Check file permissions and integrity',
autoFixable: false
});
}
try {
const permissionValidation = this.permissionManager.validatePermissions(configPath);
if (permissionValidation.isValid) {
validationDetails.securePermissions = true;
}
else {
issues.push({
type: 'permissions',
severity: 'warning',
description: 'Configuration file has insecure permissions',
suggestedFix: 'Run: polyv-cli config fix-permissions',
autoFixable: true
});
}
}
catch (permError) {
issues.push({
type: 'permissions',
severity: 'warning',
description: 'Cannot check file permissions',
suggestedFix: 'Manually verify file permissions are secure (600)',
autoFixable: false
});
}
}
const isValid = issues.filter(i => i.severity === 'critical').length === 0;
const criticalCount = issues.filter(i => i.severity === 'critical').length;
const warningCount = issues.filter(i => i.severity === 'warning').length;
let message;
if (isValid && issues.length === 0) {
message = 'Configuration file is healthy and valid';
}
else if (isValid) {
message = `Configuration file is valid but has ${warningCount} warning(s)`;
}
else {
message = `Configuration file is corrupted with ${criticalCount} critical issue(s) and ${warningCount} warning(s)`;
}
return {
isValid,
message,
issues,
validationDetails
};
}
catch (error) {
return {
isValid: false,
message: `Integrity check failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
issues: [{
type: 'file_access',
severity: 'critical',
description: 'Failed to perform integrity check',
suggestedFix: 'Check file path and permissions',
autoFixable: false
}],
validationDetails
};
}
}
validateConfigStructure(config) {
if (!config || typeof config !== 'object') {
return false;
}
if (!config.version || typeof config.version !== 'string') {
return false;
}
if (!config.accounts || typeof config.accounts !== 'object') {
return false;
}
if (!config.metadata || typeof config.metadata !== 'object') {
return false;
}
if (!config.metadata.createdAt || typeof config.metadata.createdAt !== 'string') {
return false;
}
if (!config.metadata.updatedAt || typeof config.metadata.updatedAt !== 'string') {
return false;
}
for (const accountName in config.accounts) {
const account = config.accounts[accountName];
if (!account || typeof account !== 'object') {
return false;
}
if (!account.name || typeof account.name !== 'string') {
return false;
}
if (!account.appId || typeof account.appId !== 'string') {
return false;
}
if (!account.appSecret) {
return false;
}
if (!account.createdAt || typeof account.createdAt !== 'string') {
return false;
}
if (!account.updatedAt || typeof account.updatedAt !== 'string') {
return false;
}
}
return true;
}
validateEncryptionData(config) {
if (!config.accounts) {
return true;
}
try {
for (const accountName in config.accounts) {
const account = config.accounts[accountName];
if (!account.appSecret) {
continue;
}
if (typeof account.appSecret === 'object') {
if (!account.appSecret.algorithm ||
!account.appSecret.iv ||
!account.appSecret.authTag ||
!account.appSecret.encrypted) {
return false;
}
}
else if (typeof account.appSecret === 'string') {
try {
const decoded = Buffer.from(account.appSecret, 'base64').toString('utf8');
JSON.parse(decoded);
}
catch {
}
}
else {
return false;
}
}
return true;
}
catch {
return false;
}
}
getRecoveryStrategies(integrityResult) {
const strategies = [];
const autoFixableIssues = integrityResult.issues.filter(i => i.autoFixable);
if (autoFixableIssues.length > 0) {
strategies.push({
id: 'auto_fix',
name: 'Auto-fix Minor Issues',
description: 'Automatically fix permission and minor structural issues',
steps: [
'Fix file permissions (set to 600)',
'Repair minor structural issues',
'Validate configuration after fixes'
],
riskLevel: 'low',
dataPreservation: 'full'
});
}
strategies.push({
id: 'restore_backup',
name: 'Restore from Backup',
description: 'Restore configuration from the most recent valid backup',
steps: [
'Locate most recent backup file',
'Validate backup integrity',
'Replace current configuration with backup',
'Set secure file permissions'
],
riskLevel: 'low',
dataPreservation: 'full'
});
if (integrityResult.validationDetails.fileAccessible &&
integrityResult.validationDetails.validJson) {
strategies.push({
id: 'manual_fix',
name: 'Manual Configuration Repair',
description: 'Manually repair specific issues in the configuration',
steps: [
'Create backup of current configuration',
'Guide through manual repair process',
'Validate each fixed section',
'Test encryption/decryption functionality'
],
riskLevel: 'medium',
dataPreservation: 'partial'
});
}
strategies.push({
id: 'recreate',
name: 'Create New Configuration',
description: 'Create a completely new configuration file (data loss)',
steps: [
'Backup existing configuration (if possible)',
'Create new configuration structure',
'Guide through account re-addition process',
'Set secure permissions and encryption'
],
riskLevel: 'high',
dataPreservation: 'none'
});
const hasEncryptionIssues = integrityResult.issues.some(i => i.type === 'encryption');
if (hasEncryptionIssues) {
strategies.push({
id: 'key_recovery',
name: 'Encryption Key Recovery',
description: 'Attempt to recover using different encryption keys',
steps: [
'Check for POLYV_MASTER_KEY environment variable',
'Try default key generation',
'Guide through key recovery process',
'Test decryption with recovered key'
],
riskLevel: 'medium',
dataPreservation: 'full'
});
}
return strategies;
}
executeRecovery(strategyId, configPath, options = {}) {
const stepsExecuted = [];
const warnings = [];
try {
switch (strategyId) {
case 'auto_fix':
return this.executeAutoFix(configPath, stepsExecuted, warnings);
case 'restore_backup':
return this.executeBackupRestore(configPath, options.backupPath, stepsExecuted, warnings);
case 'manual_fix':
return this.executeManualFix(configPath, stepsExecuted, warnings);
case 'recreate':
return this.executeRecreate(configPath, stepsExecuted, warnings);
case 'key_recovery':
return this.executeKeyRecovery(configPath, options.newMasterKey, stepsExecuted, warnings);
default:
return {
success: false,
message: `Unknown recovery strategy: ${strategyId}`,
strategyUsed: strategyId,
stepsExecuted,
warnings
};
}
}
catch (error) {
return {
success: false,
message: `Recovery failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
strategyUsed: strategyId,
stepsExecuted,
warnings
};
}
}
executeAutoFix(configPath, stepsExecuted, warnings) {
stepsExecuted.push('Starting auto-fix process');
const permissionResult = this.permissionManager.repairConfigurationFile(configPath);
if (permissionResult.success) {
stepsExecuted.push('Fixed file permissions');
}
else {
warnings.push(`Could not fix permissions: ${permissionResult.message}`);
}
stepsExecuted.push('Auto-fix completed');
return {
success: true,
message: 'Auto-fix completed successfully',
strategyUsed: 'auto_fix',
stepsExecuted,
warnings
};
}
executeBackupRestore(configPath, backupPath, stepsExecuted, warnings) {
stepsExecuted.push('Starting backup restore process');
if (!backupPath) {
const backups = this.findBackupFiles(configPath);
if (backups.length === 0) {
return {
success: false,
message: 'No backup files found',
strategyUsed: 'restore_backup',
stepsExecuted,
warnings: ['No backup files available for restoration']
};
}
backupPath = backups[0].path;
stepsExecuted.push(`Found backup file: ${backupPath}`);
}
const backupIntegrity = this.checkIntegrity(backupPath);
if (!backupIntegrity.isValid) {
return {
success: false,
message: 'Backup file is also corrupted',
strategyUsed: 'restore_backup',
stepsExecuted,
warnings: ['Backup file integrity check failed']
};
}
stepsExecuted.push('Backup integrity validated');
const currentBackupPath = `${configPath}.corrupted.${Date.now()}`;
if (fs.existsSync(configPath)) {
fs.copyFileSync(configPath, currentBackupPath);
stepsExecuted.push(`Backed up corrupted file to: ${currentBackupPath}`);
}
fs.copyFileSync(backupPath, configPath);
stepsExecuted.push('Configuration restored from backup');
this.permissionManager.setSecurePermissions(configPath);
stepsExecuted.push('Set secure file permissions');
return {
success: true,
message: 'Configuration successfully restored from backup',
strategyUsed: 'restore_backup',
stepsExecuted,
warnings,
backupFile: currentBackupPath
};
}
executeManualFix(_configPath, stepsExecuted, warnings) {
stepsExecuted.push('Manual fix requires interactive process');
warnings.push('Manual fix must be performed interactively');
return {
success: false,
message: 'Manual fix requires interactive CLI process',
strategyUsed: 'manual_fix',
stepsExecuted,
warnings
};
}
executeRecreate(configPath, stepsExecuted, warnings) {
stepsExecuted.push('Starting configuration recreation');
let backupFile;
if (fs.existsSync(configPath)) {
backupFile = `${configPath}.backup.${Date.now()}`;
fs.copyFileSync(configPath, backupFile);
stepsExecuted.push(`Backed up existing configuration to: ${backupFile}`);
}
const newConfig = this.versionManager.createNewConfiguration();
const configDir = path.dirname(configPath);
if (!fs.existsSync(configDir)) {
fs.mkdirSync(configDir, { recursive: true, mode: 0o700 });
stepsExecuted.push('Created configuration directory');
}
fs.writeFileSync(configPath, JSON.stringify(newConfig, null, 2), 'utf8');
stepsExecuted.push('Created new configuration file');
this.permissionManager.setSecurePermissions(configPath);
stepsExecuted.push('Set secure file permissions');
warnings.push('All previous account data has been lost');
warnings.push('You will need to re-add your accounts');
return {
success: true,
message: 'New configuration file created successfully',
strategyUsed: 'recreate',
stepsExecuted,
warnings,
recoveredConfig: newConfig,
...(backupFile && { backupFile })
};
}
executeKeyRecovery(_configPath, _newMasterKey, stepsExecuted, warnings) {
stepsExecuted.push('Key recovery requires encryption key');
warnings.push('Key recovery must be performed with correct master key');
return {
success: false,
message: 'Key recovery requires valid master key',
strategyUsed: 'key_recovery',
stepsExecuted,
warnings
};
}
findBackupFiles(configPath) {
const backups = [];
const configDir = path.dirname(configPath);
const configName = path.basename(configPath);
try {
if (!fs.existsSync(configDir)) {
return backups;
}
const files = fs.readdirSync(configDir);
const backupPattern = new RegExp(`^${configName}\\.backup\\.(\\d+)$`);
for (const file of files) {
if (backupPattern.test(file)) {
const filePath = path.join(configDir, file);
const stats = fs.statSync(filePath);
const integrity = this.checkIntegrity(filePath);
backups.push({
path: filePath,
createdAt: stats.mtime.toISOString(),
size: stats.size,
isValid: integrity.isValid
});
}
}
backups.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
}
catch (error) {
}
return backups;
}
createBackup(configPath) {
try {
if (!fs.existsSync(configPath)) {
return null;
}
const backupPath = `${configPath}.backup.${Date.now()}`;
fs.copyFileSync(configPath, backupPath);
this.permissionManager.setSecurePermissions(backupPath);
return backupPath;
}
catch {
return null;
}
}
getRecoveryGuidance(integrityResult) {
const guidance = [];
if (integrityResult.isValid) {
guidance.push('✅ Configuration file is healthy');
if (integrityResult.issues.length > 0) {
guidance.push('⚠️ Minor issues detected:');
for (const issue of integrityResult.issues) {
guidance.push(` • ${issue.description}`);
guidance.push(` Fix: ${issue.suggestedFix}`);
}
}
return guidance;
}
guidance.push('❌ Configuration file has critical issues');
guidance.push('');
guidance.push('Recommended recovery steps:');
guidance.push('');
const criticalIssues = integrityResult.issues.filter(i => i.severity === 'critical');
const warningIssues = integrityResult.issues.filter(i => i.severity === 'warning');
if (criticalIssues.length > 0) {
guidance.push('🚨 Critical Issues:');
for (const issue of criticalIssues) {
guidance.push(` • ${issue.description}`);
guidance.push(` Solution: ${issue.suggestedFix}`);
}
guidance.push('');
}
if (warningIssues.length > 0) {
guidance.push('⚠️ Warnings:');
for (const issue of warningIssues) {
guidance.push(` • ${issue.description}`);
guidance.push(` Solution: ${issue.suggestedFix}`);
}
guidance.push('');
}
guidance.push('💡 Recovery Options:');
guidance.push(' 1. Try: polyv-cli config recover --strategy auto_fix');
guidance.push(' 2. Try: polyv-cli config recover --strategy restore_backup');
guidance.push(' 3. Last resort: polyv-cli config recover --strategy recreate');
guidance.push('');
guidance.push('⚠️ Always backup your configuration before recovery attempts');
return guidance;
}
}
exports.ConfigRecoveryManager = ConfigRecoveryManager;
exports.defaultConfigRecoveryManager = new ConfigRecoveryManager();
//# sourceMappingURL=config-recovery-manager.js.map