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.
122 lines
4.7 KiB
PHP
122 lines
4.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
return [
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Self-Hosted Mode
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| When enabled, the application runs in self-hosted mode which skips
|
|
| payment/subscription requirements during onboarding.
|
|
|
|
|
*/
|
|
|
|
'self_hosted' => env('SELF_HOSTED', true),
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Media Size Limits
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| Per-type size caps in megabytes. Single source of truth — direct
|
|
| uploads (StoreAssetRequest, AssetController::storeChunked), URL
|
|
| fetches (MediaAttacher), and the MediaType enum all read from here.
|
|
|
|
|
*/
|
|
|
|
'media' => [
|
|
'max_size_mb' => [
|
|
'image' => (int) env('MEDIA_IMAGE_MAX_SIZE_MB', 10),
|
|
'video' => (int) env('MEDIA_VIDEO_MAX_SIZE_MB', 1024),
|
|
],
|
|
],
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Google Authentication
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| Enable or disable "Login with Google" on the login and register pages.
|
|
| Disable this if you don't have Google OAuth credentials configured.
|
|
|
|
|
*/
|
|
|
|
'google_auth_enabled' => env('GOOGLE_AUTH_ENABLED', false),
|
|
|
|
'github_auth_enabled' => env('GITHUB_AUTH_ENABLED', false),
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Social Platforms
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| Configure which social platforms are enabled in the application.
|
|
| Set to false to temporarily disable a platform (e.g., when credentials
|
|
| are revoked, expired, or pending approval).
|
|
|
|
|
*/
|
|
|
|
'platforms' => [
|
|
'linkedin' => [
|
|
'enabled' => env('LINKEDIN_ENABLED', true),
|
|
'api' => env('LINKEDIN_API', 'https://api.linkedin.com'),
|
|
// OAuth host is different from the data API (api.linkedin.com).
|
|
'oauth_api' => env('LINKEDIN_OAUTH_API', 'https://www.linkedin.com'),
|
|
],
|
|
'linkedin-page' => [
|
|
'enabled' => env('LINKEDIN_PAGE_ENABLED', true),
|
|
'api' => env('LINKEDIN_PAGE_API', 'https://api.linkedin.com'),
|
|
],
|
|
'x' => [
|
|
'enabled' => env('X_ENABLED', true),
|
|
'api' => env('X_API', 'https://api.x.com/2'),
|
|
],
|
|
'tiktok' => [
|
|
'enabled' => env('TIKTOK_ENABLED', true),
|
|
'api' => env('TIKTOK_API', 'https://open.tiktokapis.com/v2'),
|
|
],
|
|
'youtube' => [
|
|
'enabled' => env('YOUTUBE_ENABLED', true),
|
|
'data_api' => env('YOUTUBE_DATA_API', 'https://www.googleapis.com/youtube/v3'),
|
|
'analytics_api' => env('YOUTUBE_ANALYTICS_API', 'https://youtubeanalytics.googleapis.com/v2'),
|
|
'oauth_api' => env('YOUTUBE_OAUTH_API', 'https://oauth2.googleapis.com'),
|
|
],
|
|
'facebook' => [
|
|
'enabled' => env('FACEBOOK_ENABLED', true),
|
|
'graph_api' => env('FACEBOOK_GRAPH_API', 'https://graph.facebook.com/v25.0'),
|
|
],
|
|
'instagram' => [
|
|
'enabled' => env('INSTAGRAM_ENABLED', true),
|
|
'graph_api' => env('INSTAGRAM_GRAPH_API', 'https://graph.instagram.com/v25.0'),
|
|
// graph.instagram.com (no version) is the auth/refresh host.
|
|
'auth_api' => env('INSTAGRAM_AUTH_API', 'https://graph.instagram.com'),
|
|
],
|
|
'instagram-facebook' => [
|
|
'enabled' => env('INSTAGRAM_FACEBOOK_ENABLED', true),
|
|
'graph_api' => env('INSTAGRAM_FACEBOOK_GRAPH_API', 'https://graph.facebook.com/v25.0'),
|
|
],
|
|
'threads' => [
|
|
'enabled' => env('THREADS_ENABLED', true),
|
|
'graph_api' => env('THREADS_GRAPH_API', 'https://graph.threads.net/v1.0'),
|
|
// graph.threads.net (no version) is the auth/refresh host.
|
|
'auth_api' => env('THREADS_AUTH_API', 'https://graph.threads.net'),
|
|
],
|
|
'pinterest' => [
|
|
'enabled' => env('PINTEREST_ENABLED', true),
|
|
'api' => env('PINTEREST_API', 'https://api.pinterest.com/v5'),
|
|
],
|
|
'bluesky' => [
|
|
'enabled' => env('BLUESKY_ENABLED', true),
|
|
'public_appview' => env('BLUESKY_PUBLIC_APPVIEW', 'https://public.api.bsky.app'),
|
|
// Default PDS used when the account has no `meta.service` override.
|
|
'default_service' => env('BLUESKY_DEFAULT_SERVICE', 'https://bsky.social'),
|
|
],
|
|
'mastodon' => [
|
|
'enabled' => env('MASTODON_ENABLED', true),
|
|
],
|
|
],
|
|
|
|
];
|