Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/sim/app/workspace/[workspaceId]/knowledge/[id]/base.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ import {
useUpdateKnowledgeBase,
} from '@/hooks/queries/kb/knowledge'
import { useInlineRename } from '@/hooks/use-inline-rename'
import { useOAuthReturnForKBConnectors } from '@/hooks/use-oauth-return'

const logger = createLogger('KnowledgeBase')

Expand Down Expand Up @@ -189,6 +190,7 @@ export function KnowledgeBase({
}: KnowledgeBaseProps) {
const params = useParams()
const workspaceId = propWorkspaceId || (params.workspaceId as string)
useOAuthReturnForKBConnectors(id)
const { removeKnowledgeBase } = useKnowledgeBasesList(workspaceId, { enabled: false })
const userPermissions = useUserPermissionsContext()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
Tooltip,
} from '@/components/emcn'
import { useSession } from '@/lib/auth/auth-client'
import { consumeOAuthReturnContext, writeOAuthReturnContext } from '@/lib/credentials/client-state'
import {
getCanonicalScopesForProvider,
getProviderIdFromServiceId,
Expand Down Expand Up @@ -288,8 +289,25 @@ export function AddConnectorModal({ open, onOpenChange, knowledgeBaseId }: AddCo
return
}

writeOAuthReturnContext({
origin: 'kb-connectors',
knowledgeBaseId,
displayName,
providerId: connectorProviderId,
preCount: credentials.length,
workspaceId,
requestedAt: Date.now(),
})

setShowOAuthModal(true)
}, [connectorConfig, connectorProviderId, workspaceId, session?.user?.name])
}, [
connectorConfig,
connectorProviderId,
workspaceId,
session?.user?.name,
knowledgeBaseId,
credentials.length,
])

