Compare commits

..

7 Commits

Author SHA1 Message Date
Waleed
2df65527d3 v0.3.55: landing page / OTP improvements, DB package separation, Webhooks fixes, Sharepoint Improvement 2025-09-17 23:48:35 -07:00
Adam Gough
d0b69455e2 Improvement(sharepoint): added more operations in sharepoint (#1369)
* added list tools

* not working yet

* improved read and create

* added scopes

* updated sharepoint tools

* added greptile comments

---------

Co-authored-by: Adam Gough <adamgough@Mac.attlocal.net>
2025-09-17 19:16:12 -07:00
Vikhyath Mondreti
6028b1f5c0 fix(dockerfile): needs dummy db url (#1368) 2025-09-17 18:56:54 -07:00
Waleed
658cf11299 improvement(idempotency): added atomic claims to prevent duplicate processing for long-running workflows (#1366)
* improvement(idempotency): added atomic claims to prevent duplicate processing for long-running workflows

* ack PR comments
2025-09-17 17:17:55 -07:00
Waleed
6312df3a07 feat(signup): added back to login functionality to OTP page (#1365)
* update infra and remove railway

* feat(signup): added back to login functionalityfrom OTP page

* remove placeholders from docker commands, simplified login flow

* Revert "update infra and remove railway"

This reverts commit abfa2f8d51.
2025-09-17 17:17:37 -07:00
Vikhyath Mondreti
9de7a00373 improvement(code-structure): move db into separate package (#1364)
* improvement(code-structure): move db into separate package

* make db separate package

* remake bun lock

* update imports to not maintain two separate ones

* fix CI for tests by adding dummy url

* vercel build fix attempt

* update bun lock

* regenerate bun lock

* fix mocks

* remove db commands from apps/sim package json
2025-09-17 15:41:13 -07:00
Waleed
325a666a8b improvement(landing): insert prompt into copilot panel from landing, open panel on entry (#1363)
* update infra and remove railway

* improvement(landing): insert prompt into copilot panel from landing, open panel on entry

* Revert "update infra and remove railway"

This reverts commit abfa2f8d51.

* fixes

* remove debug logs

* go back to old env
2025-09-17 12:28:22 -07:00
428 changed files with 2591 additions and 1390 deletions

View File

@@ -32,6 +32,7 @@ jobs:
env:
NODE_OPTIONS: '--no-warnings'
NEXT_PUBLIC_APP_URL: 'https://www.sim.ai'
DATABASE_URL: 'postgresql://postgres:postgres@localhost:5432/simstudio'
ENCRYPTION_KEY: '7cf672e460e430c1fba707575c2b0e2ad5a99dddf9b7b7e3b5646e630861db1c' # dummy key for CI only
run: bun run test
@@ -39,6 +40,7 @@ jobs:
env:
NODE_OPTIONS: '--no-warnings'
NEXT_PUBLIC_APP_URL: 'https://www.sim.ai'
DATABASE_URL: 'postgresql://postgres:postgres@localhost:5432/simstudio'
STRIPE_SECRET_KEY: 'dummy_key_for_ci_only'
STRIPE_WEBHOOK_SECRET: 'dummy_secret_for_ci_only'
RESEND_API_KEY: 'dummy_key_for_ci_only'
@@ -71,7 +73,7 @@ jobs:
run: bun install
- name: Apply migrations
working-directory: ./apps/sim
working-directory: ./packages/db
env:
DATABASE_URL: ${{ github.ref == 'refs/heads/main' && secrets.DATABASE_URL || secrets.STAGING_DATABASE_URL }}
run: bunx drizzle-kit migrate
run: bunx drizzle-kit migrate --config=./drizzle.config.ts

View File

@@ -125,10 +125,11 @@ Update your `.env` file with the database URL:
DATABASE_URL="postgresql://postgres:your_password@localhost:5432/simstudio"
```
4. Set up the database:
4. Set up the database (from packages/db):
```bash
bunx drizzle-kit migrate
cd packages/db
bunx drizzle-kit migrate --config=./drizzle.config.ts
```
5. Start the development servers:

View File

@@ -1,6 +1,6 @@
---
title: Sharepoint
description: Read and create pages
description: Work with pages and lists
---
import { BlockInfoCard } from "@/components/ui/block-info-card"
@@ -61,7 +61,7 @@ In Sim, the SharePoint integration empowers your agents to create and access Sha
## Usage Instructions
Integrate Sharepoint into the workflow. Can read and create pages, and list sites. Requires OAuth.
Integrate SharePoint into the workflow. Read/create pages, list sites, and work with lists (read, create, update items). Requires OAuth.
@@ -124,6 +124,65 @@ List details of all SharePoint sites
| --------- | ---- | ----------- |
| `site` | object | Information about the current SharePoint site |
### `sharepoint_create_list`
Create a new list in a SharePoint site
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `siteId` | string | No | The ID of the SharePoint site \(internal use\) |
| `siteSelector` | string | No | Select the SharePoint site |
| `listDisplayName` | string | Yes | Display name of the list to create |
| `listDescription` | string | No | Description of the list |
| `listTemplate` | string | No | List template name \(e.g., 'genericList'\) |
| `pageContent` | string | No | Optional JSON of columns. Either a top-level array of column definitions or an object with \{ columns: \[...\] \}. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `list` | object | Created SharePoint list information |
### `sharepoint_get_list`
Get metadata (and optionally columns/items) for a SharePoint list
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `siteSelector` | string | No | Select the SharePoint site |
| `siteId` | string | No | The ID of the SharePoint site \(internal use\) |
| `listId` | string | No | The ID of the list to retrieve |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `list` | object | Information about the SharePoint list |
### `sharepoint_update_list`
Update the properties (fields) on a SharePoint list item
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `siteSelector` | string | No | Select the SharePoint site |
| `siteId` | string | No | The ID of the SharePoint site \(internal use\) |
| `listId` | string | No | The ID of the list containing the item |
| `itemId` | string | Yes | The ID of the list item to update |
| `listItemFields` | object | Yes | Field values to update on the list item |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `item` | object | Updated SharePoint list item |
## Notes

View File

@@ -4,19 +4,9 @@ import { env } from '@/lib/env'
import { isProd } from '@/lib/environment'
export async function getOAuthProviderStatus() {
const githubAvailable = !!(
env.GITHUB_CLIENT_ID &&
env.GITHUB_CLIENT_SECRET &&
env.GITHUB_CLIENT_ID !== 'placeholder' &&
env.GITHUB_CLIENT_SECRET !== 'placeholder'
)
const githubAvailable = !!(env.GITHUB_CLIENT_ID && env.GITHUB_CLIENT_SECRET)
const googleAvailable = !!(
env.GOOGLE_CLIENT_ID &&
env.GOOGLE_CLIENT_SECRET &&
env.GOOGLE_CLIENT_ID !== 'placeholder' &&
env.GOOGLE_CLIENT_SECRET !== 'placeholder'
)
const googleAvailable = !!(env.GOOGLE_CLIENT_ID && env.GOOGLE_CLIENT_SECRET)
return { githubAvailable, googleAvailable, isProduction: isProd }
}

View File

@@ -37,12 +37,6 @@ export function SocialLoginButtons({
setIsGithubLoading(true)
try {
await client.signIn.social({ provider: 'github', callbackURL })
// Mark that the user has previously logged in
if (typeof window !== 'undefined') {
localStorage.setItem('has_logged_in_before', 'true')
document.cookie = 'has_logged_in_before=true; path=/; max-age=31536000; SameSite=Lax' // 1 year expiry
}
} catch (err: any) {
let errorMessage = 'Failed to sign in with GitHub'
@@ -66,13 +60,6 @@ export function SocialLoginButtons({
setIsGoogleLoading(true)
try {
await client.signIn.social({ provider: 'google', callbackURL })
// Mark that the user has previously logged in
if (typeof window !== 'undefined') {
localStorage.setItem('has_logged_in_before', 'true')
// Also set a cookie to enable middleware to check login status
document.cookie = 'has_logged_in_before=true; path=/; max-age=31536000; SameSite=Lax' // 1 year expiry
}
} catch (err: any) {
let errorMessage = 'Failed to sign in with Google'

View File

@@ -74,12 +74,12 @@ const validatePassword = (passwordValue: string): string[] => {
if (!PASSWORD_VALIDATIONS.required.test(passwordValue)) {
errors.push(PASSWORD_VALIDATIONS.required.message)
return errors // Return early for required field
return errors
}
if (!PASSWORD_VALIDATIONS.notEmpty.test(passwordValue)) {
errors.push(PASSWORD_VALIDATIONS.notEmpty.message)
return errors // Return early for empty field
return errors
}
return errors
@@ -104,11 +104,9 @@ export default function LoginPage({
const [showValidationError, setShowValidationError] = useState(false)
const [buttonClass, setButtonClass] = useState('auth-button-gradient')
// Initialize state for URL parameters
const [callbackUrl, setCallbackUrl] = useState('/workspace')
const [isInviteFlow, setIsInviteFlow] = useState(false)
// Forgot password states
const [forgotPasswordOpen, setForgotPasswordOpen] = useState(false)
const [forgotPasswordEmail, setForgotPasswordEmail] = useState('')
const [isSubmittingReset, setIsSubmittingReset] = useState(false)
@@ -117,25 +115,20 @@ export default function LoginPage({
message: string
}>({ type: null, message: '' })
// Email validation state
const [email, setEmail] = useState('')
const [emailErrors, setEmailErrors] = useState<string[]>([])
const [showEmailValidationError, setShowEmailValidationError] = useState(false)
// Extract URL parameters after component mounts to avoid SSR issues
useEffect(() => {
setMounted(true)
// Only access search params on the client side
if (searchParams) {
const callback = searchParams.get('callbackUrl')
if (callback) {
// Validate the callbackUrl before setting it
if (validateCallbackUrl(callback)) {
setCallbackUrl(callback)
} else {
logger.warn('Invalid callback URL detected and blocked:', { url: callback })
// Keep the default safe value ('/workspace')
}
}
@@ -143,12 +136,10 @@ export default function LoginPage({
setIsInviteFlow(inviteFlow)
}
// Check if CSS variable has been customized
const checkCustomBrand = () => {
const computedStyle = getComputedStyle(document.documentElement)
const brandAccent = computedStyle.getPropertyValue('--brand-accent-hex').trim()
// Check if the CSS variable exists and is different from the default
if (brandAccent && brandAccent !== '#6f3dfa') {
setButtonClass('auth-button-custom')
} else {
@@ -158,7 +149,6 @@ export default function LoginPage({
checkCustomBrand()
// Also check on window resize or theme changes
window.addEventListener('resize', checkCustomBrand)
const observer = new MutationObserver(checkCustomBrand)
observer.observe(document.documentElement, {
@@ -189,7 +179,6 @@ export default function LoginPage({
const newEmail = e.target.value
setEmail(newEmail)
// Silently validate but don't show errors until submit
const errors = validateEmailField(newEmail)
setEmailErrors(errors)
setShowEmailValidationError(false)
@@ -199,7 +188,6 @@ export default function LoginPage({
const newPassword = e.target.value
setPassword(newPassword)
// Silently validate but don't show errors until submit
const errors = validatePassword(newPassword)
setPasswordErrors(errors)
setShowValidationError(false)
@@ -210,26 +198,23 @@ export default function LoginPage({
setIsLoading(true)
const formData = new FormData(e.currentTarget)
const email = formData.get('email') as string
const emailRaw = formData.get('email') as string
const email = emailRaw.trim().toLowerCase()
// Validate email on submit
const emailValidationErrors = validateEmailField(email)
setEmailErrors(emailValidationErrors)
setShowEmailValidationError(emailValidationErrors.length > 0)
// Validate password on submit
const passwordValidationErrors = validatePassword(password)
setPasswordErrors(passwordValidationErrors)
setShowValidationError(passwordValidationErrors.length > 0)
// If there are validation errors, stop submission
if (emailValidationErrors.length > 0 || passwordValidationErrors.length > 0) {
setIsLoading(false)
return
}
try {
// Final validation before submission
const safeCallbackUrl = validateCallbackUrl(callbackUrl) ? callbackUrl : '/workspace'
const result = await client.signIn.email(
@@ -291,33 +276,13 @@ export default function LoginPage({
setIsLoading(false)
return
}
// Mark that the user has previously logged in
if (typeof window !== 'undefined') {
localStorage.setItem('has_logged_in_before', 'true')
document.cookie = 'has_logged_in_before=true; path=/; max-age=31536000; SameSite=Lax' // 1 year expiry
}
} catch (err: any) {
// Handle only the special verification case that requires a redirect
if (err.message?.includes('not verified') || err.code?.includes('EMAIL_NOT_VERIFIED')) {
try {
await client.emailOtp.sendVerificationOtp({
email,
type: 'email-verification',
})
if (typeof window !== 'undefined') {
sessionStorage.setItem('verificationEmail', email)
}
router.push('/verify')
return
} catch (_verifyErr) {
setPasswordErrors(['Failed to send verification code. Please try again later.'])
setShowValidationError(true)
setIsLoading(false)
return
if (typeof window !== 'undefined') {
sessionStorage.setItem('verificationEmail', email)
}
router.push('/verify')
return
}
console.error('Uncaught login error:', err)

View File

@@ -24,7 +24,6 @@ function ResetPasswordContent() {
text: '',
})
// Validate token presence
useEffect(() => {
if (!token) {
setStatusMessage({
@@ -60,7 +59,6 @@ function ResetPasswordContent() {
text: 'Password reset successful! Redirecting to login...',
})
// Redirect to login page after 1.5 seconds
setTimeout(() => {
router.push('/login?resetSuccess=true')
}, 1500)

View File

@@ -30,12 +30,10 @@ export function RequestResetForm({
const [buttonClass, setButtonClass] = useState('auth-button-gradient')
useEffect(() => {
// Check if CSS variable has been customized
const checkCustomBrand = () => {
const computedStyle = getComputedStyle(document.documentElement)
const brandAccent = computedStyle.getPropertyValue('--brand-accent-hex').trim()
// Check if the CSS variable exists and is different from the default
if (brandAccent && brandAccent !== '#6f3dfa') {
setButtonClass('auth-button-custom')
} else {
@@ -45,7 +43,6 @@ export function RequestResetForm({
checkCustomBrand()
// Also check on window resize or theme changes
window.addEventListener('resize', checkCustomBrand)
const observer = new MutationObserver(checkCustomBrand)
observer.observe(document.documentElement, {
@@ -132,12 +129,10 @@ export function SetNewPasswordForm({
const [buttonClass, setButtonClass] = useState('auth-button-gradient')
useEffect(() => {
// Check if CSS variable has been customized
const checkCustomBrand = () => {
const computedStyle = getComputedStyle(document.documentElement)
const brandAccent = computedStyle.getPropertyValue('--brand-accent-hex').trim()
// Check if the CSS variable exists and is different from the default
if (brandAccent && brandAccent !== '#6f3dfa') {
setButtonClass('auth-button-custom')
} else {
@@ -147,7 +142,6 @@ export function SetNewPasswordForm({
checkCustomBrand()
// Also check on window resize or theme changes
window.addEventListener('resize', checkCustomBrand)
const observer = new MutationObserver(checkCustomBrand)
observer.observe(document.documentElement, {
@@ -164,7 +158,6 @@ export function SetNewPasswordForm({
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
// Simple validation
if (password.length < 8) {
setValidationMessage('Password must be at least 8 characters long')
return

View File

@@ -2,7 +2,6 @@ import { env, isTruthy } from '@/lib/env'
import { getOAuthProviderStatus } from '@/app/(auth)/components/oauth-provider-checker'
import SignupForm from '@/app/(auth)/signup/signup-form'
// Force dynamic rendering to avoid prerender errors with search params
export const dynamic = 'force-dynamic'
export default async function SignupPage() {

View File

@@ -95,7 +95,6 @@ function SignupFormContent({
const [isInviteFlow, setIsInviteFlow] = useState(false)
const [buttonClass, setButtonClass] = useState('auth-button-gradient')
// Name validation state
const [name, setName] = useState('')
const [nameErrors, setNameErrors] = useState<string[]>([])
const [showNameValidationError, setShowNameValidationError] = useState(false)
@@ -107,29 +106,24 @@ function SignupFormContent({
setEmail(emailParam)
}
// Handle redirection for invitation flow
const redirectParam = searchParams.get('redirect')
if (redirectParam) {
setRedirectUrl(redirectParam)
// Check if this is part of an invitation flow
if (redirectParam.startsWith('/invite/')) {
setIsInviteFlow(true)
}
}
// Explicitly check for invite_flow parameter
const inviteFlowParam = searchParams.get('invite_flow')
if (inviteFlowParam === 'true') {
setIsInviteFlow(true)
}
// Check if CSS variable has been customized
const checkCustomBrand = () => {
const computedStyle = getComputedStyle(document.documentElement)
const brandAccent = computedStyle.getPropertyValue('--brand-accent-hex').trim()
// Check if the CSS variable exists and is different from the default
if (brandAccent && brandAccent !== '#6f3dfa') {
setButtonClass('auth-button-custom')
} else {
@@ -139,7 +133,6 @@ function SignupFormContent({
checkCustomBrand()
// Also check on window resize or theme changes
window.addEventListener('resize', checkCustomBrand)
const observer = new MutationObserver(checkCustomBrand)
observer.observe(document.documentElement, {
@@ -153,7 +146,6 @@ function SignupFormContent({
}
}, [searchParams])
// Validate password and return array of error messages
const validatePassword = (passwordValue: string): string[] => {
const errors: string[] = []
@@ -180,18 +172,17 @@ function SignupFormContent({
return errors
}
// Validate name and return array of error messages
const validateName = (nameValue: string): string[] => {
const errors: string[] = []
if (!NAME_VALIDATIONS.required.test(nameValue)) {
errors.push(NAME_VALIDATIONS.required.message)
return errors // Return early for required field
return errors
}
if (!NAME_VALIDATIONS.notEmpty.test(nameValue)) {
errors.push(NAME_VALIDATIONS.notEmpty.message)
return errors // Return early for empty field
return errors
}
if (!NAME_VALIDATIONS.validCharacters.regex.test(nameValue.trim())) {
@@ -209,7 +200,6 @@ function SignupFormContent({
const newPassword = e.target.value
setPassword(newPassword)
// Silently validate but don't show errors
const errors = validatePassword(newPassword)
setPasswordErrors(errors)
setShowValidationError(false)
@@ -228,12 +218,10 @@ function SignupFormContent({
const newEmail = e.target.value
setEmail(newEmail)
// Silently validate but don't show errors until submit
const errors = validateEmailField(newEmail)
setEmailErrors(errors)
setShowEmailValidationError(false)
// Clear any previous server-side email errors when the user starts typing
if (emailError) {
setEmailError('')
}
@@ -244,7 +232,8 @@ function SignupFormContent({
setIsLoading(true)
const formData = new FormData(e.currentTarget)
const emailValue = formData.get('email') as string
const emailValueRaw = formData.get('email') as string
const emailValue = emailValueRaw.trim().toLowerCase()
const passwordValue = formData.get('password') as string
const nameValue = formData.get('name') as string
@@ -348,7 +337,6 @@ function SignupFormContent({
return
}
// Refresh session to get the new user data immediately after signup
try {
await refetchSession()
logger.info('Session refreshed after successful signup')
@@ -356,34 +344,23 @@ function SignupFormContent({
logger.error('Failed to refresh session after signup:', sessionError)
}
// For new signups, always require verification
if (typeof window !== 'undefined') {
sessionStorage.setItem('verificationEmail', emailValue)
localStorage.setItem('has_logged_in_before', 'true')
// Set cookie flag for middleware check
document.cookie = 'requiresEmailVerification=true; path=/; max-age=900; SameSite=Lax' // 15 min expiry
document.cookie = 'has_logged_in_before=true; path=/; max-age=31536000; SameSite=Lax'
// Store invitation flow state if applicable
if (isInviteFlow && redirectUrl) {
sessionStorage.setItem('inviteRedirectUrl', redirectUrl)
sessionStorage.setItem('isInviteFlow', 'true')
}
}
// Send verification OTP manually
try {
await client.emailOtp.sendVerificationOtp({
email: emailValue,
type: 'email-verification',
type: 'sign-in',
})
} catch (otpError) {
logger.error('Failed to send OTP:', otpError)
// Continue anyway - user can use resend button
} catch (otpErr) {
logger.warn('Failed to send sign-in OTP after signup; user can press Resend', otpErr)
}
// Always redirect to verification for new signups
router.push('/verify?fromSignup=true')
} catch (error) {
logger.error('Signup error:', error)

View File

@@ -1,14 +1,11 @@
import { env } from '@/lib/env'
import { isProd } from '@/lib/environment'
import { getBaseUrl } from '@/lib/urls/utils'
import { VerifyContent } from '@/app/(auth)/verify/verify-content'
// Force dynamic rendering to avoid prerender errors with search params
export const dynamic = 'force-dynamic'
export default function VerifyPage() {
const baseUrl = getBaseUrl()
const hasResendKey = Boolean(env.RESEND_API_KEY && env.RESEND_API_KEY !== 'placeholder')
const hasResendKey = Boolean(env.RESEND_API_KEY)
return <VerifyContent hasResendKey={hasResendKey} baseUrl={baseUrl} isProduction={isProd} />
return <VerifyContent hasResendKey={hasResendKey} isProduction={isProd} />
}

View File

@@ -3,7 +3,6 @@
import { useEffect, useState } from 'react'
import { useRouter, useSearchParams } from 'next/navigation'
import { client, useSession } from '@/lib/auth-client'
import { env, isTruthy } from '@/lib/env'
import { createLogger } from '@/lib/logs/console/logger'
const logger = createLogger('useVerification')
@@ -47,61 +46,39 @@ export function useVerification({
useEffect(() => {
if (typeof window !== 'undefined') {
// Get stored email
const storedEmail = sessionStorage.getItem('verificationEmail')
if (storedEmail) {
setEmail(storedEmail)
}
// Check for redirect information
const storedRedirectUrl = sessionStorage.getItem('inviteRedirectUrl')
if (storedRedirectUrl) {
setRedirectUrl(storedRedirectUrl)
}
// Check if this is an invite flow
const storedIsInviteFlow = sessionStorage.getItem('isInviteFlow')
if (storedIsInviteFlow === 'true') {
setIsInviteFlow(true)
}
}
// Also check URL parameters for redirect information
const redirectParam = searchParams.get('redirectAfter')
if (redirectParam) {
setRedirectUrl(redirectParam)
}
// Check for invite_flow parameter
const inviteFlowParam = searchParams.get('invite_flow')
if (inviteFlowParam === 'true') {
setIsInviteFlow(true)
}
}, [searchParams])
// Send initial OTP code if this is the first load
useEffect(() => {
if (email && !isSendingInitialOtp && hasResendKey) {
setIsSendingInitialOtp(true)
// Only send verification OTP if we're coming from login page
// Skip this if coming from signup since the OTP is already sent
if (!searchParams.get('fromSignup')) {
client.emailOtp
.sendVerificationOtp({
email,
type: 'email-verification',
})
.then(() => {})
.catch((error) => {
logger.error('Failed to send initial verification code:', error)
setErrorMessage('Failed to send verification code. Please use the resend button.')
})
}
}
}, [email, isSendingInitialOtp, searchParams, hasResendKey])
}, [email, isSendingInitialOtp, hasResendKey])
// Enable the verify button when all 6 digits are entered
const isOtpComplete = otp.length === 6
async function verifyCode() {
@@ -112,25 +89,24 @@ export function useVerification({
setErrorMessage('')
try {
// Call the verification API with the OTP code
const response = await client.emailOtp.verifyEmail({
email,
const normalizedEmail = email.trim().toLowerCase()
const response = await client.signIn.emailOtp({
email: normalizedEmail,
otp,
})
// Check if verification was successful
if (response && !response.error) {
setIsVerified(true)
// Clear verification requirements and session storage
try {
await refetchSession()
} catch (e) {
logger.warn('Failed to refetch session after verification', e)
}
if (typeof window !== 'undefined') {
sessionStorage.removeItem('verificationEmail')
// Clear the verification requirement flag
document.cookie =
'requiresEmailVerification=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT'
// Also clear invite-related items
if (isInviteFlow) {
sessionStorage.removeItem('inviteRedirectUrl')
sessionStorage.removeItem('isInviteFlow')
@@ -139,24 +115,20 @@ export function useVerification({
setTimeout(() => {
if (isInviteFlow && redirectUrl) {
// For invitation flow, redirect to the invitation page
window.location.href = redirectUrl
} else {
// Default redirect to dashboard
window.location.href = '/workspace'
}
}, 1000)
} else {
logger.info('Setting invalid OTP state - API error response')
const message = 'Invalid verification code. Please check and try again.'
// Set both state variables to ensure the error shows
setIsInvalidOtp(true)
setErrorMessage(message)
logger.info('Error state after API error:', {
isInvalidOtp: true,
errorMessage: message,
})
// Clear the OTP input on invalid code
setOtp('')
}
} catch (error: any) {
@@ -171,7 +143,6 @@ export function useVerification({
message = 'Too many failed attempts. Please request a new code.'
}
// Set both state variables to ensure the error shows
setIsInvalidOtp(true)
setErrorMessage(message)
logger.info('Error state after caught error:', {
@@ -179,7 +150,6 @@ export function useVerification({
errorMessage: message,
})
// Clear the OTP input on error
setOtp('')
} finally {
setIsLoading(false)
@@ -192,10 +162,11 @@ export function useVerification({
setIsLoading(true)
setErrorMessage('')
const normalizedEmail = email.trim().toLowerCase()
client.emailOtp
.sendVerificationOtp({
email,
type: 'email-verification',
email: normalizedEmail,
type: 'sign-in',
})
.then(() => {})
.catch(() => {
@@ -207,7 +178,6 @@ export function useVerification({
}
function handleOtpChange(value: string) {
// Only clear error when user is actively typing a new code
if (value.length === 6) {
setIsInvalidOtp(false)
setErrorMessage('')
@@ -215,12 +185,11 @@ export function useVerification({
setOtp(value)
}
// Auto-submit when OTP is complete
useEffect(() => {
if (otp.length === 6 && email && !isLoading && !isVerified) {
const timeoutId = setTimeout(() => {
verifyCode()
}, 300) // Small delay to ensure UI is ready
}, 300)
return () => clearTimeout(timeoutId)
}
@@ -229,17 +198,8 @@ export function useVerification({
useEffect(() => {
if (typeof window !== 'undefined') {
if (!isProduction || !hasResendKey) {
const storedEmail = sessionStorage.getItem('verificationEmail')
}
const isDevOrDocker = !isProduction || isTruthy(env.DOCKER_BUILD)
if (isDevOrDocker || !hasResendKey) {
setIsVerified(true)
document.cookie =
'requiresEmailVerification=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT'
const timeoutId = setTimeout(() => {
window.location.href = '/workspace'
}, 1000)

View File

@@ -11,7 +11,6 @@ import { soehne } from '@/app/fonts/soehne/soehne'
interface VerifyContentProps {
hasResendKey: boolean
baseUrl: string
isProduction: boolean
}
@@ -56,30 +55,13 @@ function VerificationForm({
setCountdown(30)
}
const handleCancelVerification = () => {
// Clear verification data
if (typeof window !== 'undefined') {
sessionStorage.removeItem('verificationEmail')
sessionStorage.removeItem('inviteRedirectUrl')
sessionStorage.removeItem('isInviteFlow')
// Clear the verification requirement cookie
document.cookie = 'requiresEmailVerification=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT'
}
// Redirect to login
router.push('/login')
}
const [buttonClass, setButtonClass] = useState('auth-button-gradient')
useEffect(() => {
// Check if CSS variable has been customized
const checkCustomBrand = () => {
const computedStyle = getComputedStyle(document.documentElement)
const brandAccent = computedStyle.getPropertyValue('--brand-accent-hex').trim()
// Check if the CSS variable exists and is different from the default
if (brandAccent && brandAccent !== '#6f3dfa') {
setButtonClass('auth-button-custom')
} else {
@@ -89,7 +71,6 @@ function VerificationForm({
checkCustomBrand()
// Also check on window resize or theme changes
window.addEventListener('resize', checkCustomBrand)
const observer = new MutationObserver(checkCustomBrand)
observer.observe(document.documentElement, {
@@ -232,21 +213,27 @@ function VerificationForm({
</div>
)}
{/* <div className='text-center font-light text-[14px]'>
<div className='text-center font-light text-[14px]'>
<button
onClick={handleCancelVerification}
onClick={() => {
if (typeof window !== 'undefined') {
sessionStorage.removeItem('verificationEmail')
sessionStorage.removeItem('inviteRedirectUrl')
sessionStorage.removeItem('isInviteFlow')
}
router.push('/signup')
}}
className='font-medium text-[var(--brand-accent-hex)] underline-offset-4 transition hover:text-[var(--brand-accent-hover-hex)] hover:underline'
>
Back to login
Back to signup
</button>
</div> */}
</div>
</div>
)}
</>
)
}
// Fallback component while the verification form is loading
function VerificationFormFallback() {
return (
<div className='text-center'>
@@ -258,7 +245,7 @@ function VerificationFormFallback() {
)
}
export function VerifyContent({ hasResendKey, baseUrl, isProduction }: VerifyContentProps) {
export function VerifyContent({ hasResendKey, isProduction }: VerifyContentProps) {
return (
<Suspense fallback={<VerificationFormFallback />}>
<VerificationForm hasResendKey={hasResendKey} isProduction={isProduction} />

View File

@@ -32,6 +32,7 @@ import {
StripeIcon,
SupabaseIcon,
} from '@/components/icons'
import { LandingPromptStorage } from '@/lib/browser-storage'
import { soehne } from '@/app/fonts/soehne/soehne'
import {
CARD_WIDTH,
@@ -271,6 +272,7 @@ export default function Hero() {
*/
const handleSubmit = () => {
if (!isEmpty) {
LandingPromptStorage.store(textValue)
router.push('/signup')
}
}

View File

@@ -349,7 +349,7 @@ export function mockExecutionDependencies() {
})),
}))
vi.mock('@/db', () => ({
vi.mock('@sim/db', () => ({
db: mockDb,
}))
}
@@ -395,7 +395,7 @@ export async function getMockedDependencies() {
const workflowUtilsModule = await import('@/lib/workflows/utils')
const executorModule = await import('@/executor')
const serializerModule = await import('@/serializer')
const dbModule = await import('@/db')
const dbModule = await import('@sim/db')
return {
decryptSecret: utilsModule.decryptSecret,
@@ -428,7 +428,7 @@ export function mockScheduleStatusDb({
schedule?: any[]
workflow?: any[]
} = {}) {
vi.doMock('@/db', () => {
vi.doMock('@sim/db', () => {
let callCount = 0
const select = vi.fn().mockImplementation(() => ({
@@ -469,7 +469,7 @@ export function mockScheduleExecuteDb({
workflowRecord?: any
envRecord?: any
}): void {
vi.doMock('@/db', () => {
vi.doMock('@sim/db', () => {
const select = vi.fn().mockImplementation(() => ({
from: vi.fn().mockImplementation((table: any) => {
const tbl = String(table)
@@ -544,7 +544,7 @@ export function mockAuth(user: MockUser = mockUser): MockAuthResult {
* Mock common schema patterns
*/
export function mockCommonSchemas() {
vi.doMock('@/db/schema', () => ({
vi.doMock('@sim/db/schema', () => ({
workflowFolder: {
id: 'id',
userId: 'userId',
@@ -597,7 +597,7 @@ export function mockDrizzleOrm() {
* Mock knowledge-related database schemas
*/
export function mockKnowledgeSchemas() {
vi.doMock('@/db/schema', () => ({
vi.doMock('@sim/db/schema', () => ({
knowledgeBase: {
id: 'kb_id',
userId: 'user_id',
@@ -1091,7 +1091,7 @@ export function createMockDatabase(options: MockDatabaseOptions = {}) {
transaction: createTransactionMock(),
}
vi.doMock('@/db', () => ({ db: mockDb }))
vi.doMock('@sim/db', () => ({ db: mockDb }))
return {
mockDb,

View File

@@ -34,13 +34,11 @@ describe('OAuth Connections API Route', () => {
getSession: mockGetSession,
}))
vi.doMock('@/db', () => ({
vi.doMock('@sim/db', () => ({
db: mockDb,
}))
vi.doMock('@/db/schema', () => ({
account: { userId: 'userId', providerId: 'providerId' },
user: { email: 'email', id: 'id' },
eq: vi.fn((field, value) => ({ field, value, type: 'eq' })),
}))
vi.doMock('drizzle-orm', () => ({

View File

@@ -1,11 +1,10 @@
import { account, db, user } from '@sim/db'
import { eq } from 'drizzle-orm'
import { jwtDecode } from 'jwt-decode'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { generateRequestId } from '@/lib/utils'
import { db } from '@/db'
import { account, user } from '@/db/schema'
const logger = createLogger('OAuthConnectionsAPI')

View File

@@ -45,11 +45,11 @@ describe('OAuth Credentials API Route', () => {
parseProvider: mockParseProvider,
}))
vi.doMock('@/db', () => ({
vi.doMock('@sim/db', () => ({
db: mockDb,
}))
vi.doMock('@/db/schema', () => ({
vi.doMock('@sim/db/schema', () => ({
account: { userId: 'userId', providerId: 'providerId' },
user: { email: 'email', id: 'id' },
}))

View File

@@ -1,3 +1,5 @@
import { db } from '@sim/db'
import { account, user, workflow } from '@sim/db/schema'
import { and, eq } from 'drizzle-orm'
import { jwtDecode } from 'jwt-decode'
import { type NextRequest, NextResponse } from 'next/server'
@@ -7,8 +9,6 @@ import type { OAuthService } from '@/lib/oauth/oauth'
import { parseProvider } from '@/lib/oauth/oauth'
import { getUserEntityPermissions } from '@/lib/permissions/utils'
import { generateRequestId } from '@/lib/utils'
import { db } from '@/db'
import { account, user, workflow } from '@/db/schema'
export const dynamic = 'force-dynamic'

View File

@@ -32,11 +32,11 @@ describe('OAuth Disconnect API Route', () => {
getSession: mockGetSession,
}))
vi.doMock('@/db', () => ({
vi.doMock('@sim/db', () => ({
db: mockDb,
}))
vi.doMock('@/db/schema', () => ({
vi.doMock('@sim/db/schema', () => ({
account: { userId: 'userId', providerId: 'providerId' },
}))

View File

@@ -1,10 +1,10 @@
import { db } from '@sim/db'
import { account } from '@sim/db/schema'
import { and, eq, like, or } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { generateRequestId } from '@/lib/utils'
import { db } from '@/db'
import { account } from '@/db/schema'
export const dynamic = 'force-dynamic'

View File

@@ -1,11 +1,11 @@
import { db } from '@sim/db'
import { account } from '@sim/db/schema'
import { eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { generateRequestId } from '@/lib/utils'
import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils'
import { db } from '@/db'
import { account } from '@/db/schema'
export const dynamic = 'force-dynamic'

View File

@@ -1,11 +1,11 @@
import { db } from '@sim/db'
import { account } from '@sim/db/schema'
import { eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { generateRequestId } from '@/lib/utils'
import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils'
import { db } from '@/db'
import { account } from '@/db/schema'
export const dynamic = 'force-dynamic'

View File

@@ -31,7 +31,7 @@ describe('OAuth Utils', () => {
getSession: vi.fn().mockResolvedValue(mockSession),
}))
vi.doMock('@/db', () => ({
vi.doMock('@sim/db', () => ({
db: mockDb,
}))

View File

@@ -1,9 +1,9 @@
import { db } from '@sim/db'
import { account, workflow } from '@sim/db/schema'
import { and, desc, eq } from 'drizzle-orm'
import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { refreshOAuthToken } from '@/lib/oauth/oauth'
import { db } from '@/db'
import { account, workflow } from '@/db/schema'
const logger = createLogger('OAuthUtilsAPI')

View File

@@ -1,11 +1,11 @@
import { db } from '@sim/db'
import { account } from '@sim/db/schema'
import { eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { generateRequestId } from '@/lib/utils'
import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils'
import { db } from '@/db'
import { account } from '@/db/schema'
export const dynamic = 'force-dynamic'

View File

@@ -1,11 +1,11 @@
import { db } from '@sim/db'
import { account } from '@sim/db/schema'
import { eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { generateRequestId } from '@/lib/utils'
import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils'
import { db } from '@/db'
import { account } from '@/db/schema'
export const dynamic = 'force-dynamic'

View File

@@ -1,11 +1,11 @@
import { db } from '@sim/db'
import { subscription as subscriptionTable, user } from '@sim/db/schema'
import { and, eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { requireStripeClient } from '@/lib/billing/stripe-client'
import { env } from '@/lib/env'
import { createLogger } from '@/lib/logs/console/logger'
import { db } from '@/db'
import { subscription as subscriptionTable, user } from '@/db/schema'
const logger = createLogger('BillingPortal')

View File

@@ -1,11 +1,11 @@
import { db } from '@sim/db'
import { member, userStats } from '@sim/db/schema'
import { and, eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { getSimplifiedBillingSummary } from '@/lib/billing/core/billing'
import { getOrganizationBillingData } from '@/lib/billing/core/organization'
import { createLogger } from '@/lib/logs/console/logger'
import { db } from '@/db'
import { member, userStats } from '@/db/schema'
const logger = createLogger('UnifiedBillingAPI')

View File

@@ -1,3 +1,5 @@
import { db } from '@sim/db'
import { userStats } from '@sim/db/schema'
import { eq, sql } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
@@ -5,8 +7,6 @@ import { checkInternalApiKey } from '@/lib/copilot/utils'
import { isBillingEnabled } from '@/lib/environment'
import { createLogger } from '@/lib/logs/console/logger'
import { generateRequestId } from '@/lib/utils'
import { db } from '@/db'
import { userStats } from '@/db/schema'
import { calculateCost } from '@/providers/utils'
const logger = createLogger('billing-update-cost')

View File

@@ -1,3 +1,5 @@
import { db } from '@sim/db'
import { chat } from '@sim/db/schema'
import { eq } from 'drizzle-orm'
import type { NextRequest } from 'next/server'
import { z } from 'zod'
@@ -8,8 +10,6 @@ import { getRedisClient, markMessageAsProcessed, releaseLock } from '@/lib/redis
import { generateRequestId } from '@/lib/utils'
import { addCorsHeaders, setChatAuthCookie } from '@/app/api/chat/utils'
import { createErrorResponse, createSuccessResponse } from '@/app/api/workflows/utils'
import { db } from '@/db'
import { chat } from '@/db/schema'
const logger = createLogger('ChatOtpAPI')

View File

@@ -84,7 +84,7 @@ describe('Chat Subdomain API Route', () => {
}),
}))
vi.doMock('@/db', () => {
vi.doMock('@sim/db', () => {
const mockSelect = vi.fn().mockImplementation((fields) => {
if (fields && fields.isDeployed !== undefined) {
return {
@@ -153,7 +153,7 @@ describe('Chat Subdomain API Route', () => {
})
it('should return 404 for non-existent subdomain', async () => {
vi.doMock('@/db', () => {
vi.doMock('@sim/db', () => {
const mockLimit = vi.fn().mockReturnValue([])
const mockWhere = vi.fn().mockReturnValue({ limit: mockLimit })
const mockFrom = vi.fn().mockReturnValue({ where: mockWhere })
@@ -181,7 +181,7 @@ describe('Chat Subdomain API Route', () => {
})
it('should return 403 for inactive chat', async () => {
vi.doMock('@/db', () => {
vi.doMock('@sim/db', () => {
const mockLimit = vi.fn().mockReturnValue([
{
id: 'chat-id',
@@ -299,7 +299,7 @@ describe('Chat Subdomain API Route', () => {
it('should return 503 when workflow is not available', async () => {
// Override the default workflow result to return non-deployed
vi.doMock('@/db', () => {
vi.doMock('@sim/db', () => {
// Track call count to return different results
let callCount = 0

View File

@@ -1,3 +1,5 @@
import { db } from '@sim/db'
import { chat, workflow } from '@sim/db/schema'
import { eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { createLogger } from '@/lib/logs/console/logger'
@@ -10,8 +12,6 @@ import {
validateChatAuth,
} from '@/app/api/chat/utils'
import { createErrorResponse, createSuccessResponse } from '@/app/api/workflows/utils'
import { db } from '@/db'
import { chat, workflow } from '@/db/schema'
const logger = createLogger('ChatSubdomainAPI')

View File

@@ -30,7 +30,7 @@ describe('Chat Edit API Route', () => {
mockSet.mockReturnValue({ where: mockWhere })
mockDelete.mockReturnValue({ where: mockWhere })
vi.doMock('@/db', () => ({
vi.doMock('@sim/db', () => ({
db: {
select: mockSelect,
update: mockUpdate,
@@ -38,7 +38,7 @@ describe('Chat Edit API Route', () => {
},
}))
vi.doMock('@/db/schema', () => ({
vi.doMock('@sim/db/schema', () => ({
chat: { id: 'id', subdomain: 'subdomain', userId: 'userId' },
}))

View File

@@ -1,3 +1,5 @@
import { db } from '@sim/db'
import { chat } from '@sim/db/schema'
import { eq } from 'drizzle-orm'
import type { NextRequest } from 'next/server'
import { z } from 'zod'
@@ -8,8 +10,6 @@ import { getEmailDomain } from '@/lib/urls/utils'
import { encryptSecret } from '@/lib/utils'
import { checkChatAccess } from '@/app/api/chat/utils'
import { createErrorResponse, createSuccessResponse } from '@/app/api/workflows/utils'
import { db } from '@/db'
import { chat } from '@/db/schema'
export const dynamic = 'force-dynamic'

View File

@@ -29,14 +29,14 @@ describe('Chat API Route', () => {
mockInsert.mockReturnValue({ values: mockValues })
mockValues.mockReturnValue({ returning: mockReturning })
vi.doMock('@/db', () => ({
vi.doMock('@sim/db', () => ({
db: {
select: mockSelect,
insert: mockInsert,
},
}))
vi.doMock('@/db/schema', () => ({
vi.doMock('@sim/db/schema', () => ({
chat: { userId: 'userId', subdomain: 'subdomain' },
workflow: { id: 'id', userId: 'userId', isDeployed: 'isDeployed' },
}))

View File

@@ -1,3 +1,5 @@
import { db } from '@sim/db'
import { chat } from '@sim/db/schema'
import { eq } from 'drizzle-orm'
import type { NextRequest } from 'next/server'
import { v4 as uuidv4 } from 'uuid'
@@ -9,8 +11,6 @@ import { createLogger } from '@/lib/logs/console/logger'
import { encryptSecret } from '@/lib/utils'
import { checkWorkflowAccessForChatCreation } from '@/app/api/chat/utils'
import { createErrorResponse, createSuccessResponse } from '@/app/api/workflows/utils'
import { db } from '@/db'
import { chat } from '@/db/schema'
const logger = createLogger('ChatAPI')

View File

@@ -27,14 +27,14 @@ describe('Subdomain Validation API Route', () => {
mockWhere.mockReturnValue({ limit: mockLimit })
// Mock the database
vi.doMock('@/db', () => ({
vi.doMock('@sim/db', () => ({
db: {
select: mockSelect,
},
}))
// Mock the schema
vi.doMock('@/db/schema', () => ({
vi.doMock('@sim/db/schema', () => ({
chat: {
subdomain: 'subdomain',
},

View File

@@ -1,10 +1,10 @@
import { db } from '@sim/db'
import { chat } from '@sim/db/schema'
import { eq } from 'drizzle-orm'
import { NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { createErrorResponse, createSuccessResponse } from '@/app/api/workflows/utils'
import { db } from '@/db'
import { chat } from '@/db/schema'
const logger = createLogger('SubdomainValidateAPI')

View File

@@ -7,7 +7,7 @@ import type { NextResponse } from 'next/server'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { env } from '@/lib/env'
vi.mock('@/db', () => ({
vi.mock('@sim/db', () => ({
db: {
select: vi.fn(),
update: vi.fn(),

View File

@@ -1,3 +1,5 @@
import { db } from '@sim/db'
import { chat, userStats, workflow } from '@sim/db/schema'
import { eq, sql } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { v4 as uuidv4 } from 'uuid'
@@ -12,8 +14,6 @@ import { processStreamingBlockLogs } from '@/lib/tokenization'
import { getEmailDomain } from '@/lib/urls/utils'
import { decryptSecret, generateRequestId } from '@/lib/utils'
import { getBlock } from '@/blocks'
import { db } from '@/db'
import { chat, userStats, workflow } from '@/db/schema'
import { Executor } from '@/executor'
import type { BlockLog, ExecutionResult } from '@/executor/types'
import { Serializer } from '@/serializer'

View File

@@ -1,9 +1,9 @@
import { db } from '@sim/db'
import { userStats } from '@sim/db/schema'
import { eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { checkInternalApiKey } from '@/lib/copilot/utils'
import { createLogger } from '@/lib/logs/console/logger'
import { db } from '@/db'
import { userStats } from '@/db/schema'
const logger = createLogger('CopilotApiKeysValidate')

View File

@@ -46,7 +46,7 @@ describe('Copilot Chat API Route', () => {
mockUpdate.mockReturnValue({ set: mockSet })
mockSet.mockReturnValue({ where: mockWhere })
vi.doMock('@/db', () => ({
vi.doMock('@sim/db', () => ({
db: {
select: mockSelect,
insert: mockInsert,
@@ -54,7 +54,7 @@ describe('Copilot Chat API Route', () => {
},
}))
vi.doMock('@/db/schema', () => ({
vi.doMock('@sim/db/schema', () => ({
copilotChats: {
id: 'id',
userId: 'userId',

View File

@@ -1,3 +1,5 @@
import { db } from '@sim/db'
import { copilotChats } from '@sim/db/schema'
import { and, desc, eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
@@ -18,8 +20,6 @@ import { generateChatTitle } from '@/lib/sim-agent/utils'
import { createFileContent, isSupportedFileType } from '@/lib/uploads/file-utils'
import { S3_COPILOT_CONFIG } from '@/lib/uploads/setup'
import { downloadFile, getStorageProvider } from '@/lib/uploads/storage-client'
import { db } from '@/db'
import { copilotChats } from '@/db/schema'
const logger = createLogger('CopilotChatAPI')

View File

@@ -32,14 +32,14 @@ describe('Copilot Chat Update Messages API Route', () => {
mockUpdate.mockReturnValue({ set: mockSet })
mockSet.mockReturnValue({ where: vi.fn().mockResolvedValue(undefined) }) // Different where for update
vi.doMock('@/db', () => ({
vi.doMock('@sim/db', () => ({
db: {
select: mockSelect,
update: mockUpdate,
},
}))
vi.doMock('@/db/schema', () => ({
vi.doMock('@sim/db/schema', () => ({
copilotChats: {
id: 'id',
userId: 'userId',

View File

@@ -1,3 +1,5 @@
import { db } from '@sim/db'
import { copilotChats } from '@sim/db/schema'
import { and, eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
@@ -9,8 +11,6 @@ import {
createUnauthorizedResponse,
} from '@/lib/copilot/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { db } from '@/db'
import { copilotChats } from '@/db/schema'
const logger = createLogger('CopilotChatUpdateAPI')

View File

@@ -1,3 +1,5 @@
import { db } from '@sim/db'
import { copilotChats } from '@sim/db/schema'
import { desc, eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import {
@@ -6,8 +8,6 @@ import {
createUnauthorizedResponse,
} from '@/lib/copilot/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { db } from '@/db'
import { copilotChats } from '@/db/schema'
const logger = createLogger('CopilotChatsListAPI')

View File

@@ -28,13 +28,13 @@ describe('Copilot Checkpoints Revert API Route', () => {
mockWhere.mockReturnValue({ then: mockThen })
mockThen.mockResolvedValue(null) // Default: no data found
vi.doMock('@/db', () => ({
vi.doMock('@sim/db', () => ({
db: {
select: mockSelect,
},
}))
vi.doMock('@/db/schema', () => ({
vi.doMock('@sim/db/schema', () => ({
workflowCheckpoints: {
id: 'id',
userId: 'userId',

View File

@@ -1,3 +1,5 @@
import { db } from '@sim/db'
import { workflowCheckpoints, workflow as workflowTable } from '@sim/db/schema'
import { and, eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
@@ -9,8 +11,6 @@ import {
createUnauthorizedResponse,
} from '@/lib/copilot/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { db } from '@/db'
import { workflowCheckpoints, workflow as workflowTable } from '@/db/schema'
const logger = createLogger('CheckpointRevertAPI')

View File

@@ -49,14 +49,14 @@ describe('Copilot Checkpoints API Route', () => {
mockInsert.mockReturnValue({ values: mockValues })
mockValues.mockReturnValue({ returning: mockReturning })
vi.doMock('@/db', () => ({
vi.doMock('@sim/db', () => ({
db: {
select: mockSelect,
insert: mockInsert,
},
}))
vi.doMock('@/db/schema', () => ({
vi.doMock('@sim/db/schema', () => ({
copilotChats: mockCopilotChats,
workflowCheckpoints: mockWorkflowCheckpoints,
}))

View File

@@ -1,3 +1,5 @@
import { db } from '@sim/db'
import { copilotChats, workflowCheckpoints } from '@sim/db/schema'
import { and, desc, eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
@@ -9,8 +11,6 @@ import {
createUnauthorizedResponse,
} from '@/lib/copilot/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { db } from '@/db'
import { copilotChats, workflowCheckpoints } from '@/db/schema'
const logger = createLogger('WorkflowCheckpointsAPI')

View File

@@ -1,3 +1,5 @@
import { db } from '@sim/db'
import { copilotFeedback } from '@sim/db/schema'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import {
@@ -8,8 +10,6 @@ import {
createUnauthorizedResponse,
} from '@/lib/copilot/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { db } from '@/db'
import { copilotFeedback } from '@/db/schema'
const logger = createLogger('CopilotFeedbackAPI')

View File

@@ -1,11 +1,11 @@
import { db } from '@sim/db'
import { environment } from '@sim/db/schema'
import { eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { decryptSecret, encryptSecret, generateRequestId } from '@/lib/utils'
import { db } from '@/db'
import { environment } from '@/db/schema'
import type { EnvironmentVariable } from '@/stores/settings/environment/types'
const logger = createLogger('EnvironmentAPI')

View File

@@ -127,7 +127,7 @@ describe('Individual Folder API Route', () => {
mockAuthenticatedUser()
const dbMock = createFolderDbMock()
vi.doMock('@/db', () => dbMock)
vi.doMock('@sim/db', () => dbMock)
const req = createMockRequest('PUT', {
name: 'Updated Folder Name',
@@ -152,7 +152,7 @@ describe('Individual Folder API Route', () => {
mockAuthenticatedUser()
const dbMock = createFolderDbMock()
vi.doMock('@/db', () => dbMock)
vi.doMock('@sim/db', () => dbMock)
const req = createMockRequest('PUT', {
name: 'Updated Folder',
@@ -171,7 +171,7 @@ describe('Individual Folder API Route', () => {
mockUnauthenticated()
const dbMock = createFolderDbMock()
vi.doMock('@/db', () => dbMock)
vi.doMock('@sim/db', () => dbMock)
const req = createMockRequest('PUT', {
name: 'Updated Folder',
@@ -193,7 +193,7 @@ describe('Individual Folder API Route', () => {
mockGetUserEntityPermissions.mockResolvedValue('read') // Read-only permissions
const dbMock = createFolderDbMock()
vi.doMock('@/db', () => dbMock)
vi.doMock('@sim/db', () => dbMock)
const req = createMockRequest('PUT', {
name: 'Updated Folder',
@@ -215,7 +215,7 @@ describe('Individual Folder API Route', () => {
mockGetUserEntityPermissions.mockResolvedValue('write') // Write permissions
const dbMock = createFolderDbMock()
vi.doMock('@/db', () => dbMock)
vi.doMock('@sim/db', () => dbMock)
const req = createMockRequest('PUT', {
name: 'Updated Folder',
@@ -237,7 +237,7 @@ describe('Individual Folder API Route', () => {
mockGetUserEntityPermissions.mockResolvedValue('admin') // Admin permissions
const dbMock = createFolderDbMock()
vi.doMock('@/db', () => dbMock)
vi.doMock('@sim/db', () => dbMock)
const req = createMockRequest('PUT', {
name: 'Updated Folder',
@@ -258,7 +258,7 @@ describe('Individual Folder API Route', () => {
mockAuthenticatedUser()
const dbMock = createFolderDbMock()
vi.doMock('@/db', () => dbMock)
vi.doMock('@sim/db', () => dbMock)
const req = createMockRequest('PUT', {
name: 'Updated Folder',
@@ -293,7 +293,7 @@ describe('Individual Folder API Route', () => {
}),
})
vi.doMock('@/db', () => dbMock)
vi.doMock('@sim/db', () => dbMock)
const req = createMockRequest('PUT', {
name: ' Folder With Spaces ',
@@ -314,7 +314,7 @@ describe('Individual Folder API Route', () => {
const dbMock = createFolderDbMock({
throwError: true,
})
vi.doMock('@/db', () => dbMock)
vi.doMock('@sim/db', () => dbMock)
const req = createMockRequest('PUT', {
name: 'Updated Folder',
@@ -340,7 +340,7 @@ describe('Individual Folder API Route', () => {
mockAuthenticatedUser()
const dbMock = createFolderDbMock()
vi.doMock('@/db', () => dbMock)
vi.doMock('@sim/db', () => dbMock)
const req = createMockRequest('PUT', {
name: '', // Empty name
@@ -359,7 +359,7 @@ describe('Individual Folder API Route', () => {
mockAuthenticatedUser()
const dbMock = createFolderDbMock()
vi.doMock('@/db', () => dbMock)
vi.doMock('@sim/db', () => dbMock)
// Create a request with invalid JSON
const req = new Request('http://localhost:3000/api/folders/folder-1', {
@@ -396,7 +396,7 @@ describe('Individual Folder API Route', () => {
folderLookupResult: { id: 'folder-3', parentId: null, name: 'Folder 3' },
circularCheckResults,
})
vi.doMock('@/db', () => dbMock)
vi.doMock('@sim/db', () => dbMock)
const req = createMockRequest('PUT', {
name: 'Updated Folder 3',
@@ -425,7 +425,7 @@ describe('Individual Folder API Route', () => {
})
// Mock the recursive deletion function
vi.doMock('@/db', () => dbMock)
vi.doMock('@sim/db', () => dbMock)
const req = createMockRequest('DELETE')
const params = Promise.resolve({ id: 'folder-1' })
@@ -445,7 +445,7 @@ describe('Individual Folder API Route', () => {
mockUnauthenticated()
const dbMock = createFolderDbMock()
vi.doMock('@/db', () => dbMock)
vi.doMock('@sim/db', () => dbMock)
const req = createMockRequest('DELETE')
const params = Promise.resolve({ id: 'folder-1' })
@@ -465,7 +465,7 @@ describe('Individual Folder API Route', () => {
mockGetUserEntityPermissions.mockResolvedValue('read') // Read-only permissions
const dbMock = createFolderDbMock()
vi.doMock('@/db', () => dbMock)
vi.doMock('@sim/db', () => dbMock)
const req = createMockRequest('DELETE')
const params = Promise.resolve({ id: 'folder-1' })
@@ -485,7 +485,7 @@ describe('Individual Folder API Route', () => {
mockGetUserEntityPermissions.mockResolvedValue('write') // Write permissions (not enough for delete)
const dbMock = createFolderDbMock()
vi.doMock('@/db', () => dbMock)
vi.doMock('@sim/db', () => dbMock)
const req = createMockRequest('DELETE')
const params = Promise.resolve({ id: 'folder-1' })
@@ -507,7 +507,7 @@ describe('Individual Folder API Route', () => {
const dbMock = createFolderDbMock({
folderLookupResult: mockFolder,
})
vi.doMock('@/db', () => dbMock)
vi.doMock('@sim/db', () => dbMock)
const req = createMockRequest('DELETE')
const params = Promise.resolve({ id: 'folder-1' })
@@ -528,7 +528,7 @@ describe('Individual Folder API Route', () => {
const dbMock = createFolderDbMock({
throwError: true,
})
vi.doMock('@/db', () => dbMock)
vi.doMock('@sim/db', () => dbMock)
const req = createMockRequest('DELETE')
const params = Promise.resolve({ id: 'folder-1' })

View File

@@ -1,10 +1,10 @@
import { db } from '@sim/db'
import { workflow, workflowFolder } from '@sim/db/schema'
import { and, eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { getUserEntityPermissions } from '@/lib/permissions/utils'
import { db } from '@/db'
import { workflow, workflowFolder } from '@/db/schema'
const logger = createLogger('FoldersIDAPI')

View File

@@ -75,7 +75,7 @@ describe('Folders API Route', () => {
mockGetUserEntityPermissions.mockResolvedValue('admin')
vi.doMock('@/db', () => ({
vi.doMock('@sim/db', () => ({
db: {
select: mockSelect,
insert: mockInsert,

View File

@@ -1,10 +1,10 @@
import { db } from '@sim/db'
import { workflowFolder } from '@sim/db/schema'
import { and, asc, desc, eq, isNull } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { getUserEntityPermissions } from '@/lib/permissions/utils'
import { db } from '@/db'
import { workflowFolder } from '@/db/schema'
const logger = createLogger('FoldersAPI')

View File

@@ -83,7 +83,7 @@ describe('Document By ID API Route', () => {
beforeEach(async () => {
resetMocks()
vi.doMock('@/db', () => ({
vi.doMock('@sim/db', () => ({
db: mockDbChain,
}))

View File

@@ -90,7 +90,7 @@ describe('Knowledge Base Documents API Route', () => {
beforeEach(async () => {
resetMocks()
vi.doMock('@/db', () => ({
vi.doMock('@sim/db', () => ({
db: mockDbChain,
}))

View File

@@ -72,7 +72,7 @@ describe('Knowledge Base By ID API Route', () => {
beforeEach(async () => {
vi.clearAllMocks()
vi.doMock('@/db', () => ({
vi.doMock('@sim/db', () => ({
db: mockDbChain,
}))

View File

@@ -33,7 +33,7 @@ describe('Knowledge Base API Route', () => {
beforeEach(async () => {
vi.clearAllMocks()
vi.doMock('@/db', () => ({
vi.doMock('@sim/db', () => ({
db: mockDbChain,
}))

View File

@@ -128,7 +128,7 @@ describe('Knowledge Search API Route', () => {
beforeEach(async () => {
vi.clearAllMocks()
vi.doMock('@/db', () => ({
vi.doMock('@sim/db', () => ({
db: mockDbChain,
}))

View File

@@ -15,7 +15,7 @@ vi.mock('@/lib/logs/console/logger', () => ({
error: vi.fn(),
})),
}))
vi.mock('@/db')
vi.mock('@sim/db')
vi.mock('@/lib/knowledge/documents/utils', () => ({
retryWithExponentialBackoff: (fn: any) => fn(),
}))

View File

@@ -1,7 +1,7 @@
import { db } from '@sim/db'
import { document, embedding } from '@sim/db/schema'
import { and, eq, inArray, isNull, sql } from 'drizzle-orm'
import { createLogger } from '@/lib/logs/console/logger'
import { db } from '@/db'
import { document, embedding } from '@/db/schema'
const logger = createLogger('KnowledgeSearchUtils')

View File

@@ -84,7 +84,7 @@ vi.stubGlobal(
})
)
vi.mock('@/db', () => {
vi.mock('@sim/db', () => {
const selectBuilder = {
from(table: any) {
return {

View File

@@ -1,7 +1,7 @@
import { db } from '@sim/db'
import { document, embedding, knowledgeBase } from '@sim/db/schema'
import { and, eq, isNull } from 'drizzle-orm'
import { getUserEntityPermissions } from '@/lib/permissions/utils'
import { db } from '@/db'
import { document, embedding, knowledgeBase } from '@/db/schema'
export interface KnowledgeBaseData {
id: string

View File

@@ -1,10 +1,10 @@
import { db } from '@sim/db'
import { permissions, workflow, workflowExecutionLogs } from '@sim/db/schema'
import { and, eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { generateRequestId } from '@/lib/utils'
import { db } from '@/db'
import { permissions, workflow, workflowExecutionLogs } from '@/db/schema'
const logger = createLogger('LogDetailsByIdAPI')

View File

@@ -1,4 +1,7 @@
import { PutObjectCommand } from '@aws-sdk/client-s3'
// Dynamic import for S3 client to avoid client-side bundling
import { db } from '@sim/db'
import { subscription, user, workflow, workflowExecutionLogs } from '@sim/db/schema'
import { and, eq, inArray, lt, sql } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { verifyCronAuth } from '@/lib/auth/internal'
@@ -6,9 +9,6 @@ import { env } from '@/lib/env'
import { createLogger } from '@/lib/logs/console/logger'
import { snapshotService } from '@/lib/logs/execution/snapshot/service'
import { deleteFile, isUsingCloudStorage } from '@/lib/uploads'
// Dynamic import for S3 client to avoid client-side bundling
import { db } from '@/db'
import { subscription, user, workflow, workflowExecutionLogs } from '@/db/schema'
export const dynamic = 'force-dynamic'

View File

@@ -1,8 +1,8 @@
import { db } from '@sim/db'
import { workflowExecutionLogs, workflowExecutionSnapshots } from '@sim/db/schema'
import { eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { createLogger } from '@/lib/logs/console/logger'
import { db } from '@/db'
import { workflowExecutionLogs, workflowExecutionSnapshots } from '@/db/schema'
const logger = createLogger('LogsByExecutionIdAPI')

View File

@@ -1,11 +1,11 @@
import { db } from '@sim/db'
import { permissions, workflow, workflowExecutionLogs } from '@sim/db/schema'
import { and, desc, eq, gte, inArray, lte, type SQL, sql } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { generateRequestId } from '@/lib/utils'
import { db } from '@/db'
import { permissions, workflow, workflowExecutionLogs } from '@/db/schema'
const logger = createLogger('LogsAPI')

View File

@@ -1,11 +1,11 @@
import { db } from '@sim/db'
import { mcpServers } from '@sim/db/schema'
import { and, eq, isNull } from 'drizzle-orm'
import type { NextRequest } from 'next/server'
import { createLogger } from '@/lib/logs/console/logger'
import { withMcpAuth } from '@/lib/mcp/middleware'
import { mcpService } from '@/lib/mcp/service'
import { createMcpErrorResponse, createMcpSuccessResponse } from '@/lib/mcp/utils'
import { db } from '@/db'
import { mcpServers } from '@/db/schema'
const logger = createLogger('McpServerRefreshAPI')

View File

@@ -1,3 +1,5 @@
import { db } from '@sim/db'
import { mcpServers } from '@sim/db/schema'
import { and, eq, isNull } from 'drizzle-orm'
import type { NextRequest } from 'next/server'
import { createLogger } from '@/lib/logs/console/logger'
@@ -5,8 +7,6 @@ import { getParsedBody, withMcpAuth } from '@/lib/mcp/middleware'
import { mcpService } from '@/lib/mcp/service'
import { validateMcpServerUrl } from '@/lib/mcp/url-validator'
import { createMcpErrorResponse, createMcpSuccessResponse } from '@/lib/mcp/utils'
import { db } from '@/db'
import { mcpServers } from '@/db/schema'
const logger = createLogger('McpServerAPI')

View File

@@ -1,3 +1,5 @@
import { db } from '@sim/db'
import { mcpServers } from '@sim/db/schema'
import { and, eq, isNull } from 'drizzle-orm'
import type { NextRequest } from 'next/server'
import { createLogger } from '@/lib/logs/console/logger'
@@ -6,8 +8,6 @@ import { mcpService } from '@/lib/mcp/service'
import type { McpTransport } from '@/lib/mcp/types'
import { validateMcpServerUrl } from '@/lib/mcp/url-validator'
import { createMcpErrorResponse, createMcpSuccessResponse } from '@/lib/mcp/utils'
import { db } from '@/db'
import { mcpServers } from '@/db/schema'
const logger = createLogger('McpServersAPI')

View File

@@ -1,9 +1,9 @@
import { db } from '@sim/db'
import { memory } from '@sim/db/schema'
import { and, eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { createLogger } from '@/lib/logs/console/logger'
import { generateRequestId } from '@/lib/utils'
import { db } from '@/db'
import { memory } from '@/db/schema'
const logger = createLogger('MemoryByIdAPI')

View File

@@ -1,9 +1,9 @@
import { db } from '@sim/db'
import { memory } from '@sim/db/schema'
import { and, eq, isNull, like } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { createLogger } from '@/lib/logs/console/logger'
import { generateRequestId } from '@/lib/utils'
import { db } from '@/db'
import { memory } from '@/db/schema'
const logger = createLogger('MemoryAPI')

View File

@@ -1,9 +1,5 @@
import { randomUUID } from 'crypto'
import { and, eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { db } from '@/db'
import { db } from '@sim/db'
import {
invitation,
member,
@@ -12,7 +8,11 @@ import {
user,
type WorkspaceInvitationStatus,
workspaceInvitation,
} from '@/db/schema'
} from '@sim/db/schema'
import { and, eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console/logger'
const logger = createLogger('OrganizationInvitation')

View File

@@ -1,4 +1,14 @@
import { randomUUID } from 'crypto'
import { db } from '@sim/db'
import {
invitation,
member,
organization,
user,
type WorkspaceInvitationStatus,
workspace,
workspaceInvitation,
} from '@sim/db/schema'
import { and, eq, inArray, isNull } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import {
@@ -16,16 +26,6 @@ import { quickValidateEmail } from '@/lib/email/validation'
import { env } from '@/lib/env'
import { createLogger } from '@/lib/logs/console/logger'
import { hasWorkspaceAdminAccess } from '@/lib/permissions/utils'
import { db } from '@/db'
import {
invitation,
member,
organization,
user,
type WorkspaceInvitationStatus,
workspace,
workspaceInvitation,
} from '@/db/schema'
const logger = createLogger('OrganizationInvitations')

View File

@@ -1,10 +1,10 @@
import { db } from '@sim/db'
import { member, user, userStats } from '@sim/db/schema'
import { and, eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { getUserUsageData } from '@/lib/billing/core/usage'
import { createLogger } from '@/lib/logs/console/logger'
import { db } from '@/db'
import { member, user, userStats } from '@/db/schema'
const logger = createLogger('OrganizationMemberAPI')

View File

@@ -1,4 +1,6 @@
import { randomUUID } from 'crypto'
import { db } from '@sim/db'
import { invitation, member, organization, user, userStats } from '@sim/db/schema'
import { and, eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { getEmailSubject, renderInvitationEmail } from '@/components/emails/render-email'
@@ -9,8 +11,6 @@ import { sendEmail } from '@/lib/email/mailer'
import { quickValidateEmail } from '@/lib/email/validation'
import { env } from '@/lib/env'
import { createLogger } from '@/lib/logs/console/logger'
import { db } from '@/db'
import { invitation, member, organization, user, userStats } from '@/db/schema'
const logger = createLogger('OrganizationMembersAPI')

View File

@@ -1,3 +1,5 @@
import { db } from '@sim/db'
import { member, organization } from '@sim/db/schema'
import { and, eq, ne } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
@@ -7,8 +9,6 @@ import {
updateOrganizationSeats,
} from '@/lib/billing/validation/seat-management'
import { createLogger } from '@/lib/logs/console/logger'
import { db } from '@/db'
import { member, organization } from '@/db/schema'
const logger = createLogger('OrganizationAPI')

View File

@@ -1,9 +1,9 @@
import { db } from '@sim/db'
import { member, permissions, user, workspace } from '@sim/db/schema'
import { and, eq, or } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { db } from '@/db'
import { member, permissions, user, workspace } from '@/db/schema'
const logger = createLogger('OrganizationWorkspacesAPI')

View File

@@ -1,11 +1,11 @@
import { db } from '@sim/db'
import { workflow, workflowSchedule } from '@sim/db/schema'
import { eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { getUserEntityPermissions } from '@/lib/permissions/utils'
import { generateRequestId } from '@/lib/utils'
import { db } from '@/db'
import { workflow, workflowSchedule } from '@/db/schema'
const logger = createLogger('ScheduleAPI')

View File

@@ -1,11 +1,11 @@
import { db } from '@sim/db'
import { workflow, workflowSchedule } from '@sim/db/schema'
import { eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { getUserEntityPermissions } from '@/lib/permissions/utils'
import { generateRequestId } from '@/lib/utils'
import { db } from '@/db'
import { workflow, workflowSchedule } from '@/db/schema'
const logger = createLogger('ScheduleStatusAPI')

View File

@@ -33,7 +33,7 @@ describe('Scheduled Workflow Execution API Route', () => {
})),
}))
vi.doMock('@/db', () => {
vi.doMock('@sim/db', () => {
const mockDb = {
select: vi.fn().mockImplementation(() => ({
from: vi.fn().mockImplementation((table: string) => {
@@ -147,7 +147,7 @@ describe('Scheduled Workflow Execution API Route', () => {
})
it('should handle case with no due schedules', async () => {
vi.doMock('@/db', () => {
vi.doMock('@sim/db', () => {
const mockDb = {
select: vi.fn().mockImplementation(() => ({
from: vi.fn().mockImplementation(() => ({
@@ -183,7 +183,7 @@ describe('Scheduled Workflow Execution API Route', () => {
})
it('should handle scheduler-level errors gracefully', async () => {
vi.doMock('@/db', () => {
vi.doMock('@sim/db', () => {
const mockDb = {
select: vi.fn().mockImplementation(() => {
throw new Error('Database error')

View File

@@ -1,3 +1,5 @@
import { db } from '@sim/db'
import { userStats, workflow, workflowSchedule } from '@sim/db/schema'
import { Cron } from 'croner'
import { and, eq, lte, not, sql } from 'drizzle-orm'
import { NextResponse } from 'next/server'
@@ -18,8 +20,6 @@ import {
import { decryptSecret, generateRequestId } from '@/lib/utils'
import { loadWorkflowFromNormalizedTables } from '@/lib/workflows/db-helpers'
import { updateWorkflowRunCounts } from '@/lib/workflows/utils'
import { db } from '@/db'
import { userStats, workflow, workflowSchedule } from '@/db/schema'
import { Executor } from '@/executor'
import { Serializer } from '@/serializer'
import { RateLimiter } from '@/services/queue'

View File

@@ -52,7 +52,7 @@ describe('Schedule Configuration API Route', () => {
// Create mock database with test schedules
// Mock the database to return workflow data for authorization check
vi.doMock('@/db', () => {
vi.doMock('@sim/db', () => {
let callCount = 0
const mockDb = {
select: vi.fn().mockImplementation(() => ({
@@ -201,7 +201,7 @@ describe('Schedule Configuration API Route', () => {
where: vi.fn().mockResolvedValue([]),
}))
vi.doMock('@/db', () => ({
vi.doMock('@sim/db', () => ({
db: {
select: vi.fn().mockImplementation(() => ({
from: vi.fn().mockImplementation(() => ({
@@ -271,7 +271,7 @@ describe('Schedule Configuration API Route', () => {
*/
it('should handle errors gracefully', async () => {
// Mock the db to throw an error on insert
vi.doMock('@/db', () => ({
vi.doMock('@sim/db', () => ({
db: {
select: vi.fn().mockImplementation(() => ({
from: vi.fn().mockImplementation(() => ({

View File

@@ -1,3 +1,5 @@
import { db } from '@sim/db'
import { workflow, workflowSchedule } from '@sim/db/schema'
import { and, eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
@@ -13,8 +15,6 @@ import {
validateCronExpression,
} from '@/lib/schedules/utils'
import { generateRequestId } from '@/lib/utils'
import { db } from '@/db'
import { workflow, workflowSchedule } from '@/db/schema'
const logger = createLogger('ScheduledAPI')

View File

@@ -1,3 +1,5 @@
import { db } from '@sim/db'
import { templates, workflow } from '@sim/db/schema'
import { eq, sql } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
@@ -5,8 +7,6 @@ import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { hasAdminPermission } from '@/lib/permissions/utils'
import { generateRequestId } from '@/lib/utils'
import { db } from '@/db'
import { templates, workflow } from '@/db/schema'
const logger = createLogger('TemplateByIdAPI')

View File

@@ -1,11 +1,11 @@
import { db } from '@sim/db'
import { templateStars, templates } from '@sim/db/schema'
import { and, eq, sql } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { v4 as uuidv4 } from 'uuid'
import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { generateRequestId } from '@/lib/utils'
import { db } from '@/db'
import { templateStars, templates } from '@/db/schema'
const logger = createLogger('TemplateStarAPI')

View File

@@ -1,11 +1,11 @@
import { db } from '@sim/db'
import { templates, workflow, workflowBlocks, workflowEdges } from '@sim/db/schema'
import { eq, sql } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { v4 as uuidv4 } from 'uuid'
import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { generateRequestId } from '@/lib/utils'
import { db } from '@/db'
import { templates, workflow, workflowBlocks, workflowEdges } from '@/db/schema'
const logger = createLogger('TemplateUseAPI')

View File

@@ -1,3 +1,5 @@
import { db } from '@sim/db'
import { templateStars, templates, workflow } from '@sim/db/schema'
import { and, desc, eq, ilike, or, sql } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { v4 as uuidv4 } from 'uuid'
@@ -5,8 +7,6 @@ import { z } from 'zod'
import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { generateRequestId } from '@/lib/utils'
import { db } from '@/db'
import { templateStars, templates, workflow } from '@/db/schema'
const logger = createLogger('TemplatesAPI')

View File

@@ -91,7 +91,7 @@ describe('Custom Tools API Routes', () => {
mockDelete.mockReturnValue({ where: mockWhere })
// Mock database
vi.doMock('@/db', () => ({
vi.doMock('@sim/db', () => ({
db: {
select: mockSelect,
insert: mockInsert,
@@ -110,7 +110,7 @@ describe('Custom Tools API Routes', () => {
}))
// Mock schema
vi.doMock('@/db/schema', () => ({
vi.doMock('@sim/db/schema', () => ({
customTools: {
userId: 'userId', // Add these properties to enable WHERE clauses with eq()
id: 'id',

View File

@@ -1,3 +1,5 @@
import { db } from '@sim/db'
import { customTools } from '@sim/db/schema'
import { eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
@@ -5,8 +7,6 @@ import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { generateRequestId } from '@/lib/utils'
import { getUserId } from '@/app/api/auth/oauth/utils'
import { db } from '@/db'
import { customTools } from '@/db/schema'
const logger = createLogger('CustomToolsAPI')

View File

@@ -1,11 +1,11 @@
import { db } from '@sim/db'
import { account } from '@sim/db/schema'
import { and, eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { generateRequestId } from '@/lib/utils'
import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils'
import { db } from '@/db'
import { account } from '@/db/schema'
export const dynamic = 'force-dynamic'

View File

@@ -1,11 +1,11 @@
import { db } from '@sim/db'
import { account } from '@sim/db/schema'
import { and, eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { generateRequestId } from '@/lib/utils'
import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils'
import { db } from '@/db'
import { account } from '@/db/schema'
export const dynamic = 'force-dynamic'
const logger = createLogger('GmailLabelsAPI')

View File

@@ -1,11 +1,11 @@
import { randomUUID } from 'crypto'
import { db } from '@sim/db'
import { account } from '@sim/db/schema'
import { eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils'
import { db } from '@/db'
import { account } from '@/db/schema'
import type { PlannerTask } from '@/tools/microsoft_planner/types'
const logger = createLogger('MicrosoftPlannerTasksAPI')

View File

@@ -1,11 +1,11 @@
import { randomUUID } from 'crypto'
import { db } from '@sim/db'
import { account } from '@sim/db/schema'
import { eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils'
import { db } from '@/db'
import { account } from '@/db/schema'
export const dynamic = 'force-dynamic'

View File

@@ -1,11 +1,11 @@
import { randomUUID } from 'crypto'
import { db } from '@sim/db'
import { account } from '@sim/db/schema'
import { eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils'
import { db } from '@/db'
import { account } from '@/db/schema'
export const dynamic = 'force-dynamic'

View File

@@ -1,11 +1,11 @@
import { db } from '@sim/db'
import { account } from '@sim/db/schema'
import { eq } from 'drizzle-orm'
import { NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { generateRequestId } from '@/lib/utils'
import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils'
import { db } from '@/db'
import { account } from '@/db/schema'
export const dynamic = 'force-dynamic'

View File

@@ -1,11 +1,11 @@
import { randomUUID } from 'crypto'
import { db } from '@sim/db'
import { account } from '@sim/db/schema'
import { eq } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { createLogger } from '@/lib/logs/console/logger'
import { refreshAccessTokenIfNeeded } from '@/app/api/auth/oauth/utils'
import { db } from '@/db'
import { account } from '@/db/schema'
export const dynamic = 'force-dynamic'

Some files were not shown because too many files have changed in this diff Show More