Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 1 addition & 33 deletions apps/sim/app/(auth)/login/login-form.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
'use client'

import { useMemo, useRef, useState } from 'react'
import { Turnstile, type TurnstileInstance } from '@marsidev/react-turnstile'
import { useRef, useState } from 'react'
import { createLogger } from '@sim/logger'
import { Eye, EyeOff } from 'lucide-react'
import Link from 'next/link'
Expand Down Expand Up @@ -88,8 +87,6 @@ export default function LoginPage({
const [passwordErrors, setPasswordErrors] = useState<string[]>([])
const [showValidationError, setShowValidationError] = useState(false)
const [formError, setFormError] = useState<string | null>(null)
const turnstileRef = useRef<TurnstileInstance>(null)
const turnstileSiteKey = useMemo(() => getEnv('NEXT_PUBLIC_TURNSTILE_SITE_KEY'), [])
const buttonClass = useBrandedButtonClass()

const callbackUrlParam = searchParams?.get('callbackUrl')
Expand Down Expand Up @@ -169,20 +166,6 @@ export default function LoginPage({
const safeCallbackUrl = callbackUrl
let errorHandled = false

// Execute Turnstile challenge on submit and get a fresh token
let token: string | undefined
if (turnstileSiteKey && turnstileRef.current) {
try {
turnstileRef.current.reset()
turnstileRef.current.execute()
token = await turnstileRef.current.getResponsePromise(15_000)
} catch {
setFormError('Captcha verification failed. Please try again.')
setIsLoading(false)
return
}
}

setFormError(null)
const result = await client.signIn.email(
{
Expand All @@ -191,11 +174,6 @@ export default function LoginPage({
callbackURL: safeCallbackUrl,
},
{
fetchOptions: {
headers: {
...(token ? { 'x-captcha-response': token } : {}),
},
},
onError: (ctx) => {
logger.error('Login error:', ctx.error)

Expand Down Expand Up @@ -464,16 +442,6 @@ export default function LoginPage({
</div>
</div>

{turnstileSiteKey && (
<div className='absolute'>
<Turnstile
ref={turnstileRef}
siteKey={turnstileSiteKey}
options={{ size: 'invisible', execution: 'execute' }}
/>
</div>
)}

{resetSuccessMessage && (
<div className='text-[#4CAF50] text-xs'>
<p>{resetSuccessMessage}</p>
Expand Down
40 changes: 28 additions & 12 deletions apps/sim/app/(auth)/signup/signup-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ function SignupFormContent({
const [showEmailValidationError, setShowEmailValidationError] = useState(false)
const [formError, setFormError] = useState<string | null>(null)
const turnstileRef = useRef<TurnstileInstance>(null)
const captchaResolveRef = useRef<((token: string) => void) | null>(null)
const captchaRejectRef = useRef<((reason: Error) => void) | null>(null)
const turnstileSiteKey = useMemo(() => getEnv('NEXT_PUBLIC_TURNSTILE_SITE_KEY'), [])
const buttonClass = useBrandedButtonClass()

Expand Down Expand Up @@ -249,17 +251,30 @@ function SignupFormContent({

const sanitizedName = trimmedName

// Execute Turnstile challenge on submit and get a fresh token
let token: string | undefined
if (turnstileSiteKey && turnstileRef.current) {
const widget = turnstileRef.current
if (turnstileSiteKey && widget) {
let timeoutId: ReturnType<typeof setTimeout> | undefined
try {
turnstileRef.current.reset()
turnstileRef.current.execute()
token = await turnstileRef.current.getResponsePromise(15_000)
widget.reset()
token = await Promise.race([
new Promise<string>((resolve, reject) => {
captchaResolveRef.current = resolve
captchaRejectRef.current = reject
widget.execute()
}),
new Promise<string>((_, reject) => {
timeoutId = setTimeout(() => reject(new Error('Captcha timed out')), 15_000)
}),
])
} catch {
setFormError('Captcha verification failed. Please try again.')
setIsLoading(false)
return
} finally {
clearTimeout(timeoutId)
captchaResolveRef.current = null
captchaRejectRef.current = null
}
}

Expand Down Expand Up @@ -478,13 +493,14 @@ function SignupFormContent({
</div>

{turnstileSiteKey && (
<div className='absolute'>
<Turnstile
ref={turnstileRef}
siteKey={turnstileSiteKey}
options={{ size: 'invisible', execution: 'execute' }}
/>
</div>
<Turnstile
ref={turnstileRef}
siteKey={turnstileSiteKey}
onSuccess={(token) => captchaResolveRef.current?.(token)}
onError={() => captchaRejectRef.current?.(new Error('Captcha verification failed'))}
onExpire={() => captchaRejectRef.current?.(new Error('Captcha token expired'))}
options={{ execution: 'execute' }}
/>
)}

{formError && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { Step } from 'react-joyride'

export const navTourSteps: Step[] = [
{
target: '[data-item-id="home"]',
target: '[data-tour="nav-home"]',
title: 'Home',
content:
'Your starting point. Describe what you want to build in plain language or pick a template to get started.',
Expand All @@ -11,62 +11,62 @@ export const navTourSteps: Step[] = [
spotlightPadding: 0,
},
{
target: '[data-item-id="search"]',
target: '[data-tour="nav-search"]',
title: 'Search',
content: 'Quickly find workflows, blocks, and tools. Use Cmd+K to open it from anywhere.',
placement: 'right',
disableBeacon: true,
spotlightPadding: 0,
},
{
target: '[data-item-id="tables"]',
target: '[data-tour="nav-tables"]',
title: 'Tables',
content:
'Store and query structured data. Your workflows can read and write to tables directly.',
placement: 'right',
disableBeacon: true,
},
{
target: '[data-item-id="files"]',
target: '[data-tour="nav-files"]',
title: 'Files',
content: 'Upload and manage files that your workflows can process, transform, or reference.',
placement: 'right',
disableBeacon: true,
},
{
target: '[data-item-id="knowledge-base"]',
target: '[data-tour="nav-knowledge-base"]',
title: 'Knowledge Base',
content:
'Build knowledge bases from your documents. Set up connectors to give your agents realtime access to your data sources from sources like Notion, Drive, Slack, Confluence, and more.',
placement: 'right',
disableBeacon: true,
},
{
target: '[data-item-id="scheduled-tasks"]',
target: '[data-tour="nav-scheduled-tasks"]',
title: 'Scheduled Tasks',
content:
'View and manage background tasks. Set up new tasks, or view the tasks the Mothership is monitoring for upcoming or past executions.',
placement: 'right',
disableBeacon: true,
},
{
target: '[data-item-id="logs"]',
target: '[data-tour="nav-logs"]',
title: 'Logs',
content:
'Monitor every workflow execution. See inputs, outputs, errors, and timing for each run. View analytics on performance and costs, filter previous runs, and view snapshots of the workflow at the time of execution.',
placement: 'right',
disableBeacon: true,
},
{
target: '.tasks-section',
target: '[data-tour="nav-tasks"]',
title: 'Tasks',
content:
'Tasks that work for you. Mothership can create, edit, and delete resource throughout the platform. It can also perform actions on your behalf, like sending emails, creating tasks, and more.',
placement: 'right',
disableBeacon: true,
},
{
target: '.workflows-section',
target: '[data-tour="nav-workflows"]',
title: 'Workflows',
content:
'All your workflows live here. Create new ones with the + button and organize them into folders. Deploy your workflows as API, webhook, schedule, or chat widget. Then hit Run to test it out.',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,22 @@ export function TourTooltipAdapter({
}
}, [step])

const refCallback = useCallback(
/**
* Forwards the Joyride tooltip ref safely, handling both
* callback refs and RefObject refs from the library.
* Memoized to prevent ref churn (null → node cycling) on re-renders.
*/
const setJoyrideRef = useCallback(
(node: HTMLDivElement | null) => {
if (tooltipProps.ref) {
;(tooltipProps.ref as React.RefCallback<HTMLDivElement>)(node)
const { ref } = tooltipProps
if (!ref) return
if (typeof ref === 'function') {
ref(node)
} else {
;(ref as React.MutableRefObject<HTMLDivElement | null>).current = node
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[tooltipProps.ref]
)

Expand All @@ -86,7 +96,7 @@ export function TourTooltipAdapter({
return (
<>
<div
ref={refCallback}
ref={setJoyrideRef}
role={tooltipProps.role}
aria-modal={tooltipProps['aria-modal']}
style={{ position: 'absolute', opacity: 0, pointerEvents: 'none', width: 0, height: 0 }}
Expand Down
Loading
Loading