Skip to content

Commit d1732ba

Browse files
settings & profile pages updated
1 parent aa29888 commit d1732ba

File tree

13 files changed

+3704
-1478
lines changed

13 files changed

+3704
-1478
lines changed

.env.example

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,6 @@ OPENAI_API_KEY="your_openai_api_key_here"
1212
TOGETHER_AI_API_KEY="your_together_ai_api_key_here"
1313
XAI_API_KEY="your_xai_api_key_here"
1414

15-
# Google Services
16-
GOOGLE_CLIENT_ID="your_google_client_id_here"
17-
GOOGLE_VERTEX_CREDENTIALS="your_google_vertex_credentials_here"
18-
1915
# GitHub Configuration
2016
GITHUB_PERSONAL_ACCESS_TOKEN="your_github_personal_access_token_here"
2117
NEXT_PUBLIC_GITHUB_CLIENT_ID="your_github_client_id_here"

app/actions/profile.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { cookies } from "next/headers"
55
import { revalidatePath } from "next/cache"
66

77
export interface ProfileData {
8+
email: string
89
id?: string
910
first_name: string | null
1011
last_name: string | null
@@ -75,6 +76,7 @@ export async function getProfile(): Promise<ProfileData | null> {
7576
id: user.id,
7677
first_name: "",
7778
last_name: "",
79+
email: user.email || "",
7880
company: null,
7981
job_title: null,
8082
location: null,

app/actions/settings.ts

Lines changed: 360 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,360 @@
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

Comments
 (0)