@@ -9,6 +9,15 @@ import { parseStringPromise } from "xml2js";
99const BASE_URL = process . env . NEXT_PUBLIC_API_BASE_URL ;
1010const API_KEY = process . env . NEXT_PUBLIC_X_API_KEY ;
1111
12+ // Utility function to detect provider from URL
13+ const detectProvider = ( url : string ) : string => {
14+ const urlLower = url . toLowerCase ( ) ;
15+ if ( urlLower . includes ( 'medium.com' ) ) return 'medium' ;
16+ if ( urlLower . includes ( 'instagram.com' ) ) return 'instagram' ;
17+ if ( urlLower . includes ( 'huggingface.co' ) ) return 'huggingface' ;
18+ return 'generic' ;
19+ } ;
20+
1221/**
1322 * Fetch resource with Next.js caching
1423 */
@@ -228,29 +237,108 @@ export const getLinkedInProfileData = async (
228237} ;
229238
230239/**
231- * API to add user to Nocodb table for analytics
240+ * API to add user to Supabase via edge function for analytics
232241 */
233- export const addUserToNocodb = async ( user : Profile | null ) => {
242+ export const addUserToSupabase = async ( user : Profile | null , searchParams ?: URLSearchParams ) => {
234243 if ( ! user ) return ;
235- const url = `https://app.nocodb.com/api/v2/tables/${ process . env . NOCODB_TABLE_ID } /records` ;
236- const headers = {
237- accept : "application/json" ,
238- "xc-token" : process . env . NOCODB_API_KEY || "" ,
239- "Content-Type" : "application/json" ,
240- } ;
244+
245+ const supabaseUrl = process . env . SUPABASE_URL ;
246+ const supabaseAnonKey = process . env . SUPABASE_KEY ;
247+
248+ if ( ! supabaseUrl || ! supabaseAnonKey ) {
249+ console . error ( "Supabase configuration missing" ) ;
250+ return ;
251+ }
241252
242- const data = {
253+ const url = `${ supabaseUrl } /functions/v1/devb-io` ;
254+
255+ // Map user data to match Supabase function whitelist
256+ const mappedData : Record < string , string > = {
243257 name : user . username ,
244- socials : user . social_accounts ,
258+ "full name" : user . name ,
259+ "devb profile" : `https://devb.io/${ user . username } ` ,
260+ github : `https://github.com/${ user . username } ` ,
261+ } ;
262+
263+ // Add query parameters if available
264+ if ( searchParams ) {
265+ // UTM parameters
266+ const utmSource = searchParams . get ( 'utm_source' ) ;
267+ const utmMedium = searchParams . get ( 'utm_medium' ) ;
268+ const utmCampaign = searchParams . get ( 'utm_campaign' ) ;
269+ const utmTerm = searchParams . get ( 'utm_term' ) ;
270+ const utmContent = searchParams . get ( 'utm_content' ) ;
271+
272+ // Referral parameter
273+ const ref = searchParams . get ( 'ref' ) ;
274+
275+ // Add to mapped data if they exist
276+ if ( utmSource ) mappedData [ 'utm_source' ] = utmSource ;
277+ if ( utmMedium ) mappedData [ 'utm_medium' ] = utmMedium ;
278+ if ( utmCampaign ) mappedData [ 'utm_campaign' ] = utmCampaign ;
279+ if ( utmTerm ) mappedData [ 'utm_term' ] = utmTerm ;
280+ if ( utmContent ) mappedData [ 'utm_content' ] = utmContent ;
281+ if ( ref ) mappedData [ 'ref' ] = ref ;
282+ }
283+
284+ // Counter for generic URLs
285+ let genericCounter = 1 ;
286+
287+ // Add social accounts based on provider
288+ user . social_accounts ?. forEach ( ( account ) => {
289+ const provider = account . provider . toLowerCase ( ) ;
290+
291+ // If provider is generic, detect the actual platform
292+ const actualProvider = provider === "generic" ? detectProvider ( account . url ) : provider ;
293+
294+ switch ( actualProvider ) {
295+ case "linkedin" :
296+ mappedData [ "Linkedin" ] = account . url ;
297+ break ;
298+ case "twitter" :
299+ mappedData [ "twitter" ] = account . url ;
300+ break ;
301+ case "medium" :
302+ mappedData [ "Medium" ] = account . url ;
303+ break ;
304+ case "instagram" :
305+ mappedData [ "instagram" ] = account . url ;
306+ break ;
307+ case "huggingface" :
308+ // Could add huggingface to whitelist if needed
309+ break ;
310+ case "generic" :
311+ // Check if it's a devb.io link
312+ if ( account . url . includes ( "devb.io" ) ) {
313+ mappedData [ "devb" ] = account . url ;
314+ } else {
315+ // For other generic URLs, number them
316+ mappedData [ `generic ${ genericCounter } ` ] = account . url ;
317+ genericCounter ++ ;
318+ }
319+ break ;
320+ }
321+ } ) ;
322+
323+ const headers = {
324+ "Content-Type" : "application/json" ,
325+ "Authorization" : `Bearer ${ supabaseAnonKey } ` ,
245326 } ;
246327
247328 try {
248- await fetch ( url , {
329+ const response = await fetch ( url , {
249330 method : "POST" ,
250331 headers : headers ,
251- body : JSON . stringify ( data ) ,
332+ body : JSON . stringify ( mappedData ) ,
252333 } ) ;
334+
335+ if ( ! response . ok ) {
336+ throw new Error ( `HTTP error! status: ${ response . status } ` ) ;
337+ }
338+
339+ const result = await response . json ( ) ;
340+ console . log ( "User data sent to Supabase:" , result ) ;
253341 } catch ( error ) {
254- console . error ( "Error:" , error ) ;
342+ console . error ( "Error sending data to Supabase :" , error ) ;
255343 }
256344} ;
0 commit comments