diff --git a/apps/sim/app/(auth)/login/login-form.tsx b/apps/sim/app/(auth)/login/login-form.tsx index 16298b4204..ffd6c3515b 100644 --- a/apps/sim/app/(auth)/login/login-form.tsx +++ b/apps/sim/app/(auth)/login/login-form.tsx @@ -304,6 +304,15 @@ export default function LoginPage({ return } + const emailValidation = quickValidateEmail(forgotPasswordEmail.trim().toLowerCase()) + if (!emailValidation.isValid) { + setResetStatus({ + type: 'error', + message: 'Please enter a valid email address', + }) + return + } + try { setIsSubmittingReset(true) setResetStatus({ type: null, message: '' }) @@ -321,7 +330,23 @@ export default function LoginPage({ if (!response.ok) { const errorData = await response.json() - throw new Error(errorData.message || 'Failed to request password reset') + let errorMessage = errorData.message || 'Failed to request password reset' + + if ( + errorMessage.includes('Invalid body parameters') || + errorMessage.includes('invalid email') + ) { + errorMessage = 'Please enter a valid email address' + } else if (errorMessage.includes('Email is required')) { + errorMessage = 'Please enter your email address' + } else if ( + errorMessage.includes('user not found') || + errorMessage.includes('User not found') + ) { + errorMessage = 'No account found with this email address' + } + + throw new Error(errorMessage) } setResetStatus({ @@ -497,7 +522,8 @@ export default function LoginPage({ Reset Password - Enter your email address and we'll send you a link to reset your password. + Enter your email address and we'll send you a link to reset your password if your + account exists.
@@ -512,14 +538,20 @@ export default function LoginPage({ placeholder='Enter your email' required type='email' - className='border-neutral-700/80 bg-neutral-900 text-white placeholder:text-white/60 focus:border-[var(--brand-primary-hover-hex)]/70 focus:ring-[var(--brand-primary-hover-hex)]/20' + className={cn( + 'border-neutral-700/80 bg-neutral-900 text-white placeholder:text-white/60 focus:border-[var(--brand-primary-hover-hex)]/70 focus:ring-[var(--brand-primary-hover-hex)]/20', + resetStatus.type === 'error' && 'border-red-500 focus-visible:ring-red-500' + )} /> + {resetStatus.type === 'error' && ( +
+

{resetStatus.message}

+
+ )}
- {resetStatus.type && ( -
- {resetStatus.message} + {resetStatus.type === 'success' && ( +
+

{resetStatus.message}

)} - - - ) : ( - <> - - - - )} - - -
- - - - + : 'Sign in to your account to accept this invitation' + } + icon='userPlus' + actions={[ + ...(isNewUser + ? [ + { + label: 'Create an account', + onClick: () => + router.push(`/signup?callbackUrl=${callbackUrl}&invite_flow=true`), + }, + { + label: 'I already have an account', + onClick: () => + router.push(`/login?callbackUrl=${callbackUrl}&invite_flow=true`), + variant: 'outline' as const, + }, + ] + : [ + { + label: 'Sign in', + onClick: () => + router.push(`/login?callbackUrl=${callbackUrl}&invite_flow=true`), + }, + { + label: 'Create an account', + onClick: () => + router.push(`/signup?callbackUrl=${callbackUrl}&invite_flow=true&new=true`), + variant: 'outline' as const, + }, + ]), + { + label: 'Return to Home', + onClick: () => router.push('/'), + }, + ]} + /> + ) } - // Show loading state if (isLoading || isPending) { return ( -
-
- Sim Logo -
- -

Loading invitation...

- - -
+ + + ) } - // Show error state if (error) { + const errorReason = searchParams.get('error') + const isExpiredError = errorReason === 'expired' + return ( -
-
- Sim Logo -
-
-
- -
-

- Invitation Error -

-

{error}

- - -
- - -
+ + router.push('/'), + }, + ]} + /> + ) } - // Show success state if (accepted) { return ( -
-
- Sim Logo -
-
-
- -
-

Welcome!

-

- You have successfully joined {invitationDetails?.name || 'the workspace'}. Redirecting - to your workspace... -

- - -
- - -
+ + router.push('/'), + }, + ]} + /> + ) } - // Show invitation details return ( -
-
- Sim Logo -
- -
-
- {invitationType === 'organization' ? ( - - ) : ( - - )} -
- -

- {invitationType === 'organization' ? 'Organization Invitation' : 'Workspace Invitation'} -

- -

- You've been invited to join{' '} - - {invitationDetails?.name || `a ${invitationType}`} - - . Click accept below to join. -

- -
- - -
-
- - -
+ + router.push('/'), + variant: 'ghost', + }, + ]} + /> + ) } diff --git a/apps/sim/app/invite/[id]/utils.ts b/apps/sim/app/invite/[id]/utils.ts new file mode 100644 index 0000000000..61c90f5867 --- /dev/null +++ b/apps/sim/app/invite/[id]/utils.ts @@ -0,0 +1,28 @@ +export function getErrorMessage(reason: string): string { + switch (reason) { + case 'missing-token': + return 'The invitation link is invalid or missing a required parameter.' + case 'invalid-token': + return 'The invitation link is invalid or has already been used.' + case 'expired': + return 'This invitation has expired. Please ask for a new invitation.' + case 'already-processed': + return 'This invitation has already been accepted or declined.' + case 'email-mismatch': + return 'This invitation was sent to a different email address. Please log in with the correct account or contact the person who invited you.' + case 'workspace-not-found': + return 'The workspace associated with this invitation could not be found.' + case 'user-not-found': + return 'Your user account could not be found. Please try logging out and logging back in.' + case 'already-member': + return 'You are already a member of this organization or workspace.' + case 'invalid-invitation': + return 'This invitation is invalid or no longer exists.' + case 'missing-invitation-id': + return 'The invitation link is missing required information. Please use the original invitation link.' + case 'server-error': + return 'An unexpected error occurred while processing your invitation. Please try again later.' + default: + return 'An unknown error occurred while processing your invitation.' + } +} diff --git a/apps/sim/app/invite/components/index.ts b/apps/sim/app/invite/components/index.ts new file mode 100644 index 0000000000..e95425f803 --- /dev/null +++ b/apps/sim/app/invite/components/index.ts @@ -0,0 +1,2 @@ +export { InviteLayout } from './layout' +export { InviteStatusCard } from './status-card' diff --git a/apps/sim/app/invite/components/layout.tsx b/apps/sim/app/invite/components/layout.tsx new file mode 100644 index 0000000000..a3d01e34d0 --- /dev/null +++ b/apps/sim/app/invite/components/layout.tsx @@ -0,0 +1,56 @@ +'use client' + +import Image from 'next/image' +import { useBrandConfig } from '@/lib/branding/branding' +import { GridPattern } from '@/app/(landing)/components/grid-pattern' + +interface InviteLayoutProps { + children: React.ReactNode +} + +export function InviteLayout({ children }: InviteLayoutProps) { + const brandConfig = useBrandConfig() + + return ( +
+ {/* Background pattern */} +
+ ) +} diff --git a/apps/sim/app/invite/components/status-card.tsx b/apps/sim/app/invite/components/status-card.tsx new file mode 100644 index 0000000000..51f0ca691b --- /dev/null +++ b/apps/sim/app/invite/components/status-card.tsx @@ -0,0 +1,121 @@ +'use client' + +import { CheckCircle2, Mail, RotateCcw, ShieldX, UserPlus, Users2 } from 'lucide-react' +import { useRouter } from 'next/navigation' +import { Button } from '@/components/ui/button' +import { LoadingAgent } from '@/components/ui/loading-agent' + +interface InviteStatusCardProps { + type: 'login' | 'loading' | 'error' | 'success' | 'invitation' + title: string + description: string | React.ReactNode + icon?: 'userPlus' | 'mail' | 'users' | 'error' | 'success' + actions?: Array<{ + label: string + onClick: () => void + variant?: 'default' | 'outline' | 'ghost' + disabled?: boolean + loading?: boolean + }> + isExpiredError?: boolean +} + +const iconMap = { + userPlus: UserPlus, + mail: Mail, + users: Users2, + error: ShieldX, + success: CheckCircle2, +} + +const iconColorMap = { + userPlus: 'text-[#701ffc]', + mail: 'text-[#701ffc]', + users: 'text-[#701ffc]', + error: 'text-red-500 dark:text-red-400', + success: 'text-green-500 dark:text-green-400', +} + +const iconBgMap = { + userPlus: 'bg-[#701ffc]/10', + mail: 'bg-[#701ffc]/10', + users: 'bg-[#701ffc]/10', + error: 'bg-red-50 dark:bg-red-950/20', + success: 'bg-green-50 dark:bg-green-950/20', +} + +export function InviteStatusCard({ + type, + title, + description, + icon, + actions = [], + isExpiredError = false, +}: InviteStatusCardProps) { + const router = useRouter() + + if (type === 'loading') { + return ( +
+ +

{description}

+
+ ) + } + + const IconComponent = icon ? iconMap[icon] : null + const iconColor = icon ? iconColorMap[icon] : '' + const iconBg = icon ? iconBgMap[icon] : '' + + return ( +
+ {IconComponent && ( +
+ +
+ )} + +

{title}

+ +

{description}

+ +
+ {isExpiredError && ( + + )} + + {actions.map((action, index) => ( + + ))} +
+
+ ) +} diff --git a/apps/sim/app/invite/invite-error/invite-error.tsx b/apps/sim/app/invite/invite-error/invite-error.tsx deleted file mode 100644 index 8169091f21..0000000000 --- a/apps/sim/app/invite/invite-error/invite-error.tsx +++ /dev/null @@ -1,116 +0,0 @@ -'use client' - -import { useEffect, useState } from 'react' -import { RotateCcw, ShieldX } from 'lucide-react' -import Image from 'next/image' -import Link from 'next/link' -import { useSearchParams } from 'next/navigation' -import { Button } from '@/components/ui/button' -import { useBrandConfig } from '@/lib/branding/branding' - -function getErrorMessage(reason: string, details?: string): string { - switch (reason) { - case 'missing-token': - return 'The invitation link is invalid or missing a required parameter.' - case 'invalid-token': - return 'The invitation link is invalid or has already been used.' - case 'expired': - return 'This invitation has expired. Please ask for a new invitation.' - case 'already-processed': - return 'This invitation has already been accepted or declined.' - case 'email-mismatch': - return details - ? details - : 'This invitation was sent to a different email address than the one you are logged in with.' - case 'workspace-not-found': - return 'The workspace associated with this invitation could not be found.' - case 'user-not-found': - return 'Your user account could not be found. Please try logging out and logging back in.' - case 'already-member': - return 'You are already a member of this organization or workspace.' - case 'invalid-invitation': - return 'This invitation is invalid or no longer exists.' - case 'missing-invitation-id': - return 'The invitation link is missing required information. Please use the original invitation link.' - case 'server-error': - return 'An unexpected error occurred while processing your invitation. Please try again later.' - default: - return 'An unknown error occurred while processing your invitation.' - } -} - -export default function InviteError() { - const searchParams = useSearchParams() - const reason = searchParams?.get('reason') || 'unknown' - const details = searchParams?.get('details') - const [errorMessage, setErrorMessage] = useState('') - const brandConfig = useBrandConfig() - - useEffect(() => { - // Only set the error message on the client side - setErrorMessage(getErrorMessage(reason, details || undefined)) - }, [reason, details]) - - // Provide a fallback message for SSR - const displayMessage = errorMessage || 'Loading error details...' - - const isExpiredError = reason === 'expired' - - return ( -
- {/* Logo */} -
- Sim Logo -
- -
-
- -
- -

Invitation Error

- -

- {displayMessage} -

- -
- {isExpiredError && ( - - )} - - -
-
- - -
- ) -} diff --git a/apps/sim/app/invite/invite-error/page.tsx b/apps/sim/app/invite/invite-error/page.tsx deleted file mode 100644 index 646f4a3d8e..0000000000 --- a/apps/sim/app/invite/invite-error/page.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import InviteError from '@/app/invite/invite-error/invite-error' - -export const dynamic = 'force-dynamic' - -export default function InviteErrorPage() { - return -} diff --git a/apps/sim/app/unsubscribe/page.tsx b/apps/sim/app/unsubscribe/page.tsx index 658de1ee5d..c9ca1f2693 100644 --- a/apps/sim/app/unsubscribe/page.tsx +++ b/apps/sim/app/unsubscribe/page.tsx @@ -1,401 +1,3 @@ -'use client' +import Unsubscribe from './unsubscribe' -import { Suspense, useEffect, useState } from 'react' -import { CheckCircle, Heart, Info, Loader2, XCircle } from 'lucide-react' -import { useSearchParams } from 'next/navigation' -import { Button, Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui' -import { useBrandConfig } from '@/lib/branding/branding' - -interface UnsubscribeData { - success: boolean - email: string - token: string - emailType: string - isTransactional: boolean - currentPreferences: { - unsubscribeAll?: boolean - unsubscribeMarketing?: boolean - unsubscribeUpdates?: boolean - unsubscribeNotifications?: boolean - } -} - -function UnsubscribeContent() { - const searchParams = useSearchParams() - const [loading, setLoading] = useState(true) - const [data, setData] = useState(null) - const [error, setError] = useState(null) - const [processing, setProcessing] = useState(false) - const [unsubscribed, setUnsubscribed] = useState(false) - const brand = useBrandConfig() - - const email = searchParams.get('email') - const token = searchParams.get('token') - - useEffect(() => { - if (!email || !token) { - setError('Missing email or token in URL') - setLoading(false) - return - } - - // Validate the unsubscribe link - fetch( - `/api/users/me/settings/unsubscribe?email=${encodeURIComponent(email)}&token=${encodeURIComponent(token)}` - ) - .then((res) => res.json()) - .then((data) => { - if (data.success) { - setData(data) - } else { - setError(data.error || 'Invalid unsubscribe link') - } - }) - .catch(() => { - setError('Failed to validate unsubscribe link') - }) - .finally(() => { - setLoading(false) - }) - }, [email, token]) - - const handleUnsubscribe = async (type: 'all' | 'marketing' | 'updates' | 'notifications') => { - if (!email || !token) return - - setProcessing(true) - - try { - const response = await fetch('/api/users/me/settings/unsubscribe', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - email, - token, - type, - }), - }) - - const result = await response.json() - - if (result.success) { - setUnsubscribed(true) - // Update the data to reflect the change - if (data) { - // Type-safe property construction with validation - const validTypes = ['all', 'marketing', 'updates', 'notifications'] as const - if (validTypes.includes(type)) { - if (type === 'all') { - setData({ - ...data, - currentPreferences: { - ...data.currentPreferences, - unsubscribeAll: true, - }, - }) - } else { - const propertyKey = `unsubscribe${type.charAt(0).toUpperCase()}${type.slice(1)}` as - | 'unsubscribeMarketing' - | 'unsubscribeUpdates' - | 'unsubscribeNotifications' - setData({ - ...data, - currentPreferences: { - ...data.currentPreferences, - [propertyKey]: true, - }, - }) - } - } - } - } else { - setError(result.error || 'Failed to unsubscribe') - } - } catch (error) { - setError('Failed to process unsubscribe request') - } finally { - setProcessing(false) - } - } - - if (loading) { - return ( -
- - - - - -
- ) - } - - if (error) { - return ( -
- - - - Invalid Unsubscribe Link - - This unsubscribe link is invalid or has expired - - - -
-

- Error: {error} -

-
- -
-

This could happen if:

-
    -
  • The link is missing required parameters
  • -
  • The link has expired or been used already
  • -
  • The link was copied incorrectly
  • -
-
- -
- - -
- -
-

- Need immediate help? Email us at{' '} - - {brand.supportEmail} - -

-
-
-
-
- ) - } - - // Handle transactional emails - if (data?.isTransactional) { - return ( -
- - - - Important Account Emails - - This email contains important information about your account - - - -
-

