@@ -35,6 +35,11 @@ export default function AccountContent() {
3535 const [ referralWallets , setReferralWallets ] = useState < ReferralWallet [ ] > ( [ ] ) ;
3636 const [ loadingWallets , setLoadingWallets ] = useState ( false ) ;
3737 const [ showAllWallets , setShowAllWallets ] = useState ( false ) ;
38+ const [ aiGatewayKeyDraft , setAiGatewayKeyDraft ] = useState ( "" ) ;
39+ const [ aiGatewayKeySet , setAiGatewayKeySet ] = useState ( false ) ;
40+ const [ settingsSaving , setSettingsSaving ] = useState ( false ) ;
41+ const [ settingsStatus , setSettingsStatus ] = useState < string | null > ( null ) ;
42+ const [ settingsError , setSettingsError ] = useState < string | null > ( null ) ;
3843
3944 const fetchReferralWallets = useCallback ( async ( ) => {
4045 const token = localStorage . getItem ( "tc_access_token" ) ;
@@ -59,6 +64,21 @@ export default function AccountContent() {
5964 fetchReferralWallets ( ) ;
6065 } , [ fetchReferralWallets ] ) ;
6166
67+ useEffect ( ( ) => {
68+ if ( ! signedIn ) return ;
69+ async function loadSettings ( ) {
70+ try {
71+ const res = await fetch ( "/api/settings" , { headers : authHeaders ( ) , cache : "no-store" } ) ;
72+ if ( ! res . ok ) return ;
73+ const data = await res . json ( ) ;
74+ setAiGatewayKeySet ( ! ! data . secrets ?. AI_GATEWAY_API_KEY ?. isSet ) ;
75+ } catch {
76+ // ignore
77+ }
78+ }
79+ loadSettings ( ) ;
80+ } , [ signedIn ] ) ;
81+
6282 useEffect ( ( ) => {
6383 if ( profile ) {
6484 setDisplayName ( profile . display_name || "" ) ;
@@ -174,6 +194,28 @@ export default function AccountContent() {
174194 }
175195 } ;
176196
197+ const saveAiGatewayKey = async ( value : string | null ) => {
198+ setSettingsSaving ( true ) ;
199+ setSettingsStatus ( null ) ;
200+ setSettingsError ( null ) ;
201+ try {
202+ const res = await fetch ( "/api/settings" , {
203+ method : "PUT" ,
204+ headers : authHeaders ( { "Content-Type" : "application/json" } ) ,
205+ body : JSON . stringify ( { secrets : { AI_GATEWAY_API_KEY : value } } ) ,
206+ } ) ;
207+ const data = await res . json ( ) ;
208+ if ( ! res . ok ) throw new Error ( data . error || "Failed to save settings" ) ;
209+ setAiGatewayKeySet ( ! ! data . secrets ?. AI_GATEWAY_API_KEY ?. isSet ) ;
210+ setAiGatewayKeyDraft ( "" ) ;
211+ setSettingsStatus ( value === null ? "Cleared" : "Saved" ) ;
212+ } catch ( error ) {
213+ setSettingsError ( ( error as Error ) . message ) ;
214+ } finally {
215+ setSettingsSaving ( false ) ;
216+ }
217+ } ;
218+
177219 return (
178220 < div className = "min-h-screen bg-tc-darker" >
179221 { /* Nav */ }
@@ -276,6 +318,47 @@ export default function AccountContent() {
276318 ) }
277319 </ div >
278320
321+ { /* Global Module Settings */ }
322+ < div className = "bg-tc-card border border-tc-border rounded-xl p-6" >
323+ < h2 className = "text-lg font-semibold text-white mb-4" > Global Module Settings</ h2 >
324+ < div >
325+ < label className = "block text-sm text-tc-text-dim mb-1" >
326+ Vercel AI Gateway API key
327+ < span className = "ml-2 font-mono text-[10px] text-tc-text-dim" > AI_GATEWAY_API_KEY</ span >
328+ </ label >
329+ < div className = "flex flex-col sm:flex-row gap-2" >
330+ < input
331+ type = "password"
332+ value = { aiGatewayKeyDraft }
333+ placeholder = { aiGatewayKeySet ? "Saved secret set" : "vck_..." }
334+ onChange = { ( e ) => setAiGatewayKeyDraft ( e . target . value ) }
335+ className = "flex-1 bg-tc-darker border border-tc-border rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-tc-green/50"
336+ />
337+ < button
338+ onClick = { ( ) => saveAiGatewayKey ( aiGatewayKeyDraft ) }
339+ disabled = { settingsSaving || ! aiGatewayKeyDraft . trim ( ) }
340+ className = "bg-tc-green text-black px-4 py-2 rounded-lg text-sm font-semibold hover:bg-tc-green-dim disabled:opacity-50"
341+ >
342+ { settingsSaving ? "Saving..." : "Save" }
343+ </ button >
344+ { aiGatewayKeySet && (
345+ < button
346+ onClick = { ( ) => saveAiGatewayKey ( null ) }
347+ disabled = { settingsSaving }
348+ className = "border border-tc-border text-tc-text-dim px-4 py-2 rounded-lg text-sm hover:border-red-400/40 hover:text-red-400 disabled:opacity-50"
349+ >
350+ Clear
351+ </ button >
352+ ) }
353+ </ div >
354+ < p className = "text-xs text-tc-text-dim mt-2" >
355+ Used by AI-powered modules such as DeepSec. The key is stored in your account settings, not in the public plugin store.
356+ </ p >
357+ { settingsStatus && < p className = "text-xs text-tc-green mt-2" > { settingsStatus } </ p > }
358+ { settingsError && < p className = "text-xs text-red-400 mt-2" > { settingsError } </ p > }
359+ </ div >
360+ </ div >
361+
279362 { /* Referral Section — hidden for now */ }
280363 < div className = "hidden bg-tc-card border border-tc-border rounded-xl p-6" >
281364 < h2 className = "text-lg font-semibold text-white mb-4" > Referrals</ h2 >
0 commit comments