data_provider.php

5.71 KB
10/07/2025 05:23
PHP
data_provider.php
<?php
// php/data_provider.php

header('Content-Type: application/json');

// Load configuration
$config = require_once 'config.php';

// Simulate a bit of network latency for more realistic loading states (if enabled)
if ($config['performance']['simulate_latency']) {
    usleep(rand(100000, 300000)); // 100ms to 300ms
}

require_once 'api_handler.php';
require_once 'db_handler.php';
require_once 'csv_handler.php';
require_once 'json_handler.php';

// Get the requested data source type from the query parameters
$sourceType = $_GET['source'] ?? $_POST['source'] ?? null;
$params = array_merge($_GET, $_POST); // Merge both GET and POST parameters

// Security: Validate and sanitize sourceType
$allowedSources = $config['allowed_sources'];

if ($sourceType && !in_array($sourceType, $allowedSources)) {
    sendErrorJSON("Invalid data source type: '$sourceType'. Allowed sources: ".implode(', ', $allowedSources), 400);
}

// Security: Rate limiting check (if enabled)
if ($config['rate_limit']['enabled']) {
    $clientIP = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
    $rateLimitFile = sys_get_temp_dir().'/api_rate_limit_'.md5($clientIP).'.json';
    $maxRequests = $config['rate_limit']['max_requests_per_hour'];
    $timeWindow = $config['rate_limit']['time_window'];

    $rateLimitData = ['count' => 0, 'timestamp' => time()];
    if (file_exists($rateLimitFile) && is_readable($rateLimitFile)) {
        $stored = @json_decode(file_get_contents($rateLimitFile), true);
        if ($stored && is_array($stored)) {
            $rateLimitData = $stored;
        }
    }

    if (time() - $rateLimitData['timestamp'] > $timeWindow) {
        // Reset counter if time window has passed
        $rateLimitData = ['count' => 1, 'timestamp' => time()];
    } else {
        $rateLimitData['count']++;
        if ($rateLimitData['count'] > $maxRequests) {
            sendErrorJSON("Rate limit exceeded ($maxRequests requests per hour). Please try again later.", 429);
        }
    }

    // Save rate limit data (suppress errors if can't write)
    @file_put_contents($rateLimitFile, json_encode($rateLimitData));
}

// Basic error response function
/**
 * @param string $message
 * @param int $statusCode
 * @param string $sourceType
 */
function sendErrorJSON(string $message, int $statusCode = 400, ?string $sourceType = null): void
{
    global $config;

    http_response_code($statusCode);
    $response = ['error' => $message];
    if ($sourceType) {
        $response['source'] = $sourceType;
    }

    // Log errors if enabled
    if ($config['security']['log_errors']) {
        error_log("Data Provider Error ($sourceType): $message (Status: $statusCode)");
    }

    echo json_encode($response);
    exit;
}

if (!$sourceType) {
    sendErrorJSON('Data source type (`source`) not specified in the request.');
}

$data = null;

switch ($sourceType) {
    case 'api_revenue': // Matches value in index.html dropdown
        $endpoint = $params['endpoint'] ?? 'monthly_revenue'; // Default to monthly_revenue if not specified
        unset($params['source'], $params['endpoint']); // Clean up
        $data = fetchFromApi($endpoint, $params);
        if (isset($data['error'])) {
            sendErrorJSON("API Error: ".$data['error'], 500, $sourceType);
        }
        break;

    case 'db_sales': // Matches value in index.html dropdown
        $tableName = $params['table'] ?? 'sales_overview'; // Default to sales_overview
        $columns = isset($params['columns']) ? (is_array($params['columns']) ? $params['columns'] : [$params['columns']]) : ['*'];
        unset($params['source'], $params['table'], $params['columns']); // Clean up
        $data = queryDatabase($tableName, $columns, $params);
        if (isset($data['error'])) {
            sendErrorJSON("Database Error: ".$data['error'], 500, $sourceType);
        }
        break;

    case 'csv_metrics': // Matches value in index.html dropdown
        $fileName = $params['file'] ?? 'key_metrics.csv'; // Default to key_metrics.csv
        unset($params['source'], $params['file']); // Clean up
        $data = readCsvFile($fileName);
        if (isset($data['error'])) {
            sendErrorJSON("CSV Error: ".$data['error'], 500, $sourceType);
        }
        // Ensure the data is an array of objects/cards, not the table structure
        // The csv_handler for key_metrics.csv should already return it in card format.
        // If it was a generic CSV handler that returned columns/rows, we'd transform here.
        break;

    case 'json_metrics': // New JSON data source
        $fileName = $params['file'] ?? 'key_metrics.json'; // Default to key_metrics.json
        unset($params['source'], $params['file']); // Clean up
        $data = readJsonFile($fileName);
        if (isset($data['error'])) {
            sendErrorJSON("JSON Error: ".$data['error'], 500, $sourceType);
        }
        break;

    case 'json_chart': // JSON chart data source
        $fileName = $params['file'] ?? 'chart_data.json'; // Default to chart_data.json
        unset($params['source'], $params['file']); // Clean up
        $data = readJsonFile($fileName);
        if (isset($data['error'])) {
            sendErrorJSON("JSON Error: ".$data['error'], 500, $sourceType);
        }
        break;

    default:
        sendErrorJSON("Unsupported data source type: '$sourceType'. Please check the 'source' parameter.", 400, $sourceType);
        break;
}

// Final check if data is null for some other reason (should be caught by handlers)
if ($data === null) {
    sendErrorJSON("No data could be retrieved for the specified source: '$sourceType'. The handler returned null.", 500, $sourceType);
} else {
    // Add source type to the response for debugging or specific client-side handling if needed
    // $data['_source'] = $sourceType;
    echo json_encode($data);
}