const filteredEntries = useMemo(() => {
const term = searchTerm.toLowerCase().trim()
Expand Down Expand Up @@ -575,11 +593,14 @@ export function AddConnectorModal({ open, onOpenChange, knowledgeBaseId }: AddCo
{connectorConfig && connectorConfig.auth.mode === 'oauth' && connectorProviderId && (
<OAuthRequiredModal
isOpen={showOAuthModal}
onClose={() => setShowOAuthModal(false)}
onClose={() => {
consumeOAuthReturnContext()
setShowOAuthModal(false)
}}
provider={connectorProviderId}
toolName={connectorConfig.name}
requiredScopes={getCanonicalScopesForProvider(connectorProviderId)}
newScopes={connectorConfig.auth.requiredScopes || []}
newScopes={[]}
serviceId={connectorConfig.auth.provider}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
Tooltip,
} from '@/components/emcn'
import { cn } from '@/lib/core/utils/cn'
import { consumeOAuthReturnContext, writeOAuthReturnContext } from '@/lib/credentials/client-state'
import {
getCanonicalScopesForProvider,
getProviderIdFromServiceId,
Expand Down Expand Up @@ -444,7 +445,18 @@ function ConnectorCard({
{canEdit && (
<Button
variant='active'
onClick={() => setShowOAuthModal(true)}
onClick={() => {
writeOAuthReturnContext({
origin: 'kb-connectors',
knowledgeBaseId,
displayName: connectorDef?.name ?? connector.connectorType,
providerId: providerId!,
preCount: credentials?.length ?? 0,
workspaceId,
requestedAt: Date.now(),
})
setShowOAuthModal(true)
}}
className='w-full px-[8px] py-[4px] font-medium text-[12px]'
>
Update access
Expand All @@ -463,7 +475,10 @@ function ConnectorCard({
{showOAuthModal && serviceId && providerId && (
<OAuthRequiredModal
isOpen={showOAuthModal}
onClose={() => setShowOAuthModal(false)}
onClose={() => {
consumeOAuthReturnContext()
setShowOAuthModal(false)
}}
provider={providerId as OAuthProvider}
toolName={connectorDef?.name ?? connector.connectorType}
requiredScopes={getCanonicalScopesForProvider(providerId)}
Expand Down
5 changes: 3 additions & 2 deletions apps/sim/app/workspace/[workspaceId]/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use client'

import { ToastProvider } from '@/components/emcn'
import { GlobalCommandsProvider } from '@/app/workspace/[workspaceId]/providers/global-commands-provider'
import { ProviderModelsLoader } from '@/app/workspace/[workspaceId]/providers/provider-models-loader'
import { SettingsLoader } from '@/app/workspace/[workspaceId]/providers/settings-loader'
Expand All @@ -8,7 +9,7 @@ import { Sidebar } from '@/app/workspace/[workspaceId]/w/components/sidebar/side

export default function WorkspaceLayout({ children }: { children: React.ReactNode }) {
return (
<>
<ToastProvider>
<SettingsLoader />
<ProviderModelsLoader />
<GlobalCommandsProvider>
Expand All @@ -25,6 +26,6 @@ export default function WorkspaceLayout({ children }: { children: React.ReactNod
</WorkspacePermissionsProvider>
</div>
</GlobalCommandsProvider>
</>
</ToastProvider>
)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client'

import { createElement, useCallback, useEffect, useMemo, useState } from 'react'
import { createElement, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { createLogger } from '@sim/logger'
import { AlertTriangle, Check, Clipboard, Plus, Search, Share2 } from 'lucide-react'
import { useParams } from 'next/navigation'
Expand Down Expand Up @@ -28,6 +28,7 @@ import {
PENDING_CREDENTIAL_CREATE_REQUEST_EVENT,
type PendingCredentialCreateRequest,
readPendingCredentialCreateRequest,
writeOAuthReturnContext,
} from '@/lib/credentials/client-state'
import {
getCanonicalScopesForProvider,
Expand All @@ -54,6 +55,7 @@ import {
useOAuthConnections,
} from '@/hooks/queries/oauth/oauth-connections'
import { useWorkspacePermissionsQuery } from '@/hooks/queries/workspace'
import { useOAuthReturnRouter } from '@/hooks/use-oauth-return'

const logger = createLogger('IntegrationsManager')

Expand All @@ -66,6 +68,8 @@ export function IntegrationsManager() {
const params = useParams()
const workspaceId = (params?.workspaceId as string) || ''

useOAuthReturnRouter()

const [searchTerm, setSearchTerm] = useState('')
const [selectedCredentialId, setSelectedCredentialId] = useState<string | null>(null)
const [memberRole, setMemberRole] = useState<WorkspaceCredentialRole>('admin')
Expand All @@ -84,6 +88,11 @@ export function IntegrationsManager() {
const [showDeleteConfirmDialog, setShowDeleteConfirmDialog] = useState(false)
const [deleteError, setDeleteError] = useState<string | null>(null)
const [showUnsavedChangesAlert, setShowUnsavedChangesAlert] = useState(false)
const pendingReturnOriginRef = useRef<
| { type: 'workflow'; workflowId: string }
| { type: 'kb-connectors'; knowledgeBaseId: string }
| undefined
>(undefined)
const { data: session } = useSession()
const currentUserId = session?.user?.id || ''

Expand Down Expand Up @@ -278,6 +287,8 @@ export function IntegrationsManager() {

if (request.type !== 'oauth') return

pendingReturnOriginRef.current = request.returnOrigin

setShowCreateModal(true)
setShowCreateOAuthRequiredModal(false)
setCreateError(null)
Expand Down Expand Up @@ -350,6 +361,7 @@ export function IntegrationsManager() {
setCreateOAuthProviderId('')
setCreateError(null)
setShowCreateOAuthRequiredModal(false)
pendingReturnOriginRef.current = undefined
}

const handleSelectCredential = (credential: WorkspaceCredential) => {
Expand Down Expand Up @@ -397,15 +409,42 @@ export function IntegrationsManager() {
}),
})

window.sessionStorage.setItem(
'sim.oauth-connect-pending',
JSON.stringify({
const oauthPreCount = credentials.filter(
(c) => c.type === 'oauth' && c.providerId === selectedOAuthService.providerId
).length
const returnOrigin = pendingReturnOriginRef.current
pendingReturnOriginRef.current = undefined

if (returnOrigin?.type === 'workflow') {
writeOAuthReturnContext({
origin: 'workflow',
workflowId: returnOrigin.workflowId,
displayName,
providerId: selectedOAuthService.providerId,
preCount: credentials.filter((c) => c.type === 'oauth').length,
preCount: oauthPreCount,
workspaceId,
requestedAt: Date.now(),
})
)
} else if (returnOrigin?.type === 'kb-connectors') {
writeOAuthReturnContext({
origin: 'kb-connectors',
knowledgeBaseId: returnOrigin.knowledgeBaseId,
displayName,
providerId: selectedOAuthService.providerId,
preCount: oauthPreCount,
workspaceId,
requestedAt: Date.now(),
})
} else {
writeOAuthReturnContext({
origin: 'integrations',
displayName,
providerId: selectedOAuthService.providerId,
preCount: oauthPreCount,
workspaceId,
requestedAt: Date.now(),
})
}

await connectOAuthService.mutateAsync({
providerId: selectedOAuthService.providerId,
Expand Down Expand Up @@ -512,16 +551,18 @@ export function IntegrationsManager() {
}),
})

window.sessionStorage.setItem(
'sim.oauth-connect-pending',
JSON.stringify({
displayName: selectedCredential.displayName,
providerId: selectedCredential.providerId,
preCount: credentials.filter((c) => c.type === 'oauth').length,
workspaceId,
reconnect: true,
})
)
const oauthPreCount = credentials.filter(
(c) => c.type === 'oauth' && c.providerId === selectedCredential.providerId
).length
writeOAuthReturnContext({
origin: 'integrations',
displayName: selectedCredential.displayName,
providerId: selectedCredential.providerId,
preCount: oauthPreCount,
workspaceId,
reconnect: true,
requestedAt: Date.now(),
})

await connectOAuthService.mutateAsync({
providerId: selectedCredential.providerId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,10 +207,13 @@ export function CredentialSelector({
serviceId,
requiredScopes: getCanonicalScopesForProvider(effectiveProviderId),
requestedAt: Date.now(),
returnOrigin: activeWorkflowId
? { type: 'workflow', workflowId: activeWorkflowId }
: undefined,
})

navigateToSettings({ section: 'integrations' })
}, [workspaceId, effectiveProviderId, serviceId])
}, [workspaceId, effectiveProviderId, serviceId, activeWorkflowId])

const getProviderIcon = useCallback((providerName: OAuthProvider) => {
const { baseProvider } = parseProvider(providerName)
Expand Down
64 changes: 2 additions & 62 deletions apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ import { useWorkspaceEnvironment } from '@/hooks/queries/environment'
import { useAutoConnect, useSnapToGridSize } from '@/hooks/queries/general-settings'
import { useCanvasViewport } from '@/hooks/use-canvas-viewport'
import { useCollaborativeWorkflow } from '@/hooks/use-collaborative-workflow'
import { useOAuthReturnForWorkflow } from '@/hooks/use-oauth-return'
import { useStreamCleanup } from '@/hooks/use-stream-cleanup'
import { useCanvasModeStore } from '@/stores/canvas-mode'
import { useChatStore } from '@/stores/chat/store'
Expand Down Expand Up @@ -268,68 +269,7 @@ const WorkflowContent = React.memo(

const addNotification = useNotificationStore((state) => state.addNotification)

useEffect(() => {
const OAUTH_CONNECT_PENDING_KEY = 'sim.oauth-connect-pending'
const pending = window.sessionStorage.getItem(OAUTH_CONNECT_PENDING_KEY)
if (!pending) return
window.sessionStorage.removeItem(OAUTH_CONNECT_PENDING_KEY)

;(async () => {
try {
const {
displayName,
providerId,
preCount,
workspaceId: wsId,
reconnect,
} = JSON.parse(pending) as {
displayName: string
providerId: string
preCount: number
workspaceId: string
reconnect?: boolean
}

if (reconnect) {
addNotification({
level: 'info',
message: `"${displayName}" reconnected successfully.`,
})
window.dispatchEvent(
new CustomEvent('oauth-credentials-updated', {
detail: { providerId, workspaceId: wsId },
})
)
return
}

const response = await fetch(
`/api/credentials?workspaceId=${encodeURIComponent(wsId)}&type=oauth`
)
const data = response.ok ? await response.json() : { credentials: [] }
const oauthCredentials = (data.credentials ?? []) as Array<{
displayName: string
providerId: string | null
}>

if (oauthCredentials.length > preCount) {
addNotification({
level: 'info',
message: `"${displayName}" credential connected successfully.`,
})
} else {
const existing = oauthCredentials.find((c) => c.providerId === providerId)
const existingName = existing?.displayName || displayName
addNotification({
level: 'info',
message: `This account is already connected as "${existingName}".`,
})
}
} catch {
// Ignore malformed sessionStorage data
}
})()
}, [])
useOAuthReturnForWorkflow(workflowIdParam)

const {
workflows,
Expand Down
Loading
Loading