error and injection

This commit is contained in:
null 2026-05-28 00:18:08 -05:00
parent 3a61000c12
commit 0f272fcf19
4 changed files with 77 additions and 8 deletions

View File

@ -64,15 +64,15 @@ const apiLimiter = rateLimit({
const isDev = process.env.NODE_ENV === 'development'
const cspDirectives = {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", 'https://www.google.com/recaptcha/', 'https://www.gstatic.com/recaptcha/'],
scriptSrc: ["'self'", 'https://crm.zohopublic.com', 'https://www.google.com/recaptcha/', 'https://www.gstatic.com/recaptcha/'],
styleSrc: ["'self'", "'unsafe-inline'", 'https://fonts.googleapis.com'],
fontSrc: ["'self'", 'https://fonts.gstatic.com'],
imgSrc: ["'self'", 'data:'],
connectSrc: isDev ? ["'self'", 'ws://localhost:*'] : ["'self'"],
frameSrc: ["'self'", 'https://www.google.com/recaptcha/', 'https://recaptcha.google.com/recaptcha/'],
frameSrc: ["'self'", 'https://crm.zoho.com', 'https://www.google.com/recaptcha/', 'https://recaptcha.google.com/recaptcha/'],
objectSrc: ["'none'"],
baseUri: ["'self'"],
formAction: ["'self'"],
formAction: ["'self'", 'https://crm.zoho.com'],
}
// Note: connectSrc currently allows 'self' only. Zoho API calls are server-to-server

View File

@ -0,0 +1,50 @@
import { Component } from 'react'
import { Link } from 'react-router-dom'
class ErrorBoundary extends Component {
constructor(props) {
super(props)
this.state = { hasError: false, error: null }
}
static getDerivedStateFromError(error) {
return { hasError: true, error }
}
componentDidCatch(error, info) {
console.error('[ErrorBoundary] Uncaught error:', error, info.componentStack)
}
render() {
if (!this.state.hasError) return this.props.children
return (
<div className="min-h-screen bg-primary-navy flex items-center justify-center px-4">
<div className="text-center text-white max-w-md">
<p className="text-primary-cyan text-sm font-semibold uppercase tracking-widest mb-4">Something went wrong</p>
<h1 className="text-4xl font-bold mb-4">Unexpected Error</h1>
<p className="text-white/70 mb-8">
A problem occurred while loading this page. Please try refreshing, or contact us if the issue continues.
</p>
<div className="flex flex-col sm:flex-row gap-3 justify-center">
<button
onClick={() => window.location.reload()}
className="inline-flex h-11 items-center justify-center rounded-md bg-primary-cyan px-6 text-sm font-semibold text-primary-navy hover:bg-white transition-colors"
>
Refresh Page
</button>
<Link
to="/"
onClick={() => this.setState({ hasError: false, error: null })}
className="inline-flex h-11 items-center justify-center rounded-md border border-white/35 px-6 text-sm font-semibold text-white hover:bg-white/10 transition-colors"
>
Back to Home
</Link>
</div>
</div>
</div>
)
}
}
export default ErrorBoundary

View File

@ -5,13 +5,16 @@ import { Toaster } from 'sonner'
import { HelmetProvider } from 'react-helmet-async'
import router from './router.jsx'
import App from './App.jsx'
import ErrorBoundary from './components/ErrorBoundary.jsx'
// Wrap the router with providers
const Root = () => (
<StrictMode>
<HelmetProvider>
<RouterProvider router={router} />
<Toaster position="top-right" />
<ErrorBoundary>
<RouterProvider router={router} />
<Toaster position="top-right" />
</ErrorBoundary>
</HelmetProvider>
</StrictMode>
)

View File

@ -36,15 +36,28 @@ const Contact = () => {
useEffect(() => {
const iframe = document.getElementById('zoho_webform_iframe')
if (!iframe) return
const handleLoad = () => {
if (!isSubmitting) return
let fallbackTimer = null
const handleSuccess = () => {
if (fallbackTimer) clearTimeout(fallbackTimer)
setIsSubmitting(false)
toast.success("Thanks! We'll be in touch shortly.")
setFormState({ 'Last Name': '', Company: '', Email: '', Phone: '', 'Zip Code': '', Description: '' })
setErrors({ 'Last Name': '', Company: '', Email: '', 'Zip Code': '', Description: '', recaptcha_token: '' })
}
const handleLoad = () => { if (isSubmitting) handleSuccess() }
if (isSubmitting) {
fallbackTimer = setTimeout(handleSuccess, 3500)
}
iframe.addEventListener('load', handleLoad)
return () => iframe.removeEventListener('load', handleLoad)
return () => {
iframe.removeEventListener('load', handleLoad)
if (fallbackTimer) clearTimeout(fallbackTimer)
}
}, [isSubmitting])
useEffect(() => {
@ -80,6 +93,9 @@ const Contact = () => {
e.preventDefault()
if (!validateForm()) return
setIsSubmitting(true)
if (typeof _wfa_track !== 'undefined' && _wfa_track.wfa_submit) {
_wfa_track.wfa_submit(e)
}
formRef.current.submit()
}