Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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 @@ -397,15 +408,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 +550,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