exec(" CREATE TABLE IF NOT EXISTS `system_settings` ( `setting_key` VARCHAR(64) PRIMARY KEY, `setting_value` TEXT NULL, `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; "); $ensured = true; } catch (Throwable $e) { error_log("ensure_system_settings_table error: " . $e->getMessage()); } } /** * Haalt alle systeeminstellingen in 1 geoptimaliseerde query op en bewaart deze in het static runtime geheugen */ function get_all_system_settings(bool $forceRefresh = false): array { static $settingsCache = null; if ($settingsCache !== null && !$forceRefresh) { return $settingsCache; } $settingsCache = []; try { $db = get_db_connection(); ensure_system_settings_table($db); $stmt = $db->query("SELECT setting_key, setting_value FROM system_settings"); while ($row = $stmt->fetch()) { $val = $row['setting_value']; $decoded = json_decode($val, true); $settingsCache[$row['setting_key']] = (json_last_error() === JSON_ERROR_NONE) ? $decoded : $val; } } catch (Throwable $e) { error_log("get_all_system_settings error: " . $e->getMessage()); } return $settingsCache; } /** * Haalt een enkele instelling op uit het geheugen (of fallback default) */ function get_system_setting(string $key, $default = null) { $all = get_all_system_settings(); return array_key_exists($key, $all) ? $all[$key] : $default; } /** * Slaat een instelling op in MariaDB en ververst direct het cachegeheugen */ function set_system_setting(string $key, $value): bool { try { $db = get_db_connection(); ensure_system_settings_table($db); $valToStore = is_array($value) || is_object($value) ? json_encode($value) : (string)$value; $stmt = $db->prepare(" INSERT INTO system_settings (setting_key, setting_value) VALUES (?, ?) ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value), updated_at = NOW() "); $ok = $stmt->execute([$key, $valToStore]); if ($ok) { get_all_system_settings(true); } return $ok; } catch (Throwable $e) { error_log("set_system_setting error for {$key}: " . $e->getMessage()); return false; } } /** * Haalt alle organisatie werkdag standaarden op (Release v1.4) */ function get_workday_settings(): array { $rawDays = get_system_setting('workdays', '1,2,3,4,5'); $workdays = array_values(array_filter(array_map('intval', explode(',', $rawDays)), fn($d) => $d >= 1 && $d <= 7)); if (empty($workdays)) { $workdays = [1, 2, 3, 4, 5]; } return [ 'workday_start' => get_system_setting('workday_start', '08:30'), 'workday_end' => get_system_setting('workday_end', '17:00'), 'lunch_start' => get_system_setting('lunch_start', '12:30'), 'lunch_end' => get_system_setting('lunch_end', '13:00'), 'pomodoro_focus' => (int)get_system_setting('pomodoro_focus', 25), 'pomodoro_deep' => (int)get_system_setting('pomodoro_deep', 50), 'pomodoro_break' => (int)get_system_setting('pomodoro_break', 5), 'default_task_duration' => (int)get_system_setting('default_task_duration', 30), 'workdays' => $workdays, 'workdays_str' => implode(',', $workdays), ]; } /** * Haalt AI configuratie-instellingen op (Release v2.0 - Google Gemini Pro & OpenAI) * Ondersteunt organisatiebrede en optioneel persoonlijke sleutels. */ function get_ai_settings(?int $userId = null): array { if (!function_exists('fociz_decrypt')) { require_once __DIR__ . '/auth.php'; } $apiKey = ''; $isPersonal = false; // 1. Controleer persoonlijke sleutel van de ingelogde gebruiker if ($userId !== null && $userId > 0) { try { $db = get_db_connection(); $stmt = $db->prepare("SELECT access_token, settings, is_enabled FROM integrations WHERE user_id = ? AND provider IN ('GEMINI', 'AI') AND is_enabled = 1 ORDER BY id DESC LIMIT 1"); $stmt->execute([$userId]); $row = $stmt->fetch(); if ($row) { // Sleutel kan in access_token staan of in settings['api_key'] if (!empty($row['access_token'])) { $apiKey = fociz_decrypt($row['access_token']); $isPersonal = true; } elseif (!empty($row['settings'])) { $userSettings = json_decode($row['settings'], true) ?: []; if (!empty($userSettings['api_key'])) { $apiKey = fociz_decrypt($userSettings['api_key']); $isPersonal = true; } } if (!empty($row['settings'])) { $userSettings = json_decode($row['settings'], true) ?: []; if (!empty($userSettings['provider'])) { $personalProvider = $userSettings['provider']; } if (!empty($userSettings['model'])) { $personalModel = $userSettings['model']; } } } } catch (Throwable $e) { error_log("Fout bij ophalen persoonlijke AI sleutel: " . $e->getMessage()); } } // 2. Fallback naar organisatiebrede instellingen if (empty($apiKey)) { $savedKeyRaw = get_system_setting('ai_api_key', ''); if (!empty($savedKeyRaw)) { $apiKey = fociz_decrypt($savedKeyRaw); } } // 3. Fallback naar .env variabelen (GEMINI_API_KEY of OPENAI_API_KEY) if (empty($apiKey)) { $apiKey = fociz_env('GEMINI_API_KEY', '') ?: fociz_env('OPENAI_API_KEY', ''); } $provider = !empty($personalProvider) ? $personalProvider : get_system_setting('ai_provider', 'gemini'); $model = !empty($personalModel) ? $personalModel : get_system_setting('ai_model', 'gemini-1.5-pro'); return [ 'ai_enabled' => (bool)get_system_setting('ai_enabled', !empty($apiKey)), 'ai_provider' => $provider, 'ai_api_key' => $apiKey, 'ai_model' => $model, 'has_key' => !empty($apiKey), 'is_personal_key'=> $isPersonal, ]; } /** * Haalt geconfigureerde sessie idle timeout in seconden op conform NIST SP 800-63B / ISO 27001 * Toegestane waarden: 900 (15m), 1800 (30m - default), 3600 (60m), 7200 (120m) */ function get_session_idle_timeout(): int { $timeout = (int)get_system_setting('session_idle_timeout', 1800); $validTimeouts = [900, 1800, 3600, 7200]; return in_array($timeout, $validTimeouts, true) ? $timeout : 1800; }