Skip to content

Commit 1438028

Browse files
authored
improvement(utils): removed duplicate logic to get base url with fallback, made util (#337)
* improvement(utils): removed duplicate logic to get base url with fallback, made util * acknowledged PR comments
1 parent fcc590c commit 1438028

File tree

8 files changed

+76
-26
lines changed

8 files changed

+76
-26
lines changed

sim/app/(auth)/verify/page.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import { isProd } from '@/lib/environment'
22
import { VerifyContent } from './verify-content'
3+
import { getBaseUrl } from '@/lib/urls/utils'
34

45
export default function VerifyPage() {
5-
const protocol = isProd ? 'https' : 'http'
6-
const appUrl = process.env.NEXT_PUBLIC_APP_URL || 'localhost:3000'
7-
const baseUrl = `${protocol}://${appUrl}`
6+
const baseUrl = getBaseUrl()
87

98
const hasResendKey = Boolean(
109
process.env.RESEND_API_KEY && process.env.RESEND_API_KEY !== 'placeholder'

sim/app/api/chat/edit/[id]/route.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { chat } from '@/db/schema'
77
import { createErrorResponse, createSuccessResponse } from '@/app/api/workflows/utils'
88
import { z } from 'zod'
99
import { encryptSecret } from '@/lib/utils'
10+
import { getBaseDomain } from '@/lib/urls/utils'
1011

1112
export const dynamic = 'force-dynamic'
1213

@@ -67,17 +68,15 @@ export async function GET(
6768
// Create a new result object without the password
6869
const { password, ...safeData } = chatInstance[0]
6970

70-
// Check if we're in development or production
7171
const isDevelopment = process.env.NODE_ENV === 'development'
72+
7273
const chatUrl = isDevelopment
73-
? `http://${chatInstance[0].subdomain}.localhost:3000`
74+
? `http://${chatInstance[0].subdomain}.${getBaseDomain()}`
7475
: `https://${chatInstance[0].subdomain}.simstudio.ai`
7576

76-
// For security, don't return the actual password value
7777
const result = {
7878
...safeData,
7979
chatUrl,
80-
// Include password presence flag but not the actual value
8180
hasPassword: !!password
8281
}
8382

@@ -228,12 +227,12 @@ export async function PATCH(
228227
.set(updateData)
229228
.where(eq(chat.id, chatId))
230229

231-
// Return success response
232230
const updatedSubdomain = subdomain || existingChat[0].subdomain
233-
// Check if we're in development or production
231+
234232
const isDevelopment = process.env.NODE_ENV === 'development'
233+
235234
const chatUrl = isDevelopment
236-
? `http://${updatedSubdomain}.localhost:3000`
235+
? `http://${updatedSubdomain}.${getBaseDomain()}`
237236
: `https://${updatedSubdomain}.simstudio.ai`
238237

239238
logger.info(`Chat "${chatId}" updated successfully`)

sim/app/w/[id]/components/control-bar/components/deploy-modal/components/chat-deploy/chat-deploy.tsx

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import { Skeleton } from '@/components/ui/skeleton'
3232
import { Textarea } from '@/components/ui/textarea'
3333
import { createLogger } from '@/lib/logs/console-logger'
3434
import { cn } from '@/lib/utils'
35+
import { getBaseDomain } from '@/lib/urls/utils'
3536
import { useNotificationStore } from '@/stores/notifications/store'
3637
import { OutputSelect } from '@/app/w/[id]/components/panel/components/chat/components/output-select/output-select'
3738
import { OutputConfig } from '@/stores/panel/chat/types'
@@ -54,6 +55,11 @@ type AuthType = 'public' | 'password' | 'email'
5455

5556
const isDevelopment = process.env.NODE_ENV === 'development'
5657

58+
const getDomainSuffix = (() => {
59+
const suffix = isDevelopment ? `.${getBaseDomain()}` : '.simstudio.ai'
60+
return () => suffix
61+
})()
62+
5763
// Define Zod schema for API request validation
5864
const chatSchema = z.object({
5965
workflowId: z.string().min(1, 'Workflow ID is required'),
@@ -810,11 +816,20 @@ export function ChatDeploy({
810816
}
811817

812818
if (deployedChatUrl) {
813-
// Extract just the subdomain from the URL
814819
const url = new URL(deployedChatUrl)
815820
const hostname = url.hostname
816821
const isDevelopmentUrl = hostname.includes('localhost')
817-
const domainSuffix = isDevelopmentUrl ? '.localhost:3000' : '.simstudio.ai'
822+
823+
let domainSuffix
824+
if (isDevelopmentUrl) {
825+
const baseDomain = getBaseDomain()
826+
const baseHost = baseDomain.split(':')[0]
827+
const port = url.port || (baseDomain.includes(':') ? baseDomain.split(':')[1] : '3000')
828+
domainSuffix = `.${baseHost}:${port}`
829+
} else {
830+
domainSuffix = '.simstudio.ai'
831+
}
832+
818833
const subdomainPart = isDevelopmentUrl
819834
? hostname.split('.')[0]
820835
: hostname.split('.simstudio.ai')[0]
@@ -911,7 +926,7 @@ export function ChatDeploy({
911926
disabled={isDeploying}
912927
/>
913928
<div className="h-10 px-3 flex items-center border border-l-0 rounded-r-md bg-muted text-muted-foreground text-sm font-medium whitespace-nowrap">
914-
{isDevelopment ? '.localhost:3000' : '.simstudio.ai'}
929+
{getDomainSuffix()}
915930
</div>
916931
{!isCheckingSubdomain && subdomainAvailable === true && subdomain && (
917932
<div className="absolute right-14 flex items-center">

sim/lib/urls/utils.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* Returns the base URL of the application, respecting environment variables for deployment environments
3+
* @returns The base URL string (e.g., 'http://localhost:3000' or 'https://example.com')
4+
*/
5+
export function getBaseUrl(): string {
6+
if (typeof window !== 'undefined') {
7+
return window.location.origin
8+
}
9+
10+
const baseUrl = process.env.NEXT_PUBLIC_APP_URL
11+
if (baseUrl) {
12+
if (baseUrl.startsWith('http://') || baseUrl.startsWith('https://')) {
13+
return baseUrl
14+
}
15+
16+
const isProd = process.env.NODE_ENV === 'production'
17+
const protocol = isProd ? 'https://' : 'http://'
18+
return `${protocol}${baseUrl}`
19+
}
20+
21+
return 'http://localhost:3000'
22+
}
23+
24+
/**
25+
* Returns just the domain and port part of the application URL
26+
* @returns The domain with port if applicable (e.g., 'localhost:3000' or 'simstudio.ai')
27+
*/
28+
export function getBaseDomain(): string {
29+
try {
30+
const url = new URL(getBaseUrl())
31+
return url.host // host includes port if specified
32+
} catch (e) {
33+
const isProd = process.env.NODE_ENV === 'production'
34+
return isProd ? 'simstudio.ai' : 'localhost:3000'
35+
}
36+
}

sim/middleware.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { NextRequest, NextResponse } from 'next/server'
22
import { getSessionCookie } from 'better-auth/cookies'
33
import { verifyToken } from './lib/waitlist/token'
44
import { createLogger } from '@/lib/logs/console-logger'
5+
import { getBaseDomain } from '@/lib/urls/utils'
56

67
const logger = createLogger('Middleware')
78

@@ -15,7 +16,8 @@ const SUSPICIOUS_UA_PATTERNS = [
1516
/^\(\)\s*{/, // Command execution attempt
1617
/\b(sqlmap|nikto|gobuster|dirb|nmap)\b/i // Known scanning tools
1718
]
18-
const BASE_DOMAIN = isDevelopment ? 'localhost:3000' : 'simstudio.ai'
19+
20+
const BASE_DOMAIN = getBaseDomain()
1921

2022
export async function middleware(request: NextRequest) {
2123
// Check for active session

sim/tools/http/request.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,21 @@
11
import { HttpMethod, TableRow, ToolConfig } from '../types'
22
import { createLogger } from '@/lib/logs/console-logger'
33
import { RequestParams, RequestResponse } from './types'
4+
import { getBaseUrl } from '@/lib/urls/utils'
45

56
const logger = createLogger('HTTPRequestTool')
67

78
// Function to get the appropriate referer based on environment
89
const getReferer = (): string => {
9-
// Check if we're in a browser environment
1010
if (typeof window !== 'undefined') {
1111
return window.location.origin
1212
}
1313

14-
// Use environment variable if available (server-side)
15-
if (process.env.NEXT_PUBLIC_APP_URL) {
16-
return process.env.NEXT_PUBLIC_APP_URL
14+
try {
15+
return getBaseUrl()
16+
} catch (error) {
17+
return 'http://localhost:3000'
1718
}
18-
19-
// Fallback for development
20-
return 'http://localhost:3000/'
2119
}
2220

2321
/**

sim/tools/mistral/parser.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { createLogger } from '@/lib/logs/console-logger'
22
import { ToolConfig } from '../types'
33
import { MistralParserInput, MistralParserOutput } from './types'
4+
import { getBaseUrl } from '@/lib/urls/utils'
45

56
const logger = createLogger('MistralParserTool')
67

@@ -97,7 +98,8 @@ export const mistralParserTool: ToolConfig<MistralParserInput, MistralParserOutp
9798
// Make sure the file path is an absolute URL
9899
if (uploadedFilePath.startsWith('/')) {
99100
// If it's a relative path starting with /, convert to absolute URL
100-
const baseUrl = process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'
101+
const baseUrl = getBaseUrl()
102+
if (!baseUrl) throw new Error('Failed to get base URL for file path conversion')
101103
uploadedFilePath = `${baseUrl}${uploadedFilePath}`
102104
}
103105

sim/tools/openai/dalle.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { ToolConfig } from '../types'
22
import { createLogger } from '@/lib/logs/console-logger'
3+
import { getBaseUrl } from '@/lib/urls/utils'
34

45
const logger = createLogger('DalleTool')
56

@@ -79,18 +80,16 @@ export const dalleTool: ToolConfig = {
7980
logger.info('Using model:', modelName)
8081

8182
try {
82-
// Fetch the image using the proxy/image endpoint instead of direct fetch
8383
logger.info('Fetching image from URL via proxy...')
84-
// Get the base URL from environment or use a fallback
85-
const baseUrl = process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'
84+
const baseUrl = getBaseUrl()
8685
const proxyUrl = new URL(`/api/proxy/image`, baseUrl)
8786
proxyUrl.searchParams.append('url', imageUrl)
8887

8988
const imageResponse = await fetch(proxyUrl.toString(), {
9089
headers: {
9190
Accept: 'image/*, */*',
9291
},
93-
cache: 'no-store', // Don't use cache
92+
cache: 'no-store',
9493
})
9594

9695
if (!imageResponse.ok) {

0 commit comments

Comments
 (0)