Skip to content

Commit dcf40be

Browse files
committed
copilot + oauth name comflict
1 parent 77bb048 commit dcf40be

2 files changed

Lines changed: 232 additions & 84 deletions

File tree

apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/credentials/credentials-manager.tsx

Lines changed: 77 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import { createElement, useEffect, useMemo, useState } from 'react'
44
import { createLogger } from '@sim/logger'
5-
import { Plus, Search, Share2, Trash2 } from 'lucide-react'
5+
import { AlertTriangle, Plus, Search, Share2, Trash2 } from 'lucide-react'
66
import { useParams } from 'next/navigation'
77
import {
88
Badge,
@@ -297,7 +297,7 @@ export function CredentialsManager() {
297297
[createSecretScope]
298298
)
299299
const selectedExistingEnvCredential = useMemo(() => {
300-
if (createType !== 'secret') return null
300+
if (createType !== 'secret' || createSecretInputMode !== 'single') return null
301301
const envKey = normalizeEnvKeyInput(createEnvKey)
302302
if (!envKey) return null
303303
return (
@@ -306,7 +306,31 @@ export function CredentialsManager() {
306306
row.type === createSecretType && (row.envKey || '').toLowerCase() === envKey.toLowerCase()
307307
) ?? null
308308
)
309-
}, [credentials, createEnvKey, createSecretType, createType])
309+
}, [credentials, createEnvKey, createSecretType, createType, createSecretInputMode])
310+
311+
const crossScopeEnvConflict = useMemo(() => {
312+
if (createType !== 'secret' || createSecretInputMode !== 'single') return null
313+
if (createSecretScope !== 'personal') return null
314+
const envKey = normalizeEnvKeyInput(createEnvKey)
315+
if (!envKey) return null
316+
return (
317+
credentials.find(
318+
(row) =>
319+
row.type === 'env_workspace' && (row.envKey || '').toLowerCase() === envKey.toLowerCase()
320+
) ?? null
321+
)
322+
}, [credentials, createEnvKey, createSecretScope, createType, createSecretInputMode])
323+
324+
const existingOAuthDisplayName = useMemo(() => {
325+
if (createType !== 'oauth') return null
326+
const name = createDisplayName.trim()
327+
if (!name) return null
328+
return (
329+
credentials.find(
330+
(row) => row.type === 'oauth' && row.displayName.toLowerCase() === name.toLowerCase()
331+
) ?? null
332+
)
333+
}, [credentials, createDisplayName, createType])
310334
const selectedEnvCurrentValue = useMemo(() => {
311335
if (!selectedCredential || selectedCredential.type === 'oauth') return ''
312336
const envKey = selectedCredential.envKey || ''
@@ -1309,6 +1333,20 @@ export function CredentialsManager() {
13091333
/>
13101334
</div>
13111335
</div>
1336+
{existingOAuthDisplayName && (
1337+
<div className='rounded-[8px] border border-red-500/50 bg-red-50 p-[12px] dark:bg-red-950/30'>
1338+
<div className='flex items-start gap-[10px]'>
1339+
<AlertTriangle className='mt-[1px] h-4 w-4 flex-shrink-0 text-red-600 dark:text-red-400' />
1340+
<p className='text-[12px] text-red-700 dark:text-red-300'>
1341+
A credential named{' '}
1342+
<span className='font-medium'>
1343+
{existingOAuthDisplayName.displayName}
1344+
</span>{' '}
1345+
already exists.
1346+
</p>
1347+
</div>
1348+
</div>
1349+
)}
13121350
</div>
13131351
) : (
13141352
<div className='flex flex-col gap-[10px]'>
@@ -1411,28 +1449,29 @@ export function CredentialsManager() {
14111449
</div>
14121450

14131451
{selectedExistingEnvCredential && (
1414-
<div className='rounded-[8px] border border-[var(--brand-9)]/40 bg-[var(--surface-3)] px-[10px] py-[8px]'>
1415-
<p className='text-[12px] text-[var(--text-primary)]'>
1416-
This secret key already maps to credential{' '}
1417-
<span className='font-medium'>
1418-
{selectedExistingEnvCredential.displayName}
1419-
</span>
1420-
.
1421-
</p>
1422-
<p className='mt-[4px] text-[11px] text-[var(--text-tertiary)]'>
1423-
Create will update the secret value and reuse the existing credential.
1424-
</p>
1425-
<Button
1426-
variant='ghost'
1427-
className='mt-[6px]'
1428-
onClick={() => {
1429-
setSelectedCredentialId(selectedExistingEnvCredential.id)
1430-
setShowCreateModal(false)
1431-
resetCreateForm()
1432-
}}
1433-
>
1434-
Open existing credential
1435-
</Button>
1452+
<div className='rounded-[8px] border border-red-500/50 bg-red-50 p-[12px] dark:bg-red-950/30'>
1453+
<div className='flex items-start gap-[10px]'>
1454+
<AlertTriangle className='mt-[1px] h-4 w-4 flex-shrink-0 text-red-600 dark:text-red-400' />
1455+
<p className='text-[12px] text-red-700 dark:text-red-300'>
1456+
A secret with key{' '}
1457+
<span className='font-medium'>
1458+
{selectedExistingEnvCredential.displayName}
1459+
</span>{' '}
1460+
already exists.
1461+
</p>
1462+
</div>
1463+
</div>
1464+
)}
1465+
{!selectedExistingEnvCredential && crossScopeEnvConflict && (
1466+
<div className='rounded-[8px] border border-amber-500/50 bg-amber-50 p-[12px] dark:bg-amber-950/30'>
1467+
<div className='flex items-start gap-[10px]'>
1468+
<AlertTriangle className='mt-[1px] h-4 w-4 flex-shrink-0 text-amber-600 dark:text-amber-400' />
1469+
<p className='text-[12px] text-amber-700 dark:text-amber-300'>
1470+
A workspace secret with key{' '}
1471+
<span className='font-medium'>{crossScopeEnvConflict.envKey}</span>{' '}
1472+
already exists. Workspace secrets take precedence at runtime.
1473+
</p>
1474+
</div>
14361475
</div>
14371476
)}
14381477
</>
@@ -1471,8 +1510,13 @@ export function CredentialsManager() {
14711510
)}
14721511

14731512
{createError && (
1474-
<div className='whitespace-pre-wrap rounded-[8px] border border-[var(--status-red)]/40 bg-[var(--status-red)]/10 px-[10px] py-[8px] text-[12px] text-[var(--status-red)]'>
1475-
{createError}
1513+
<div className='rounded-[8px] border border-red-500/50 bg-red-50 p-[12px] dark:bg-red-950/30'>
1514+
<div className='flex items-start gap-[10px]'>
1515+
<AlertTriangle className='mt-[1px] h-4 w-4 flex-shrink-0 text-red-600 dark:text-red-400' />
1516+
<p className='whitespace-pre-wrap text-[12px] text-red-700 dark:text-red-300'>
1517+
{createError}
1518+
</p>
1519+
</div>
14761520
</div>
14771521
)}
14781522
</div>
@@ -1488,10 +1532,13 @@ export function CredentialsManager() {
14881532
(createType === 'oauth'
14891533
? !createOAuthProviderId ||
14901534
!createDisplayName.trim() ||
1491-
connectOAuthService.isPending
1535+
connectOAuthService.isPending ||
1536+
Boolean(existingOAuthDisplayName)
14921537
: createSecretInputMode === 'bulk'
14931538
? !createBulkText.trim()
1494-
: !createEnvKey.trim() || !createEnvValue.trim()) ||
1539+
: !createEnvKey.trim() ||
1540+
!createEnvValue.trim() ||
1541+
Boolean(selectedExistingEnvCredential)) ||
14951542
createCredential.isPending ||
14961543
savePersonalEnvironment.isPending ||
14971544
upsertWorkspaceEnvironment.isPending ||
@@ -1508,9 +1555,7 @@ export function CredentialsManager() {
15081555
upsertWorkspaceEnvironment.isPending
15091556
? 'Importing...'
15101557
: 'Import all'
1511-
: selectedExistingEnvCredential
1512-
? 'Update and use existing'
1513-
: 'Create'}
1558+
: 'Create'}
15141559
</Button>
15151560
</ModalFooter>
15161561
</ModalContent>

0 commit comments

Comments
 (0)