diff --git a/apps/sim/app/api/auth/oauth/utils.ts b/apps/sim/app/api/auth/oauth/utils.ts index feade337e8..7a09891b57 100644 --- a/apps/sim/app/api/auth/oauth/utils.ts +++ b/apps/sim/app/api/auth/oauth/utils.ts @@ -278,7 +278,22 @@ export async function refreshTokenIfNeeded( logger.info(`[${requestId}] Successfully refreshed access token`) return { accessToken: refreshedToken, refreshed: true } } catch (error) { - logger.error(`[${requestId}] Error refreshing token`, error) + logger.warn( + `[${requestId}] Refresh attempt failed, checking if another concurrent request succeeded` + ) + + const freshCredential = await getCredential(requestId, credentialId, credential.userId) + if (freshCredential?.accessToken) { + const freshExpiresAt = freshCredential.accessTokenExpiresAt + const stillValid = !freshExpiresAt || freshExpiresAt > new Date() + + if (stillValid) { + logger.info(`[${requestId}] Found valid token from concurrent refresh, using it`) + return { accessToken: freshCredential.accessToken, refreshed: true } + } + } + + logger.error(`[${requestId}] Refresh failed and no valid token found in DB`, error) throw error } } diff --git a/apps/sim/app/changelog.xml/route.ts b/apps/sim/app/changelog.xml/route.ts index 7b8f2dab00..5e0752056d 100644 --- a/apps/sim/app/changelog.xml/route.ts +++ b/apps/sim/app/changelog.xml/route.ts @@ -48,7 +48,7 @@ export async function GET() { Sim Changelog - https://sim.dev/changelog + https://sim.ai/changelog Latest changes, fixes and updates in Sim. en-us ${items} diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/channel-selector/channel-selector-input.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/channel-selector/channel-selector-input.tsx index 5fe0441624..f91daaaf00 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/channel-selector/channel-selector-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/channel-selector/channel-selector-input.tsx @@ -19,6 +19,7 @@ interface ChannelSelectorInputProps { onChannelSelect?: (channelId: string) => void isPreview?: boolean previewValue?: any | null + previewContextValues?: Record } export function ChannelSelectorInput({ @@ -28,15 +29,18 @@ export function ChannelSelectorInput({ onChannelSelect, isPreview = false, previewValue, + previewContextValues, }: ChannelSelectorInputProps) { const params = useParams() const workflowIdFromUrl = (params?.workflowId as string) || '' - // Use the proper hook to get the current value and setter (same as file-selector) const [storeValue, setStoreValue] = useSubBlockValue(blockId, subBlock.id) - // Reactive upstream fields const [authMethod] = useSubBlockValue(blockId, 'authMethod') const [botToken] = useSubBlockValue(blockId, 'botToken') const [connectedCredential] = useSubBlockValue(blockId, 'credential') + + const effectiveAuthMethod = previewContextValues?.authMethod ?? authMethod + const effectiveBotToken = previewContextValues?.botToken ?? botToken + const effectiveCredential = previewContextValues?.credential ?? connectedCredential const [selectedChannelId, setSelectedChannelId] = useState('') const [_channelInfo, setChannelInfo] = useState(null) @@ -49,16 +53,16 @@ export function ChannelSelectorInput({ isPreview, }) - // Choose credential strictly based on auth method + // Choose credential strictly based on auth method - use effective values const credential: string = - (authMethod as string) === 'bot_token' - ? (botToken as string) || '' - : (connectedCredential as string) || '' + (effectiveAuthMethod as string) === 'bot_token' + ? (effectiveBotToken as string) || '' + : (effectiveCredential as string) || '' // Determine if connected OAuth credential is foreign (not applicable for bot tokens) const { isForeignCredential } = useForeignCredential( 'slack', - (authMethod as string) === 'bot_token' ? '' : (connectedCredential as string) || '' + (effectiveAuthMethod as string) === 'bot_token' ? '' : (effectiveCredential as string) || '' ) // Get the current value from the store or prop value if in preview mode (same pattern as file-selector) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/tool-input/tool-input.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/tool-input/tool-input.tsx index 9eef0a2043..d2c5871362 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/tool-input/tool-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/tool-input/tool-input.tsx @@ -1,5 +1,6 @@ -import React, { useCallback, useEffect, useState } from 'react' -import { AlertCircle, PlusIcon, Server, WrenchIcon, XIcon } from 'lucide-react' +import type React from 'react' +import { useCallback, useEffect, useState } from 'react' +import { PlusIcon, Server, WrenchIcon, XIcon } from 'lucide-react' import { useParams } from 'next/navigation' import { Button } from '@/components/ui/button' import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover' @@ -374,42 +375,40 @@ function FileUploadSyncWrapper({ ) } -// Error boundary component for tool input -class ToolInputErrorBoundary extends React.Component< - { children: React.ReactNode; blockName?: string }, - { hasError: boolean; error?: Error } -> { - constructor(props: any) { - super(props) - this.state = { hasError: false } - } - - static getDerivedStateFromError(error: Error) { - return { hasError: true, error } - } - - componentDidCatch(error: Error, info: React.ErrorInfo) { - console.error('ToolInput error:', error, info) - } - - render() { - if (this.state.hasError) { - return ( -
-
- - Tool Configuration Error -
-

- {this.props.blockName ? `Block "${this.props.blockName}": ` : ''} - Invalid tool reference. Please check the workflow configuration. -

-
- ) - } - - return this.props.children - } +function ChannelSelectorSyncWrapper({ + blockId, + paramId, + value, + onChange, + uiComponent, + disabled, + previewContextValues, +}: { + blockId: string + paramId: string + value: string + onChange: (value: string) => void + uiComponent: any + disabled: boolean + previewContextValues?: Record +}) { + return ( + + + + ) } export function ToolInput({ @@ -1060,19 +1059,14 @@ export function ToolInput({ case 'channel-selector': return ( - ) diff --git a/apps/sim/lib/oauth/oauth.ts b/apps/sim/lib/oauth/oauth.ts index 803100b752..a253d02b71 100644 --- a/apps/sim/lib/oauth/oauth.ts +++ b/apps/sim/lib/oauth/oauth.ts @@ -858,6 +858,7 @@ function getProviderAuthConfig(provider: string): ProviderAuthConfig { clientId, clientSecret, useBasicAuth: false, + supportsRefreshTokenRotation: true, } } case 'reddit': {