From e729f920139f0c65902be2d6b2c32466b08375e8 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Mon, 20 Oct 2025 17:54:52 +0000
Subject: [PATCH] =Form updates

---
 inc/integrations/Integrations.php |   93 ++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 88 insertions(+), 5 deletions(-)

diff --git a/inc/integrations/Integrations.php b/inc/integrations/Integrations.php
index 76f0c56..5f47ff6 100644
--- a/inc/integrations/Integrations.php
+++ b/inc/integrations/Integrations.php
@@ -35,6 +35,7 @@
 	protected array $apiEndpoints = [];   // Valid endpoint paths for this service
 	protected string $apiVersion = '';     // API version string (e.g., 'v2', '2024-01-01')
 
+	protected int $refresh_interval = 0; //seconds before expiry to refresh tokens. 0 to disable
 
 	/**
 	 * OAuth Configuration
@@ -806,7 +807,9 @@
 
 				// Retry with backoff for server errors
 				if ($attempt < $this->maxRetries && !$this->isClientError($e)) {
-					sleep($this->retryDelays[$attempt - 1] ?? 5);
+					$delay = pow(2, $attempt) * 1000000; // 2^attempt seconds in microseconds
+					$jitter = rand(0, $delay * 0.3); // Add 0-30% jitter
+					usleep($delay + $jitter);
 				} else {
 					break;
 				}
@@ -1341,10 +1344,21 @@
 		}
 
 		if (!empty($this->credentials)) {
-			if ($this->isOAuthService && $this->hasOAuthCredentials() && !$this->isOAuthValid()) {
-				$this->logDebug('OAuth token expired, attempting refresh');
-				if (!$this->refreshOAuthToken()) {
-					$this->logError('Failed to refresh OAuth token');
+			if ($this->isOAuthService && $this->hasOAuthCredentials()) {
+				// Check if token is expired first
+				if (!$this->isOAuthValid()) {
+					$this->logDebug('OAuth token expired, attempting refresh');
+					if (!$this->refreshOAuthToken()) {
+						$this->logError('Failed to refresh expired OAuth token');
+					}
+				}
+				// Check if we should proactively refresh (before expiry)
+				elseif ($this->shouldRefreshToken()) {
+					$this->logDebug('OAuth token should be refreshed proactively');
+					if (!$this->refreshOAuthToken()) {
+						$this->logError('Failed to proactively refresh OAuth token');
+						// Not critical - token is still valid
+					}
 				}
 			}
 			$this->initialize();
@@ -1786,6 +1800,75 @@
 	}
 
 	/**
+	 * Check if token should be proactively refreshed
+	 * Different from isOAuthValid() which checks if token is actually expired
+	 */
+	protected function shouldRefreshToken(): bool
+	{
+		if (!$this->isOAuthService || $this->refresh_interval === 0) {
+			return false;
+		}
+
+		// If no expiry info, we can't proactively refresh
+		if (empty($this->credentials['expires_at'])) {
+			return false;
+		}
+
+		$expires_at = intval($this->credentials['expires_at']);
+		$time_until_expiry = $expires_at - time();
+
+		// Refresh if we're within the refresh interval window
+		return $time_until_expiry > 0 && $time_until_expiry <= $this->refresh_interval;
+	}
+	/**
+	 * Get time until token refresh is recommended
+	 * Useful for displaying in admin UI
+	 */
+	public function getTimeUntilRefresh(): ?int
+	{
+		if ($this->refresh_interval === 0 || empty($this->credentials['expires_at'])) {
+			return null;
+		}
+
+		$expires_at = intval($this->credentials['expires_at']);
+		$refresh_at = $expires_at - $this->refresh_interval;
+		$time_until_refresh = $refresh_at - time();
+
+		return max(0, $time_until_refresh);
+	}
+
+	/**
+	 * Get token freshness status
+	 * Returns: 'fresh', 'should_refresh', 'expired', or 'no_expiry_info'
+	 */
+	public function getTokenStatus(): string
+	{
+		if (!$this->isOAuthService) {
+			return 'not_oauth';
+		}
+
+		if (empty($this->credentials['access_token'])) {
+			return 'no_token';
+		}
+
+		if (empty($this->credentials['expires_at'])) {
+			return 'no_expiry_info';
+		}
+
+		$expires_at = intval($this->credentials['expires_at']);
+		$now = time();
+
+		if ($expires_at <= $now) {
+			return 'expired';
+		}
+
+		if ($this->shouldRefreshToken()) {
+			return 'should_refresh';
+		}
+
+		return 'fresh';
+	}
+	/**
 	 * Refresh OAuth token
 	 */
 	protected function refreshOAuthToken(): bool

--
Gitblit v1.10.0