- Transactional emails like password resets, account confirmations, - and security alerts cannot be unsubscribed from as they contain essential - information for your account security and functionality. -

-
- -
-

- If you no longer wish to receive these emails, you can: -

-
    -
  • Close your account entirely
  • -
  • Contact our support team for assistance
  • -
-
- -
- - -
-
-
-
- ) - } - - if (unsubscribed) { - return ( -
- - - - Successfully Unsubscribed - - You have been unsubscribed from our emails. You will stop receiving emails within 48 - hours. - - - -

- If you change your mind, you can always update your email preferences in your account - settings or contact us at{' '} - - {brand.supportEmail} - -

-
-
-
- ) - } - - return ( -
- - - - We're sorry to see you go! - - We understand email preferences are personal. Choose which emails you'd like to - stop receiving from Sim. - -
-

- Email: {data?.email} -

-
-
- -
- - -
- or choose specific types: -
- - - - - - -
- -
-
-

- Note: You'll continue receiving important account emails like - password resets and security alerts. -

-
- -

- Questions? Contact us at{' '} - - {brand.supportEmail} - -

-
-
-
-
- ) -} - -export default function UnsubscribePage() { - return ( - - - - - - - - } - > - - - ) -} +export default Unsubscribe diff --git a/apps/sim/app/unsubscribe/unsubscribe.tsx b/apps/sim/app/unsubscribe/unsubscribe.tsx new file mode 100644 index 0000000000..58de6e18b0 --- /dev/null +++ b/apps/sim/app/unsubscribe/unsubscribe.tsx @@ -0,0 +1,401 @@ +'use client' + +import { Suspense, useEffect, useState } from 'react' +import { CheckCircle, Heart, Info, Loader2, XCircle } from 'lucide-react' +import { useSearchParams } from 'next/navigation' +import { Button, Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui' +import { useBrandConfig } from '@/lib/branding/branding' + +interface UnsubscribeData { + success: boolean + email: string + token: string + emailType: string + isTransactional: boolean + currentPreferences: { + unsubscribeAll?: boolean + unsubscribeMarketing?: boolean + unsubscribeUpdates?: boolean + unsubscribeNotifications?: boolean + } +} + +function UnsubscribeContent() { + const searchParams = useSearchParams() + const [loading, setLoading] = useState(true) + const [data, setData] = useState(null) + const [error, setError] = useState(null) + const [processing, setProcessing] = useState(false) + const [unsubscribed, setUnsubscribed] = useState(false) + const brand = useBrandConfig() + + const email = searchParams.get('email') + const token = searchParams.get('token') + + useEffect(() => { + if (!email || !token) { + setError('Missing email or token in URL') + setLoading(false) + return + } + + // Validate the unsubscribe link + fetch( + `/api/users/me/settings/unsubscribe?email=${encodeURIComponent(email)}&token=${encodeURIComponent(token)}` + ) + .then((res) => res.json()) + .then((data) => { + if (data.success) { + setData(data) + } else { + setError(data.error || 'Invalid unsubscribe link') + } + }) + .catch(() => { + setError('Failed to validate unsubscribe link') + }) + .finally(() => { + setLoading(false) + }) + }, [email, token]) + + const handleUnsubscribe = async (type: 'all' | 'marketing' | 'updates' | 'notifications') => { + if (!email || !token) return + + setProcessing(true) + + try { + const response = await fetch('/api/users/me/settings/unsubscribe', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + email, + token, + type, + }), + }) + + const result = await response.json() + + if (result.success) { + setUnsubscribed(true) + // Update the data to reflect the change + if (data) { + // Type-safe property construction with validation + const validTypes = ['all', 'marketing', 'updates', 'notifications'] as const + if (validTypes.includes(type)) { + if (type === 'all') { + setData({ + ...data, + currentPreferences: { + ...data.currentPreferences, + unsubscribeAll: true, + }, + }) + } else { + const propertyKey = `unsubscribe${type.charAt(0).toUpperCase()}${type.slice(1)}` as + | 'unsubscribeMarketing' + | 'unsubscribeUpdates' + | 'unsubscribeNotifications' + setData({ + ...data, + currentPreferences: { + ...data.currentPreferences, + [propertyKey]: true, + }, + }) + } + } + } + } else { + setError(result.error || 'Failed to unsubscribe') + } + } catch (error) { + setError('Failed to process unsubscribe request') + } finally { + setProcessing(false) + } + } + + if (loading) { + return ( +
+ + + + + +
+ ) + } + + if (error) { + return ( +
+ + + + Invalid Unsubscribe Link + + This unsubscribe link is invalid or has expired + + + +
+

+ Error: {error} +

+
+ +
+

This could happen if:

+
    +
  • The link is missing required parameters
  • +
  • The link has expired or been used already
  • +
  • The link was copied incorrectly
  • +
+
+ +
+ + +
+ +
+

+ Need immediate help? Email us at{' '} + + {brand.supportEmail} + +

+
+
+
+
+ ) + } + + // Handle transactional emails + if (data?.isTransactional) { + return ( +
+ + + + Important Account Emails + + This email contains important information about your account + + + +
+

+ Transactional emails like password resets, account confirmations, + and security alerts cannot be unsubscribed from as they contain essential + information for your account security and functionality. +

+
+ +
+

+ If you no longer wish to receive these emails, you can: +

+
    +
  • Close your account entirely
  • +
  • Contact our support team for assistance
  • +
