Skip to content

Commit 496bcb0

Browse files
committed
fix(chat): preserve configured origin in deployment URLs
1 parent f951ea1 commit 496bcb0

4 files changed

Lines changed: 31 additions & 17 deletions

File tree

  • apps/sim
    • app
      • api/v2/workflows/[workflowId]/deployments/chat
      • workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/chat
    • lib/chat-deployments

apps/sim/app/api/v2/workflows/[workflowId]/deployments/chat/route.test.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@
44
import {
55
MockV2ApiKeyUnauthenticatedError,
66
resetDbChainMock,
7+
resetEnvMock,
8+
setEnv,
79
V2_OPERATION_RATE_LIMIT_ALLOWED,
810
V2_PREAUTH_RATE_LIMIT_ALLOWED,
911
v2ApiKeyAuthModuleMock,
1012
v2RateLimiterModuleMock,
1113
v2RouteMocks,
1214
} from '@sim/testing'
1315
import { NextRequest } from 'next/server'
14-
import { beforeEach, describe, expect, it, vi } from 'vitest'
16+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
1517
import { PermissionGroupCapabilityError } from '@/lib/permission-groups/capability-error'
1618

1719
const mocks = vi.hoisted(() => ({
@@ -149,6 +151,7 @@ function uniqueViolation(constraint: string) {
149151
const validBody = { identifier: 'support', title: 'Support chat' }
150152

151153
describe('/api/v2/workflows/[workflowId]/deployments/chat', () => {
154+
afterEach(resetEnvMock)
152155
beforeEach(() => {
153156
vi.clearAllMocks()
154157
resetDbChainMock()
@@ -250,6 +253,16 @@ describe('/api/v2/workflows/[workflowId]/deployments/chat', () => {
250253
})
251254

252255
describe('PUT', () => {
256+
it('returns the configured www chat URL to CLI callers on deploy and read', async () => {
257+
setEnv({ NEXT_PUBLIC_APP_URL: 'https://www.dev.sim.ai' })
258+
mocks.getLiveChatDeployment.mockResolvedValueOnce(null).mockResolvedValue(chatRow())
259+
260+
for (const response of [await put(validBody), await get()]) {
261+
expect(response.status).toBe(200)
262+
expect((await response.json()).data.url).toBe('https://www.dev.sim.ai/chat/support')
263+
}
264+
})
265+
253266
it('creates the chat when the workflow publishes none', async () => {
254267
mocks.getLiveChatDeployment.mockResolvedValueOnce(null).mockResolvedValue(chatRow())
255268

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/chat/chat.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ import { Check, TriangleAlert } from '@sim/emcn/icons'
2020
import { createLogger } from '@sim/logger'
2121
import { getErrorMessage } from '@sim/utils/errors'
2222
import { GeneratedPasswordInput } from '@/components/ui'
23+
import { buildChatDeploymentUrl } from '@/lib/chat-deployments/urls'
2324
import { useDeploymentShape } from '@/lib/core/config/deployment-shape'
24-
import { getBaseUrl, getEmailDomain } from '@/lib/core/utils/urls'
2525
import { validateAllowlistEntry } from '@/lib/messaging/email/validation'
2626
import { formatInternalOutputSelector } from '@/lib/workflows/streaming/output-selector'
2727
import { OutputSelect } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/chat/components/output-select/output-select'
@@ -497,7 +497,7 @@ export function ChatDeploy({
497497
{ text: existingChat?.title || 'this chat', bold: true },
498498
'? ',
499499
{
500-
text: `This will remove the chat at "${getEmailDomain()}/chat/${existingChat?.identifier ?? ''}" and make it unavailable to all users.`,
500+
text: `This will remove the chat at "${buildChatDeploymentUrl(existingChat?.identifier ?? '').replace(/^https?:\/\//, '')}" and make it unavailable to all users.`,
501501
error: true,
502502
},
503503
' This action cannot be undone.',
@@ -554,7 +554,7 @@ interface IdentifierInputProps {
554554
}
555555

556556
const getDomainPrefix = (() => {
557-
const prefix = `${getEmailDomain()}/chat/`
557+
const prefix = buildChatDeploymentUrl('').replace(/^https?:\/\//, '')
558558
return () => prefix
559559
})()
560560

@@ -581,7 +581,7 @@ function IdentifierInput({
581581
onChange(lowercaseValue)
582582
}
583583

584-
const fullUrl = `${getBaseUrl()}/chat/${value}`
584+
const fullUrl = buildChatDeploymentUrl(value)
585585
const displayUrl = fullUrl.replace(/^https?:\/\//, '')
586586

587587
return (

apps/sim/lib/chat-deployments/urls.test.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,16 @@ describe('buildChatDeploymentUrl', () => {
1717
expect(buildChatDeploymentUrl('support')).toBe('https://sim.ai/chat/support')
1818
})
1919

20-
it('strips the www prefix, because the deployed chat answers on the bare host', () => {
21-
setEnv({ NEXT_PUBLIC_APP_URL: 'https://www.sim.ai' })
20+
it.each([
21+
'https://www.dev.sim.ai',
22+
'https://www.staging.sim.ai',
23+
'https://www.sim.ai',
24+
'https://www.custom.example:8443',
25+
'http://localhost:3000',
26+
])('preserves the configured origin %s', (origin) => {
27+
setEnv({ NEXT_PUBLIC_APP_URL: `${origin}/` })
2228

23-
expect(buildChatDeploymentUrl('support')).toBe('https://sim.ai/chat/support')
29+
expect(buildChatDeploymentUrl('support')).toBe(`${origin}/chat/support`)
2430
})
2531

2632
/**

apps/sim/lib/chat-deployments/urls.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ import { getBaseUrl, getEmailDomain } from '@/lib/core/utils/urls'
55
* The public URL a deployed chat answers on.
66
*
77
* There is no chat subdomain: `proxy.ts` routes chat purely by the `/chat/`
8-
* path, so the URL is the app host plus the identifier. The `www.` prefix is
9-
* stripped because the deployed chat is served on the bare host.
8+
* path, so the URL must preserve the configured app origin, including `www.`
9+
* and any port. Host aliases may redirect, drop the chat path, or have a
10+
* different login cookie scope.
1011
*
1112
* Single source of truth for the previously independent constructions — the
1213
* deploy orchestration, the manage read, and the manage write — which had
@@ -25,11 +26,5 @@ export function buildChatDeploymentUrl(identifier: string): string {
2526
} catch {
2627
return `${isDev ? 'http' : 'https'}://${getEmailDomain()}/chat/${identifier}`
2728
}
28-
try {
29-
const url = new URL(baseUrl)
30-
const host = url.host.startsWith('www.') ? url.host.slice('www.'.length) : url.host
31-
return `${url.protocol}//${host}/chat/${identifier}`
32-
} catch {
33-
return `${baseUrl}/chat/${identifier}`
34-
}
29+
return `${baseUrl}/chat/${identifier}`
3530
}

0 commit comments

Comments
 (0)