1+ "use server"
2+
3+ import { createServerClient , type CookieOptions } from "@supabase/ssr"
4+ import { cookies } from "next/headers"
5+ import { revalidatePath } from "next/cache"
6+
7+ export interface UserSettings {
8+ id ?: string
9+ user_id : string
10+ // Appearance settings
11+ theme : "light" | "dark" | "system"
12+ language : string
13+ timezone : string
14+ compact_mode : boolean
15+ animations_enabled : boolean
16+ sound_enabled : boolean
17+
18+ // Privacy settings
19+ profile_visibility : "public" | "private" | "contacts"
20+ activity_status : boolean
21+ project_visibility : "public" | "private" | "contacts"
22+ analytics_enabled : boolean
23+ personalization_enabled : boolean
24+ third_party_sharing : boolean
25+
26+ // Communication settings
27+ email_notifications : boolean
28+ marketing_communications : boolean
29+ community_communications : boolean
30+ security_alerts : boolean
31+
32+ // Account settings
33+ two_factor_enabled : boolean
34+ session_timeout : number
35+ password_changed_at ?: string
36+
37+ created_at ?: string
38+ updated_at ?: string
39+ }
40+
41+ export interface ApiKeyData {
42+ [ x : string ] : string | string [ ] | undefined
43+ id ?: string
44+ user_id : string
45+ name : string
46+ key_prefix : string
47+ permissions : string [ ]
48+ last_used_at ?: string
49+ expires_at ?: string
50+ created_at ?: string
51+ updated_at ?: string
52+ }
53+
54+ export interface IntegrationData {
55+ id ?: string
56+ user_id : string
57+ provider : string
58+ provider_id : string
59+ provider_username ?: string
60+ access_token ?: string
61+ refresh_token ?: string
62+ permissions : string [ ]
63+ last_sync_at ?: string
64+ connected_at : string
65+ updated_at ?: string
66+ }
67+
68+ const createSupabaseClient = async ( ) => {
69+ const cookieStore = cookies ( )
70+ return createServerClient (
71+ process . env . NEXT_PUBLIC_SUPABASE_URL ! ,
72+ process . env . NEXT_PUBLIC_SUPABASE_ANON_KEY ! ,
73+ {
74+ cookies : {
75+ async get ( name : string ) {
76+ return ( await cookieStore ) . get ( name ) ?. value
77+ } ,
78+ async set ( name : string , value : string , options : CookieOptions ) {
79+ try {
80+ ( await cookieStore ) . set ( name , value , options )
81+ } catch ( error ) {
82+ console . warn ( `Failed to set cookie '${ name } ':` , error )
83+ }
84+ } ,
85+ async remove ( name : string ) {
86+ try {
87+ ( await cookieStore ) . delete ( name )
88+ } catch ( error ) {
89+ console . warn ( `Failed to delete cookie '${ name } ':` , error )
90+ }
91+ } ,
92+ } ,
93+ }
94+ )
95+ }
96+
97+ export async function getUserSettings ( ) : Promise < UserSettings | null > {
98+ try {
99+ const supabase = await createSupabaseClient ( )
100+ const { data : { user } } = await supabase . auth . getUser ( )
101+
102+ if ( ! user ) {
103+ console . log ( "No user found, cannot fetch settings." )
104+ return null
105+ }
106+
107+ const { data, error } = await supabase
108+ . from ( "user_settings" )
109+ . select ( "*" )
110+ . eq ( "user_id" , user . id )
111+ . single ( )
112+
113+ if ( error ) {
114+ if ( error . code === 'PGRST116' ) {
115+ // No settings found, return defaults
116+ const defaultSettings : UserSettings = {
117+ user_id : user . id ,
118+ theme : "system" ,
119+ language : "en" ,
120+ timezone : Intl . DateTimeFormat ( ) . resolvedOptions ( ) . timeZone ,
121+ compact_mode : false ,
122+ animations_enabled : true ,
123+ sound_enabled : true ,
124+ profile_visibility : "private" ,
125+ activity_status : true ,
126+ project_visibility : "private" ,
127+ analytics_enabled : true ,
128+ personalization_enabled : true ,
129+ third_party_sharing : false ,
130+ email_notifications : true ,
131+ marketing_communications : false ,
132+ community_communications : true ,
133+ security_alerts : true ,
134+ two_factor_enabled : false ,
135+ session_timeout : 30
136+ }
137+
138+ // Create default settings in database
139+ const { data : newSettings , error : createError } = await supabase
140+ . from ( "user_settings" )
141+ . insert ( defaultSettings )
142+ . select ( )
143+ . single ( )
144+
145+ if ( createError ) {
146+ console . error ( "Error creating default settings:" , createError )
147+ return defaultSettings
148+ }
149+
150+ return newSettings
151+ }
152+ console . error ( "Error fetching user settings:" , error )
153+ throw error
154+ }
155+
156+ return data as UserSettings
157+ } catch ( error ) {
158+ console . error ( "Error in getUserSettings:" , error )
159+ return null
160+ }
161+ }
162+
163+ export async function updateUserSettings (
164+ settingsData : Partial < UserSettings >
165+ ) : Promise < { success : boolean ; error ?: any } > {
166+ try {
167+ const supabase = await createSupabaseClient ( )
168+ const { data : { user } } = await supabase . auth . getUser ( )
169+
170+ if ( ! user ) {
171+ console . error ( "User not authenticated for settings update." )
172+ return { success : false , error : "User not authenticated" }
173+ }
174+
175+ const { user_id, id, created_at, ...updateData } = settingsData
176+ updateData . updated_at = new Date ( ) . toISOString ( )
177+
178+ const { error } = await supabase
179+ . from ( "user_settings" )
180+ . update ( updateData )
181+ . eq ( "user_id" , user . id )
182+
183+ if ( error ) {
184+ console . error ( "Error updating user settings:" , error )
185+ return { success : false , error }
186+ }
187+
188+ revalidatePath ( "/settings" )
189+ return { success : true }
190+ } catch ( error ) {
191+ console . error ( "Error in updateUserSettings:" , error )
192+ return { success : false , error }
193+ }
194+ }
195+
196+ export async function getUserApiKeys ( ) : Promise < ApiKeyData [ ] > {
197+ try {
198+ const supabase = await createSupabaseClient ( )
199+ const { data : { user } } = await supabase . auth . getUser ( )
200+
201+ if ( ! user ) {
202+ return [ ]
203+ }
204+
205+ const { data, error } = await supabase
206+ . from ( "user_api_keys" )
207+ . select ( "*" )
208+ . eq ( "user_id" , user . id )
209+ . order ( "created_at" , { ascending : false } )
210+
211+ if ( error ) {
212+ console . error ( "Error fetching API keys:" , error )
213+ throw error
214+ }
215+
216+ return data || [ ]
217+ } catch ( error ) {
218+ console . error ( "Error in getUserApiKeys:" , error )
219+ return [ ]
220+ }
221+ }
222+
223+ export async function createApiKey (
224+ keyData : Omit < ApiKeyData , "id" | "user_id" | "created_at" | "updated_at" >
225+ ) : Promise < { success : boolean ; apiKey ?: ApiKeyData ; error ?: any } > {
226+ try {
227+ const supabase = await createSupabaseClient ( )
228+ const { data : { user } } = await supabase . auth . getUser ( )
229+
230+ if ( ! user ) {
231+ return { success : false , error : "User not authenticated" }
232+ }
233+
234+ const keyPrefix = keyData . permissions ?. includes ( 'write' ) ? 'ak_live' : 'ak_test'
235+ const keyBody = Array . from ( crypto . getRandomValues ( new Uint8Array ( 32 ) ) )
236+ . map ( b => b . toString ( 16 ) . padStart ( 2 , '0' ) )
237+ . join ( '' )
238+
239+ const fullKey = `${ keyPrefix } _${ keyBody } `
240+
241+ const newKeyData = {
242+ ...keyData ,
243+ user_id : user . id ,
244+ key_prefix : keyPrefix ,
245+ created_at : new Date ( ) . toISOString ( ) ,
246+ updated_at : new Date ( ) . toISOString ( )
247+ }
248+
249+ const { data, error } = await supabase
250+ . from ( "user_api_keys" )
251+ . insert ( newKeyData )
252+ . select ( )
253+ . single ( )
254+
255+ if ( error ) {
256+ console . error ( "Error creating API key:" , error )
257+ return { success : false , error }
258+ }
259+
260+ // Return the data with the full key for display (one time only)
261+ const result = { ...data , full_key : fullKey }
262+
263+ revalidatePath ( "/settings/api-keys" )
264+ return { success : true , apiKey : result }
265+ } catch ( error ) {
266+ console . error ( "Error in createApiKey:" , error )
267+ return { success : false , error }
268+ }
269+ }
270+
271+ export async function deleteApiKey (
272+ keyId : string
273+ ) : Promise < { success : boolean ; error ?: any } > {
274+ try {
275+ const supabase = await createSupabaseClient ( )
276+ const { data : { user } } = await supabase . auth . getUser ( )
277+
278+ if ( ! user ) {
279+ return { success : false , error : "User not authenticated" }
280+ }
281+
282+ const { error } = await supabase
283+ . from ( "user_api_keys" )
284+ . delete ( )
285+ . eq ( "id" , keyId )
286+ . eq ( "user_id" , user . id )
287+
288+ if ( error ) {
289+ console . error ( "Error deleting API key:" , error )
290+ return { success : false , error }
291+ }
292+
293+ revalidatePath ( "/settings/api-keys" )
294+ return { success : true }
295+ } catch ( error ) {
296+ console . error ( "Error in deleteApiKey:" , error )
297+ return { success : false , error }
298+ }
299+ }
300+
301+ export async function getUserIntegrations ( ) : Promise < IntegrationData [ ] > {
302+ try {
303+ const supabase = await createSupabaseClient ( )
304+ const { data : { user } } = await supabase . auth . getUser ( )
305+
306+ if ( ! user ) {
307+ return [ ]
308+ }
309+
310+ const { data, error } = await supabase
311+ . from ( "user_integrations" )
312+ . select ( "*" )
313+ . eq ( "user_id" , user . id )
314+ . order ( "connected_at" , { ascending : false } )
315+
316+ if ( error ) {
317+ console . error ( "Error fetching integrations:" , error )
318+ throw error
319+ }
320+
321+ return data || [ ]
322+ } catch ( error ) {
323+ console . error ( "Error in getUserIntegrations:" , error )
324+ return [ ]
325+ }
326+ }
327+
328+ export async function updateIntegration (
329+ integrationId : string ,
330+ updates : Partial < IntegrationData >
331+ ) : Promise < { success : boolean ; error ?: any } > {
332+ try {
333+ const supabase = await createSupabaseClient ( )
334+ const { data : { user } } = await supabase . auth . getUser ( )
335+
336+ if ( ! user ) {
337+ return { success : false , error : "User not authenticated" }
338+ }
339+
340+ const { user_id, id, connected_at, ...updateData } = updates
341+ updateData . updated_at = new Date ( ) . toISOString ( )
342+
343+ const { error } = await supabase
344+ . from ( "user_integrations" )
345+ . update ( updateData )
346+ . eq ( "id" , integrationId )
347+ . eq ( "user_id" , user . id )
348+
349+ if ( error ) {
350+ console . error ( "Error updating integration:" , error )
351+ return { success : false , error }
352+ }
353+
354+ revalidatePath ( "/settings/integrations" )
355+ return { success : true }
356+ } catch ( error ) {
357+ console . error ( "Error in updateIntegration:" , error )
358+ return { success : false , error }
359+ }
360+ }
0 commit comments