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
3 changes: 2 additions & 1 deletion apps/sim/app/(auth)/login/login-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { client } from '@/lib/auth-client'
import { quickValidateEmail } from '@/lib/email/validation'
import { env, isFalsy, isTruthy } from '@/lib/env'
import { createLogger } from '@/lib/logs/console/logger'
import { getBaseUrl } from '@/lib/urls/utils'
import { cn } from '@/lib/utils'
import { SocialLoginButtons } from '@/app/(auth)/components/social-login-buttons'
import { SSOLoginButton } from '@/app/(auth)/components/sso-login-button'
Expand Down Expand Up @@ -322,7 +323,7 @@ export default function LoginPage({
},
body: JSON.stringify({
email: forgotPasswordEmail,
redirectTo: `${window.location.origin}/reset-password`,
redirectTo: `${getBaseUrl()}/reset-password`,
}),
})

Expand Down
5 changes: 2 additions & 3 deletions apps/sim/app/api/billing/portal/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { and, eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { requireStripeClient } from '@/lib/billing/stripe-client'
import { env } from '@/lib/env'
import { createLogger } from '@/lib/logs/console/logger'
import { getBaseUrl } from '@/lib/urls/utils'

const logger = createLogger('BillingPortal')

Expand All @@ -21,8 +21,7 @@ export async function POST(request: NextRequest) {
const context: 'user' | 'organization' =
body?.context === 'organization' ? 'organization' : 'user'
const organizationId: string | undefined = body?.organizationId || undefined
const returnUrl: string =
body?.returnUrl || `${env.NEXT_PUBLIC_APP_URL}/workspace?billing=updated`
const returnUrl: string = body?.returnUrl || `${getBaseUrl()}/workspace?billing=updated`

const stripe = requireStripeClient()

Expand Down
4 changes: 2 additions & 2 deletions apps/sim/app/api/chat/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import type { NextRequest } from 'next/server'
import { v4 as uuidv4 } from 'uuid'
import { z } from 'zod'
import { getSession } from '@/lib/auth'
import { env } from '@/lib/env'
import { isDev } from '@/lib/environment'
import { createLogger } from '@/lib/logs/console/logger'
import { getBaseUrl } from '@/lib/urls/utils'
import { encryptSecret } from '@/lib/utils'
import { checkWorkflowAccessForChatCreation } from '@/app/api/chat/utils'
import { createErrorResponse, createSuccessResponse } from '@/app/api/workflows/utils'
Expand Down Expand Up @@ -171,7 +171,7 @@ export async function POST(request: NextRequest) {

// Return successful response with chat URL
// Generate chat URL using path-based routing instead of subdomains
const baseUrl = env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'
const baseUrl = getBaseUrl()

let chatUrl: string
try {
Expand Down
3 changes: 2 additions & 1 deletion apps/sim/app/api/files/download/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { type NextRequest, NextResponse } from 'next/server'
import { createLogger } from '@/lib/logs/console/logger'
import { getPresignedUrl, getPresignedUrlWithConfig, isUsingCloudStorage } from '@/lib/uploads'
import { BLOB_EXECUTION_FILES_CONFIG, S3_EXECUTION_FILES_CONFIG } from '@/lib/uploads/setup'
import { getBaseUrl } from '@/lib/urls/utils'
import { createErrorResponse } from '@/app/api/files/utils'

const logger = createLogger('FileDownload')
Expand Down Expand Up @@ -81,7 +82,7 @@ export async function POST(request: NextRequest) {
}
} else {
// For local storage, return the direct path
const downloadUrl = `${process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'}/api/files/serve/${key}`
const downloadUrl = `${getBaseUrl()}/api/files/serve/${key}`

return NextResponse.json({
downloadUrl,
Expand Down
5 changes: 3 additions & 2 deletions apps/sim/app/api/function/execute/route.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { createContext, Script } from 'vm'
import { type NextRequest, NextResponse } from 'next/server'
import { env, isTruthy } from '@/lib/env'
import { MAX_EXECUTION_DURATION } from '@/lib/execution/constants'
import { executeInE2B } from '@/lib/execution/e2b'
import { CodeLanguage, DEFAULT_CODE_LANGUAGE, isValidCodeLanguage } from '@/lib/execution/languages'
import { createLogger } from '@/lib/logs/console/logger'
import { validateProxyUrl } from '@/lib/security/input-validation'
import { generateRequestId } from '@/lib/utils'
export const dynamic = 'force-dynamic'
export const runtime = 'nodejs'
export const maxDuration = MAX_EXECUTION_DURATION
// Segment config exports must be statically analyzable.
// Mirror MAX_EXECUTION_DURATION (210s) from '@/lib/execution/constants'.
export const maxDuration = 210

const logger = createLogger('FunctionExecuteAPI')

Expand Down
6 changes: 3 additions & 3 deletions apps/sim/app/api/organizations/[id]/invitations/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ import {
} from '@/lib/billing/validation/seat-management'
import { sendEmail } from '@/lib/email/mailer'
import { quickValidateEmail } from '@/lib/email/validation'
import { env } from '@/lib/env'
import { createLogger } from '@/lib/logs/console/logger'
import { hasWorkspaceAdminAccess } from '@/lib/permissions/utils'
import { getBaseUrl } from '@/lib/urls/utils'

const logger = createLogger('OrganizationInvitations')

Expand Down Expand Up @@ -339,7 +339,7 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
organizationEntry[0]?.name || 'organization',
role,
workspaceInvitationsWithNames,
`${env.NEXT_PUBLIC_APP_URL}/invite/${orgInvitation.id}`
`${getBaseUrl()}/invite/${orgInvitation.id}`
)

emailResult = await sendEmail({
Expand All @@ -352,7 +352,7 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
const emailHtml = await renderInvitationEmail(
inviter[0]?.name || 'Someone',
organizationEntry[0]?.name || 'organization',
`${env.NEXT_PUBLIC_APP_URL}/invite/${orgInvitation.id}`,
`${getBaseUrl()}/invite/${orgInvitation.id}`,
email
)

Expand Down
4 changes: 2 additions & 2 deletions apps/sim/app/api/organizations/[id]/members/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import { getUserUsageData } from '@/lib/billing/core/usage'
import { validateSeatAvailability } from '@/lib/billing/validation/seat-management'
import { sendEmail } from '@/lib/email/mailer'
import { quickValidateEmail } from '@/lib/email/validation'
import { env } from '@/lib/env'
import { createLogger } from '@/lib/logs/console/logger'
import { getBaseUrl } from '@/lib/urls/utils'

const logger = createLogger('OrganizationMembersAPI')

Expand Down Expand Up @@ -260,7 +260,7 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
const emailHtml = await renderInvitationEmail(
inviter[0]?.name || 'Someone',
organizationEntry[0]?.name || 'organization',
`${env.NEXT_PUBLIC_APP_URL}/invite/organization?id=${invitationId}`,
`${getBaseUrl()}/invite/organization?id=${invitationId}`,
normalizedEmail
)

Expand Down
10 changes: 2 additions & 8 deletions apps/sim/app/api/webhooks/[id]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { webhook, workflow } from '@sim/db/schema'
import { eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { env } from '@/lib/env'
import { createLogger } from '@/lib/logs/console/logger'
import { getUserEntityPermissions } from '@/lib/permissions/utils'
import { getBaseUrl } from '@/lib/urls/utils'
import { generateRequestId } from '@/lib/utils'
import { getOAuthToken } from '@/app/api/auth/oauth/utils'

Expand Down Expand Up @@ -282,13 +282,7 @@ export async function DELETE(

if (!resolvedExternalId) {
try {
if (!env.NEXT_PUBLIC_APP_URL) {
logger.error(
`[${requestId}] NEXT_PUBLIC_APP_URL not configured, cannot match Airtable webhook`
)
throw new Error('NEXT_PUBLIC_APP_URL must be configured')
}
const expectedNotificationUrl = `${env.NEXT_PUBLIC_APP_URL}/api/webhooks/trigger/${foundWebhook.path}`
const expectedNotificationUrl = `${getBaseUrl()}/api/webhooks/trigger/${foundWebhook.path}`

const listUrl = `https://api.airtable.com/v0/bases/${baseId}/webhooks`
const listResp = await fetch(listUrl, {
Expand Down
9 changes: 2 additions & 7 deletions apps/sim/app/api/webhooks/[id]/test-url/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { db, webhook, workflow } from '@sim/db'
import { eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { env } from '@/lib/env'
import { createLogger } from '@/lib/logs/console/logger'
import { getUserEntityPermissions } from '@/lib/permissions/utils'
import { getBaseUrl } from '@/lib/urls/utils'
import { generateRequestId } from '@/lib/utils'
import { signTestWebhookToken } from '@/lib/webhooks/test-tokens'

Expand Down Expand Up @@ -64,13 +64,8 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
return NextResponse.json({ error: 'Forbidden' }, { status: 403 })
}

if (!env.NEXT_PUBLIC_APP_URL) {
logger.error(`[${requestId}] NEXT_PUBLIC_APP_URL not configured`)
return NextResponse.json({ error: 'Server configuration error' }, { status: 500 })
}

const token = await signTestWebhookToken(id, ttlSeconds)
const url = `${env.NEXT_PUBLIC_APP_URL}/api/webhooks/test/${id}?token=${encodeURIComponent(token)}`
const url = `${getBaseUrl()}/api/webhooks/test/${id}?token=${encodeURIComponent(token)}`

logger.info(`[${requestId}] Minted test URL for webhook ${id}`)
return NextResponse.json({
Expand Down
11 changes: 2 additions & 9 deletions apps/sim/app/api/webhooks/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { and, desc, eq } from 'drizzle-orm'
import { nanoid } from 'nanoid'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { env } from '@/lib/env'
import { createLogger } from '@/lib/logs/console/logger'
import { getUserEntityPermissions } from '@/lib/permissions/utils'
import { getBaseUrl } from '@/lib/urls/utils'
import { generateRequestId } from '@/lib/utils'
import { getOAuthToken } from '@/app/api/auth/oauth/utils'

Expand Down Expand Up @@ -467,14 +467,7 @@ async function createAirtableWebhookSubscription(
)
}

if (!env.NEXT_PUBLIC_APP_URL) {
logger.error(
`[${requestId}] NEXT_PUBLIC_APP_URL not configured, cannot register Airtable webhook`
)
throw new Error('NEXT_PUBLIC_APP_URL must be configured for Airtable webhook registration')
}

const notificationUrl = `${env.NEXT_PUBLIC_APP_URL}/api/webhooks/trigger/${path}`
const notificationUrl = `${getBaseUrl()}/api/webhooks/trigger/${path}`

const airtableApiUrl = `https://api.airtable.com/v0/bases/${baseId}/webhooks`

Expand Down
12 changes: 2 additions & 10 deletions apps/sim/app/api/webhooks/test/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { db } from '@sim/db'
import { webhook } from '@sim/db/schema'
import { eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { env } from '@/lib/env'
import { createLogger } from '@/lib/logs/console/logger'
import { getBaseUrl } from '@/lib/urls/utils'
import { generateRequestId } from '@/lib/utils'

const logger = createLogger('WebhookTestAPI')
Expand Down Expand Up @@ -35,15 +35,7 @@ export async function GET(request: NextRequest) {
const provider = foundWebhook.provider || 'generic'
const providerConfig = (foundWebhook.providerConfig as Record<string, any>) || {}

if (!env.NEXT_PUBLIC_APP_URL) {
logger.error(`[${requestId}] NEXT_PUBLIC_APP_URL not configured, cannot test webhook`)
return NextResponse.json(
{ success: false, error: 'NEXT_PUBLIC_APP_URL must be configured' },
{ status: 500 }
)
}
const baseUrl = env.NEXT_PUBLIC_APP_URL
const webhookUrl = `${baseUrl}/api/webhooks/trigger/${foundWebhook.path}`
const webhookUrl = `${getBaseUrl()}/api/webhooks/trigger/${foundWebhook.path}`

logger.info(`[${requestId}] Testing webhook for provider: ${provider}`, {
webhookId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,21 @@ describe('Workspace Invitation [invitationId] API Route', () => {
hasWorkspaceAdminAccess: mockHasWorkspaceAdminAccess,
}))

vi.doMock('@/lib/env', () => ({
env: {
vi.doMock('@/lib/env', () => {
const mockEnv = {
NEXT_PUBLIC_APP_URL: 'https://test.sim.ai',
BILLING_ENABLED: false,
},
isTruthy: (value: string | boolean | number | undefined) =>
typeof value === 'string'
? value.toLowerCase() === 'true' || value === '1'
: Boolean(value),
getEnv: (variable: string) => process.env[variable],
}))
}
return {
env: mockEnv,
isTruthy: (value: string | boolean | number | undefined) =>
typeof value === 'string'
? value.toLowerCase() === 'true' || value === '1'
: Boolean(value),
getEnv: (variable: string) =>
mockEnv[variable as keyof typeof mockEnv] ?? process.env[variable],
}
})

mockTransaction = vi.fn()
const mockDbChain = {
Expand Down Expand Up @@ -384,17 +388,21 @@ describe('Workspace Invitation [invitationId] API Route', () => {
vi.doMock('@/lib/permissions/utils', () => ({
hasWorkspaceAdminAccess: vi.fn(),
}))
vi.doMock('@/lib/env', () => ({
env: {
vi.doMock('@/lib/env', () => {
const mockEnv = {
NEXT_PUBLIC_APP_URL: 'https://test.sim.ai',
BILLING_ENABLED: false,
},
isTruthy: (value: string | boolean | number | undefined) =>
typeof value === 'string'
? value.toLowerCase() === 'true' || value === '1'
: Boolean(value),
getEnv: (variable: string) => process.env[variable],
}))
}
return {
env: mockEnv,
isTruthy: (value: string | boolean | number | undefined) =>
typeof value === 'string'
? value.toLowerCase() === 'true' || value === '1'
: Boolean(value),
getEnv: (variable: string) =>
mockEnv[variable as keyof typeof mockEnv] ?? process.env[variable],
}
})
vi.doMock('@sim/db/schema', () => ({
workspaceInvitation: { id: 'id' },
}))
Expand Down
Loading