AlkantarClanX12
| Current Path : /www/capitalgmcbuickregina_830/public/wp-content/plugins/leadbox/includes/ |
| Current File : /www/capitalgmcbuickregina_830/public/wp-content/plugins/leadbox/includes/health-check.php |
<?php
/**
* Dealership Health Check Module
* Add this code to your existing core plugin
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
class DealershipHealthCheck
{
private $secret_key;
private $inventory_file_path;
private $max_file_age = 7200; // 120 minutes in seconds
private $max_response_time = 10; // seconds
private $min_new_vehicles = 5;
private $min_used_vehicles = 5;
public function __construct()
{
// In the construct, we ONLY register hooks.
// Logic that uses WP functions should go in those hooks.
$this->inventory_file_path = WP_CONTENT_DIR . '/uploads/data/inventory.json';
// Execute late initialization (key generation and constant adjustments) on 'init'
add_action('init', array($this, 'late_initialization'), 0);
// Register REST API endpoint (can be done in the constructor).
add_action('rest_api_init', array($this, 'register_health_endpoint'));
// Add admin menu page (admin_menu is called late enough)
add_action('admin_menu', array($this, 'add_admin_menu'));
// Add settings link on plugins page (optional - if you want)
add_filter('plugin_action_links_' . plugin_basename(__FILE__), array($this, 'add_settings_link'));
}
/**
* Logic that requires loaded functions from the WordPress core.
* Runs in the 'init' hook.
*/
public function late_initialization()
{
// Generate or retrieve the secret key
$this->secret_key = get_option('dealership_health_check_secret');
if (empty($this->secret_key)) {
$this->secret_key = wp_generate_password(32, false);
update_option('dealership_health_check_secret', $this->secret_key);
}
// Apply customizations from wp-config.php (WITHOUT "Undefined constant" error)
// The constant is used without the '$' prefix since defined() guarantees its existence.
/*
// If the constant is defined, use its value; otherwise, use the class's default value.
if (defined('HEALTH_CHECK_MAX_FILE_AGE')) {
$this->max_file_age = HEALTH_CHECK_MAX_FILE_AGE;
}
if (defined('HEALTH_CHECK_MIN_NEW_VEHICLES')) {
$this->min_new_vehicles = HEALTH_CHECK_MIN_NEW_VEHICLES;
}
if (defined('HEALTH_CHECK_MIN_USED_VEHICLES')) {
$this->min_used_vehicles = HEALTH_CHECK_MIN_USED_VEHICLES;
}
*/
}
/**
* Add admin submenu page under Leadbox Settings
*/
public function add_admin_menu()
{
// We use add_submenu_page() to nest the page.
// The first parameter is the parent menu slug. We assume 'leadbox-settings'.
add_submenu_page(
'leadbox-settings', // Parent menu SLUG
'Health Check', // Page title
'Health Check', // Menu title
'manage_options', // Capability required
'dealership-health-check', // Menu slug
array($this, 'render_admin_page') // Callback function
);
}
/**
* IMPORTANT NOTE: We no longer need the add_settings_link() method
* that uses plugin_basename(__FILE__) because the menu now loads
* when the parent plugin registers its menu. You can keep the filter,
* but the link on the plugins page will now point to the submenu.
*/
/**
* Add settings link on plugins page
*/
public function add_settings_link($links)
{
$settings_link = '<a href="' . admin_url('admin.php?page=dealership-health-check') . '">View Health Check</a>';
array_unshift($links, $settings_link);
return $links;
}
/**
* Render admin page
*/
public function render_admin_page()
{
// Handle secret key regeneration
if (isset($_POST['regenerate_key']) && check_admin_referer('regenerate_health_check_key')) {
$this->secret_key = wp_generate_password(32, false);
update_option('dealership_health_check_secret', $this->secret_key);
echo '<div class="notice notice-success"><p>Secret key regenerated successfully!</p></div>';
}
// Handle test health check
$test_result = null;
if (isset($_POST['test_health_check']) && check_admin_referer('test_health_check')) {
$test_result = $this->run_test_check();
}
// We use home_url() which returns the base URL of the site, which is better for these cases.
$health_url = rest_url('lbx/health') . '?key=' . $this->secret_key;
$site_url = get_site_url();
$site_name = get_bloginfo('name');
?>
<div class="wrap">
<h1>
<span class="dashicons dashicons-heart" style="font-size: 30px; width: 30px; height: 30px;"></span>
Dealership Health Check
</h1>
<div class="card" style="max-width: 800px; margin-top: 20px;">
<h2>Health Check Endpoint</h2>
<p>Use this URL in Uptime Robot to monitor your website's critical functionality.</p>
<table class="form-table">
<tr>
<th scope="row">Site Name:</th>
<td><strong><?php echo esc_html($site_name); ?></strong></td>
</tr>
<tr>
<th scope="row">Site URL:</th>
<td><?php echo esc_html($site_url); ?></td>
</tr>
<tr>
<th scope="row">Health Check URL:</th>
<td>
<input type="text" readonly value="<?php echo esc_attr($health_url); ?>"
style="width: 100%; font-family: monospace; padding: 8px;"
onclick="this.select();" />
<p class="description">Click to select, then copy this entire URL for Uptime Robot</p>
</td>
</tr>
<tr>
<th scope="row">Secret Key:</th>
<td>
<code style="background: #f0f0f1; padding: 8px; display: block; word-break: break-all;">
<?php echo esc_html($this->secret_key); ?>
</code>
<p class="description">Keep this secure. Only share with authorized personnel.</p>
</td>
</tr>
</table>
<form method="post" style="margin-top: 20px;">
<?php wp_nonce_field('regenerate_health_check_key'); ?>
<button type="submit" name="regenerate_key" class="button button-secondary"
onclick="return confirm('This will invalidate your current Uptime Robot configuration. Continue?');">
Regenerate Secret Key
</button>
</form>
</div>
<div class="card" style="max-width: 800px; margin-top: 20px;">
<h2>Test Health Check</h2>
<p>Test your health check configuration without using Uptime Robot.</p>
<form method="post">
<?php wp_nonce_field('test_health_check'); ?>
<button type="submit" name="test_health_check" class="button button-primary">
Run Health Check Now
</button>
</form>
<?php if ($test_result): ?>
<div style="margin-top: 20px; padding: 15px; background: <?php echo $test_result['status'] === 'healthy' ? '#d4edda' : '#f8d7da'; ?>; border-left: 4px solid <?php echo $test_result['status'] === 'healthy' ? '#28a745' : '#dc3545'; ?>;">
<h3 style="margin-top: 0;">
<?php if ($test_result['status'] === 'healthy'): ?>
✅ Status: Healthy
<?php else: ?>
❌ Status: Unhealthy
<?php endif; ?>
</h3>
<p><strong>Response Time:</strong> <?php echo $test_result['response_time']; ?>s</p>
<table class="widefat" style="margin-top: 15px;">
<thead>
<tr>
<th>Check</th>
<th>Status</th>
<th>Details</th>
</tr>
</thead>
<tbody>
<?php foreach ($test_result['checks'] as $check_name => $check_data): ?>
<tr>
<td><strong><?php echo esc_html(ucwords(str_replace('_', ' ', $check_name))); ?></strong></td>
<td>
<?php if ($check_data['passed']): ?>
<span style="color: #28a745;">✓ Passed</span>
<?php else: ?>
<span style="color: #dc3545;">✗ Failed</span>
<?php endif; ?>
</td>
<td>
<?php echo esc_html($check_data['message']); ?>
<?php if (isset($check_data['new_vehicles'])): ?>
<br><small>New: <?php echo $check_data['new_vehicles']; ?> | Used: <?php echo $check_data['used_vehicles']; ?></small>
<?php endif; ?>
<?php if (isset($check_data['last_modified'])): ?>
<br><small>Last Modified: <?php echo $check_data['last_modified']; ?></small>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
<div class="card" style="max-width: 800px; margin-top: 20px;">
<h2>What Gets Monitored</h2>
<ul style="line-height: 1.8;">
<li><strong>Database:</strong> WordPress database connectivity</li>
<li><strong>File Exists:</strong> Inventory file at /wp-content/uploads/data/inventory.json</li>
<li><strong>File Age:</strong> Must be updated within <?php echo ($this->max_file_age / 60); ?> minutes</li>
<li><strong>Vehicle Counts:</strong> Valid JSON with at least <?php echo $this->min_new_vehicles; ?> vehicles</li>
<li><strong>Response Time:</strong> Check must complete in under <?php echo $this->max_response_time; ?> seconds</li>
</ul>
</div>
<div class="card" style="max-width: 800px; margin-top: 20px;">
<h2>Quick Setup for Uptime Robot</h2>
<ol style="line-height: 1.8;">
<li>Copy the <strong>Health Check URL</strong> above</li>
<li>Log in to Uptime Robot → Add New Monitor</li>
<li>Monitor Type: <strong>HTTP(s)</strong></li>
<li>Friendly Name: <strong><?php echo esc_html($site_name); ?> - Health Check</strong></li>
<li>URL: Paste the Health Check URL</li>
<li>Monitoring Interval: <strong>5 minutes</strong></li>
<li>Monitor Timeout: <strong>30 seconds</strong></li>
<li>Click <strong>Create Monitor</strong></li>
</ol>
<p><a href="<?php echo admin_url('admin.php?page=dealership-health-check-docs'); ?>" class="button">View Full Documentation</a></p>
</div>
</div>
<style>
.card h2 {
margin-top: 0;
}
.form-table th {
width: 200px;
}
</style>
<?php
}
/**
* Run test health check
*/
private function run_test_check()
{
$start_time = microtime(true);
$checks = array();
$overall_status = 'healthy';
// Run all checks
$checks['database'] = $this->check_database();
if (!$checks['database']['passed']) {
$overall_status = 'unhealthy';
}
$checks['file_exists'] = $this->check_file_exists();
if (!$checks['file_exists']['passed']) {
$overall_status = 'unhealthy';
}
if ($checks['file_exists']['passed']) {
$checks['file_age'] = $this->check_file_age();
if (!$checks['file_age']['passed']) {
$overall_status = 'unhealthy';
}
// Vehicle counts validates JSON implicitly
$checks['vehicle_counts'] = $this->check_vehicle_counts();
if (!$checks['vehicle_counts']['passed']) {
$overall_status = 'unhealthy';
}
}
$response_time = round(microtime(true) - $start_time, 3);
$checks['response_time'] = array(
'passed' => $response_time < $this->max_response_time,
'message' => $response_time . 's',
'threshold' => $this->max_response_time . 's'
);
if ($response_time >= $this->max_response_time) {
$overall_status = 'unhealthy';
}
return array(
'status' => $overall_status,
'checks' => $checks,
'response_time' => $response_time
);
}
/**
* Register the health check REST endpoint
*/
public function register_health_endpoint()
{
register_rest_route('lbx', '/health', array(
'methods' => 'GET',
'callback' => array($this, 'health_check'),
'permission_callback' => '__return_true'
));
}
/**
* Main health check callback
*/
public function health_check($request)
{
$start_time = microtime(true);
// Verify secret key
$provided_key = $request->get_param('key');
if ($provided_key !== $this->secret_key) {
return new WP_REST_Response(array(
'status' => 'error',
'message' => 'Invalid or missing authentication key'
), 401);
}
$checks = array();
$overall_status = 'healthy';
// 1. Database connectivity check
$db_check = $this->check_database();
$checks['database'] = $db_check;
if (!$db_check['passed']) {
$overall_status = 'unhealthy';
}
// 2. Inventory file exists check
$file_exists = $this->check_file_exists();
$checks['file_exists'] = $file_exists;
if (!$file_exists['passed']) {
$overall_status = 'unhealthy';
}
// 3. File age check
if ($file_exists['passed']) {
$file_age = $this->check_file_age();
$checks['file_age'] = $file_age;
if (!$file_age['passed']) {
$overall_status = 'unhealthy';
}
// 4. Vehicle count check (validates JSON implicitly)
$vehicle_count = $this->check_vehicle_counts();
$checks['vehicle_counts'] = $vehicle_count;
if (!$vehicle_count['passed']) {
$overall_status = 'unhealthy';
}
}
// Calculate response time
$response_time = round(microtime(true) - $start_time, 3);
$checks['response_time'] = array(
'passed' => $response_time < $this->max_response_time,
'message' => $response_time . 's',
'threshold' => $this->max_response_time . 's'
);
if ($response_time >= $this->max_response_time) {
$overall_status = 'unhealthy';
}
// Determine HTTP status code
$http_status = ($overall_status === 'healthy') ? 200 : 503;
// Return response
return new WP_REST_Response(array(
'status' => $overall_status,
'timestamp' => current_time('c'),
'checks' => $checks,
'response_time' => $response_time
), $http_status);
}
/**
* Check database connectivity
*/
private function check_database()
{
global $wpdb;
try {
$result = $wpdb->get_var("SELECT 1");
if ($result == 1) {
return array(
'passed' => true,
'message' => 'Database connection successful'
);
} else {
return array(
'passed' => false,
'message' => 'Database query returned unexpected result'
);
}
} catch (Exception $e) {
return array(
'passed' => false,
'message' => 'Database connection failed: ' . $e->getMessage()
);
}
}
/**
* Check if inventory file exists
*/
private function check_file_exists()
{
if (file_exists($this->inventory_file_path)) {
return array(
'passed' => true,
'message' => 'Inventory file exists'
);
} else {
return array(
'passed' => false,
'message' => 'Inventory file not found at: ' . $this->inventory_file_path
);
}
}
/**
* Check inventory file age
*/
private function check_file_age()
{
$file_modified_time = filemtime($this->inventory_file_path);
$current_time = time();
$age_in_seconds = $current_time - $file_modified_time;
$age_in_minutes = round($age_in_seconds / 60);
if ($age_in_seconds <= $this->max_file_age) {
return array(
'passed' => true,
'message' => 'File age: ' . $age_in_minutes . ' minutes',
'last_modified' => date('Y-m-d H:i:s', $file_modified_time)
);
} else {
return array(
'passed' => false,
'message' => 'File is too old: ' . $age_in_minutes . ' minutes (max: 120 minutes)',
'last_modified' => date('Y-m-d H:i:s', $file_modified_time)
);
}
}
/**
* Check vehicle counts (new and used)
* Also validates JSON structure implicitly
*/
private function check_vehicle_counts()
{
// Read inventory file
$json_content = file_get_contents($this->inventory_file_path);
if ($json_content === false) {
return array(
'passed' => false,
'message' => 'Failed to read inventory file'
);
}
// Parse JSON
$data = json_decode($json_content, true);
if (json_last_error() !== JSON_ERROR_NONE) {
return array(
'passed' => false,
'message' => 'Invalid JSON: ' . json_last_error_msg()
);
}
// Validate structure
if (!isset($data['vehicles']) || !is_array($data['vehicles'])) {
return array(
'passed' => false,
'message' => 'JSON structure invalid: missing or invalid "vehicles" array'
);
}
// Count vehicles
$new_count = 0;
$used_count = 0;
foreach ($data['vehicles'] as $vehicle) {
if (isset($vehicle['condition'])) {
if ($vehicle['condition'] === 'New') {
$new_count++;
} elseif ($vehicle['condition'] === 'Used') {
$used_count++;
}
}
}
// Check thresholds
$passed = (($new_count+$used_count) >= $this->min_new_vehicles);
$messages = array();
if ($new_count < $this->min_new_vehicles) {
$messages[] = "Insufficient new vehicles: {$new_count} (minimum: {$this->min_new_vehicles})";
}
if ($used_count < $this->min_used_vehicles) {
$messages[] = "Insufficient used vehicles: {$used_count} (minimum: {$this->min_used_vehicles})";
}
if ($passed) {
return array(
'passed' => true,
'message' => "Vehicle counts OK (JSON valid)",
'new_vehicles' => $new_count,
'used_vehicles' => $used_count
);
} else {
return array(
'passed' => false,
'message' => implode('; ', $messages),
'new_vehicles' => $new_count,
'used_vehicles' => $used_count
);
}
}
}
// Initialize the health check module
// Add this line to your existing plugin's main file:
// new DealershipHealthCheck();