- 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
803 lines
26 KiB
PHP
803 lines
26 KiB
PHP
<?php
|
|
/*******************************************************************************************************************
|
|
| Software Name : EasyStream
|
|
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
|
| Software Author : (c) Sami Ahmed
|
|
|*******************************************************************************************************************
|
|
|
|
|
|*******************************************************************************************************************
|
|
| This source file is subject to the EasyStream Proprietary License Agreement.
|
|
|
|
|
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
|
|*******************************************************************************************************************
|
|
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
|
|*******************************************************************************************************************/
|
|
|
|
defined('_ISVALID') or header('Location: /error');
|
|
|
|
/**
|
|
* Admin Dashboard and Management System
|
|
*/
|
|
class VAdmin
|
|
{
|
|
private $logger;
|
|
private $db;
|
|
private static $instance = null;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->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
|
|
];
|
|
}
|
|
}
|
|
} |