@@ -7,20 +7,30 @@ import { Icon } from "../components/Icon";
77 * Email and inquiry details are required so we can properly route and respond.
88 * Inserts one row into the private preview_requests queue (RLS: anonymous insert only).
99 */
10- const DEFAULT_SUPABASE_URL = "https://lrqlrufwrytpwhgclmyo.supabase.co" ;
11- const DEFAULT_PUBLISHABLE_KEY =
12- "sb_publishable_BAsTV49V04O0WZVtVgohqg_BD5JReFE" ;
13-
14- const baseUrl = (
15- import . meta. env . VITE_SUPABASE_URL || DEFAULT_SUPABASE_URL
16- ) . replace ( / \/ + $ / , "" ) ;
17- const FORM_ENDPOINT = `${ baseUrl } /rest/v1/preview_requests` ;
18- const FORM_KEY =
19- import . meta. env . VITE_SUPABASE_PUBLISHABLE_KEY ||
20- import . meta. env . VITE_SUPABASE_ANON_KEY ||
21- DEFAULT_PUBLISHABLE_KEY ;
10+ // The preview form posts into the same Supabase project that powers the app.
11+ // It must never fall back to a hardcoded production project: a self-hosted
12+ // build without VITE_SUPABASE_* would otherwise write interest requests into
13+ // someone else's database. When unconfigured, the form renders a clear note.
14+ /**
15+ * Read the target project lazily (not at module scope) so test and runtime
16+ * environments can configure VITE_SUPABASE_* after import, and so a build
17+ * without the variables still renders a clear "not configured" note instead
18+ * of silently posting into a hardcoded production project.
19+ */
20+ function previewFormConfig ( ) : { endpoint : string ; key : string } | null {
21+ const baseUrl = import . meta. env . VITE_SUPABASE_URL ?. trim ( ) ;
22+ const key =
23+ import . meta. env . VITE_SUPABASE_PUBLISHABLE_KEY ?. trim ( ) ||
24+ import . meta. env . VITE_SUPABASE_ANON_KEY ?. trim ( ) ;
25+ if ( ! baseUrl || ! key ) return null ;
26+ return {
27+ endpoint : `${ baseUrl . replace ( / \/ + $ / , "" ) } /rest/v1/preview_requests` ,
28+ key,
29+ } ;
30+ }
2231
2332export function PreviewForm ( { initialEmail = "" } : { initialEmail ?: string } ) {
33+ const config = previewFormConfig ( ) ;
2434 const [ email , setEmail ] = useState ( initialEmail ) ;
2535 const [ inquiry , setInquiry ] = useState ( "" ) ;
2636 const [ error , setError ] = useState < string | null > ( null ) ;
@@ -33,6 +43,7 @@ export function PreviewForm({ initialEmail = "" }: { initialEmail?: string }) {
3343
3444 const submit = async ( event : React . FormEvent ) => {
3545 event . preventDefault ( ) ;
46+ if ( ! config ) return ;
3647 setError ( null ) ;
3748
3849 const trimmedEmail = email . trim ( ) ;
@@ -50,11 +61,11 @@ export function PreviewForm({ initialEmail = "" }: { initialEmail?: string }) {
5061
5162 setSending ( true ) ;
5263 try {
53- const response = await fetch ( FORM_ENDPOINT , {
64+ const response = await fetch ( config . endpoint , {
5465 method : "POST" ,
5566 headers : {
56- apikey : FORM_KEY ,
57- Authorization : `Bearer ${ FORM_KEY } ` ,
67+ apikey : config . key ,
68+ Authorization : `Bearer ${ config . key } ` ,
5869 "Content-Type" : "application/json" ,
5970 Prefer : "return=minimal" ,
6071 } ,
@@ -90,6 +101,16 @@ export function PreviewForm({ initialEmail = "" }: { initialEmail?: string }) {
90101 }
91102 } ;
92103
104+ if ( ! config ) {
105+ return (
106+ < div className = "hp-form" role = "status" >
107+ < p className = "hp-form-note" >
108+ The preview request form is not configured for this deployment.
109+ </ p >
110+ </ div >
111+ ) ;
112+ }
113+
93114 if ( sent ) {
94115 return (
95116 < div className = "hp-form-success" role = "status" >
0 commit comments