+
+ +
+ + +
+
+
+
+ ) + } + + if (unsubscribed) { + return ( +
+ + + + Successfully Unsubscribed + + You have been unsubscribed from our emails. You will stop receiving emails within 48 + hours. + + + +

+ If you change your mind, you can always update your email preferences in your account + settings or contact us at{' '} + + {brand.supportEmail} + +

+
+
+
+ ) + } + + return ( +
+ + + + We're sorry to see you go! + + We understand email preferences are personal. Choose which emails you'd like to + stop receiving from Sim. + +
+

+ Email: {data?.email} +

+
+
+ +
+ + +
+ or choose specific types: +
+ + + + + + +
+ +
+
+

+ Note: You'll continue receiving important account emails like + password resets and security alerts. +

+
+ +

+ Questions? Contact us at{' '} + + {brand.supportEmail} + +

+
+
+
+
+ ) +} + +export default function Unsubscribe() { + return ( + + + + + + + + } + > + + + ) +} diff --git a/apps/sim/app/workspace/[workspaceId]/logs/components/filters/components/workflow.tsx b/apps/sim/app/workspace/[workspaceId]/logs/components/filters/components/workflow.tsx index fbf4e5357e..90fa03a6df 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/components/filters/components/workflow.tsx +++ b/apps/sim/app/workspace/[workspaceId]/logs/components/filters/components/workflow.tsx @@ -26,7 +26,7 @@ export default function Workflow() { const fetchWorkflows = async () => { try { setLoading(true) - const response = await fetch('/api/workflows/sync') + const response = await fetch('/api/workflows') if (response.ok) { const { data } = await response.json() const workflowOptions: WorkflowOption[] = data.map((workflow: any) => ({ diff --git a/apps/sim/app/workspace/[workspaceId]/logs/components/frozen-canvas/frozen-canvas.tsx b/apps/sim/app/workspace/[workspaceId]/logs/components/frozen-canvas/frozen-canvas.tsx index b506bfefac..9adb54cdf6 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/components/frozen-canvas/frozen-canvas.tsx +++ b/apps/sim/app/workspace/[workspaceId]/logs/components/frozen-canvas/frozen-canvas.tsx @@ -502,7 +502,7 @@ export function FrozenCanvas({ setLoading(true) setError(null) - const response = await fetch(`/api/logs/${executionId}/frozen-canvas`) + const response = await fetch(`/api/logs/execution/${executionId}`) if (!response.ok) { throw new Error(`Failed to fetch frozen canvas data: ${response.statusText}`) } diff --git a/apps/sim/app/workspace/[workspaceId]/logs/logs.tsx b/apps/sim/app/workspace/[workspaceId]/logs/logs.tsx index 68a46a7d9e..aa51ac7ef0 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/logs.tsx +++ b/apps/sim/app/workspace/[workspaceId]/logs/logs.tsx @@ -161,7 +161,7 @@ export default function Logs() { Promise.all( idsToFetch.map(async ({ id, merge }) => { try { - const res = await fetch(`/api/logs/by-id/${id}`, { signal: controller.signal }) + const res = await fetch(`/api/logs/${id}`, { signal: controller.signal }) if (!res.ok) return const body = await res.json() const detailed = body?.data @@ -216,7 +216,7 @@ export default function Logs() { Promise.all( idsToFetch.map(async ({ id, merge }) => { try { - const res = await fetch(`/api/logs/by-id/${id}`, { signal: controller.signal }) + const res = await fetch(`/api/logs/${id}`, { signal: controller.signal }) if (!res.ok) return const body = await res.json() const detailed = body?.data @@ -274,7 +274,7 @@ export default function Logs() { Promise.all( idsToFetch.map(async ({ id, merge }) => { try { - const res = await fetch(`/api/logs/by-id/${id}`, { signal: controller.signal }) + const res = await fetch(`/api/logs/${id}`, { signal: controller.signal }) if (!res.ok) return const body = await res.json() const detailed = body?.data diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/deploy-modal/components/deployment-info/components/example-command/example-command.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/deploy-modal/components/deployment-info/components/example-command/example-command.tsx index 62d220c9c8..fd2b7d677b 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/deploy-modal/components/deployment-info/components/example-command/example-command.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/components/deploy-modal/components/deployment-info/components/example-command/example-command.tsx @@ -79,7 +79,7 @@ export function ExampleCommand({ case 'rate-limits': { const baseUrlForRateLimit = baseEndpoint.split('/api/workflows/')[0] return `curl -H "X-API-Key: ${apiKey}" \\ - ${baseUrlForRateLimit}/api/users/rate-limit` + ${baseUrlForRateLimit}/api/users/me/rate-limit` } default: @@ -119,7 +119,7 @@ export function ExampleCommand({ case 'rate-limits': { const baseUrlForRateLimit = baseEndpoint.split('/api/workflows/')[0] return `curl -H "X-API-Key: SIM_API_KEY" \\ - ${baseUrlForRateLimit}/api/users/rate-limit` + ${baseUrlForRateLimit}/api/users/me/rate-limit` } default: diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/control-bar.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/control-bar.tsx index b453c54139..4eb3d8e46d 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/control-bar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/control-bar/control-bar.tsx @@ -321,7 +321,7 @@ export function ControlBar({ hasValidationErrors = false }: ControlBarProps) { try { // Primary: call server-side usage check to mirror backend enforcement - const res = await fetch('/api/usage/check', { cache: 'no-store' }) + const res = await fetch('/api/usage?context=user', { cache: 'no-store' }) if (res.ok) { const payload = await res.json() const usage = payload?.data diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/user-input.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/user-input.tsx index 756b7c8965..4e763ea3dd 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/user-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/user-input.tsx @@ -385,7 +385,7 @@ const UserInput = forwardRef( if (isLoadingWorkflows || workflows.length > 0) return try { setIsLoadingWorkflows(true) - const resp = await fetch('/api/workflows/sync') + const resp = await fetch('/api/workflows') if (!resp.ok) throw new Error(`Failed to load workflows: ${resp.status}`) const data = await resp.json() const items = Array.isArray(data?.data) ? data.data : [] diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/credential-selector/credential-selector.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/credential-selector/credential-selector.tsx index 6d5f05a8a5..ca5e3df6b4 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/credential-selector/credential-selector.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/credential-selector/credential-selector.tsx @@ -279,7 +279,10 @@ export function CredentialSelector({ - + {isLoading ? ( diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/trigger-config/components/trigger-config-section.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/trigger-config/components/trigger-config-section.tsx index 333fc1ab13..448a1ca4fb 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/trigger-config/components/trigger-config-section.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/components/sub-block/components/trigger-config/components/trigger-config-section.tsx @@ -149,9 +149,15 @@ export function TriggerConfigSection({ - - - + + + e.stopPropagation()} + > {availableOptions.length === 0 ? 'No options available. Please select credentials first.' diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/environment/environment.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/environment/environment.tsx index 51e6a3cb9d..c991c07be0 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/environment/environment.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/environment/environment.tsx @@ -23,7 +23,6 @@ import type { EnvironmentVariable as StoreEnvironmentVariable } from '@/stores/s const logger = createLogger('EnvironmentVariables') -// Constants const GRID_COLS = 'grid grid-cols-[minmax(0,1fr),minmax(0,1fr),88px] gap-4' const INITIAL_ENV_VAR: UIEnvironmentVariable = { key: '', value: '' } @@ -66,7 +65,6 @@ export function EnvironmentVariables({ const pendingClose = useRef(false) const initialVarsRef = useRef([]) - // Filter environment variables based on search term const filteredEnvVars = useMemo(() => { if (!searchTerm.trim()) { return envVars.map((envVar, index) => ({ envVar, originalIndex: index })) @@ -77,7 +75,6 @@ export function EnvironmentVariables({ .filter(({ envVar }) => envVar.key.toLowerCase().includes(searchTerm.toLowerCase())) }, [envVars, searchTerm]) - // Derived state const hasChanges = useMemo(() => { const initialVars = initialVarsRef.current.filter((v) => v.key || v.value) const currentVars = envVars.filter((v) => v.key || v.value) @@ -96,7 +93,6 @@ export function EnvironmentVariables({ if (!currentMap.has(key)) return true } - // Workspace diffs const before = initialWorkspaceVarsRef.current const after = workspaceVars const beforeKeys = Object.keys(before) @@ -109,12 +105,10 @@ export function EnvironmentVariables({ return false }, [envVars, workspaceVars]) - // Check if there are any active conflicts const hasConflicts = useMemo(() => { return envVars.some((envVar) => !!envVar.key && Object.hasOwn(workspaceVars, envVar.key)) }, [envVars, workspaceVars]) - // Intercept close attempts to check for unsaved changes const handleModalClose = (open: boolean) => { if (!open && hasChanges) { setShowUnsavedChanges(true) @@ -124,7 +118,6 @@ export function EnvironmentVariables({ } } - // Initialization effect useEffect(() => { const existingVars = Object.values(variables) const initialVars = existingVars.length ? existingVars : [INITIAL_ENV_VAR] @@ -158,14 +151,12 @@ export function EnvironmentVariables({ } }, [workspaceId, loadWorkspaceEnvironment]) - // Register close handler with parent useEffect(() => { if (registerCloseHandler) { registerCloseHandler(handleModalClose) } }, [registerCloseHandler, hasChanges]) - // Scroll effect - only when explicitly adding a new variable useEffect(() => { if (shouldScrollToBottom && scrollContainerRef.current) { scrollContainerRef.current.scrollTo({ @@ -322,8 +313,7 @@ export function EnvironmentVariables({ }), {} ) - - useEnvironmentStore.getState().setVariables(validVariables) + await useEnvironmentStore.getState().saveEnvironmentVariables(validVariables) const before = initialWorkspaceVarsRef.current const after = workspaceVars @@ -354,7 +344,6 @@ export function EnvironmentVariables({ } } - // UI rendering const renderEnvVarRow = (envVar: UIEnvironmentVariable, originalIndex: number) => { const isConflict = !!envVar.key && Object.hasOwn(workspaceVars, envVar.key) return ( diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/usage-limit/usage-limit.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/usage-limit/usage-limit.tsx index 4f64d889bf..186cc874d6 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/usage-limit/usage-limit.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/usage-limit/usage-limit.tsx @@ -113,7 +113,7 @@ export const UsageLimit = forwardRef( throw new Error('Organization ID is required') } - const response = await fetch('/api/usage-limits', { + const response = await fetch('/api/usage', { method: 'PUT', headers: { 'Content-Type': 'application/json', diff --git a/apps/sim/blocks/blocks/agent.ts b/apps/sim/blocks/blocks/agent.ts index 8dfc9bccce..3d52aa3d18 100644 --- a/apps/sim/blocks/blocks/agent.ts +++ b/apps/sim/blocks/blocks/agent.ts @@ -16,11 +16,10 @@ import { // Get current Ollama models dynamically const getCurrentOllamaModels = () => { - return useOllamaStore.getState().models + return useProvidersStore.getState().providers.ollama.models } -import { useOllamaStore } from '@/stores/ollama/store' -import { useOpenRouterStore } from '@/stores/openrouter/store' +import { useProvidersStore } from '@/stores/providers/store' import type { ToolResponse } from '@/tools/types' const logger = createLogger('AgentBlock') @@ -158,8 +157,9 @@ Create a system prompt appropriately detailed for the request, using clear langu placeholder: 'Type or select a model...', required: true, options: () => { - const ollamaModels = useOllamaStore.getState().models - const openrouterModels = useOpenRouterStore.getState().models + const providersState = useProvidersStore.getState() + const ollamaModels = providersState.providers.ollama.models + const openrouterModels = providersState.providers.openrouter.models const baseModels = Object.keys(getBaseModelProviders()) const allModels = Array.from(new Set([...baseModels, ...ollamaModels, ...openrouterModels])) diff --git a/apps/sim/blocks/blocks/evaluator.ts b/apps/sim/blocks/blocks/evaluator.ts index 0f3e05ea81..52e0899ed9 100644 --- a/apps/sim/blocks/blocks/evaluator.ts +++ b/apps/sim/blocks/blocks/evaluator.ts @@ -4,7 +4,7 @@ import { createLogger } from '@/lib/logs/console/logger' import type { BlockConfig, ParamType } from '@/blocks/types' import type { ProviderId } from '@/providers/types' import { getAllModelProviders, getBaseModelProviders, getHostedModels } from '@/providers/utils' -import { useOllamaStore } from '@/stores/ollama/store' +import { useProvidersStore } from '@/stores/providers/store' import type { ToolResponse } from '@/tools/types' const logger = createLogger('EvaluatorBlock') @@ -177,7 +177,7 @@ export const EvaluatorBlock: BlockConfig = { layout: 'half', required: true, options: () => { - const ollamaModels = useOllamaStore.getState().models + const ollamaModels = useProvidersStore.getState().providers.ollama.models const baseModels = Object.keys(getBaseModelProviders()) return [...baseModels, ...ollamaModels].map((model) => ({ label: model, diff --git a/apps/sim/blocks/blocks/router.ts b/apps/sim/blocks/blocks/router.ts index 825dec5a03..7fe72de2f2 100644 --- a/apps/sim/blocks/blocks/router.ts +++ b/apps/sim/blocks/blocks/router.ts @@ -3,7 +3,7 @@ import { isHosted } from '@/lib/environment' import type { BlockConfig } from '@/blocks/types' import type { ProviderId } from '@/providers/types' import { getAllModelProviders, getBaseModelProviders, getHostedModels } from '@/providers/utils' -import { useOllamaStore } from '@/stores/ollama/store' +import { useProvidersStore } from '@/stores/providers/store' import type { ToolResponse } from '@/tools/types' interface RouterResponse extends ToolResponse { @@ -119,7 +119,7 @@ export const RouterBlock: BlockConfig = { type: 'dropdown', layout: 'half', options: () => { - const ollamaModels = useOllamaStore.getState().models + const ollamaModels = useProvidersStore.getState().providers.ollama.models const baseModels = Object.keys(getBaseModelProviders()) return [...baseModels, ...ollamaModels].map((model) => ({ label: model, diff --git a/apps/sim/components/emails/workspace-invitation.tsx b/apps/sim/components/emails/workspace-invitation.tsx index ce03cb8576..81834cecbd 100644 --- a/apps/sim/components/emails/workspace-invitation.tsx +++ b/apps/sim/components/emails/workspace-invitation.tsx @@ -39,8 +39,11 @@ export const WorkspaceInvitationEmail = ({ let enhancedLink = invitationLink try { - // If the link is pointing to the API endpoint directly, update it to use the client route - if (invitationLink.includes('/api/workspaces/invitations/accept')) { + // If the link is pointing to any API endpoint directly, update it to use the client route + if ( + invitationLink.includes('/api/workspaces/invitations/accept') || + invitationLink.match(/\/api\/workspaces\/invitations\/[^?]+\?token=/) + ) { const url = new URL(invitationLink) const token = url.searchParams.get('token') if (token) { diff --git a/apps/sim/components/ui/switch.tsx b/apps/sim/components/ui/switch.tsx index 7ea9f2a74d..f07fc75f05 100644 --- a/apps/sim/components/ui/switch.tsx +++ b/apps/sim/components/ui/switch.tsx @@ -10,7 +10,7 @@ const Switch = React.forwardRef< >(({ className, ...props }, ref) => ( statement-breakpoint +ALTER TABLE "workspace_invitation" ALTER COLUMN "status" SET DEFAULT 'pending'::"public"."workspace_invitation_status";--> statement-breakpoint +ALTER TABLE "workspace_invitation" ALTER COLUMN "status" SET DATA TYPE "public"."workspace_invitation_status" USING "status"::"public"."workspace_invitation_status"; \ No newline at end of file diff --git a/apps/sim/db/migrations/meta/0083_snapshot.json b/apps/sim/db/migrations/meta/0083_snapshot.json new file mode 100644 index 0000000000..e4f270a28a --- /dev/null +++ b/apps/sim/db/migrations/meta/0083_snapshot.json @@ -0,0 +1,6049 @@ +{ + "id": "f4d42c71-1238-4445-b29d-7cc74d1f748c", + "prevId": "5466d2f8-8b28-47d3-816f-07bbb3721d3d", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "account_user_id_idx": { + "name": "account_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.api_key": { + "name": "api_key", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "last_used": { + "name": "last_used", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "api_key_user_id_user_id_fk": { + "name": "api_key_user_id_user_id_fk", + "tableFrom": "api_key", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "api_key_key_unique": { + "name": "api_key_key_unique", + "nullsNotDistinct": false, + "columns": ["key"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat": { + "name": "chat", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subdomain": { + "name": "subdomain", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "customizations": { + "name": "customizations", + "type": "json", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "auth_type": { + "name": "auth_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'public'" + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "allowed_emails": { + "name": "allowed_emails", + "type": "json", + "primaryKey": false, + "notNull": false, + "default": "'[]'" + }, + "output_configs": { + "name": "output_configs", + "type": "json", + "primaryKey": false, + "notNull": false, + "default": "'[]'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "subdomain_idx": { + "name": "subdomain_idx", + "columns": [ + { + "expression": "subdomain", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chat_workflow_id_workflow_id_fk": { + "name": "chat_workflow_id_workflow_id_fk", + "tableFrom": "chat", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_user_id_user_id_fk": { + "name": "chat_user_id_user_id_fk", + "tableFrom": "chat", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.copilot_chats": { + "name": "copilot_chats", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "messages": { + "name": "messages", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'" + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'claude-3-7-sonnet-latest'" + }, + "conversation_id": { + "name": "conversation_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "preview_yaml": { + "name": "preview_yaml", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "copilot_chats_user_id_idx": { + "name": "copilot_chats_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_chats_workflow_id_idx": { + "name": "copilot_chats_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_chats_user_workflow_idx": { + "name": "copilot_chats_user_workflow_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_chats_created_at_idx": { + "name": "copilot_chats_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_chats_updated_at_idx": { + "name": "copilot_chats_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "copilot_chats_user_id_user_id_fk": { + "name": "copilot_chats_user_id_user_id_fk", + "tableFrom": "copilot_chats", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "copilot_chats_workflow_id_workflow_id_fk": { + "name": "copilot_chats_workflow_id_workflow_id_fk", + "tableFrom": "copilot_chats", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.copilot_feedback": { + "name": "copilot_feedback", + "schema": "", + "columns": { + "feedback_id": { + "name": "feedback_id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_query": { + "name": "user_query", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "agent_response": { + "name": "agent_response", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_positive": { + "name": "is_positive", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "feedback": { + "name": "feedback", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workflow_yaml": { + "name": "workflow_yaml", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "copilot_feedback_user_id_idx": { + "name": "copilot_feedback_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_feedback_chat_id_idx": { + "name": "copilot_feedback_chat_id_idx", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_feedback_user_chat_idx": { + "name": "copilot_feedback_user_chat_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_feedback_is_positive_idx": { + "name": "copilot_feedback_is_positive_idx", + "columns": [ + { + "expression": "is_positive", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "copilot_feedback_created_at_idx": { + "name": "copilot_feedback_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "copilot_feedback_user_id_user_id_fk": { + "name": "copilot_feedback_user_id_user_id_fk", + "tableFrom": "copilot_feedback", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "copilot_feedback_chat_id_copilot_chats_id_fk": { + "name": "copilot_feedback_chat_id_copilot_chats_id_fk", + "tableFrom": "copilot_feedback", + "tableTo": "copilot_chats", + "columnsFrom": ["chat_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.custom_tools": { + "name": "custom_tools", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "schema": { + "name": "schema", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "custom_tools_user_id_user_id_fk": { + "name": "custom_tools_user_id_user_id_fk", + "tableFrom": "custom_tools", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.docs_embeddings": { + "name": "docs_embeddings", + "schema": "", + "columns": { + "chunk_id": { + "name": "chunk_id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "chunk_text": { + "name": "chunk_text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_document": { + "name": "source_document", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_link": { + "name": "source_link", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "header_text": { + "name": "header_text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "header_level": { + "name": "header_level", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "token_count": { + "name": "token_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "embedding": { + "name": "embedding", + "type": "vector(1536)", + "primaryKey": false, + "notNull": true + }, + "embedding_model": { + "name": "embedding_model", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'text-embedding-3-small'" + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "chunk_text_tsv": { + "name": "chunk_text_tsv", + "type": "tsvector", + "primaryKey": false, + "notNull": false, + "generated": { + "as": "to_tsvector('english', \"docs_embeddings\".\"chunk_text\")", + "type": "stored" + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "docs_emb_source_document_idx": { + "name": "docs_emb_source_document_idx", + "columns": [ + { + "expression": "source_document", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "docs_emb_header_level_idx": { + "name": "docs_emb_header_level_idx", + "columns": [ + { + "expression": "header_level", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "docs_emb_source_header_idx": { + "name": "docs_emb_source_header_idx", + "columns": [ + { + "expression": "source_document", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "header_level", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "docs_emb_model_idx": { + "name": "docs_emb_model_idx", + "columns": [ + { + "expression": "embedding_model", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "docs_emb_created_at_idx": { + "name": "docs_emb_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "docs_embedding_vector_hnsw_idx": { + "name": "docs_embedding_vector_hnsw_idx", + "columns": [ + { + "expression": "embedding", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "vector_cosine_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hnsw", + "with": { + "m": 16, + "ef_construction": 64 + } + }, + "docs_emb_metadata_gin_idx": { + "name": "docs_emb_metadata_gin_idx", + "columns": [ + { + "expression": "metadata", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + }, + "docs_emb_chunk_text_fts_idx": { + "name": "docs_emb_chunk_text_fts_idx", + "columns": [ + { + "expression": "chunk_text_tsv", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "docs_embedding_not_null_check": { + "name": "docs_embedding_not_null_check", + "value": "\"embedding\" IS NOT NULL" + }, + "docs_header_level_check": { + "name": "docs_header_level_check", + "value": "\"header_level\" >= 1 AND \"header_level\" <= 6" + } + }, + "isRLSEnabled": false + }, + "public.document": { + "name": "document", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "knowledge_base_id": { + "name": "knowledge_base_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "filename": { + "name": "filename", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_url": { + "name": "file_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_size": { + "name": "file_size", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chunk_count": { + "name": "chunk_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "token_count": { + "name": "token_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "character_count": { + "name": "character_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "processing_status": { + "name": "processing_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "processing_started_at": { + "name": "processing_started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "processing_completed_at": { + "name": "processing_completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "processing_error": { + "name": "processing_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "tag1": { + "name": "tag1", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag2": { + "name": "tag2", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag3": { + "name": "tag3", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag4": { + "name": "tag4", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag5": { + "name": "tag5", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag6": { + "name": "tag6", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag7": { + "name": "tag7", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "uploaded_at": { + "name": "uploaded_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "doc_kb_id_idx": { + "name": "doc_kb_id_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_filename_idx": { + "name": "doc_filename_idx", + "columns": [ + { + "expression": "filename", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_kb_uploaded_at_idx": { + "name": "doc_kb_uploaded_at_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "uploaded_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_processing_status_idx": { + "name": "doc_processing_status_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "processing_status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_tag1_idx": { + "name": "doc_tag1_idx", + "columns": [ + { + "expression": "tag1", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_tag2_idx": { + "name": "doc_tag2_idx", + "columns": [ + { + "expression": "tag2", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_tag3_idx": { + "name": "doc_tag3_idx", + "columns": [ + { + "expression": "tag3", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_tag4_idx": { + "name": "doc_tag4_idx", + "columns": [ + { + "expression": "tag4", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_tag5_idx": { + "name": "doc_tag5_idx", + "columns": [ + { + "expression": "tag5", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_tag6_idx": { + "name": "doc_tag6_idx", + "columns": [ + { + "expression": "tag6", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "doc_tag7_idx": { + "name": "doc_tag7_idx", + "columns": [ + { + "expression": "tag7", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "document_knowledge_base_id_knowledge_base_id_fk": { + "name": "document_knowledge_base_id_knowledge_base_id_fk", + "tableFrom": "document", + "tableTo": "knowledge_base", + "columnsFrom": ["knowledge_base_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.embedding": { + "name": "embedding", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "knowledge_base_id": { + "name": "knowledge_base_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "document_id": { + "name": "document_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chunk_index": { + "name": "chunk_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "chunk_hash": { + "name": "chunk_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content_length": { + "name": "content_length", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "token_count": { + "name": "token_count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "embedding": { + "name": "embedding", + "type": "vector(1536)", + "primaryKey": false, + "notNull": false + }, + "embedding_model": { + "name": "embedding_model", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'text-embedding-3-small'" + }, + "start_offset": { + "name": "start_offset", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "end_offset": { + "name": "end_offset", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "tag1": { + "name": "tag1", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag2": { + "name": "tag2", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag3": { + "name": "tag3", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag4": { + "name": "tag4", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag5": { + "name": "tag5", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag6": { + "name": "tag6", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tag7": { + "name": "tag7", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "content_tsv": { + "name": "content_tsv", + "type": "tsvector", + "primaryKey": false, + "notNull": false, + "generated": { + "as": "to_tsvector('english', \"embedding\".\"content\")", + "type": "stored" + } + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "emb_kb_id_idx": { + "name": "emb_kb_id_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_doc_id_idx": { + "name": "emb_doc_id_idx", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_doc_chunk_idx": { + "name": "emb_doc_chunk_idx", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "chunk_index", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_kb_model_idx": { + "name": "emb_kb_model_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "embedding_model", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_kb_enabled_idx": { + "name": "emb_kb_enabled_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_doc_enabled_idx": { + "name": "emb_doc_enabled_idx", + "columns": [ + { + "expression": "document_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "enabled", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "embedding_vector_hnsw_idx": { + "name": "embedding_vector_hnsw_idx", + "columns": [ + { + "expression": "embedding", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "vector_cosine_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hnsw", + "with": { + "m": 16, + "ef_construction": 64 + } + }, + "emb_tag1_idx": { + "name": "emb_tag1_idx", + "columns": [ + { + "expression": "tag1", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_tag2_idx": { + "name": "emb_tag2_idx", + "columns": [ + { + "expression": "tag2", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_tag3_idx": { + "name": "emb_tag3_idx", + "columns": [ + { + "expression": "tag3", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_tag4_idx": { + "name": "emb_tag4_idx", + "columns": [ + { + "expression": "tag4", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_tag5_idx": { + "name": "emb_tag5_idx", + "columns": [ + { + "expression": "tag5", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_tag6_idx": { + "name": "emb_tag6_idx", + "columns": [ + { + "expression": "tag6", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_tag7_idx": { + "name": "emb_tag7_idx", + "columns": [ + { + "expression": "tag7", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "emb_content_fts_idx": { + "name": "emb_content_fts_idx", + "columns": [ + { + "expression": "content_tsv", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": { + "embedding_knowledge_base_id_knowledge_base_id_fk": { + "name": "embedding_knowledge_base_id_knowledge_base_id_fk", + "tableFrom": "embedding", + "tableTo": "knowledge_base", + "columnsFrom": ["knowledge_base_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "embedding_document_id_document_id_fk": { + "name": "embedding_document_id_document_id_fk", + "tableFrom": "embedding", + "tableTo": "document", + "columnsFrom": ["document_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "embedding_not_null_check": { + "name": "embedding_not_null_check", + "value": "\"embedding\" IS NOT NULL" + } + }, + "isRLSEnabled": false + }, + "public.environment": { + "name": "environment", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "variables": { + "name": "variables", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "environment_user_id_user_id_fk": { + "name": "environment_user_id_user_id_fk", + "tableFrom": "environment", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "environment_user_id_unique": { + "name": "environment_user_id_unique", + "nullsNotDistinct": false, + "columns": ["user_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.invitation": { + "name": "invitation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "inviter_id": { + "name": "inviter_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "invitation_email_idx": { + "name": "invitation_email_idx", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "invitation_organization_id_idx": { + "name": "invitation_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "invitation_inviter_id_user_id_fk": { + "name": "invitation_inviter_id_user_id_fk", + "tableFrom": "invitation", + "tableTo": "user", + "columnsFrom": ["inviter_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "invitation_organization_id_organization_id_fk": { + "name": "invitation_organization_id_organization_id_fk", + "tableFrom": "invitation", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.knowledge_base": { + "name": "knowledge_base", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "token_count": { + "name": "token_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "embedding_model": { + "name": "embedding_model", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'text-embedding-3-small'" + }, + "embedding_dimension": { + "name": "embedding_dimension", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1536 + }, + "chunking_config": { + "name": "chunking_config", + "type": "json", + "primaryKey": false, + "notNull": true, + "default": "'{\"maxSize\": 1024, \"minSize\": 1, \"overlap\": 200}'" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "kb_user_id_idx": { + "name": "kb_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kb_workspace_id_idx": { + "name": "kb_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kb_user_workspace_idx": { + "name": "kb_user_workspace_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kb_deleted_at_idx": { + "name": "kb_deleted_at_idx", + "columns": [ + { + "expression": "deleted_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "knowledge_base_user_id_user_id_fk": { + "name": "knowledge_base_user_id_user_id_fk", + "tableFrom": "knowledge_base", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "knowledge_base_workspace_id_workspace_id_fk": { + "name": "knowledge_base_workspace_id_workspace_id_fk", + "tableFrom": "knowledge_base", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.knowledge_base_tag_definitions": { + "name": "knowledge_base_tag_definitions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "knowledge_base_id": { + "name": "knowledge_base_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tag_slot": { + "name": "tag_slot", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "field_type": { + "name": "field_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'text'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "kb_tag_definitions_kb_slot_idx": { + "name": "kb_tag_definitions_kb_slot_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "tag_slot", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kb_tag_definitions_kb_display_name_idx": { + "name": "kb_tag_definitions_kb_display_name_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "display_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "kb_tag_definitions_kb_id_idx": { + "name": "kb_tag_definitions_kb_id_idx", + "columns": [ + { + "expression": "knowledge_base_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "knowledge_base_tag_definitions_knowledge_base_id_knowledge_base_id_fk": { + "name": "knowledge_base_tag_definitions_knowledge_base_id_knowledge_base_id_fk", + "tableFrom": "knowledge_base_tag_definitions", + "tableTo": "knowledge_base", + "columnsFrom": ["knowledge_base_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.marketplace": { + "name": "marketplace", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "state": { + "name": "state", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "author_name": { + "name": "author_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "views": { + "name": "views", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "marketplace_workflow_id_workflow_id_fk": { + "name": "marketplace_workflow_id_workflow_id_fk", + "tableFrom": "marketplace", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "marketplace_author_id_user_id_fk": { + "name": "marketplace_author_id_user_id_fk", + "tableFrom": "marketplace", + "tableTo": "user", + "columnsFrom": ["author_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.member": { + "name": "member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "member_user_id_idx": { + "name": "member_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "member_organization_id_idx": { + "name": "member_organization_id_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "member_user_id_user_id_fk": { + "name": "member_user_id_user_id_fk", + "tableFrom": "member", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "member_organization_id_organization_id_fk": { + "name": "member_organization_id_organization_id_fk", + "tableFrom": "member", + "tableTo": "organization", + "columnsFrom": ["organization_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.memory": { + "name": "memory", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "memory_key_idx": { + "name": "memory_key_idx", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "memory_workflow_idx": { + "name": "memory_workflow_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "memory_workflow_key_idx": { + "name": "memory_workflow_key_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "memory_workflow_id_workflow_id_fk": { + "name": "memory_workflow_id_workflow_id_fk", + "tableFrom": "memory", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization": { + "name": "organization", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "logo": { + "name": "logo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "org_usage_limit": { + "name": "org_usage_limit", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.permissions": { + "name": "permissions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permission_type": { + "name": "permission_type", + "type": "permission_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "permissions_user_id_idx": { + "name": "permissions_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "permissions_entity_idx": { + "name": "permissions_entity_idx", + "columns": [ + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "permissions_user_entity_type_idx": { + "name": "permissions_user_entity_type_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "permissions_user_entity_permission_idx": { + "name": "permissions_user_entity_permission_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "permission_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "permissions_user_entity_idx": { + "name": "permissions_user_entity_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "permissions_unique_constraint": { + "name": "permissions_unique_constraint", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "permissions_user_id_user_id_fk": { + "name": "permissions_user_id_user_id_fk", + "tableFrom": "permissions", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "active_organization_id": { + "name": "active_organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "session_user_id_idx": { + "name": "session_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "session_token_idx": { + "name": "session_token_idx", + "columns": [ + { + "expression": "token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "session_active_organization_id_organization_id_fk": { + "name": "session_active_organization_id_organization_id_fk", + "tableFrom": "session", + "tableTo": "organization", + "columnsFrom": ["active_organization_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_token_unique": { + "name": "session_token_unique", + "nullsNotDistinct": false, + "columns": ["token"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.settings": { + "name": "settings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "theme": { + "name": "theme", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'system'" + }, + "auto_connect": { + "name": "auto_connect", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "auto_fill_env_vars": { + "name": "auto_fill_env_vars", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "auto_pan": { + "name": "auto_pan", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "console_expanded_by_default": { + "name": "console_expanded_by_default", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "telemetry_enabled": { + "name": "telemetry_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "email_preferences": { + "name": "email_preferences", + "type": "json", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "general": { + "name": "general", + "type": "json", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "settings_user_id_user_id_fk": { + "name": "settings_user_id_user_id_fk", + "tableFrom": "settings", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "settings_user_id_unique": { + "name": "settings_user_id_unique", + "nullsNotDistinct": false, + "columns": ["user_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.subscription": { + "name": "subscription", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "plan": { + "name": "plan", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripe_customer_id": { + "name": "stripe_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripe_subscription_id": { + "name": "stripe_subscription_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "period_start": { + "name": "period_start", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "period_end": { + "name": "period_end", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "cancel_at_period_end": { + "name": "cancel_at_period_end", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "seats": { + "name": "seats", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "trial_start": { + "name": "trial_start", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "trial_end": { + "name": "trial_end", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "json", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "subscription_reference_status_idx": { + "name": "subscription_reference_status_idx", + "columns": [ + { + "expression": "reference_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "check_enterprise_metadata": { + "name": "check_enterprise_metadata", + "value": "plan != 'enterprise' OR metadata IS NOT NULL" + } + }, + "isRLSEnabled": false + }, + "public.template_stars": { + "name": "template_stars", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "template_id": { + "name": "template_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "starred_at": { + "name": "starred_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "template_stars_user_id_idx": { + "name": "template_stars_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "template_stars_template_id_idx": { + "name": "template_stars_template_id_idx", + "columns": [ + { + "expression": "template_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "template_stars_user_template_idx": { + "name": "template_stars_user_template_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "template_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "template_stars_template_user_idx": { + "name": "template_stars_template_user_idx", + "columns": [ + { + "expression": "template_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "template_stars_starred_at_idx": { + "name": "template_stars_starred_at_idx", + "columns": [ + { + "expression": "starred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "template_stars_template_starred_at_idx": { + "name": "template_stars_template_starred_at_idx", + "columns": [ + { + "expression": "template_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "starred_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "template_stars_user_template_unique": { + "name": "template_stars_user_template_unique", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "template_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "template_stars_user_id_user_id_fk": { + "name": "template_stars_user_id_user_id_fk", + "tableFrom": "template_stars", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "template_stars_template_id_templates_id_fk": { + "name": "template_stars_template_id_templates_id_fk", + "tableFrom": "template_stars", + "tableTo": "templates", + "columnsFrom": ["template_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.templates": { + "name": "templates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "author": { + "name": "author", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "views": { + "name": "views", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "stars": { + "name": "stars", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'#3972F6'" + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'FileText'" + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "state": { + "name": "state", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "templates_workflow_id_idx": { + "name": "templates_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "templates_user_id_idx": { + "name": "templates_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "templates_category_idx": { + "name": "templates_category_idx", + "columns": [ + { + "expression": "category", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "templates_views_idx": { + "name": "templates_views_idx", + "columns": [ + { + "expression": "views", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "templates_stars_idx": { + "name": "templates_stars_idx", + "columns": [ + { + "expression": "stars", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "templates_category_views_idx": { + "name": "templates_category_views_idx", + "columns": [ + { + "expression": "category", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "views", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "templates_category_stars_idx": { + "name": "templates_category_stars_idx", + "columns": [ + { + "expression": "category", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "stars", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "templates_user_category_idx": { + "name": "templates_user_category_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "category", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "templates_created_at_idx": { + "name": "templates_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "templates_updated_at_idx": { + "name": "templates_updated_at_idx", + "columns": [ + { + "expression": "updated_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "templates_workflow_id_workflow_id_fk": { + "name": "templates_workflow_id_workflow_id_fk", + "tableFrom": "templates", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "templates_user_id_user_id_fk": { + "name": "templates_user_id_user_id_fk", + "tableFrom": "templates", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "stripe_customer_id": { + "name": "stripe_customer_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_email_unique": { + "name": "user_email_unique", + "nullsNotDistinct": false, + "columns": ["email"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_rate_limits": { + "name": "user_rate_limits", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "sync_api_requests": { + "name": "sync_api_requests", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "async_api_requests": { + "name": "async_api_requests", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "window_start": { + "name": "window_start", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "last_request_at": { + "name": "last_request_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "is_rate_limited": { + "name": "is_rate_limited", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "rate_limit_reset_at": { + "name": "rate_limit_reset_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "user_rate_limits_user_id_user_id_fk": { + "name": "user_rate_limits_user_id_user_id_fk", + "tableFrom": "user_rate_limits", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_stats": { + "name": "user_stats", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "total_manual_executions": { + "name": "total_manual_executions", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_api_calls": { + "name": "total_api_calls", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_webhook_triggers": { + "name": "total_webhook_triggers", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_scheduled_executions": { + "name": "total_scheduled_executions", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_chat_executions": { + "name": "total_chat_executions", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_tokens_used": { + "name": "total_tokens_used", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_cost": { + "name": "total_cost", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "current_usage_limit": { + "name": "current_usage_limit", + "type": "numeric", + "primaryKey": false, + "notNull": false, + "default": "'10'" + }, + "usage_limit_updated_at": { + "name": "usage_limit_updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "current_period_cost": { + "name": "current_period_cost", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "last_period_cost": { + "name": "last_period_cost", + "type": "numeric", + "primaryKey": false, + "notNull": false, + "default": "'0'" + }, + "total_copilot_cost": { + "name": "total_copilot_cost", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "total_copilot_tokens": { + "name": "total_copilot_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_copilot_calls": { + "name": "total_copilot_calls", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_active": { + "name": "last_active", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "billing_blocked": { + "name": "billing_blocked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "user_stats_user_id_user_id_fk": { + "name": "user_stats_user_id_user_id_fk", + "tableFrom": "user_stats", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_stats_user_id_unique": { + "name": "user_stats_user_id_unique", + "nullsNotDistinct": false, + "columns": ["user_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "verification_identifier_idx": { + "name": "verification_identifier_idx", + "columns": [ + { + "expression": "identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.waitlist": { + "name": "waitlist", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "waitlist_email_unique": { + "name": "waitlist_email_unique", + "nullsNotDistinct": false, + "columns": ["email"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.webhook": { + "name": "webhook", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "block_id": { + "name": "block_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "provider_config": { + "name": "provider_config", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "path_idx": { + "name": "path_idx", + "columns": [ + { + "expression": "path", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "webhook_workflow_id_workflow_id_fk": { + "name": "webhook_workflow_id_workflow_id_fk", + "tableFrom": "webhook", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "webhook_block_id_workflow_blocks_id_fk": { + "name": "webhook_block_id_workflow_blocks_id_fk", + "tableFrom": "webhook", + "tableTo": "workflow_blocks", + "columnsFrom": ["block_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow": { + "name": "workflow", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "folder_id": { + "name": "folder_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'#3972F6'" + }, + "last_synced": { + "name": "last_synced", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "is_deployed": { + "name": "is_deployed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "deployed_state": { + "name": "deployed_state", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "deployed_at": { + "name": "deployed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "pinned_api_key": { + "name": "pinned_api_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "collaborators": { + "name": "collaborators", + "type": "json", + "primaryKey": false, + "notNull": true, + "default": "'[]'" + }, + "run_count": { + "name": "run_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_run_at": { + "name": "last_run_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "variables": { + "name": "variables", + "type": "json", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "is_published": { + "name": "is_published", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "marketplace_data": { + "name": "marketplace_data", + "type": "json", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "workflow_user_id_idx": { + "name": "workflow_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_workspace_id_idx": { + "name": "workflow_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_user_workspace_idx": { + "name": "workflow_user_workspace_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_user_id_user_id_fk": { + "name": "workflow_user_id_user_id_fk", + "tableFrom": "workflow", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_workspace_id_workspace_id_fk": { + "name": "workflow_workspace_id_workspace_id_fk", + "tableFrom": "workflow", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_folder_id_workflow_folder_id_fk": { + "name": "workflow_folder_id_workflow_folder_id_fk", + "tableFrom": "workflow", + "tableTo": "workflow_folder", + "columnsFrom": ["folder_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_blocks": { + "name": "workflow_blocks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "position_x": { + "name": "position_x", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "position_y": { + "name": "position_y", + "type": "numeric", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "horizontal_handles": { + "name": "horizontal_handles", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "is_wide": { + "name": "is_wide", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "advanced_mode": { + "name": "advanced_mode", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "trigger_mode": { + "name": "trigger_mode", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "height": { + "name": "height", + "type": "numeric", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "sub_blocks": { + "name": "sub_blocks", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "outputs": { + "name": "outputs", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "parent_id": { + "name": "parent_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "extent": { + "name": "extent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_blocks_workflow_id_idx": { + "name": "workflow_blocks_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_blocks_parent_id_idx": { + "name": "workflow_blocks_parent_id_idx", + "columns": [ + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_blocks_workflow_parent_idx": { + "name": "workflow_blocks_workflow_parent_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_blocks_workflow_type_idx": { + "name": "workflow_blocks_workflow_type_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_blocks_workflow_id_workflow_id_fk": { + "name": "workflow_blocks_workflow_id_workflow_id_fk", + "tableFrom": "workflow_blocks", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_checkpoints": { + "name": "workflow_checkpoints", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "message_id": { + "name": "message_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workflow_state": { + "name": "workflow_state", + "type": "json", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_checkpoints_user_id_idx": { + "name": "workflow_checkpoints_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_checkpoints_workflow_id_idx": { + "name": "workflow_checkpoints_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_checkpoints_chat_id_idx": { + "name": "workflow_checkpoints_chat_id_idx", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_checkpoints_message_id_idx": { + "name": "workflow_checkpoints_message_id_idx", + "columns": [ + { + "expression": "message_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_checkpoints_user_workflow_idx": { + "name": "workflow_checkpoints_user_workflow_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_checkpoints_workflow_chat_idx": { + "name": "workflow_checkpoints_workflow_chat_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_checkpoints_created_at_idx": { + "name": "workflow_checkpoints_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_checkpoints_chat_created_at_idx": { + "name": "workflow_checkpoints_chat_created_at_idx", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_checkpoints_user_id_user_id_fk": { + "name": "workflow_checkpoints_user_id_user_id_fk", + "tableFrom": "workflow_checkpoints", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_checkpoints_workflow_id_workflow_id_fk": { + "name": "workflow_checkpoints_workflow_id_workflow_id_fk", + "tableFrom": "workflow_checkpoints", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_checkpoints_chat_id_copilot_chats_id_fk": { + "name": "workflow_checkpoints_chat_id_copilot_chats_id_fk", + "tableFrom": "workflow_checkpoints", + "tableTo": "copilot_chats", + "columnsFrom": ["chat_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_edges": { + "name": "workflow_edges", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_block_id": { + "name": "source_block_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "target_block_id": { + "name": "target_block_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_handle": { + "name": "source_handle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_handle": { + "name": "target_handle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_edges_workflow_id_idx": { + "name": "workflow_edges_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_edges_source_block_idx": { + "name": "workflow_edges_source_block_idx", + "columns": [ + { + "expression": "source_block_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_edges_target_block_idx": { + "name": "workflow_edges_target_block_idx", + "columns": [ + { + "expression": "target_block_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_edges_workflow_source_idx": { + "name": "workflow_edges_workflow_source_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "source_block_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_edges_workflow_target_idx": { + "name": "workflow_edges_workflow_target_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "target_block_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_edges_workflow_id_workflow_id_fk": { + "name": "workflow_edges_workflow_id_workflow_id_fk", + "tableFrom": "workflow_edges", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_edges_source_block_id_workflow_blocks_id_fk": { + "name": "workflow_edges_source_block_id_workflow_blocks_id_fk", + "tableFrom": "workflow_edges", + "tableTo": "workflow_blocks", + "columnsFrom": ["source_block_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_edges_target_block_id_workflow_blocks_id_fk": { + "name": "workflow_edges_target_block_id_workflow_blocks_id_fk", + "tableFrom": "workflow_edges", + "tableTo": "workflow_blocks", + "columnsFrom": ["target_block_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_execution_logs": { + "name": "workflow_execution_logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "execution_id": { + "name": "execution_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "state_snapshot_id": { + "name": "state_snapshot_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "level": { + "name": "level", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "trigger": { + "name": "trigger", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ended_at": { + "name": "ended_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "total_duration_ms": { + "name": "total_duration_ms", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "execution_data": { + "name": "execution_data", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "cost": { + "name": "cost", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "files": { + "name": "files", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_execution_logs_workflow_id_idx": { + "name": "workflow_execution_logs_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_execution_id_idx": { + "name": "workflow_execution_logs_execution_id_idx", + "columns": [ + { + "expression": "execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_trigger_idx": { + "name": "workflow_execution_logs_trigger_idx", + "columns": [ + { + "expression": "trigger", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_level_idx": { + "name": "workflow_execution_logs_level_idx", + "columns": [ + { + "expression": "level", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_started_at_idx": { + "name": "workflow_execution_logs_started_at_idx", + "columns": [ + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_execution_id_unique": { + "name": "workflow_execution_logs_execution_id_unique", + "columns": [ + { + "expression": "execution_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_execution_logs_workflow_started_at_idx": { + "name": "workflow_execution_logs_workflow_started_at_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "started_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_execution_logs_workflow_id_workflow_id_fk": { + "name": "workflow_execution_logs_workflow_id_workflow_id_fk", + "tableFrom": "workflow_execution_logs", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_execution_logs_state_snapshot_id_workflow_execution_snapshots_id_fk": { + "name": "workflow_execution_logs_state_snapshot_id_workflow_execution_snapshots_id_fk", + "tableFrom": "workflow_execution_logs", + "tableTo": "workflow_execution_snapshots", + "columnsFrom": ["state_snapshot_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_execution_snapshots": { + "name": "workflow_execution_snapshots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "state_hash": { + "name": "state_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "state_data": { + "name": "state_data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_snapshots_workflow_id_idx": { + "name": "workflow_snapshots_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_snapshots_hash_idx": { + "name": "workflow_snapshots_hash_idx", + "columns": [ + { + "expression": "state_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_snapshots_workflow_hash_idx": { + "name": "workflow_snapshots_workflow_hash_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "state_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_snapshots_created_at_idx": { + "name": "workflow_snapshots_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_execution_snapshots_workflow_id_workflow_id_fk": { + "name": "workflow_execution_snapshots_workflow_id_workflow_id_fk", + "tableFrom": "workflow_execution_snapshots", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_folder": { + "name": "workflow_folder", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parent_id": { + "name": "parent_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'#6B7280'" + }, + "is_expanded": { + "name": "is_expanded", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "sort_order": { + "name": "sort_order", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_folder_user_idx": { + "name": "workflow_folder_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_folder_workspace_parent_idx": { + "name": "workflow_folder_workspace_parent_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_folder_parent_sort_idx": { + "name": "workflow_folder_parent_sort_idx", + "columns": [ + { + "expression": "parent_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "sort_order", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_folder_user_id_user_id_fk": { + "name": "workflow_folder_user_id_user_id_fk", + "tableFrom": "workflow_folder", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_folder_workspace_id_workspace_id_fk": { + "name": "workflow_folder_workspace_id_workspace_id_fk", + "tableFrom": "workflow_folder", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_schedule": { + "name": "workflow_schedule", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "block_id": { + "name": "block_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cron_expression": { + "name": "cron_expression", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "next_run_at": { + "name": "next_run_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "last_ran_at": { + "name": "last_ran_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "trigger_type": { + "name": "trigger_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "timezone": { + "name": "timezone", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'UTC'" + }, + "failed_count": { + "name": "failed_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "last_failed_at": { + "name": "last_failed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_schedule_workflow_block_unique": { + "name": "workflow_schedule_workflow_block_unique", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "block_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_schedule_workflow_id_workflow_id_fk": { + "name": "workflow_schedule_workflow_id_workflow_id_fk", + "tableFrom": "workflow_schedule", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workflow_schedule_block_id_workflow_blocks_id_fk": { + "name": "workflow_schedule_block_id_workflow_blocks_id_fk", + "tableFrom": "workflow_schedule", + "tableTo": "workflow_blocks", + "columnsFrom": ["block_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workflow_subflows": { + "name": "workflow_subflows", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workflow_id": { + "name": "workflow_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workflow_subflows_workflow_id_idx": { + "name": "workflow_subflows_workflow_id_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workflow_subflows_workflow_type_idx": { + "name": "workflow_subflows_workflow_type_idx", + "columns": [ + { + "expression": "workflow_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workflow_subflows_workflow_id_workflow_id_fk": { + "name": "workflow_subflows_workflow_id_workflow_id_fk", + "tableFrom": "workflow_subflows", + "tableTo": "workflow", + "columnsFrom": ["workflow_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace": { + "name": "workspace", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "workspace_owner_id_user_id_fk": { + "name": "workspace_owner_id_user_id_fk", + "tableFrom": "workspace", + "tableTo": "user", + "columnsFrom": ["owner_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_environment": { + "name": "workspace_environment", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "variables": { + "name": "variables", + "type": "json", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_environment_workspace_unique": { + "name": "workspace_environment_workspace_unique", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "workspace_environment_workspace_id_idx": { + "name": "workspace_environment_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_environment_workspace_id_workspace_id_fk": { + "name": "workspace_environment_workspace_id_workspace_id_fk", + "tableFrom": "workspace_environment", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_invitation": { + "name": "workspace_invitation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "inviter_id": { + "name": "inviter_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'member'" + }, + "status": { + "name": "status", + "type": "workspace_invitation_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permissions": { + "name": "permissions", + "type": "permission_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'admin'" + }, + "org_invitation_id": { + "name": "org_invitation_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "workspace_invitation_workspace_id_workspace_id_fk": { + "name": "workspace_invitation_workspace_id_workspace_id_fk", + "tableFrom": "workspace_invitation", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_invitation_inviter_id_user_id_fk": { + "name": "workspace_invitation_inviter_id_user_id_fk", + "tableFrom": "workspace_invitation", + "tableTo": "user", + "columnsFrom": ["inviter_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "workspace_invitation_token_unique": { + "name": "workspace_invitation_token_unique", + "nullsNotDistinct": false, + "columns": ["token"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.permission_type": { + "name": "permission_type", + "schema": "public", + "values": ["admin", "write", "read"] + }, + "public.workspace_invitation_status": { + "name": "workspace_invitation_status", + "schema": "public", + "values": ["pending", "accepted", "rejected", "cancelled"] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/apps/sim/db/migrations/meta/_journal.json b/apps/sim/db/migrations/meta/_journal.json index 7de75ccfb1..18ceddc8dc 100644 --- a/apps/sim/db/migrations/meta/_journal.json +++ b/apps/sim/db/migrations/meta/_journal.json @@ -575,6 +575,13 @@ "when": 1756767479124, "tag": "0082_light_blockbuster", "breakpoints": true + }, + { + "idx": 83, + "version": "7", + "when": 1756768177306, + "tag": "0083_ambiguous_dreadnoughts", + "breakpoints": true } ] } diff --git a/apps/sim/db/schema.ts b/apps/sim/db/schema.ts index 41992603d3..03daecea9a 100644 --- a/apps/sim/db/schema.ts +++ b/apps/sim/db/schema.ts @@ -639,9 +639,17 @@ export const workspace = pgTable('workspace', { updatedAt: timestamp('updated_at').notNull().defaultNow(), }) -// Define the permission enum export const permissionTypeEnum = pgEnum('permission_type', ['admin', 'write', 'read']) +export const workspaceInvitationStatusEnum = pgEnum('workspace_invitation_status', [ + 'pending', + 'accepted', + 'rejected', + 'cancelled', +]) + +export type WorkspaceInvitationStatus = (typeof workspaceInvitationStatusEnum.enumValues)[number] + export const workspaceInvitation = pgTable('workspace_invitation', { id: text('id').primaryKey(), workspaceId: text('workspace_id') @@ -652,7 +660,7 @@ export const workspaceInvitation = pgTable('workspace_invitation', { .notNull() .references(() => user.id, { onDelete: 'cascade' }), role: text('role').notNull().default('member'), - status: text('status').notNull().default('pending'), + status: workspaceInvitationStatusEnum('status').notNull().default('pending'), token: text('token').notNull().unique(), permissions: permissionTypeEnum('permissions').notNull().default('admin'), orgInvitationId: text('org_invitation_id'), diff --git a/apps/sim/hooks/use-subscription-state.ts b/apps/sim/hooks/use-subscription-state.ts index 0eeddeb519..6ef937d14a 100644 --- a/apps/sim/hooks/use-subscription-state.ts +++ b/apps/sim/hooks/use-subscription-state.ts @@ -153,7 +153,7 @@ export function useUsageLimit() { setIsLoading(true) setError(null) - const response = await fetch('/api/usage-limits?context=user') + const response = await fetch('/api/usage?context=user') if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`) @@ -180,7 +180,7 @@ export function useUsageLimit() { const updateLimit = async (newLimit: number) => { try { - const response = await fetch('/api/usage-limits?context=user', { + const response = await fetch('/api/usage?context=user', { method: 'PUT', headers: { 'Content-Type': 'application/json', diff --git a/apps/sim/lib/billing/validation/seat-management.ts b/apps/sim/lib/billing/validation/seat-management.ts index d2c43755aa..74808ffe73 100644 --- a/apps/sim/lib/billing/validation/seat-management.ts +++ b/apps/sim/lib/billing/validation/seat-management.ts @@ -114,7 +114,6 @@ export async function getOrganizationSeatInfo( organizationId: string ): Promise { try { - // Get organization details const organizationData = await db .select({ id: organization.id, @@ -128,14 +127,12 @@ export async function getOrganizationSeatInfo( return null } - // Get organization subscription directly (referenceId = organizationId) const subscription = await getOrganizationSubscription(organizationId) if (!subscription) { return null } - // Get current member count const memberCount = await db .select({ count: count() }) .from(member) @@ -143,11 +140,8 @@ export async function getOrganizationSeatInfo( const currentSeats = memberCount[0]?.count || 0 - // Determine seat limits const maxSeats = subscription.seats || 1 - // Enterprise plans have fixed seats (can't self-serve changes) - // Team plans can add seats through Stripe const canAddSeats = subscription.plan !== 'enterprise' const availableSeats = Math.max(0, maxSeats - currentSeats) @@ -183,14 +177,12 @@ export async function validateBulkInvitations( validationResult: SeatValidationResult }> { try { - // Remove duplicates and validate email format const uniqueEmails = [...new Set(emailList)] const validEmails = uniqueEmails.filter( (email) => quickValidateEmail(email.trim().toLowerCase()).isValid ) const duplicateEmails = emailList.filter((email, index) => emailList.indexOf(email) !== index) - // Check for existing members const existingMembers = await db .select({ userEmail: user.email }) .from(member) @@ -200,7 +192,6 @@ export async function validateBulkInvitations( const existingEmails = existingMembers.map((m) => m.userEmail) const newEmails = validEmails.filter((email) => !existingEmails.includes(email)) - // Check for pending invitations const pendingInvitations = await db .select({ email: invitation.email }) .from(invitation) @@ -209,7 +200,6 @@ export async function validateBulkInvitations( const pendingEmails = pendingInvitations.map((i) => i.email) const finalEmailsToInvite = newEmails.filter((email) => !pendingEmails.includes(email)) - // Validate seat availability const seatsNeeded = finalEmailsToInvite.length const validationResult = await validateSeatAvailability(organizationId, seatsNeeded) @@ -258,14 +248,12 @@ export async function updateOrganizationSeats( updatedBy: string ): Promise<{ success: boolean; error?: string }> { try { - // Get current organization subscription directly (referenceId = organizationId) const subscriptionRecord = await getOrganizationSubscription(organizationId) if (!subscriptionRecord) { return { success: false, error: 'No active subscription found' } } - // Validate minimum seat requirements const memberCount = await db .select({ count: count() }) .from(member) @@ -280,7 +268,6 @@ export async function updateOrganizationSeats( } } - // Update subscription seat count await db .update(subscription) .set({ @@ -320,7 +307,6 @@ export async function validateMemberRemoval( removedBy: string ): Promise<{ canRemove: boolean; reason?: string }> { try { - // Get member details const memberRecord = await db .select({ role: member.role }) .from(member) @@ -331,12 +317,10 @@ export async function validateMemberRemoval( return { canRemove: false, reason: 'Member not found in organization' } } - // Check if trying to remove the organization owner if (memberRecord[0].role === 'owner') { return { canRemove: false, reason: 'Cannot remove organization owner' } } - // Check if the person removing has sufficient permissions const removerMemberRecord = await db .select({ role: member.role }) .from(member) @@ -350,22 +334,18 @@ export async function validateMemberRemoval( const removerRole = removerMemberRecord[0].role const targetRole = memberRecord[0].role - // Permission hierarchy: owner > admin > member if (removerRole === 'owner') { - // Owners can remove anyone except themselves return userIdToRemove === removedBy ? { canRemove: false, reason: 'Cannot remove yourself as owner' } : { canRemove: true } } if (removerRole === 'admin') { - // Admins can remove members but not other admins or owners return targetRole === 'member' ? { canRemove: true } : { canRemove: false, reason: 'Insufficient permissions to remove this member' } } - // Members cannot remove other members return { canRemove: false, reason: 'Insufficient permissions' } } catch (error) { logger.error('Failed to validate member removal', { @@ -390,7 +370,6 @@ export async function getOrganizationSeatAnalytics(organizationId: string) { return null } - // Get member activity data const memberActivity = await db .select({ userId: member.userId, @@ -405,7 +384,6 @@ export async function getOrganizationSeatAnalytics(organizationId: string) { .leftJoin(userStats, eq(member.userId, userStats.userId)) .where(eq(member.organizationId, organizationId)) - // Calculate utilization metrics const utilizationRate = seatInfo.maxSeats > 0 ? (seatInfo.currentSeats / seatInfo.maxSeats) * 100 : 0 diff --git a/apps/sim/lib/copilot/tools/client/workflow/list-user-workflows.ts b/apps/sim/lib/copilot/tools/client/workflow/list-user-workflows.ts index 9094c2792d..c62f11e75e 100644 --- a/apps/sim/lib/copilot/tools/client/workflow/list-user-workflows.ts +++ b/apps/sim/lib/copilot/tools/client/workflow/list-user-workflows.ts @@ -31,7 +31,7 @@ export class ListUserWorkflowsClientTool extends BaseClientTool { try { this.setState(ClientToolCallState.executing) - const res = await fetch('/api/workflows/sync', { method: 'GET' }) + const res = await fetch('/api/workflows', { method: 'GET' }) if (!res.ok) { const text = await res.text().catch(() => '') await this.markToolComplete(res.status, text || 'Failed to fetch workflows') diff --git a/apps/sim/providers/ollama/index.ts b/apps/sim/providers/ollama/index.ts index 3bf99217b9..c529ce0420 100644 --- a/apps/sim/providers/ollama/index.ts +++ b/apps/sim/providers/ollama/index.ts @@ -14,7 +14,7 @@ import { prepareToolsWithUsageControl, trackForcedToolUsage, } from '@/providers/utils' -import { useOllamaStore } from '@/stores/ollama/store' +import { useProvidersStore } from '@/stores/providers/store' import { executeTool } from '@/tools' const logger = createLogger('OllamaProvider') @@ -78,13 +78,13 @@ export const ollamaProvider: ProviderConfig = { try { const response = await fetch(`${OLLAMA_HOST}/api/tags`) if (!response.ok) { - useOllamaStore.getState().setModels([]) + useProvidersStore.getState().setModels('ollama', []) logger.warn('Ollama service is not available. The provider will be disabled.') return } const data = (await response.json()) as ModelsObject this.models = data.models.map((model) => model.name) - useOllamaStore.getState().setModels(this.models) + useProvidersStore.getState().setModels('ollama', this.models) } catch (error) { logger.warn('Ollama model instantiation failed. The provider will be disabled.', { error: error instanceof Error ? error.message : 'Unknown error', diff --git a/apps/sim/providers/utils.ts b/apps/sim/providers/utils.ts index 96240d8e03..5a71dd4cc6 100644 --- a/apps/sim/providers/utils.ts +++ b/apps/sim/providers/utils.ts @@ -30,7 +30,7 @@ import { openRouterProvider } from '@/providers/openrouter' import type { ProviderConfig, ProviderId, ProviderToolConfig } from '@/providers/types' import { xAIProvider } from '@/providers/xai' import { useCustomToolsStore } from '@/stores/custom-tools/store' -import { useOllamaStore } from '@/stores/ollama/store' +import { useProvidersStore } from '@/stores/providers/store' const logger = createLogger('ProviderUtils') @@ -576,7 +576,8 @@ export function getApiKey(provider: string, model: string, userProvidedKey?: str const hasUserKey = !!userProvidedKey // Ollama models don't require API keys - they run locally - const isOllamaModel = provider === 'ollama' || useOllamaStore.getState().models.includes(model) + const isOllamaModel = + provider === 'ollama' || useProvidersStore.getState().providers.ollama.models.includes(model) if (isOllamaModel) { return 'empty' // Ollama uses 'empty' as a placeholder API key } diff --git a/apps/sim/stores/constants.ts b/apps/sim/stores/constants.ts index eabae88e75..1438aee028 100644 --- a/apps/sim/stores/constants.ts +++ b/apps/sim/stores/constants.ts @@ -1,5 +1,4 @@ export const API_ENDPOINTS = { - SYNC: '/api/workflows/sync', ENVIRONMENT: '/api/environment', SCHEDULE: '/api/schedules', SETTINGS: '/api/settings', @@ -8,9 +7,6 @@ export const API_ENDPOINTS = { WORKSPACE_ENVIRONMENT: (id: string) => `/api/workspaces/${id}/environment`, } -// Removed SYNC_INTERVALS - Socket.IO handles real-time sync - -// Copilot tool display names - shared between client and server export const COPILOT_TOOL_DISPLAY_NAMES: Record = { search_documentation: 'Searching documentation', get_user_workflow: 'Analyzing your workflow', @@ -30,7 +26,6 @@ export const COPILOT_TOOL_DISPLAY_NAMES: Record = { reason: 'Reasoning about your workflow', } as const -// Past tense versions for completed tool calls export const COPILOT_TOOL_PAST_TENSE: Record = { search_documentation: 'Searched documentation', get_user_workflow: 'Analyzed your workflow', @@ -50,7 +45,6 @@ export const COPILOT_TOOL_PAST_TENSE: Record = { reason: 'Finished reasoning', } as const -// Error versions for failed tool calls export const COPILOT_TOOL_ERROR_NAMES: Record = { search_documentation: 'Errored searching documentation', get_user_workflow: 'Errored analyzing your workflow', diff --git a/apps/sim/stores/ollama/store.ts b/apps/sim/stores/ollama/store.ts deleted file mode 100644 index 4d52d15160..0000000000 --- a/apps/sim/stores/ollama/store.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { create } from 'zustand' -import { createLogger } from '@/lib/logs/console/logger' -import { updateOllamaProviderModels } from '@/providers/utils' -import type { OllamaStore } from '@/stores/ollama/types' - -const logger = createLogger('OllamaStore') - -// Fetch models from the server API when on client side -const fetchOllamaModels = async (): Promise => { - try { - const response = await fetch('/api/providers/ollama/models') - if (!response.ok) { - logger.warn('Failed to fetch Ollama models from API', { - status: response.status, - statusText: response.statusText, - }) - return [] - } - const data = await response.json() - return data.models || [] - } catch (error) { - logger.error('Error fetching Ollama models', { - error: error instanceof Error ? error.message : 'Unknown error', - }) - return [] - } -} - -export const useOllamaStore = create((set, get) => ({ - models: [], - isLoading: false, - setModels: (models) => { - set({ models }) - // Update the providers when models change - updateOllamaProviderModels(models) - }, - - // Fetch models from API (client-side only) - fetchModels: async () => { - if (typeof window === 'undefined') { - logger.info('Skipping client-side model fetch on server') - return - } - - if (get().isLoading) { - logger.info('Model fetch already in progress') - return - } - - logger.info('Fetching Ollama models from API') - set({ isLoading: true }) - - try { - const models = await fetchOllamaModels() - logger.info('Successfully fetched Ollama models', { - count: models.length, - models, - }) - get().setModels(models) - } catch (error) { - logger.error('Failed to fetch Ollama models', { - error: error instanceof Error ? error.message : 'Unknown error', - }) - } finally { - set({ isLoading: false }) - } - }, -})) - -// Auto-fetch models when the store is first accessed on the client -if (typeof window !== 'undefined') { - // Delay to avoid hydration issues - setTimeout(() => { - useOllamaStore.getState().fetchModels() - }, 1000) -} diff --git a/apps/sim/stores/ollama/types.ts b/apps/sim/stores/ollama/types.ts deleted file mode 100644 index 77b0fa26cd..0000000000 --- a/apps/sim/stores/ollama/types.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface OllamaStore { - models: string[] - isLoading: boolean - setModels: (models: string[]) => void - fetchModels: () => Promise -} diff --git a/apps/sim/stores/openrouter/store.ts b/apps/sim/stores/openrouter/store.ts deleted file mode 100644 index 8bb5121b76..0000000000 --- a/apps/sim/stores/openrouter/store.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { create } from 'zustand' -import { createLogger } from '@/lib/logs/console/logger' -import { updateOpenRouterProviderModels } from '@/providers/utils' -import type { OpenRouterStore } from '@/stores/openrouter/types' - -const logger = createLogger('OpenRouterStore') - -const fetchOpenRouterModels = async (): Promise => { - try { - const response = await fetch('/api/providers/openrouter/models') - if (!response.ok) { - logger.warn('Failed to fetch OpenRouter models from API', { - status: response.status, - statusText: response.statusText, - }) - return [] - } - const data = await response.json() - return data.models || [] - } catch (error) { - logger.error('Error fetching OpenRouter models', { - error: error instanceof Error ? error.message : 'Unknown error', - }) - return [] - } -} - -export const useOpenRouterStore = create((set, get) => ({ - models: [], - isLoading: false, - setModels: (models) => { - const unique = Array.from(new Set(models)) - set({ models: unique }) - updateOpenRouterProviderModels(models) - }, - - fetchModels: async () => { - if (typeof window === 'undefined') { - logger.info('Skipping client-side model fetch on server') - return - } - if (get().isLoading) { - logger.info('Model fetch already in progress') - return - } - logger.info('Fetching OpenRouter models from API') - set({ isLoading: true }) - try { - const models = await fetchOpenRouterModels() - logger.info('Successfully fetched OpenRouter models', { - count: models.length, - }) - get().setModels(models) - } catch (error) { - logger.error('Failed to fetch OpenRouter models', { - error: error instanceof Error ? error.message : 'Unknown error', - }) - } finally { - set({ isLoading: false }) - } - }, -})) - -if (typeof window !== 'undefined') { - setTimeout(() => { - useOpenRouterStore.getState().fetchModels() - }, 1000) -} diff --git a/apps/sim/stores/openrouter/types.ts b/apps/sim/stores/openrouter/types.ts deleted file mode 100644 index 19f94f5e57..0000000000 --- a/apps/sim/stores/openrouter/types.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface OpenRouterStore { - models: string[] - isLoading: boolean - setModels: (models: string[]) => void - fetchModels: () => Promise -} diff --git a/apps/sim/stores/organization/types.ts b/apps/sim/stores/organization/types.ts index 1d7fef0566..e61f7cde2f 100644 --- a/apps/sim/stores/organization/types.ts +++ b/apps/sim/stores/organization/types.ts @@ -59,7 +59,6 @@ export interface OrganizationFormData { logo: string } -// Organization billing and usage types export interface MemberUsageData { userId: string userName: string diff --git a/apps/sim/stores/providers/store.ts b/apps/sim/stores/providers/store.ts new file mode 100644 index 0000000000..5c258ffda8 --- /dev/null +++ b/apps/sim/stores/providers/store.ts @@ -0,0 +1,126 @@ +import { create } from 'zustand' +import { createLogger } from '@/lib/logs/console/logger' +import { updateOllamaProviderModels, updateOpenRouterProviderModels } from '@/providers/utils' +import type { ProviderConfig, ProviderName, ProvidersStore } from './types' + +const logger = createLogger('ProvidersStore') + +const PROVIDER_CONFIGS: Record = { + ollama: { + apiEndpoint: '/api/providers/ollama/models', + updateFunction: updateOllamaProviderModels, + }, + openrouter: { + apiEndpoint: '/api/providers/openrouter/models', + dedupeModels: true, + updateFunction: updateOpenRouterProviderModels, + }, +} + +const fetchProviderModels = async (provider: ProviderName): Promise => { + try { + const config = PROVIDER_CONFIGS[provider] + const response = await fetch(config.apiEndpoint) + + if (!response.ok) { + logger.warn(`Failed to fetch ${provider} models from API`, { + status: response.status, + statusText: response.statusText, + }) + return [] + } + + const data = await response.json() + return data.models || [] + } catch (error) { + logger.error(`Error fetching ${provider} models`, { + error: error instanceof Error ? error.message : 'Unknown error', + }) + return [] + } +} + +export const useProvidersStore = create((set, get) => ({ + providers: { + ollama: { models: [], isLoading: false }, + openrouter: { models: [], isLoading: false }, + }, + + setModels: (provider, models) => { + const config = PROVIDER_CONFIGS[provider] + + const processedModels = config.dedupeModels ? Array.from(new Set(models)) : models + + set((state) => ({ + providers: { + ...state.providers, + [provider]: { + ...state.providers[provider], + models: processedModels, + }, + }, + })) + + config.updateFunction(models) + }, + + fetchModels: async (provider) => { + if (typeof window === 'undefined') { + logger.info(`Skipping client-side ${provider} model fetch on server`) + return + } + + const currentState = get().providers[provider] + if (currentState.isLoading) { + logger.info(`${provider} model fetch already in progress`) + return + } + + logger.info(`Fetching ${provider} models from API`) + + set((state) => ({ + providers: { + ...state.providers, + [provider]: { + ...state.providers[provider], + isLoading: true, + }, + }, + })) + + try { + const models = await fetchProviderModels(provider) + logger.info(`Successfully fetched ${provider} models`, { + count: models.length, + ...(provider === 'ollama' ? { models } : {}), + }) + get().setModels(provider, models) + } catch (error) { + logger.error(`Failed to fetch ${provider} models`, { + error: error instanceof Error ? error.message : 'Unknown error', + }) + } finally { + set((state) => ({ + providers: { + ...state.providers, + [provider]: { + ...state.providers[provider], + isLoading: false, + }, + }, + })) + } + }, + + getProvider: (provider) => { + return get().providers[provider] + }, +})) + +if (typeof window !== 'undefined') { + setTimeout(() => { + const store = useProvidersStore.getState() + store.fetchModels('ollama') + store.fetchModels('openrouter') + }, 1000) +} diff --git a/apps/sim/stores/providers/types.ts b/apps/sim/stores/providers/types.ts new file mode 100644 index 0000000000..4aabdf4f80 --- /dev/null +++ b/apps/sim/stores/providers/types.ts @@ -0,0 +1,19 @@ +export type ProviderName = 'ollama' | 'openrouter' + +export interface ProviderState { + models: string[] + isLoading: boolean +} + +export interface ProvidersStore { + providers: Record + setModels: (provider: ProviderName, models: string[]) => void + fetchModels: (provider: ProviderName) => Promise + getProvider: (provider: ProviderName) => ProviderState +} + +export interface ProviderConfig { + apiEndpoint: string + dedupeModels?: boolean + updateFunction: (models: string[]) => void | Promise +} diff --git a/apps/sim/stores/settings/environment/store.ts b/apps/sim/stores/settings/environment/store.ts index 5b2b9e72b9..30f05fc0e2 100644 --- a/apps/sim/stores/settings/environment/store.ts +++ b/apps/sim/stores/settings/environment/store.ts @@ -10,7 +10,6 @@ export const useEnvironmentStore = create()((set, get) => ({ isLoading: false, error: null, - // Load environment variables from DB loadEnvironmentVariables: async () => { try { set({ isLoading: true, error: null }) @@ -43,12 +42,10 @@ export const useEnvironmentStore = create()((set, get) => ({ } }, - // Save environment variables to DB saveEnvironmentVariables: async (variables: Record) => { try { set({ isLoading: true, error: null }) - // Transform variables to the format expected by the store const transformedVariables = Object.entries(variables).reduce( (acc, [key, value]) => ({ ...acc, @@ -57,10 +54,8 @@ export const useEnvironmentStore = create()((set, get) => ({ {} ) - // Update local state immediately (optimistic update) set({ variables: transformedVariables }) - // Send to DB const response = await fetch(API_ENDPOINTS.ENVIRONMENT, { method: 'POST', headers: { @@ -89,12 +84,10 @@ export const useEnvironmentStore = create()((set, get) => ({ isLoading: false, }) - // Reload from DB to ensure consistency get().loadEnvironmentVariables() } }, - // Workspace environment actions loadWorkspaceEnvironment: async (workspaceId: string) => { try { set({ isLoading: true, error: null }) @@ -105,7 +98,6 @@ export const useEnvironmentStore = create()((set, get) => ({ } const { data } = await response.json() - // The UI component for environment modal will handle workspace section state locally. set({ isLoading: false }) return data as { workspace: Record @@ -155,15 +147,6 @@ export const useEnvironmentStore = create()((set, get) => ({ } }, - // Legacy method updated to use the new saveEnvironmentVariables - setVariables: (variables: Record) => { - get().saveEnvironmentVariables(variables) - }, - - getVariable: (key: string): string | undefined => { - return get().variables[key]?.value - }, - getAllVariables: (): Record => { return get().variables }, diff --git a/apps/sim/stores/settings/environment/types.ts b/apps/sim/stores/settings/environment/types.ts index 8d95a242db..78c484aacc 100644 --- a/apps/sim/stores/settings/environment/types.ts +++ b/apps/sim/stores/settings/environment/types.ts @@ -10,14 +10,9 @@ export interface EnvironmentState { } export interface EnvironmentStore extends EnvironmentState { - // Legacy method - setVariables: (variables: Record) => void - - // New methods for direct DB interaction loadEnvironmentVariables: () => Promise saveEnvironmentVariables: (variables: Record) => Promise - // Workspace environment loadWorkspaceEnvironment: (workspaceId: string) => Promise<{ workspace: Record personal: Record @@ -29,7 +24,5 @@ export interface EnvironmentStore extends EnvironmentState { ) => Promise removeWorkspaceEnvironmentKeys: (workspaceId: string, keys: string[]) => Promise - // Utility methods - getVariable: (key: string) => string | undefined getAllVariables: () => Record } diff --git a/apps/sim/stores/subscription/store.ts b/apps/sim/stores/subscription/store.ts index 617bca1a11..40ef9ac908 100644 --- a/apps/sim/stores/subscription/store.ts +++ b/apps/sim/stores/subscription/store.ts @@ -142,7 +142,7 @@ export const useSubscriptionStore = create()( loadUsageLimitData: async () => { try { - const response = await fetch('/api/usage-limits?context=user') + const response = await fetch('/api/usage?context=user') if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`) @@ -168,7 +168,7 @@ export const useSubscriptionStore = create()( updateUsageLimit: async (newLimit: number) => { try { - const response = await fetch('/api/usage-limits?context=user', { + const response = await fetch('/api/usage?context=user', { method: 'PUT', headers: { 'Content-Type': 'application/json', @@ -240,7 +240,7 @@ export const useSubscriptionStore = create()( // Load both subscription and usage limit data in parallel const [subscriptionResponse, usageLimitResponse] = await Promise.all([ fetch('/api/billing?context=user'), - fetch('/api/usage-limits?context=user'), + fetch('/api/usage?context=user'), ]) if (!subscriptionResponse.ok) { diff --git a/apps/sim/stores/workflows/registry/store.ts b/apps/sim/stores/workflows/registry/store.ts index 465e6bc742..c5edb5a068 100644 --- a/apps/sim/stores/workflows/registry/store.ts +++ b/apps/sim/stores/workflows/registry/store.ts @@ -34,7 +34,7 @@ async function fetchWorkflowsFromDB(workspaceId?: string): Promise { try { useWorkflowRegistry.getState().setLoading(true) - const url = new URL(API_ENDPOINTS.SYNC, window.location.origin) + const url = new URL(API_ENDPOINTS.WORKFLOWS, window.location.origin) if (workspaceId) { url.searchParams.append('workspaceId', workspaceId) @@ -940,7 +940,7 @@ export const useWorkflowRegistry = create()( }, } - const response = await fetch('/api/workflows/sync', { + const response = await fetch('/api/workflows', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({