When a provider's API was down (5xx, timeout, DNS), the hourly RefreshSocialToken job and daily VerifyWorkspaceConnections job were treating it as "token revoked" and emailing the user to reconnect. Bluesky going offline triggered false-positive disconnect notifications because Bluesky access tokens are short-lived (2h) so every hourly refresh failed during the outage. - New PlatformUnavailableException: API unreachable / 5xx, transient. TokenExpiredException stays for 4xx (token is provably bad). - New TokenRefreshClient: normalizes failure semantics for OAuth refresh HTTP calls across all providers. Takes a Platform enum so typos fail at compile time and the user-facing label comes from one source. - ConnectionVerifier: all 8 refresh*Token methods route through the new client. Hardcoded OAuth URLs (LinkedIn, YouTube) and Bluesky's default PDS host moved into config/trypost.php alongside the existing per-platform entries. - RefreshSocialToken job: PlatformUnavailableException → log warning and stop. Do NOT markAsTokenExpired, do NOT notify the user. Next scheduled tick retries. - VerifyWorkspaceConnections job: PlatformUnavailableException from the inner refresh propagates and is treated as a transient skip.
52 lines
1.9 KiB
PHP
52 lines
1.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Jobs;
|
|
|
|
use App\Exceptions\PlatformUnavailableException;
|
|
use App\Exceptions\TokenExpiredException;
|
|
use App\Models\SocialAccount;
|
|
use App\Services\Social\ConnectionVerifier;
|
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
use Illuminate\Foundation\Queue\Queueable;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Throwable;
|
|
|
|
class RefreshSocialToken implements ShouldQueue
|
|
{
|
|
use Queueable;
|
|
|
|
public int $tries = 1;
|
|
|
|
public function __construct(public SocialAccount $account) {}
|
|
|
|
public function handle(ConnectionVerifier $verifier): void
|
|
{
|
|
try {
|
|
$verifier->refreshToken($this->account);
|
|
} catch (PlatformUnavailableException $e) {
|
|
// Platform is down (5xx / network). Leave the account alone —
|
|
// next scheduled run will try again. Critically, do NOT mark
|
|
// the account expired: that would trigger a false-positive
|
|
// "reconnect your account" notification.
|
|
Log::warning('Token refresh skipped: platform unavailable', [
|
|
'account_id' => $this->account->id,
|
|
'platform' => $this->account->platform->value,
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
} catch (TokenExpiredException $e) {
|
|
// refresh_token rejected by the provider (revoked / rotated /
|
|
// expired beyond refresh). Mark the account so the user is
|
|
// notified immediately instead of waiting for the next failed
|
|
// publish or the daily verify pass.
|
|
$this->account->markAsTokenExpired($e->getMessage());
|
|
} catch (Throwable $e) {
|
|
Log::warning('Proactive token refresh failed', [
|
|
'account_id' => $this->account->id,
|
|
'platform' => $this->account->platform->value,
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
}
|
|
}
|
|
}
|