'use server'; import { createClient } from '@/lib/supabase/server'; import { customAlphabet } from 'nanoid'; import { revalidatePath } from 'next/cache'; // Generate 6-char uppercase alphanumeric codes (no confusing chars like 0/O, 1/I/L) const generateCode = customAlphabet('ABCDEFGHJKMNPQRSTUVWXYZ23456789', 6); const CODE_EXPIRY_MINUTES = 15; /** * Generate a buddy invite code for the current user. * Deletes any existing expired codes first. */ export async function generateBuddyCode(): Promise<{ success: boolean; code?: string; error?: string }> { const supabase = await createClient(); try { const { data: { user } } = await supabase.auth.getUser(); if (!user) { return { success: false, error: 'Nicht autorisiert' }; } // Delete expired codes for this user await supabase .from('buddy_invites') .delete() .eq('creator_id', user.id) .lt('expires_at', new Date().toISOString()); // Check if user already has an active code const { data: existingCode } = await supabase .from('buddy_invites') .select('code, expires_at') .eq('creator_id', user.id) .gt('expires_at', new Date().toISOString()) .single(); if (existingCode) { // Return existing code return { success: true, code: existingCode.code }; } // Generate new code const code = generateCode(); const expiresAt = new Date(Date.now() + CODE_EXPIRY_MINUTES * 60 * 1000); const { error } = await supabase .from('buddy_invites') .insert({ creator_id: user.id, code, expires_at: expiresAt.toISOString(), }); if (error) { console.error('Error creating buddy code:', error); return { success: false, error: 'Code konnte nicht erstellt werden' }; } return { success: true, code }; } catch (error) { console.error('generateBuddyCode error:', error); return { success: false, error: 'Unerwarteter Fehler' }; } } /** * Redeem a buddy invite code and create the buddy relationship. */ export async function redeemBuddyCode(code: string): Promise<{ success: boolean; buddyName?: string; error?: string }> { const supabase = await createClient(); try { const { data: { user } } = await supabase.auth.getUser(); if (!user) { return { success: false, error: 'Nicht autorisiert' }; } const normalizedCode = code.toUpperCase().replace(/[^A-Z0-9]/g, ''); if (normalizedCode.length !== 6) { return { success: false, error: 'Ungültiger Code-Format' }; } // Find the invite const { data: invite, error: findError } = await supabase .from('buddy_invites') .select('id, creator_id, expires_at') .eq('code', normalizedCode) .single(); if (findError || !invite) { return { success: false, error: 'Code nicht gefunden' }; } // Check if expired if (new Date(invite.expires_at) < new Date()) { return { success: false, error: 'Code ist abgelaufen' }; } // Cannot buddy yourself if (invite.creator_id === user.id) { return { success: false, error: 'Du kannst dich nicht selbst hinzufügen' }; } // Check if relationship already exists const { data: existingBuddy } = await supabase .from('buddies') .select('id') .eq('user_id', user.id) .eq('buddy_profile_id', invite.creator_id) .single(); if (existingBuddy) { return { success: false, error: 'Ihr seid bereits verbunden' }; } // Get creator's profile for the buddy name const { data: creatorProfile } = await supabase .from('profiles') .select('username') .eq('id', invite.creator_id) .single(); const buddyName = creatorProfile?.username || 'Buddy'; // Create buddy relationship (both directions) // 1. Redeemer adds Creator as buddy const { error: buddy1Error } = await supabase .from('buddies') .insert({ user_id: user.id, name: buddyName, buddy_profile_id: invite.creator_id, }); if (buddy1Error) { console.error('Error creating buddy (redeemer->creator):', buddy1Error); return { success: false, error: 'Verbindung konnte nicht erstellt werden' }; } // 2. Creator adds Redeemer as buddy const { data: redeemerProfile } = await supabase .from('profiles') .select('username') .eq('id', user.id) .single(); const redeemerName = redeemerProfile?.username || 'Buddy'; // Use service role or direct insert (creator will see it via RLS) const { error: buddy2Error } = await supabase .from('buddies') .insert({ user_id: invite.creator_id, name: redeemerName, buddy_profile_id: user.id, }); if (buddy2Error) { console.error('Error creating buddy (creator->redeemer):', buddy2Error); // Don't fail - at least one direction worked } // Delete the used invite await supabase .from('buddy_invites') .delete() .eq('id', invite.id); // Revalidate paths revalidatePath('/'); return { success: true, buddyName }; } catch (error) { console.error('redeemBuddyCode error:', error); return { success: false, error: 'Unerwarteter Fehler' }; } } /** * Get all buddies that are linked to real accounts (have buddy_profile_id). */ export async function getLinkedBuddies(): Promise<{ success: boolean; buddies?: Array<{ id: string; name: string; buddy_profile_id: string; username?: string }>; error?: string; }> { const supabase = await createClient(); try { const { data: { user } } = await supabase.auth.getUser(); if (!user) { return { success: false, error: 'Nicht autorisiert' }; } const { data, error } = await supabase .from('buddies') .select(` id, name, buddy_profile_id, profiles:buddy_profile_id (username) `) .eq('user_id', user.id) .not('buddy_profile_id', 'is', null) .order('name'); if (error) { console.error('Error fetching linked buddies:', error); return { success: false, error: 'Fehler beim Laden der Buddies' }; } const buddies = (data || []).map(b => ({ id: b.id, name: b.name, buddy_profile_id: b.buddy_profile_id!, username: (b.profiles as any)?.username, })); return { success: true, buddies }; } catch (error) { console.error('getLinkedBuddies error:', error); return { success: false, error: 'Unerwarteter Fehler' }; } } /** * Revoke/cancel an active buddy invite code. */ export async function revokeBuddyCode(): Promise<{ success: boolean; error?: string }> { const supabase = await createClient(); try { const { data: { user } } = await supabase.auth.getUser(); if (!user) { return { success: false, error: 'Nicht autorisiert' }; } await supabase .from('buddy_invites') .delete() .eq('creator_id', user.id); return { success: true }; } catch (error) { console.error('revokeBuddyCode error:', error); return { success: false, error: 'Unerwarteter Fehler' }; } }