fix(social): move Mastodon default instance to config + cleanup
Review follow-ups:
- verifyMastodon was the last hardcoded host left after the PR moved
LinkedIn/YouTube/Bluesky to config. Adds trypost.platforms.mastodon
.default_instance (env MASTODON_DEFAULT_INSTANCE) and reads from it.
- refreshToken() docblock now declares @throws PlatformUnavailableException
(the whole point of the PR was missing from its contract).
- Strip the new explanatory comments inside catch blocks and tests —
rationale lives in the commit / PR, not inline. The two comments
inside empty `catch (TokenExpiredException) {}` blocks stay because
there the comment is the only thing telling the reader why the
exception is swallowed.
This commit is contained in:
parent
6f96d67dbc
commit
d5e28e3d02
7 changed files with 6 additions and 22 deletions
|
|
@ -26,20 +26,12 @@ 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', [
|
||||
|
|
|
|||
|
|
@ -66,8 +66,6 @@ private function verifyAccount(ConnectionVerifier $verifier, SocialAccount $acco
|
|||
|
||||
return true;
|
||||
} catch (PlatformUnavailableException $e) {
|
||||
// Platform is down (5xx / network). The account's token is not
|
||||
// provably bad — skip silently and try again next pass.
|
||||
Log::warning('Social account verification skipped: platform unavailable', [
|
||||
'account_id' => $account->id,
|
||||
'platform' => $account->platform->value,
|
||||
|
|
|
|||
|
|
@ -28,9 +28,6 @@ public function verify(SocialAccount $account): bool
|
|||
// LinkedIn, etc.) invalidate the previous refresh_token on each
|
||||
// refresh, so proactive refreshes during races cause false-positive
|
||||
// disconnects even though the access_token still works fine.
|
||||
//
|
||||
// PlatformUnavailableException from refresh propagates naturally so
|
||||
// the caller can distinguish "platform is down" from "token bad".
|
||||
if ($account->is_token_expired) {
|
||||
$this->refreshToken($account);
|
||||
|
||||
|
|
@ -76,9 +73,10 @@ private function callVerifyEndpoint(SocialAccount $account): bool
|
|||
* Refresh the account's token via the platform-specific OAuth flow.
|
||||
* Callers that want the smart "try access_token first" behavior should
|
||||
* use verify() instead. This method always attempts a refresh under
|
||||
* the per-account lock and throws TokenExpiredException on failure.
|
||||
* the per-account lock.
|
||||
*
|
||||
* @throws TokenExpiredException if refresh fails
|
||||
* @throws TokenExpiredException if refresh is rejected by the provider (4xx)
|
||||
* @throws PlatformUnavailableException if the platform is unreachable (5xx / network)
|
||||
*/
|
||||
public function refreshToken(SocialAccount $account): void
|
||||
{
|
||||
|
|
@ -168,9 +166,6 @@ private function refreshBlueskyToken(SocialAccount $account): void
|
|||
$service = $account->meta['service'] ?? config('trypost.platforms.bluesky.default_service');
|
||||
$client = TokenRefreshClient::for(Platform::Bluesky);
|
||||
|
||||
// Try refresh token first. Connection errors / 5xx surface as
|
||||
// PlatformUnavailableException (Bluesky is down — don't touch the
|
||||
// account's status). 4xx falls through to the re-auth fallback.
|
||||
try {
|
||||
$response = $client->send(fn () => Http::withToken($account->refresh_token)
|
||||
->post("{$service}/xrpc/com.atproto.server.refreshSession"));
|
||||
|
|
@ -512,7 +507,7 @@ private function verifyBluesky(SocialAccount $account): bool
|
|||
|
||||
private function verifyMastodon(SocialAccount $account): bool
|
||||
{
|
||||
$instance = $account->meta['instance'] ?? 'https://mastodon.social';
|
||||
$instance = $account->meta['instance'] ?? config('trypost.platforms.mastodon.default_instance');
|
||||
|
||||
$response = Http::withToken($account->access_token)
|
||||
->get("{$instance}/api/v1/accounts/verify_credentials");
|
||||
|
|
|
|||
|
|
@ -116,6 +116,8 @@
|
|||
],
|
||||
'mastodon' => [
|
||||
'enabled' => env('MASTODON_ENABLED', true),
|
||||
// Default instance used when the account has no `meta.instance` override.
|
||||
'default_instance' => env('MASTODON_DEFAULT_INSTANCE', 'https://mastodon.social'),
|
||||
],
|
||||
],
|
||||
|
||||
|
|
|
|||
|
|
@ -86,7 +86,6 @@
|
|||
|
||||
(new RefreshSocialToken($this->account))->handle($verifier);
|
||||
|
||||
// Critically: account status stays Connected, no notification dispatched.
|
||||
expect($this->account->fresh()->status)->toBe(Status::Connected);
|
||||
Queue::assertNotPushed(SendNotification::class);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -411,7 +411,6 @@
|
|||
|
||||
test('bluesky 5xx during refresh raises PlatformUnavailable even when password fallback is stored', function () {
|
||||
Http::fake([
|
||||
// Both endpoints return 5xx — the platform is genuinely down.
|
||||
'bsky.social/xrpc/com.atproto.server.refreshSession' => Http::response('upstream timeout', 503),
|
||||
'bsky.social/xrpc/com.atproto.server.createSession' => Http::response('upstream timeout', 503),
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -137,7 +137,6 @@
|
|||
|
||||
VerifyWorkspaceConnections::dispatch($workspace);
|
||||
|
||||
// Status untouched, no notification email.
|
||||
expect($account->fresh()->status)->toBe(Status::Connected);
|
||||
Mail::assertNothingQueued();
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue