- Created complete documentation in docs/ directory - Added PROJECT_OVERVIEW.md with feature highlights and getting started guide - Added ARCHITECTURE.md with system design and technical details - Added SECURITY.md with comprehensive security implementation guide - Added DEVELOPMENT.md with development workflows and best practices - Added DEPLOYMENT.md with production deployment instructions - Added API.md with complete REST API documentation - Added CONTRIBUTING.md with contribution guidelines - Added CHANGELOG.md with version history and migration notes - Reorganized all documentation files into docs/ directory for better organization - Updated README.md with proper documentation links and quick navigation - Enhanced project structure with professional documentation standards
450 lines
14 KiB
PHP
450 lines
14 KiB
PHP
<?php
|
|
/*******************************************************************************************************************
|
|
| System Cleanup Job
|
|
| Handles automated system maintenance and cleanup tasks
|
|
|*******************************************************************************************************************/
|
|
|
|
class CleanupJob extends BaseJob
|
|
{
|
|
private $queueManager;
|
|
|
|
public function __construct()
|
|
{
|
|
parent::__construct();
|
|
$this->queueManager = new VQueueManager();
|
|
}
|
|
|
|
/**
|
|
* Handle cleanup job
|
|
* @param array $data Cleanup configuration data
|
|
* @return array Cleanup result
|
|
*/
|
|
public function handle($data)
|
|
{
|
|
$cleanupType = $data['type'] ?? 'full';
|
|
|
|
$this->logProgress('Starting system cleanup', [
|
|
'type' => $cleanupType,
|
|
'data' => $data
|
|
]);
|
|
|
|
$results = [];
|
|
|
|
try {
|
|
switch ($cleanupType) {
|
|
case 'full':
|
|
$results = $this->performFullCleanup($data);
|
|
break;
|
|
|
|
case 'logs':
|
|
$results['logs'] = $this->cleanupLogs($data);
|
|
break;
|
|
|
|
case 'temp_files':
|
|
$results['temp_files'] = $this->cleanupTempFiles($data);
|
|
break;
|
|
|
|
case 'old_sessions':
|
|
$results['sessions'] = $this->cleanupOldSessions($data);
|
|
break;
|
|
|
|
case 'failed_uploads':
|
|
$results['failed_uploads'] = $this->cleanupFailedUploads($data);
|
|
break;
|
|
|
|
case 'analytics':
|
|
$results['analytics'] = $this->cleanupOldAnalytics($data);
|
|
break;
|
|
|
|
default:
|
|
throw new Exception("Unknown cleanup type: {$cleanupType}");
|
|
}
|
|
|
|
$this->logProgress('System cleanup completed', [
|
|
'type' => $cleanupType,
|
|
'results' => $results
|
|
]);
|
|
|
|
return [
|
|
'success' => true,
|
|
'type' => $cleanupType,
|
|
'results' => $results,
|
|
'message' => 'Cleanup completed successfully'
|
|
];
|
|
|
|
} catch (Exception $e) {
|
|
$this->logError('System cleanup failed', [
|
|
'type' => $cleanupType,
|
|
'error' => $e->getMessage()
|
|
]);
|
|
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Perform full system cleanup
|
|
* @param array $data Configuration data
|
|
* @return array Cleanup results
|
|
*/
|
|
private function performFullCleanup($data)
|
|
{
|
|
$results = [];
|
|
|
|
$this->updateProgress(1, 8, 'Cleaning up logs');
|
|
$results['logs'] = $this->cleanupLogs($data);
|
|
|
|
$this->updateProgress(2, 8, 'Cleaning up temporary files');
|
|
$results['temp_files'] = $this->cleanupTempFiles($data);
|
|
|
|
$this->updateProgress(3, 8, 'Cleaning up old sessions');
|
|
$results['sessions'] = $this->cleanupOldSessions($data);
|
|
|
|
$this->updateProgress(4, 8, 'Cleaning up failed uploads');
|
|
$results['failed_uploads'] = $this->cleanupFailedUploads($data);
|
|
|
|
$this->updateProgress(5, 8, 'Cleaning up old analytics');
|
|
$results['analytics'] = $this->cleanupOldAnalytics($data);
|
|
|
|
$this->updateProgress(6, 8, 'Cleaning up queue jobs');
|
|
$results['queue_jobs'] = $this->queueManager->cleanupOldJobs(
|
|
$data['queue_retention_hours'] ?? 24
|
|
);
|
|
|
|
$this->updateProgress(7, 8, 'Cleaning up orphaned files');
|
|
$results['orphaned_files'] = $this->cleanupOrphanedFiles($data);
|
|
|
|
$this->updateProgress(8, 8, 'Optimizing database');
|
|
$results['database'] = $this->optimizeDatabase($data);
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Cleanup old log files
|
|
* @param array $data Configuration data
|
|
* @return array Cleanup result
|
|
*/
|
|
private function cleanupLogs($data)
|
|
{
|
|
$logDir = _FPATH . 'f_data/logs/';
|
|
$retentionDays = $data['log_retention_days'] ?? 30;
|
|
$cutoffTime = time() - ($retentionDays * 24 * 3600);
|
|
|
|
$deletedFiles = 0;
|
|
$deletedSize = 0;
|
|
|
|
if (is_dir($logDir)) {
|
|
$files = glob($logDir . '*.log');
|
|
|
|
foreach ($files as $file) {
|
|
if (filemtime($file) < $cutoffTime) {
|
|
$size = filesize($file);
|
|
if (unlink($file)) {
|
|
$deletedFiles++;
|
|
$deletedSize += $size;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return [
|
|
'deleted_files' => $deletedFiles,
|
|
'deleted_size' => $deletedSize,
|
|
'retention_days' => $retentionDays
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Cleanup temporary files
|
|
* @param array $data Configuration data
|
|
* @return array Cleanup result
|
|
*/
|
|
private function cleanupTempFiles($data)
|
|
{
|
|
$tempDir = _FPATH . 'f_data/temp/';
|
|
$maxAge = $data['temp_file_max_age_hours'] ?? 24;
|
|
$cutoffTime = time() - ($maxAge * 3600);
|
|
|
|
$deletedFiles = 0;
|
|
$deletedSize = 0;
|
|
|
|
if (is_dir($tempDir)) {
|
|
$iterator = new RecursiveIteratorIterator(
|
|
new RecursiveDirectoryIterator($tempDir, RecursiveDirectoryIterator::SKIP_DOTS)
|
|
);
|
|
|
|
foreach ($iterator as $file) {
|
|
if ($file->isFile() && $file->getMTime() < $cutoffTime) {
|
|
$size = $file->getSize();
|
|
if (unlink($file->getPathname())) {
|
|
$deletedFiles++;
|
|
$deletedSize += $size;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return [
|
|
'deleted_files' => $deletedFiles,
|
|
'deleted_size' => $deletedSize,
|
|
'max_age_hours' => $maxAge
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Cleanup old sessions
|
|
* @param array $data Configuration data
|
|
* @return array Cleanup result
|
|
*/
|
|
private function cleanupOldSessions($data)
|
|
{
|
|
$maxAge = $data['session_max_age_hours'] ?? 168; // 7 days
|
|
$cutoffTime = date('Y-m-d H:i:s', time() - ($maxAge * 3600));
|
|
|
|
try {
|
|
$db = $this->getDatabase();
|
|
|
|
// Cleanup database sessions if using database session storage
|
|
$query = "DELETE FROM db_sessions WHERE last_activity < ?";
|
|
$db->doQuery($query, [$cutoffTime]);
|
|
$deletedSessions = $db->getAffectedRows();
|
|
|
|
return [
|
|
'deleted_db_sessions' => $deletedSessions,
|
|
'max_age_hours' => $maxAge
|
|
];
|
|
|
|
} catch (Exception $e) {
|
|
$this->logError('Session cleanup failed', [
|
|
'error' => $e->getMessage()
|
|
]);
|
|
|
|
return [
|
|
'error' => $e->getMessage(),
|
|
'deleted_db_sessions' => 0
|
|
];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Cleanup failed uploads
|
|
* @param array $data Configuration data
|
|
* @return array Cleanup result
|
|
*/
|
|
private function cleanupFailedUploads($data)
|
|
{
|
|
$maxAge = $data['failed_upload_max_age_hours'] ?? 48;
|
|
$cutoffTime = date('Y-m-d H:i:s', time() - ($maxAge * 3600));
|
|
|
|
try {
|
|
$db = $this->getDatabase();
|
|
|
|
// Find failed uploads
|
|
$query = "SELECT file_key, usr_id FROM db_videofiles
|
|
WHERE processing_status = 'failed'
|
|
AND processed_at < ?";
|
|
|
|
$result = $db->doQuery($query, [$cutoffTime]);
|
|
|
|
$deletedRecords = 0;
|
|
$deletedFiles = 0;
|
|
$deletedSize = 0;
|
|
|
|
while ($row = $db->doFetch($result)) {
|
|
// Delete associated files
|
|
$userDir = _FPATH . 'f_data/media/' . $row['usr_id'] . '/';
|
|
$videoFiles = glob($userDir . $row['file_key'] . '*');
|
|
|
|
foreach ($videoFiles as $file) {
|
|
if (is_file($file)) {
|
|
$deletedSize += filesize($file);
|
|
if (unlink($file)) {
|
|
$deletedFiles++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Delete database record
|
|
$deleteQuery = "DELETE FROM db_videofiles WHERE file_key = ?";
|
|
$db->doQuery($deleteQuery, [$row['file_key']]);
|
|
$deletedRecords++;
|
|
}
|
|
|
|
return [
|
|
'deleted_records' => $deletedRecords,
|
|
'deleted_files' => $deletedFiles,
|
|
'deleted_size' => $deletedSize,
|
|
'max_age_hours' => $maxAge
|
|
];
|
|
|
|
} catch (Exception $e) {
|
|
$this->logError('Failed upload cleanup failed', [
|
|
'error' => $e->getMessage()
|
|
]);
|
|
|
|
return [
|
|
'error' => $e->getMessage(),
|
|
'deleted_records' => 0,
|
|
'deleted_files' => 0
|
|
];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Cleanup old analytics data
|
|
* @param array $data Configuration data
|
|
* @return array Cleanup result
|
|
*/
|
|
private function cleanupOldAnalytics($data)
|
|
{
|
|
$retentionDays = $data['analytics_retention_days'] ?? 90;
|
|
$cutoffTime = date('Y-m-d H:i:s', time() - ($retentionDays * 24 * 3600));
|
|
|
|
try {
|
|
$db = $this->getDatabase();
|
|
$deletedRecords = 0;
|
|
|
|
// Cleanup video analytics
|
|
$query = "DELETE FROM db_video_analytics WHERE created_at < ?";
|
|
$db->doQuery($query, [$cutoffTime]);
|
|
$deletedRecords += $db->getAffectedRows();
|
|
|
|
// Cleanup video views
|
|
$query = "DELETE FROM db_video_views WHERE created_at < ?";
|
|
$db->doQuery($query, [$cutoffTime]);
|
|
$deletedRecords += $db->getAffectedRows();
|
|
|
|
return [
|
|
'deleted_records' => $deletedRecords,
|
|
'retention_days' => $retentionDays
|
|
];
|
|
|
|
} catch (Exception $e) {
|
|
$this->logError('Analytics cleanup failed', [
|
|
'error' => $e->getMessage()
|
|
]);
|
|
|
|
return [
|
|
'error' => $e->getMessage(),
|
|
'deleted_records' => 0
|
|
];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Cleanup orphaned files
|
|
* @param array $data Configuration data
|
|
* @return array Cleanup result
|
|
*/
|
|
private function cleanupOrphanedFiles($data)
|
|
{
|
|
$mediaDir = _FPATH . 'f_data/media/';
|
|
$deletedFiles = 0;
|
|
$deletedSize = 0;
|
|
|
|
try {
|
|
if (!is_dir($mediaDir)) {
|
|
return [
|
|
'deleted_files' => 0,
|
|
'deleted_size' => 0,
|
|
'message' => 'Media directory not found'
|
|
];
|
|
}
|
|
|
|
// Get all file keys from database
|
|
$db = $this->getDatabase();
|
|
$query = "SELECT file_key FROM db_videofiles
|
|
UNION SELECT file_key FROM db_imagefiles
|
|
UNION SELECT file_key FROM db_audiofiles
|
|
UNION SELECT file_key FROM db_documentfiles";
|
|
|
|
$result = $db->doQuery($query);
|
|
$validKeys = [];
|
|
|
|
while ($row = $db->doFetch($result)) {
|
|
$validKeys[] = $row['file_key'];
|
|
}
|
|
|
|
// Scan media directory for orphaned files
|
|
$iterator = new RecursiveIteratorIterator(
|
|
new RecursiveDirectoryIterator($mediaDir, RecursiveDirectoryIterator::SKIP_DOTS)
|
|
);
|
|
|
|
foreach ($iterator as $file) {
|
|
if ($file->isFile()) {
|
|
$filename = $file->getFilename();
|
|
|
|
// Extract file key from filename (assuming format: filekey.extension)
|
|
$fileKey = pathinfo($filename, PATHINFO_FILENAME);
|
|
|
|
if (!in_array($fileKey, $validKeys)) {
|
|
$size = $file->getSize();
|
|
if (unlink($file->getPathname())) {
|
|
$deletedFiles++;
|
|
$deletedSize += $size;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return [
|
|
'deleted_files' => $deletedFiles,
|
|
'deleted_size' => $deletedSize,
|
|
'scanned_keys' => count($validKeys)
|
|
];
|
|
|
|
} catch (Exception $e) {
|
|
$this->logError('Orphaned files cleanup failed', [
|
|
'error' => $e->getMessage()
|
|
]);
|
|
|
|
return [
|
|
'error' => $e->getMessage(),
|
|
'deleted_files' => 0,
|
|
'deleted_size' => 0
|
|
];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Optimize database
|
|
* @param array $data Configuration data
|
|
* @return array Optimization result
|
|
*/
|
|
private function optimizeDatabase($data)
|
|
{
|
|
try {
|
|
$db = $this->getDatabase();
|
|
$optimizedTables = 0;
|
|
|
|
// Get all tables
|
|
$query = "SHOW TABLES";
|
|
$result = $db->doQuery($query);
|
|
|
|
while ($row = $db->doFetch($result)) {
|
|
$table = array_values($row)[0];
|
|
|
|
// Optimize table
|
|
$optimizeQuery = "OPTIMIZE TABLE `{$table}`";
|
|
$db->doQuery($optimizeQuery);
|
|
$optimizedTables++;
|
|
}
|
|
|
|
return [
|
|
'optimized_tables' => $optimizedTables,
|
|
'message' => 'Database optimization completed'
|
|
];
|
|
|
|
} catch (Exception $e) {
|
|
$this->logError('Database optimization failed', [
|
|
'error' => $e->getMessage()
|
|
]);
|
|
|
|
return [
|
|
'error' => $e->getMessage(),
|
|
'optimized_tables' => 0
|
|
];
|
|
}
|
|
}
|
|
} |