api/graphs.php

<?php
declare(strict_types=1);

/*
|--------------------------------------------------------------------------
| Graph rendering endpoint
|--------------------------------------------------------------------------
|
| Fetched on demand when a graph modal is opened (see loadGraph() in
| main.js) rather than rendered inline in every dashboard page load.
|
| Flow:
| 1. Parse ?view=today/week/month/year/all (+ day/week/month/year) into a
|    SQL WHERE fragment ($rangeSql/$rangeParams) shared by every metric.
| 2. Look up ?metric= in the $graphs dispatch table below and require the
|    matching includes/graphs/*.php file, which does the actual rendering
|    (each one is a thin wrapper around a shared renderXGraph() helper
|    from includes/graphs/helpers.php).
|
*/

require_once __DIR__ . '/../includes/core.php';
require_once __DIR__ . '/../includes/modules/auth.php';

brivaciaRequireApiAuth();
brivaciaSecurityHeaders();
require_once __DIR__ . '/../includes/graphs/helpers.php';

// The dashboard's own UI language, forwarded as a query param since this
// endpoint is fetched via JS fetch() rather than a normal page load (so
// there's no Accept-Language-driven page render to inherit it from).
if (isset($_GET['lang'])) {
    $_SERVER['HTTP_ACCEPT_LANGUAGE'] = (string)$_GET['lang'];
}

loadTranslations();

$metric = $_GET['metric'] ?? '';

$view = $_GET['view'] ?? 'today';
$allowedViews = ['today', 'week', 'month', 'year', 'all'];

if (!in_array($view, $allowedViews, true)) {
    $view = 'today';
}

$day = $_GET['day'] ?? date('Y-m-d');
$week = $_GET['week'] ?? date('o-\WW');
$month = $_GET['month'] ?? date('Y-m');
$year = substr($_GET['year'] ?? date('Y'), 0, 4);

$periodTitle = match ($view) {
    'today' => t('form.today'),
    'week' => t('form.week'),
    'month' => t('form.month'),
    'year' => t('form.year'),
    'all' => t('form.all'),
    default => '',
};

switch ($view) {
    case 'week':
        $weekStart = date('Y-m-d', strtotime($week));
        $weekEnd = date('Y-m-d', strtotime($weekStart . ' +6 days'));

        $rangeSql = 'day BETWEEN ? AND ?';
        $rangeParams = [$weekStart, $weekEnd];
        break;

    case 'month':
        $monthStart = $month . '-01';

        $rangeSql = 'day BETWEEN ? AND ?';
        $rangeParams = [
            $monthStart,
            date('Y-m-t', strtotime($monthStart)),
        ];
        break;

    case 'year':
        $rangeSql = 'day BETWEEN ? AND ?';
        $rangeParams = [$year . '-01-01', $year . '-12-31'];
        break;

    case 'all':
        $rangeSql = '1 = 1';
        $rangeParams = [];
        break;

    default:
        $rangeSql = 'day = ?';
        $rangeParams = [$day];
        break;
}

if ($currentSite !== '' && isset(brivacia_sites()[$currentSite])) {
    $rangeSql .= ' AND site = ?';
    $rangeParams[] = $currentSite;
}

$graphs = [
    'countries_bar' => __DIR__ . '/../includes/graphs/countries_bar.php',
    'countries_pie' => __DIR__ . '/../includes/graphs/countries_pie.php',
    'global_map' => __DIR__ . '/../includes/graphs/global_map.php',
    'page_views' => __DIR__ . '/../includes/graphs/page_views.php',
    'search_engines_bar' => __DIR__ . '/../includes/graphs/search_engines_bar.php',
    'search_engines_pie' => __DIR__ . '/../includes/graphs/search_engines_pie.php',
    'referrers_bar' => __DIR__ . '/../includes/graphs/referrers_bar.php',
    'referrers_pie' => __DIR__ . '/../includes/graphs/referrers_pie.php',
    'top_pages' => __DIR__ . '/../includes/graphs/top_pages.php',
    'unique_visitors' => __DIR__ . '/../includes/graphs/unique_visitors.php',
    'visits' => __DIR__ . '/../includes/graphs/visits.php',
];

if (!isset($graphs[$metric]) || !is_file($graphs[$metric])) {
    http_response_code(404);
    exit(t('graph.unavailable'));
}

require $graphs[$metric];

Contribute