import { Context } from 'hono'; import supabase from '../utils/supabase'; // Get all influencers with filtering and pagination const getInfluencers = async (c: Context) => { try { const { platform, limit = '10', offset = '0', min_followers, max_followers, sort_by = 'followers_count', sort_order = 'desc' } = c.req.query(); let query = supabase .from('influencers') .select(` influencer_id, name, platform, profile_url, external_id, followers_count, video_count, platform_count, created_at, updated_at `, { count: 'exact' }); // Apply filters if (platform) { query = query.eq('platform', platform); } if (min_followers) { query = query.gte('followers_count', Number(min_followers)); } if (max_followers) { query = query.lte('followers_count', Number(max_followers)); } // Apply sorting if (sort_by && ['followers_count', 'video_count', 'created_at'].includes(sort_by)) { query = query.order(sort_by, { ascending: sort_order === 'asc' }); } // Apply pagination query = query.range(Number(offset), Number(offset) + Number(limit) - 1); const { data: influencers, error, count } = await query; if (error) { console.error('Error fetching influencers:', error); return c.json({ error: error.message }, 500); } return c.json({ influencers, count, limit: Number(limit), offset: Number(offset) }); } catch (error) { console.error('Error in getInfluencers:', error); return c.json({ error: 'Internal server error' }, 500); } }; // Get a specific influencer by ID const getInfluencerById = async (c: Context) => { try { const { influencer_id } = c.req.param(); const { data: influencer, error } = await supabase .from('influencers') .select(` influencer_id, name, platform, profile_url, external_id, followers_count, video_count, platform_count, created_at, updated_at `) .eq('influencer_id', influencer_id) .single(); if (error) { console.error('Error fetching influencer:', error); return c.json({ error: 'Influencer not found' }, 404); } return c.json(influencer); } catch (error) { console.error('Error in getInfluencerById:', error); return c.json({ error: 'Internal server error' }, 500); } }; // Get aggregated stats for influencers const getInfluencerStats = async (c: Context) => { try { const { platform } = c.req.query(); let query = supabase .from('influencers') .select('platform, followers_count, video_count'); if (platform) { query = query.eq('platform', platform); } const { data: stats, error } = await query; if (error) { console.error('Error fetching influencer stats:', error); return c.json({ error: error.message }, 500); } const aggregatedStats = { total_influencers: stats.length, total_followers: stats.reduce((sum: number, item: any) => sum + (item.followers_count || 0), 0), total_videos: stats.reduce((sum: number, item: any) => sum + (item.video_count || 0), 0), average_followers: Math.round( stats.reduce((sum: number, item: any) => sum + (item.followers_count || 0), 0) / (stats.length || 1) ), average_videos: Math.round( stats.reduce((sum: number, item: any) => sum + (item.video_count || 0), 0) / (stats.length || 1) ), platform_distribution: stats.reduce((acc: Record, item: any) => { const platform = item.platform || 'unknown'; acc[platform] = (acc[platform] || 0) + 1; return acc; }, {}) }; return c.json(aggregatedStats); } catch (error) { console.error('Error in getInfluencerStats:', error); return c.json({ error: 'Internal server error' }, 500); } }; // Create a new influencer const createInfluencer = async (c: Context) => { try { const { name, platform, profile_url, external_id, followers_count, video_count } = await c.req.json(); if (!name) { return c.json({ error: 'Name is required' }, 400); } const { data: influencer, error } = await supabase .from('influencers') .insert({ name, platform, profile_url, external_id, followers_count: followers_count || 0, video_count: video_count || 0 }) .select() .single(); if (error) { if (error.code === '23505') { // Unique constraint violation return c.json({ error: 'An influencer with this external ID already exists' }, 409); } console.error('Error creating influencer:', error); return c.json({ error: 'Failed to create influencer' }, 500); } return c.json(influencer, 201); } catch (error) { console.error('Error in createInfluencer:', error); return c.json({ error: 'Internal server error' }, 500); } }; // Update an existing influencer const updateInfluencer = async (c: Context) => { try { const { influencer_id } = c.req.param(); const { name, platform, profile_url, external_id, followers_count, video_count } = await c.req.json(); // Check if influencer exists const { data: existingInfluencer, error: fetchError } = await supabase .from('influencers') .select('influencer_id') .eq('influencer_id', influencer_id) .single(); if (fetchError || !existingInfluencer) { return c.json({ error: 'Influencer not found' }, 404); } const updateData: any = {}; if (name !== undefined) updateData.name = name; if (platform !== undefined) updateData.platform = platform; if (profile_url !== undefined) updateData.profile_url = profile_url; if (external_id !== undefined) updateData.external_id = external_id; if (followers_count !== undefined) updateData.followers_count = followers_count; if (video_count !== undefined) updateData.video_count = video_count; updateData.updated_at = new Date().toISOString(); const { data: updatedInfluencer, error: updateError } = await supabase .from('influencers') .update(updateData) .eq('influencer_id', influencer_id) .select() .single(); if (updateError) { if (updateError.code === '23505') { // Unique constraint violation return c.json({ error: 'An influencer with this external ID already exists' }, 409); } console.error('Error updating influencer:', updateError); return c.json({ error: 'Failed to update influencer' }, 500); } return c.json(updatedInfluencer); } catch (error) { console.error('Error in updateInfluencer:', error); return c.json({ error: 'Internal server error' }, 500); } }; // Delete an influencer const deleteInfluencer = async (c: Context) => { try { const { influencer_id } = c.req.param(); // Check if influencer exists const { data: existingInfluencer, error: fetchError } = await supabase .from('influencers') .select('influencer_id') .eq('influencer_id', influencer_id) .single(); if (fetchError || !existingInfluencer) { return c.json({ error: 'Influencer not found' }, 404); } // Check if influencer has any posts const { data: posts, error: postsError } = await supabase .from('posts') .select('post_id') .eq('influencer_id', influencer_id) .limit(1); if (!postsError && posts && posts.length > 0) { return c.json({ error: 'Cannot delete influencer with existing posts. Delete their posts first or use the force parameter.' }, 400); } // Delete the influencer const { error: deleteError } = await supabase .from('influencers') .delete() .eq('influencer_id', influencer_id); if (deleteError) { console.error('Error deleting influencer:', deleteError); return c.json({ error: 'Failed to delete influencer' }, 500); } return c.body(null, 204); } catch (error) { console.error('Error in deleteInfluencer:', error); return c.json({ error: 'Internal server error' }, 500); } }; // Get all posts for a specific influencer const getInfluencerPosts = async (c: Context) => { try { const { influencer_id } = c.req.param(); const { limit = '20', offset = '0' } = c.req.query(); // Check if influencer exists const { data: influencer, error: influencerError } = await supabase .from('influencers') .select('influencer_id, name') .eq('influencer_id', influencer_id) .single(); if (influencerError || !influencer) { return c.json({ error: 'Influencer not found' }, 404); } // Get posts for the influencer const { data: posts, error: postsError, count } = await supabase .from('posts') .select(` post_id, platform, post_url, title, description, published_at, created_at, updated_at `, { count: 'exact' }) .eq('influencer_id', influencer_id) .order('published_at', { ascending: false }) .range(parseInt(offset), parseInt(offset) + parseInt(limit) - 1); if (postsError) { console.error('Error fetching influencer posts:', postsError); return c.json({ error: 'Failed to fetch influencer posts' }, 500); } return c.json({ influencer_id, influencer_name: influencer.name, posts, count, limit: parseInt(limit), offset: parseInt(offset) }); } catch (error) { console.error('Error in getInfluencerPosts:', error); return c.json({ error: 'Internal server error' }, 500); } }; export default { getInfluencers, getInfluencerById, getInfluencerStats, createInfluencer, updateInfluencer, deleteInfluencer, getInfluencerPosts };