diff --git a/app/Console/Commands/RefreshExpiringTokens.php b/app/Console/Commands/RefreshExpiringTokens.php index 38b6b689..9dc08b66 100644 --- a/app/Console/Commands/RefreshExpiringTokens.php +++ b/app/Console/Commands/RefreshExpiringTokens.php @@ -32,10 +32,10 @@ public function handle(): void ->whereNotNull('token_expires_at') ->where(function (Builder $query) { $query->where(function (Builder $extension) { - $extension->whereIn('platform', Platform::extensionModelValues()) + $extension->whereIn('platform', Platform::accessTokenExtendingPlatformValues()) ->where('token_expires_at', '<=', now()->addDay()); })->orWhere(function (Builder $rotating) { - $rotating->whereNotIn('platform', Platform::extensionModelValues()) + $rotating->whereNotIn('platform', Platform::accessTokenExtendingPlatformValues()) ->where('token_expires_at', '<=', now()->addMinutes(30)); }); }) diff --git a/app/Enums/SocialAccount/Platform.php b/app/Enums/SocialAccount/Platform.php index 59e7115a..808f865b 100644 --- a/app/Enums/SocialAccount/Platform.php +++ b/app/Enums/SocialAccount/Platform.php @@ -23,6 +23,13 @@ enum Platform: string case Telegram = 'telegram'; case Discord = 'discord'; + /** + * Fallback lifetime, in seconds, for Meta long-lived tokens (Instagram and + * Threads — see extendsAccessTokenOnRefresh) when the provider response + * omits expires_in. Meta issues these tokens for 60 days. + */ + public const LONG_LIVED_TOKEN_TTL_SECONDS = 5184000; + /** * The social network this platform belongs to. Variants that represent the * same network (LinkedIn profile vs. company page, Instagram standalone vs. @@ -278,14 +285,14 @@ public function extendsAccessTokenOnRefresh(): bool } /** - * All platform values that refresh by extending the access_token itself - * (see extendsAccessTokenOnRefresh). Because these can't be refreshed once - * expired, the proactive-refresh cron gives them a wider window than - * rotating platforms so a queue backlog can't let them lapse. + * The `platform` column values of the platforms that refresh by extending + * their access token in place (Instagram and Threads — see + * extendsAccessTokenOnRefresh), for use in database whereIn/whereNotIn + * filters. Derived from extendsAccessTokenOnRefresh() so the two never drift. * * @return array */ - public static function extensionModelValues(): array + public static function accessTokenExtendingPlatformValues(): array { return array_values(array_map( fn (self $platform): string => $platform->value, diff --git a/app/Http/Controllers/Auth/InstagramController.php b/app/Http/Controllers/Auth/InstagramController.php index 79931649..af48218c 100644 --- a/app/Http/Controllers/Auth/InstagramController.php +++ b/app/Http/Controllers/Auth/InstagramController.php @@ -69,7 +69,7 @@ public function callback(Request $request): InertiaResponse $avatarPath = $socialUser->getAvatar() ? uploadFromUrl($socialUser->getAvatar()) : null; // Calculate token expiration (long-lived tokens last 60 days) - $expiresIn = $socialUser->expiresIn ?? 5184000; // 60 days in seconds + $expiresIn = $socialUser->expiresIn ?? SocialPlatform::LONG_LIVED_TOKEN_TTL_SECONDS; $tokenExpiresAt = now()->addSeconds($expiresIn); $workspace->socialAccounts()->updateOrCreate( diff --git a/app/Http/Controllers/Auth/ThreadsController.php b/app/Http/Controllers/Auth/ThreadsController.php index f4ea63ff..6b29e568 100644 --- a/app/Http/Controllers/Auth/ThreadsController.php +++ b/app/Http/Controllers/Auth/ThreadsController.php @@ -116,7 +116,7 @@ public function callback(Request $request): InertiaResponse $longLivedData = $longLivedResponse->json(); $longLivedToken = $longLivedData['access_token'] ?? $shortLivedToken; - $expiresIn = $longLivedData['expires_in'] ?? 5184000; + $expiresIn = $longLivedData['expires_in'] ?? SocialPlatform::LONG_LIVED_TOKEN_TTL_SECONDS; // Fetch user profile $profileResponse = Http::get(config('trypost.platforms.threads.graph_api')."/{$userId}", [