<?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);
}