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
2 changes: 1 addition & 1 deletion apps/sim/app/api/billing/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { and, eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { getSimplifiedBillingSummary } from '@/lib/billing/core/billing'
import { getOrganizationBillingData } from '@/lib/billing/core/organization-billing'
import { getOrganizationBillingData } from '@/lib/billing/core/organization'
import { createLogger } from '@/lib/logs/console/logger'
import { db } from '@/db'
import { member, userStats } from '@/db/schema'
Expand Down
17 changes: 5 additions & 12 deletions apps/sim/app/api/usage-limits/route.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { getUserUsageLimitInfo, updateUserUsageLimit } from '@/lib/billing'
import { getOrganizationBillingData } from '@/lib/billing/core/organization-billing'
import {
getOrganizationBillingData,
isOrganizationOwnerOrAdmin,
} from '@/lib/billing/core/organization'
import { createLogger } from '@/lib/logs/console/logger'
import { isOrganizationOwnerOrAdmin } from '@/lib/permissions/utils'

const logger = createLogger('UnifiedUsageLimitsAPI')

Expand All @@ -25,23 +27,20 @@ export async function GET(request: NextRequest) {
const userId = searchParams.get('userId') || session.user.id
const organizationId = searchParams.get('organizationId')

// Validate context
if (!['user', 'organization'].includes(context)) {
return NextResponse.json(
{ error: 'Invalid context. Must be "user" or "organization"' },
{ status: 400 }
)
}

// For user context, ensure they can only view their own info
if (context === 'user' && userId !== session.user.id) {
return NextResponse.json(
{ error: "Cannot view other users' usage information" },
{ status: 403 }
)
}

// Get usage limit info
if (context === 'organization') {
if (!organizationId) {
return NextResponse.json(
Expand Down Expand Up @@ -107,10 +106,8 @@ export async function PUT(request: NextRequest) {
}

if (context === 'user') {
// Update user's own usage limit
await updateUserUsageLimit(userId, limit)
} else if (context === 'organization') {
// context === 'organization'
if (!organizationId) {
return NextResponse.json(
{ error: 'Organization ID is required when context=organization' },
Expand All @@ -123,10 +120,7 @@ export async function PUT(request: NextRequest) {
return NextResponse.json({ error: 'Permission denied' }, { status: 403 })
}

// Use the dedicated function to update org usage limit
const { updateOrganizationUsageLimit } = await import(
'@/lib/billing/core/organization-billing'
)
const { updateOrganizationUsageLimit } = await import('@/lib/billing/core/organization')
const result = await updateOrganizationUsageLimit(organizationId, limit)

if (!result.success) {
Expand All @@ -137,7 +131,6 @@ export async function PUT(request: NextRequest) {
return NextResponse.json({ success: true, context, userId, organizationId, data: updated })
}

// Return updated limit info
const updatedInfo = await getUserUsageLimitInfo(userId)

return NextResponse.json({
Expand Down
16 changes: 6 additions & 10 deletions apps/sim/app/api/workspaces/[id]/permissions/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@ import crypto from 'crypto'
import { and, eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { getUsersWithPermissions, hasWorkspaceAdminAccess } from '@/lib/permissions/utils'
import { db } from '@/db'
import { permissions, type permissionTypeEnum } from '@/db/schema'

const logger = createLogger('WorkspacesPermissionsAPI')

type PermissionType = (typeof permissionTypeEnum.enumValues)[number]

interface UpdatePermissionsRequest {
updates: Array<{
userId: string
permissions: PermissionType // Single permission type instead of object with booleans
permissions: PermissionType
}>
}

Expand All @@ -33,7 +36,6 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{
return NextResponse.json({ error: 'Authentication required' }, { status: 401 })
}

// Verify the current user has access to this workspace
const userPermission = await db
.select()
.from(permissions)
Expand All @@ -57,7 +59,7 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{
total: result.length,
})
} catch (error) {
console.error('Error fetching workspace permissions:', error)
logger.error('Error fetching workspace permissions:', error)
return NextResponse.json({ error: 'Failed to fetch workspace permissions' }, { status: 500 })
}
}
Expand All @@ -81,7 +83,6 @@ export async function PATCH(request: NextRequest, { params }: { params: Promise<
return NextResponse.json({ error: 'Authentication required' }, { status: 401 })
}

// Verify the current user has admin access to this workspace (either direct or through organization)
const hasAdminAccess = await hasWorkspaceAdminAccess(session.user.id, workspaceId)

if (!hasAdminAccess) {
Expand All @@ -91,10 +92,8 @@ export async function PATCH(request: NextRequest, { params }: { params: Promise<
)
}

// Parse and validate request body
const body: UpdatePermissionsRequest = await request.json()

// Prevent users from modifying their own admin permissions
const selfUpdate = body.updates.find((update) => update.userId === session.user.id)
if (selfUpdate && selfUpdate.permissions !== 'admin') {
return NextResponse.json(
Expand All @@ -103,10 +102,8 @@ export async function PATCH(request: NextRequest, { params }: { params: Promise<
)
}

// Process updates in a transaction
await db.transaction(async (tx) => {
for (const update of body.updates) {
// Delete existing permissions for this user and workspace
await tx
.delete(permissions)
.where(
Expand All @@ -117,7 +114,6 @@ export async function PATCH(request: NextRequest, { params }: { params: Promise<
)
)

// Insert the single new permission
await tx.insert(permissions).values({
id: crypto.randomUUID(),
userId: update.userId,
Expand All @@ -138,7 +134,7 @@ export async function PATCH(request: NextRequest, { params }: { params: Promise<
total: updatedUsers.length,
})
} catch (error) {
console.error('Error updating workspace permissions:', error)
logger.error('Error updating workspace permissions:', error)
return NextResponse.json({ error: 'Failed to update workspace permissions' }, { status: 500 })
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,17 @@ import {
extractPathFromOutputId,
parseOutputContentSafely,
} from '@/lib/response-format'
import { ChatMessage } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/chat/components/chat-message/chat-message'
import { OutputSelect } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/chat/components/output-select/output-select'
import {
ChatFileUpload,
ChatMessage,
OutputSelect,
} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/chat/components'
import { useWorkflowExecution } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-workflow-execution'
import type { BlockLog, ExecutionResult } from '@/executor/types'
import { useExecutionStore } from '@/stores/execution/store'
import { useChatStore } from '@/stores/panel/chat/store'
import { useConsoleStore } from '@/stores/panel/console/store'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
import { ChatFileUpload } from './components/chat-file-upload'

const logger = createLogger('ChatPanel')

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { ChatFileUpload } from './chat-file-upload/chat-file-upload'
export { ChatMessage } from './chat-message/chat-message'
export { OutputSelect } from './output-select/output-select'
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ const ImagePreview = ({
className='h-auto w-full rounded-lg border'
unoptimized
onError={(e) => {
console.error('Image failed to load:', imageSrc)
logger.error('Image failed to load:', imageSrc)
setLoadError(true)
onLoadError?.(true)
}}
Expand Down Expand Up @@ -333,7 +333,7 @@ export function ConsoleEntry({ entry, consoleWidth }: ConsoleEntryProps) {
// Clean up the URL
setTimeout(() => URL.revokeObjectURL(url), 100)
} catch (error) {
console.error('Error downloading image:', error)
logger.error('Error downloading image:', error)
alert('Failed to download image. Please try again later.')
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
} from '@/components/ui/dropdown-menu'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
import { createLogger } from '@/lib/logs/console/logger'
import { useCopilotStore } from '@/stores/copilot/store'
import { useChatStore } from '@/stores/panel/chat/store'
import { useConsoleStore } from '@/stores/panel/console/store'
Expand All @@ -19,6 +20,8 @@ import { Console } from './components/console/console'
import { Copilot } from './components/copilot/copilot'
import { Variables } from './components/variables/variables'

const logger = createLogger('Panel')

export function Panel() {
const [chatMessage, setChatMessage] = useState<string>('')
const [isHistoryDropdownOpen, setIsHistoryDropdownOpen] = useState(false)
Expand Down Expand Up @@ -67,7 +70,7 @@ export function Panel() {
try {
await deleteChat(chatId)
} catch (error) {
console.error('Error deleting chat:', error)
logger.error('Error deleting chat:', error)
}
},
[deleteChat]
Expand Down Expand Up @@ -101,7 +104,7 @@ export function Panel() {
lastLoadedWorkflowRef.current = activeWorkflowId
}
} catch (error) {
console.error('Failed to load copilot data:', error)
logger.error('Failed to load copilot data:', error)
}
},
[
Expand Down Expand Up @@ -134,14 +137,14 @@ export function Panel() {
if (!areChatsFresh(activeWorkflowId)) {
// Don't await - let it load in background while dropdown is already open
ensureCopilotDataLoaded(false).catch((error) => {
console.error('Failed to load chat history:', error)
logger.error('Failed to load chat history:', error)
})
}
}

// If streaming, just log that we're showing cached data
if (open && isSendingMessage) {
console.log('Chat history opened during stream - showing cached data only')
logger.info('Chat history opened during stream - showing cached data only')
}
},
[ensureCopilotDataLoaded, activeWorkflowId, areChatsFresh, isSendingMessage]
Expand Down Expand Up @@ -278,7 +281,7 @@ export function Panel() {
// This is a real workflow change, not just a tab switch
if (copilotWorkflowId !== activeWorkflowId || !copilotWorkflowId) {
ensureCopilotDataLoaded().catch((error) => {
console.error('Failed to auto-load copilot data on workflow change:', error)
logger.error('Failed to auto-load copilot data on workflow change:', error)
})
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ export function FileUpload({
})
}
} catch (error) {
console.error(`Error uploading ${file.name}:`, error)
logger.error(`Error uploading ${file.name}:`, error)
const errorMessage = error instanceof Error ? error.message : 'Unknown error'
uploadErrors.push(`${file.name}: ${errorMessage}`)
}
Expand Down Expand Up @@ -428,7 +428,7 @@ export function FileUpload({
deletionResults.failures.push(`${file.name}: ${errorMessage}`)
}
} catch (error) {
console.error(`Failed to delete file ${file.name}:`, error)
logger.error(`Failed to delete file ${file.name}:`, error)
deletionResults.failures.push(
`${file.name}: ${error instanceof Error ? error.message : 'Unknown error'}`
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,7 @@ export function ToolInput({
try {
return block.tools.config.tool({ operation })
} catch (error) {
console.error('Error selecting tool for operation:', error)
logger.error('Error selecting tool for operation:', error)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
import { createLogger } from '@/lib/logs/console/logger'
import { parseCronToHumanReadable } from '@/lib/schedules/utils'
import { cn, validateName } from '@/lib/utils'
import { type DiffStatus, hasDiffStatus } from '@/lib/workflows/diff/types'
Expand All @@ -23,6 +24,8 @@ import { ActionBar } from './components/action-bar/action-bar'
import { ConnectionBlocks } from './components/connection-blocks/connection-blocks'
import { SubBlock } from './components/sub-block/sub-block'

const logger = createLogger('WorkflowBlock')

interface WorkflowBlockProps {
type: string
config: BlockConfig
Expand Down Expand Up @@ -232,10 +235,10 @@ export function WorkflowBlock({ id, data }: NodeProps<WorkflowBlockProps>) {
fetchScheduleInfo(currentWorkflowId)
}
} else {
console.error('Failed to reactivate schedule')
logger.error('Failed to reactivate schedule')
}
} catch (error) {
console.error('Error reactivating schedule:', error)
logger.error('Error reactivating schedule:', error)
}
}

Expand All @@ -255,10 +258,10 @@ export function WorkflowBlock({ id, data }: NodeProps<WorkflowBlockProps>) {
fetchScheduleInfo(currentWorkflowId)
}
} else {
console.error('Failed to disable schedule')
logger.error('Failed to disable schedule')
}
} catch (error) {
console.error('Error disabling schedule:', error)
logger.error('Error disabling schedule:', error)
}
}

Expand Down Expand Up @@ -328,12 +331,12 @@ export function WorkflowBlock({ id, data }: NodeProps<WorkflowBlockProps>) {
return
}
} catch (err) {
console.error('Error fetching schedule status:', err)
logger.error('Error fetching schedule status:', err)
}

setScheduleInfo(baseInfo)
} catch (error) {
console.error('Error fetching schedule info:', error)
logger.error('Error fetching schedule info:', error)
setScheduleInfo(null)
} finally {
setIsLoadingScheduleInfo(false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@ import {
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Skeleton } from '@/components/ui/skeleton'
import { createLogger } from '@/lib/logs/console/logger'
import { useEnvironmentStore } from '@/stores/settings/environment/store'
import type { EnvironmentVariable as StoreEnvironmentVariable } from '@/stores/settings/environment/types'

const logger = createLogger('EnvironmentVariables')

// Constants
const GRID_COLS = 'grid grid-cols-[minmax(0,1fr),minmax(0,1fr),40px] gap-4'
const INITIAL_ENV_VAR: UIEnvironmentVariable = { key: '', value: '' }
Expand Down Expand Up @@ -263,7 +266,7 @@ export function EnvironmentVariables({
// Single store update that triggers sync
useEnvironmentStore.getState().setVariables(validVariables)
} catch (error) {
console.error('Failed to save environment variables:', error)
logger.error('Failed to save environment variables:', error)
}
}

Expand Down
5 changes: 4 additions & 1 deletion apps/sim/components/emails/invitation-email.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
import { format } from 'date-fns'
import { getBrandConfig } from '@/lib/branding/branding'
import { env } from '@/lib/env'
import { createLogger } from '@/lib/logs/console/logger'
import { getAssetUrl } from '@/lib/utils'
import { baseStyles } from './base-styles'
import EmailFooter from './footer'
Expand All @@ -28,6 +29,8 @@ interface InvitationEmailProps {

const baseUrl = env.NEXT_PUBLIC_APP_URL || 'https://sim.ai'

const logger = createLogger('InvitationEmail')

export const InvitationEmail = ({
inviterName = 'A team member',
organizationName = 'an organization',
Expand All @@ -49,7 +52,7 @@ export const InvitationEmail = ({
enhancedLink = `${baseUrl}/invite/${invitationId}?token=${invitationId}`
}
} catch (e) {
console.error('Error parsing invite link:', e)
logger.error('Error parsing invite link:', e)
}
}

Expand Down
Loading