logger = VLogger::getInstance(); $this->db = VDatabase::getInstance(); } public static function getInstance() { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; } /** * Get platform analytics dashboard data * @param string $period Time period (today, week, month, year) * @return array Dashboard data */ public function getDashboardAnalytics($period = 'week') { try { $timeFilter = $this->getTimeFilter($period); $analytics = [ 'overview' => $this->getOverviewStats($timeFilter), 'users' => $this->getUserStats($timeFilter), 'content' => $this->getContentStats($timeFilter), 'engagement' => $this->getEngagementStats($timeFilter), 'system' => $this->getSystemStats(), 'recent_activity' => $this->getRecentActivity(20) ]; return [ 'success' => true, 'period' => $period, 'analytics' => $analytics, 'generated_at' => date('Y-m-d H:i:s') ]; } catch (Exception $e) { $this->logger->error('Failed to get dashboard analytics', [ 'period' => $period, 'error' => $e->getMessage() ]); return [ 'success' => false, 'error' => $e->getMessage() ]; } } /** * Get user management data * @param array $filters Filters * @param int $limit Limit * @param int $offset Offset * @return array User data */ public function getUserManagement($filters = [], $limit = 50, $offset = 0) { try { $whereClause = "WHERE 1=1"; $params = []; if (isset($filters['role'])) { $whereClause .= " AND usr_role = ?"; $params[] = $filters['role']; } if (isset($filters['status'])) { if ($filters['status'] === 'active') { $whereClause .= " AND usr_active = 1 AND usr_deleted = 0"; } elseif ($filters['status'] === 'suspended') { $whereClause .= " AND usr_active = 0 AND usr_deleted = 0"; } elseif ($filters['status'] === 'deleted') { $whereClause .= " AND usr_deleted = 1"; } } if (isset($filters['search'])) { $whereClause .= " AND (usr_user LIKE ? OR usr_email LIKE ?)"; $searchTerm = '%' . $filters['search'] . '%'; $params[] = $searchTerm; $params[] = $searchTerm; } $query = "SELECT usr_id, usr_user, usr_email, usr_role, usr_active, usr_deleted, usr_verified, usr_joindate, usr_lastlogin, usr_logins, usr_v_count, usr_i_count, usr_a_count, usr_d_count FROM db_accountuser {$whereClause} ORDER BY usr_joindate DESC LIMIT ? OFFSET ?"; $params[] = $limit; $params[] = $offset; $result = $this->db->doQuery($query, $params); $users = []; while ($row = $this->db->doFetch($result)) { $users[] = $this->formatUserData($row); } // Get total count $countQuery = "SELECT COUNT(*) as total FROM db_accountuser {$whereClause}"; $countParams = array_slice($params, 0, -2); // Remove limit and offset $countResult = $this->db->doQuery($countQuery, $countParams); $totalCount = $this->db->doFetch($countResult)['total']; return [ 'success' => true, 'users' => $users, 'pagination' => [ 'total' => $totalCount, 'limit' => $limit, 'offset' => $offset, 'pages' => ceil($totalCount / $limit) ] ]; } catch (Exception $e) { $this->logger->error('Failed to get user management data', [ 'filters' => $filters, 'error' => $e->getMessage() ]); return [ 'success' => false, 'error' => $e->getMessage() ]; } } /** * Update user status * @param int $userId User ID * @param string $action Action (activate, suspend, delete, restore) * @param string $reason Reason for action * @return array Result */ public function updateUserStatus($userId, $action, $reason = '') { try { $user = $this->getUserById($userId); if (!$user) { return [ 'success' => false, 'error' => 'User not found' ]; } $updateData = ['updated_at' => date('Y-m-d H:i:s')]; switch ($action) { case 'activate': $updateData['usr_active'] = 1; $updateData['usr_deleted'] = 0; break; case 'suspend': $updateData['usr_active'] = 0; break; case 'delete': $updateData['usr_deleted'] = 1; $updateData['usr_active'] = 0; break; case 'restore': $updateData['usr_deleted'] = 0; $updateData['usr_active'] = 1; break; default: return [ 'success' => false, 'error' => 'Invalid action' ]; } $this->db->doUpdate('db_accountuser', 'usr_id', $updateData, $userId); // Log admin action $this->logAdminAction('user_status_update', [ 'target_user_id' => $userId, 'target_username' => $user['usr_user'], 'action' => $action, 'reason' => $reason ]); $this->logger->info('User status updated', [ 'user_id' => $userId, 'action' => $action, 'reason' => $reason ]); return [ 'success' => true, 'message' => "User {$action}d successfully" ]; } catch (Exception $e) { $this->logger->error('Failed to update user status', [ 'user_id' => $userId, 'action' => $action, 'error' => $e->getMessage() ]); return [ 'success' => false, 'error' => $e->getMessage() ]; } } /** * Get content moderation queue * @param string $contentType Content type filter * @param int $limit Limit * @param int $offset Offset * @return array Moderation queue */ public function getModerationQueue($contentType = 'all', $limit = 50, $offset = 0) { try { $tables = [ 'video' => 'db_videofiles', 'image' => 'db_imagefiles', 'audio' => 'db_audiofiles', 'document' => 'db_documentfiles' ]; $items = []; if ($contentType === 'all') { foreach ($tables as $type => $table) { $typeItems = $this->getModerationItemsByType($type, $table, $limit / 4, $offset); $items = array_merge($items, $typeItems); } } else { if (isset($tables[$contentType])) { $items = $this->getModerationItemsByType($contentType, $tables[$contentType], $limit, $offset); } } // Sort by upload date usort($items, function($a, $b) { return strtotime($b['uploaded_at']) - strtotime($a['uploaded_at']); }); return [ 'success' => true, 'items' => array_slice($items, 0, $limit), 'total' => count($items) ]; } catch (Exception $e) { $this->logger->error('Failed to get moderation queue', [ 'content_type' => $contentType, 'error' => $e->getMessage() ]); return [ 'success' => false, 'error' => $e->getMessage() ]; } } /** * Moderate content * @param string $contentType Content type * @param string $contentId Content ID * @param string $action Action (approve, reject, delete) * @param string $reason Reason * @return array Result */ public function moderateContent($contentType, $contentId, $action, $reason = '') { try { $tables = [ 'video' => 'db_videofiles', 'image' => 'db_imagefiles', 'audio' => 'db_audiofiles', 'document' => 'db_documentfiles' ]; if (!isset($tables[$contentType])) { return [ 'success' => false, 'error' => 'Invalid content type' ]; } $table = $tables[$contentType]; $updateData = ['moderated_at' => date('Y-m-d H:i:s')]; switch ($action) { case 'approve': $updateData['file_active'] = 1; $updateData['moderation_status'] = 'approved'; break; case 'reject': $updateData['file_active'] = 0; $updateData['moderation_status'] = 'rejected'; $updateData['moderation_reason'] = $reason; break; case 'delete': $updateData['file_active'] = 0; $updateData['moderation_status'] = 'deleted'; $updateData['moderation_reason'] = $reason; break; default: return [ 'success' => false, 'error' => 'Invalid action' ]; } $this->db->doUpdate($table, 'file_key', $updateData, $contentId); // Log admin action $this->logAdminAction('content_moderation', [ 'content_type' => $contentType, 'content_id' => $contentId, 'action' => $action, 'reason' => $reason ]); return [ 'success' => true, 'message' => "Content {$action}d successfully" ]; } catch (Exception $e) { $this->logger->error('Failed to moderate content', [ 'content_type' => $contentType, 'content_id' => $contentId, 'action' => $action, 'error' => $e->getMessage() ]); return [ 'success' => false, 'error' => $e->getMessage() ]; } } /** * Get system health status * @return array System health */ public function getSystemHealth() { try { $health = [ 'overall_status' => 'healthy', 'components' => [], 'issues' => [] ]; // Check database $dbHealth = $this->checkDatabaseHealth(); $health['components']['database'] = $dbHealth; // Check queue system $queueManager = new VQueueManager(); $queueHealth = $queueManager->healthCheck(); $health['components']['queue'] = $queueHealth; // Check file system $fsHealth = $this->checkFileSystemHealth(); $health['components']['filesystem'] = $fsHealth; // Check external services $extHealth = $this->checkExternalServices(); $health['components']['external'] = $extHealth; // Determine overall status foreach ($health['components'] as $component => $status) { if ($status['status'] === 'unhealthy') { $health['overall_status'] = 'unhealthy'; $health['issues'][] = "{$component}: " . implode(', ', $status['issues'] ?? []); } elseif ($status['status'] === 'warning' && $health['overall_status'] === 'healthy') { $health['overall_status'] = 'warning'; } } return [ 'success' => true, 'health' => $health, 'checked_at' => date('Y-m-d H:i:s') ]; } catch (Exception $e) { return [ 'success' => false, 'error' => $e->getMessage() ]; } } /** * Private helper methods */ private function getTimeFilter($period) { switch ($period) { case 'today': return "DATE(created_at) = CURDATE()"; case 'week': return "created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)"; case 'month': return "created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)"; case 'year': return "created_at >= DATE_SUB(NOW(), INTERVAL 365 DAY)"; default: return "created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)"; } } private function getOverviewStats($timeFilter) { $stats = []; // Total users $query = "SELECT COUNT(*) as count FROM db_accountuser WHERE usr_deleted = 0"; $result = $this->db->doQuery($query); $stats['total_users'] = $this->db->doFetch($result)['count']; // New users $query = "SELECT COUNT(*) as count FROM db_accountuser WHERE {$timeFilter}"; $result = $this->db->doQuery($query); $stats['new_users'] = $this->db->doFetch($result)['count']; // Total videos $query = "SELECT COUNT(*) as count FROM db_videofiles WHERE file_active = 1"; $result = $this->db->doQuery($query); $stats['total_videos'] = $this->db->doFetch($result)['count']; // New videos $query = "SELECT COUNT(*) as count FROM db_videofiles WHERE {$timeFilter}"; $result = $this->db->doQuery($query); $stats['new_videos'] = $this->db->doFetch($result)['count']; return $stats; } private function getUserStats($timeFilter) { $stats = []; // User registrations by day $query = "SELECT DATE(usr_joindate) as date, COUNT(*) as count FROM db_accountuser WHERE {$timeFilter} GROUP BY DATE(usr_joindate) ORDER BY date DESC"; $result = $this->db->doQuery($query); $registrations = []; while ($row = $this->db->doFetch($result)) { $registrations[] = $row; } $stats['registrations_by_day'] = $registrations; // User roles distribution $query = "SELECT usr_role, COUNT(*) as count FROM db_accountuser WHERE usr_deleted = 0 GROUP BY usr_role"; $result = $this->db->doQuery($query); $roles = []; while ($row = $this->db->doFetch($result)) { $roles[] = $row; } $stats['roles_distribution'] = $roles; return $stats; } private function getContentStats($timeFilter) { $stats = []; // Content uploads by type $contentTypes = [ 'videos' => 'db_videofiles', 'images' => 'db_imagefiles', 'audio' => 'db_audiofiles', 'documents' => 'db_documentfiles' ]; foreach ($contentTypes as $type => $table) { $query = "SELECT COUNT(*) as count FROM {$table} WHERE {$timeFilter}"; $result = $this->db->doQuery($query); $stats["new_{$type}"] = $this->db->doFetch($result)['count']; } return $stats; } private function getEngagementStats($timeFilter) { $stats = []; // Video views $query = "SELECT COUNT(*) as count FROM db_video_views WHERE {$timeFilter}"; $result = $this->db->doQuery($query); $stats['video_views'] = $this->db->doFetch($result)['count']; // Comments $query = "SELECT COUNT(*) as count FROM db_comments WHERE {$timeFilter}"; $result = $this->db->doQuery($query); $stats['comments'] = $this->db->doFetch($result)['count']; // Likes $query = "SELECT COUNT(*) as count FROM db_votes WHERE vote_type = 'like' AND {$timeFilter}"; $result = $this->db->doQuery($query); $stats['likes'] = $this->db->doFetch($result)['count']; return $stats; } private function getSystemStats() { $stats = []; // Queue statistics $queueManager = new VQueueManager(); $stats['queue'] = $queueManager->getQueueStatistics(); // Disk usage $stats['disk_usage'] = [ 'total' => disk_total_space('.'), 'free' => disk_free_space('.'), 'used' => disk_total_space('.') - disk_free_space('.') ]; // Memory usage $stats['memory_usage'] = [ 'current' => memory_get_usage(true), 'peak' => memory_get_peak_usage(true) ]; return $stats; } private function getRecentActivity($limit) { $activities = []; // Recent user registrations $query = "SELECT 'user_registration' as type, usr_user as title, usr_joindate as created_at FROM db_accountuser ORDER BY usr_joindate DESC LIMIT ?"; $result = $this->db->doQuery($query, [$limit / 4]); while ($row = $this->db->doFetch($result)) { $activities[] = $row; } // Recent video uploads $query = "SELECT 'video_upload' as type, file_title as title, file_date as created_at FROM db_videofiles ORDER BY file_date DESC LIMIT ?"; $result = $this->db->doQuery($query, [$limit / 4]); while ($row = $this->db->doFetch($result)) { $activities[] = $row; } // Sort by date usort($activities, function($a, $b) { return strtotime($b['created_at']) - strtotime($a['created_at']); }); return array_slice($activities, 0, $limit); } private function formatUserData($row) { return [ 'id' => $row['usr_id'], 'username' => $row['usr_user'], 'email' => $row['usr_email'], 'role' => $row['usr_role'], 'active' => (bool)$row['usr_active'], 'deleted' => (bool)$row['usr_deleted'], 'verified' => (bool)$row['usr_verified'], 'joined_at' => $row['usr_joindate'], 'last_login' => $row['usr_lastlogin'], 'login_count' => $row['usr_logins'], 'content_counts' => [ 'videos' => $row['usr_v_count'], 'images' => $row['usr_i_count'], 'audio' => $row['usr_a_count'], 'documents' => $row['usr_d_count'] ] ]; } private function getUserById($userId) { $query = "SELECT * FROM db_accountuser WHERE usr_id = ?"; $result = $this->db->doQuery($query, [$userId]); return $this->db->doFetch($result); } private function logAdminAction($action, $data) { $logData = [ 'admin_id' => $_SESSION['user_id'] ?? null, 'action' => $action, 'data' => json_encode($data), 'ip_address' => $this->getRealIpAddress(), 'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '', 'created_at' => date('Y-m-d H:i:s') ]; $this->db->doInsert('db_admin_logs', $logData); } private function getRealIpAddress() { if (!empty($_SERVER['HTTP_CLIENT_IP'])) { return $_SERVER['HTTP_CLIENT_IP']; } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { return $_SERVER['HTTP_X_FORWARDED_FOR']; } else { return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0'; } } private function getModerationItemsByType($type, $table, $limit, $offset) { $query = "SELECT f.*, u.usr_user FROM {$table} f JOIN db_accountuser u ON f.usr_id = u.usr_id WHERE f.moderation_status IS NULL OR f.moderation_status = 'pending' ORDER BY f.file_date DESC LIMIT ? OFFSET ?"; $result = $this->db->doQuery($query, [$limit, $offset]); $items = []; while ($row = $this->db->doFetch($result)) { $items[] = [ 'type' => $type, 'id' => $row['file_key'], 'title' => $row['file_title'], 'description' => $row['file_description'] ?? '', 'uploader' => $row['usr_user'], 'uploaded_at' => $row['file_date'], 'file_size' => $row['file_size'] ?? 0, 'status' => $row['moderation_status'] ?? 'pending' ]; } return $items; } private function checkDatabaseHealth() { try { $result = $this->db->doQuery("SELECT 1"); if ($result) { return [ 'status' => 'healthy', 'message' => 'Database connection working' ]; } else { return [ 'status' => 'unhealthy', 'issues' => ['Database query failed'] ]; } } catch (Exception $e) { return [ 'status' => 'unhealthy', 'issues' => ['Database connection failed: ' . $e->getMessage()] ]; } } private function checkFileSystemHealth() { $issues = []; $directories = [ 'f_data/temp', 'f_data/processed', 'f_data/media', 'logs' ]; foreach ($directories as $dir) { $fullPath = _FPATH . $dir; if (!is_dir($fullPath)) { $issues[] = "Directory missing: {$dir}"; } elseif (!is_writable($fullPath)) { $issues[] = "Directory not writable: {$dir}"; } } // Check disk space $freeSpace = disk_free_space('.'); $totalSpace = disk_total_space('.'); $usagePercent = (($totalSpace - $freeSpace) / $totalSpace) * 100; if ($usagePercent > 90) { $issues[] = "Disk usage critical: {$usagePercent}%"; } elseif ($usagePercent > 80) { $issues[] = "Disk usage high: {$usagePercent}%"; } if (empty($issues)) { return [ 'status' => 'healthy', 'message' => 'File system healthy' ]; } else { return [ 'status' => count($issues) > 2 ? 'unhealthy' : 'warning', 'issues' => $issues ]; } } private function checkExternalServices() { $issues = []; // Check FFmpeg $ffmpegOutput = shell_exec('ffmpeg -version 2>&1'); if (strpos($ffmpegOutput, 'ffmpeg version') === false) { $issues[] = 'FFmpeg not available'; } // Check Redis (optional) try { $redis = VRedis::getInstance(); if (!$redis->isConnected()) { $issues[] = 'Redis not connected (using database fallback)'; } } catch (Exception $e) { // Redis is optional, so this is just a warning } if (empty($issues)) { return [ 'status' => 'healthy', 'message' => 'External services healthy' ]; } else { return [ 'status' => 'warning', 'issues' => $issues ]; } } }