From d4e4b69e02b5fb9f2a94a3525197d02eff968cbd Mon Sep 17 00:00:00 2001 From: Niek Rabelink Date: Wed, 16 Sep 2026 13:53:54 +0200 Subject: [PATCH] Snelheid --- config/database.php | 9 ++- includes/auth.php | 12 ++++ includes/http.php | 3 +- includes/integrations/jira.php | 40 +++++++------ includes/integrations/microsoft.php | 8 +-- planner.php | 93 ++++++++++++++++------------- 6 files changed, 97 insertions(+), 68 deletions(-) diff --git a/config/database.php b/config/database.php index ac5746f..4cb4666 100644 --- a/config/database.php +++ b/config/database.php @@ -126,8 +126,14 @@ function ensure_tables_exist(PDO $pdo): void { if ($tablesChecked) return; $tablesChecked = true; + $schemaFile = file_exists(__DIR__ . '/schema.sql') ? (__DIR__ . '/schema.sql') : (__DIR__ . '/../schema.sql'); + $lockFile = sys_get_temp_dir() . '/fociz_schema_' . md5(__DIR__) . '.lock'; + + if (file_exists($lockFile) && file_exists($schemaFile) && filemtime($lockFile) >= filemtime($schemaFile)) { + return; + } + try { - $schemaFile = file_exists(__DIR__ . '/schema.sql') ? (__DIR__ . '/schema.sql') : (__DIR__ . '/../schema.sql'); if (file_exists($schemaFile)) { $sql = file_get_contents($schemaFile); $statements = array_filter(array_map('trim', explode(';', $sql))); @@ -300,6 +306,7 @@ function ensure_tables_exist(PDO $pdo): void { } catch (Throwable $e) { // Stil negeren indien system_settings tabel nog niet bestaat } + @touch($lockFile); } catch (Throwable $e) { error_log("Fout bij controleren tabellen: " . $e->getMessage()); } diff --git a/includes/auth.php b/includes/auth.php index 110cf09..8d1635e 100644 --- a/includes/auth.php +++ b/includes/auth.php @@ -347,6 +347,11 @@ function ensure_remember_token_columns(PDO $db): void { if ($columnsChecked) return; $columnsChecked = true; + $lockFile = sys_get_temp_dir() . '/fociz_auth_cols_' . md5(__DIR__) . '.lock'; + if (file_exists($lockFile)) { + return; + } + try { $result = $db->query("SHOW COLUMNS FROM `users` LIKE 'remember_token'"); if (!$result || $result->rowCount() === 0) { @@ -360,6 +365,7 @@ function ensure_remember_token_columns(PDO $db): void { if (!$resActivity || $resActivity->rowCount() === 0) { $db->exec("ALTER TABLE `users` ADD COLUMN `last_activity_at` DATETIME NULL AFTER `remember_token_expires_at`"); } + @touch($lockFile); } catch (Throwable $e) { // Stil negeren indien al aanwezig of tijdens migraties } @@ -374,6 +380,11 @@ function ensure_sessions_table(PDO $db): void { if ($sessionsTableChecked) return; $sessionsTableChecked = true; + $lockFile = sys_get_temp_dir() . '/fociz_sessions_tbl_' . md5(__DIR__) . '.lock'; + if (file_exists($lockFile)) { + return; + } + try { $db->exec(" CREATE TABLE IF NOT EXISTS `user_sessions` ( @@ -394,6 +405,7 @@ function ensure_sessions_table(PDO $db): void { CONSTRAINT `fk_user_sessions_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; "); + @touch($lockFile); } catch (Throwable $e) { error_log("ensure_sessions_table notice: " . $e->getMessage()); } diff --git a/includes/http.php b/includes/http.php index 35d00c3..27c888d 100644 --- a/includes/http.php +++ b/includes/http.php @@ -70,7 +70,8 @@ if (!function_exists('fociz_http_request')) { curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); - curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 6); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, min(4, max(1, $timeout - 1))); + curl_setopt($ch, CURLOPT_DNS_CACHE_TIMEOUT, 600); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); curl_setopt($ch, CURLOPT_HTTPHEADER, $headerLines); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $verifySsl); diff --git a/includes/integrations/jira.php b/includes/integrations/jira.php index c0e421b..8bb0675 100644 --- a/includes/integrations/jira.php +++ b/includes/integrations/jira.php @@ -343,7 +343,7 @@ function jira_fetch_project_statuses(string $domain, string $email, string $apiT $cacheKey = "jira_proj_statuses_" . $pKey . "_" . md5($cleanDomain); $cached = fociz_cache_get($cacheKey); - if (is_array($cached) && !empty($cached)) { + if ($cached !== null && is_array($cached)) { return $cached; } @@ -356,7 +356,7 @@ function jira_fetch_project_statuses(string $domain, string $email, string $apiT 'Authorization' => $authHeader, 'Accept' => 'application/json', ], - 'timeout' => 8, + 'timeout' => 4, ]); if ($response['status'] === 404 || $response['status'] === 405) { @@ -367,7 +367,7 @@ function jira_fetch_project_statuses(string $domain, string $email, string $apiT 'Authorization' => $authHeader, 'Accept' => 'application/json', ], - 'timeout' => 8, + 'timeout' => 4, ]); } @@ -403,7 +403,7 @@ function jira_fetch_project_statuses(string $domain, string $email, string $apiT fociz_cache_set($cacheKey, $statuses, 86400); // 24 uur cache } else { - fociz_cache_set($cacheKey, [], 1800); // 30 min negatieve cache + fociz_cache_set($cacheKey, [], 86400); // 24 uur negatieve cache zodat ontbrekende projecten niet herhaaldelijk vertragen } return $statuses; @@ -471,7 +471,7 @@ function jira_fetch_issues(string $domain, string $email, string $apiToken, stri 'Content-Type' => 'application/json', ], 'body' => $payload, - 'timeout' => 12, + 'timeout' => 6, ]); if ($response['status'] === 405 || $response['status'] === 400) { @@ -500,7 +500,7 @@ function jira_fetch_issues(string $domain, string $email, string $apiToken, stri 'Authorization' => $authHeader, 'Accept' => 'application/json', ], - 'timeout' => 12, + 'timeout' => 6, ]); if ($response['status'] === 404) { @@ -513,9 +513,13 @@ function jira_fetch_issues(string $domain, string $email, string $apiToken, stri $getParams = [ 'jql' => $jql, 'maxResults' => $currentPageSize, - 'startAt' => $startAt, 'fields' => implode(',', $fieldsList), ]; + if (!empty($nextPageToken)) { + $getParams['nextPageToken'] = $nextPageToken; + } elseif ($startAt > 0) { + $getParams['startAt'] = $startAt; + } $urlV2 = $cleanDomain . '/rest/api/2/search?' . http_build_query($getParams); $response = fociz_http_request($urlV2, [ 'method' => 'GET', @@ -523,7 +527,7 @@ function jira_fetch_issues(string $domain, string $email, string $apiToken, stri 'Authorization' => $authHeader, 'Accept' => 'application/json', ], - 'timeout' => 12, + 'timeout' => 6, ]); } @@ -1238,6 +1242,11 @@ function jira_get_dashboard_data(int $userId, bool $forceRefresh = false): array $availableStatuses = jira_fetch_statuses($domain, $email, $apiToken); if (!$assignedRes['success'] && !$teamRes['success']) { + // Val terug op bestaande cache snapshot indien live request faalt + $fallback = fociz_cache_get($cacheKey); + if ($fallback && is_array($fallback)) { + return array_merge($fallback, ['from_cache' => true, 'warning' => 'Kon Jira niet actueel bijwerken; tonen opgeslagen snapshot.']); + } return [ 'is_demo' => false, 'success' => false, @@ -1250,17 +1259,10 @@ function jira_get_dashboard_data(int $userId, bool $forceRefresh = false): array $assignedIssues = $assignedRes['success'] ? $assignedRes['issues'] : []; $teamIssues = $teamRes['success'] ? $teamRes['issues'] : []; - // Project-specifieke statussen ophalen voor gebruikte projecten - $activeProjKeys = ['SD', 'IN', 'AAN', 'GEN']; - $allLoaded = array_merge($assignedIssues, $teamIssues); - foreach ($allLoaded as $iss) { - $pk = strtoupper(trim($iss['project_key'] ?? '')); - if (!empty($pk) && !in_array($pk, $activeProjKeys, true)) { - $activeProjKeys[] = $pk; - } - } + // Projectstatussen: haal uitsluitend het actieve hoofdproject op om sequentiële HTTP latency te elimineren $projectStatusesMap = []; - foreach ($activeProjKeys as $pk) { + $targetProjects = ['SD']; + foreach ($targetProjects as $pk) { $pStats = jira_fetch_project_statuses($domain, $email, $apiToken, $pk); if (!empty($pStats)) { $projectStatusesMap[$pk] = $pStats; @@ -1283,7 +1285,7 @@ function jira_get_dashboard_data(int $userId, bool $forceRefresh = false): array 'project_statuses' => $projectStatusesMap, ]; - fociz_cache_set($cacheKey, $data, 180); // 3 minuten cache + fociz_cache_set($cacheKey, $data, 900); // 15 minuten cache voor supersnelle paginaladingen if (session_status() === PHP_SESSION_ACTIVE) { $_SESSION[$cacheKey] = [ diff --git a/includes/integrations/microsoft.php b/includes/integrations/microsoft.php index 12a9ca0..0773e84 100644 --- a/includes/integrations/microsoft.php +++ b/includes/integrations/microsoft.php @@ -330,7 +330,7 @@ function microsoft_fetch_events_range(int $userId, string $startDate, string $en 'Accept' => 'application/json', 'Prefer' => 'outlook.timezone="Europe/Amsterdam"', ], - 'timeout' => 12, + 'timeout' => 6, ]); // Retry bij 401 Unauthorized @@ -345,7 +345,7 @@ function microsoft_fetch_events_range(int $userId, string $startDate, string $en 'Accept' => 'application/json', 'Prefer' => 'outlook.timezone="Europe/Amsterdam"', ], - 'timeout' => 12, + 'timeout' => 6, ]); } } @@ -416,7 +416,7 @@ function microsoft_fetch_events_range(int $userId, string $startDate, string $en ]; } - fociz_cache_set($cacheKey, $events, 60); + fociz_cache_set($cacheKey, $events, 300); // 5 minuten cache return $events; } @@ -1567,7 +1567,7 @@ function microsoft_fetch_folder_messages(int $userId, string $folderId = 'inbox' 'Accept' => 'application/json', 'Prefer' => 'outlook.body-content-type="text"', ], - 'timeout' => 15, + 'timeout' => 6, ]); if ($res['status'] !== 200 || !isset($res['json']['value'])) { diff --git a/planner.php b/planner.php index 3e2336a..033773e 100644 --- a/planner.php +++ b/planner.php @@ -127,12 +127,11 @@ if ($isOutlookConnected) { // Synchroniseer / reconcilieer Fociz taken direct met actuele Outlook data // Alleen uitvoeren indien events gewijzigd zijn of bij expliciete refresh om overtollige DB load te voorkomen $eventsHash = md5(json_encode($weekOutlookEvents)); - $lastReconciledHash = $_SESSION["last_reconciled_{$userId}"] ?? ''; + $cacheReconcileKey = "last_reconciled_{$userId}"; + $lastReconciledHash = fociz_cache_get($cacheReconcileKey, ''); if ($eventsHash !== $lastReconciledHash || $forceRefresh) { microsoft_reconcile_tasks_with_outlook($userId, $weekStart, $weekEnd, $weekOutlookEvents); - if (session_status() === PHP_SESSION_ACTIVE) { - $_SESSION["last_reconciled_{$userId}"] = $eventsHash; - } + fociz_cache_set($cacheReconcileKey, $eventsHash, 86400); } } catch (Throwable $e) { error_log("Outlook synchronisatiefout in planner: " . $e->getMessage()); @@ -304,45 +303,7 @@ for ($i = 0; $i < 7; $i++) { ]; } -// 5. Haal Jira dashboard data op voor de snelle import widget en wachtrijfiltering -$jiraAssignedIssues = []; -$jiraTeamIssues = []; -$jiraAllIssues = []; -$jiraQueueFilters = []; -$isJiraLive = false; -try { - $jiraDashboardData = jira_get_dashboard_data($userId, $forceRefresh); - $jiraAssignedIssues = $jiraDashboardData['assigned_issues'] ?? []; - $jiraTeamIssues = $jiraDashboardData['team_issues'] ?? []; - $jiraQueueFilters = $jiraDashboardData['queue_filters'] ?? []; - $isJiraLive = !empty($jiraDashboardData['success']) && empty($jiraDashboardData['is_demo']); - - $seenJiraKeys = []; - foreach (array_merge($jiraAssignedIssues, $jiraTeamIssues) as $iss) { - if (!empty($iss['key']) && !isset($seenJiraKeys[$iss['key']])) { - $seenJiraKeys[$iss['key']] = true; - $jiraAllIssues[] = $iss; - } - } -} catch (Throwable $e) { - error_log("Fout bij ophalen Jira issues in planner: " . $e->getMessage()); -} -$jiraIssues = $jiraAllIssues; - -// 5b. Haal recente e-mails op voor de Outlook E-mail Widget -$recentEmails = []; -$unreadEmailCount = 0; -$mailNeedsReconsent = false; -try { - $mailData = microsoft_fetch_inbox_messages($userId, 5, false, $forceRefresh); - $recentEmails = $mailData['messages'] ?? []; - $unreadEmailCount = (int)($mailData['unread_count'] ?? 0); - $mailNeedsReconsent = !empty($mailData['needs_reconsent']); -} catch (Throwable $e) { - error_log("Fout bij ophalen recente e-mails in planner: " . $e->getMessage()); -} - -// 5c. Widget Catalogus & Raster Indeling (Fociz v2.0) +// 5. Widget Catalogus & Raster Indeling (Fociz v2.0) $availableWidgets = [ 'daily_goal' => ['id' => 'daily_goal', 'title' => 'Dagfocus & Hoofddoel', 'category' => 'Planning', 'icon' => 'target', 'enabled' => true, 'col_span' => 12, 'order' => 1, 'desc' => 'Centraal focusdoel voor de dag met snelle tellers'], 'timeline' => ['id' => 'timeline', 'title' => 'Tijdlijn & Dag/Weekoverzicht', 'category' => 'Agenda', 'icon' => 'calendar', 'enabled' => true, 'col_span' => 12, 'order' => 2, 'desc' => 'Gecombineerd weekoverzicht, dagselectie en uren-tijdlijn met time-blocking en AI'], @@ -393,6 +354,52 @@ uasort($userWidgetLayout, function($a, $b) { return strcmp($a['id'] ?? '', $b['id'] ?? ''); }); +// 5b. Haal Jira dashboard data op voor de snelle import widget (indien ingeschakeld) +$jiraAssignedIssues = []; +$jiraTeamIssues = []; +$jiraAllIssues = []; +$jiraQueueFilters = []; +$isJiraLive = false; +$isJiraWidgetEnabled = !empty($userWidgetLayout['jira_queue']['enabled']); + +if ($isJiraWidgetEnabled) { + try { + $jiraDashboardData = jira_get_dashboard_data($userId, $forceRefresh); + $jiraAssignedIssues = $jiraDashboardData['assigned_issues'] ?? []; + $jiraTeamIssues = $jiraDashboardData['team_issues'] ?? []; + $jiraQueueFilters = $jiraDashboardData['queue_filters'] ?? []; + $isJiraLive = !empty($jiraDashboardData['success']) && empty($jiraDashboardData['is_demo']); + + $seenJiraKeys = []; + foreach (array_merge($jiraAssignedIssues, $jiraTeamIssues) as $iss) { + if (!empty($iss['key']) && !isset($seenJiraKeys[$iss['key']])) { + $seenJiraKeys[$iss['key']] = true; + $jiraAllIssues[] = $iss; + } + } + } catch (Throwable $e) { + error_log("Fout bij ophalen Jira issues in planner: " . $e->getMessage()); + } +} +$jiraIssues = $jiraAllIssues; + +// 5c. Haal recente e-mails op voor de Outlook E-mail Widget (indien ingeschakeld) +$recentEmails = []; +$unreadEmailCount = 0; +$mailNeedsReconsent = false; +$isMailWidgetEnabled = !empty($userWidgetLayout['outlook_mail']['enabled']); + +if ($isMailWidgetEnabled) { + try { + $mailData = microsoft_fetch_inbox_messages($userId, 5, false, $forceRefresh); + $recentEmails = $mailData['messages'] ?? []; + $unreadEmailCount = (int)($mailData['unread_count'] ?? 0); + $mailNeedsReconsent = !empty($mailData['needs_reconsent']); + } catch (Throwable $e) { + error_log("Fout bij ophalen recente e-mails in planner: " . $e->getMessage()); + } +} + // Bereken dagstatistieken $completedTasksCount = 0; $pendingTasksCount = 0;