From 6bfe79dcbe4389897c660946b058cabf5bb1f0db Mon Sep 17 00:00:00 2001 From: dzinesco Date: Tue, 15 Jul 2025 08:56:09 -0600 Subject: [PATCH] fix: resolve admin dashboard users tab not showing users MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Created new server-side API endpoint /api/admin/users that bypasses RLS - Updated admin API router to use server-side endpoint instead of client queries - Fixed issue where client-side Supabase queries were blocked by RLS policies - Updated platform stats and recent activity to use the new endpoint - Ensures proper admin authentication and uses service role for data access Root cause: RLS policies on users table blocked client-side queries from admin dashboard Solution: Server-side API endpoint with proper admin auth and service role access 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- src/lib/admin-api-router.ts | 74 ++++++++++++++++++++++++-------- src/pages/api/admin/users.ts | 81 ++++++++++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+), 17 deletions(-) create mode 100644 src/pages/api/admin/users.ts diff --git a/src/lib/admin-api-router.ts b/src/lib/admin-api-router.ts index e0ed9cc..9cd8c3f 100644 --- a/src/lib/admin-api-router.ts +++ b/src/lib/admin-api-router.ts @@ -64,16 +64,31 @@ export class AdminApiRouter { } } - const [organizationsResult, eventsResult, ticketsResult, usersResult] = await Promise.all([ + const [organizationsResult, eventsResult, ticketsResult] = await Promise.all([ supabase.from('organizations').select('id'), supabase.from('events').select('id'), - supabase.from('tickets').select('price'), - supabase.from('users').select('id') + supabase.from('tickets').select('price') ]); + // Get user count from API endpoint to bypass RLS + let users = 0; + try { + const usersResponse = await fetch('/api/admin/users', { + method: 'GET', + credentials: 'include' + }); + if (usersResponse.ok) { + const usersResult = await usersResponse.json(); + if (usersResult.success) { + users = usersResult.data?.length || 0; + } + } + } catch (error) { + console.error('Error fetching user count for stats:', error); + } + const organizations = organizationsResult.data?.length || 0; const events = eventsResult.data?.length || 0; - const users = usersResult.data?.length || 0; const tickets = ticketsResult.data || []; const ticketCount = tickets.length; const revenue = tickets.reduce((sum, ticket) => sum + (ticket.price || 0), 0); @@ -105,12 +120,29 @@ export class AdminApiRouter { } } - const [eventsResult, usersResult, ticketsResult] = await Promise.all([ + const [eventsResult, ticketsResult] = await Promise.all([ supabase.from('events').select('*, organizations(name)').order('created_at', { ascending: false }).limit(5), - supabase.from('users').select('*, organizations(name)').order('created_at', { ascending: false }).limit(5), supabase.from('tickets').select('*, events(title)').order('created_at', { ascending: false }).limit(10) ]); + // Get recent users from API endpoint to bypass RLS + let usersResult = { data: [] }; + try { + const usersResponse = await fetch('/api/admin/users', { + method: 'GET', + credentials: 'include' + }); + if (usersResponse.ok) { + const result = await usersResponse.json(); + if (result.success) { + // Limit to 5 most recent users + usersResult.data = result.data.slice(0, 5); + } + } + } catch (error) { + console.error('Error fetching users for recent activity:', error); + } + const activities = []; // Add recent events @@ -243,22 +275,30 @@ export class AdminApiRouter { } } - const { data: users, error } = await supabase - .from('users') - .select(` - *, - organizations(name) - `) - .order('created_at', { ascending: false }); - - if (error) { + // Use server-side API endpoint to bypass RLS restrictions + const response = await fetch('/api/admin/users', { + method: 'GET', + credentials: 'include', + headers: { + 'Content-Type': 'application/json' + } + }); + if (!response.ok) { + console.error('Failed to fetch users:', response.status, response.statusText); return []; } - return users || []; - } catch (error) { + const result = await response.json(); + + if (!result.success) { + console.error('Users API error:', result.error); + return []; + } + return result.data || []; + } catch (error) { + console.error('Error fetching users:', error); return []; } } diff --git a/src/pages/api/admin/users.ts b/src/pages/api/admin/users.ts new file mode 100644 index 0000000..fad64a7 --- /dev/null +++ b/src/pages/api/admin/users.ts @@ -0,0 +1,81 @@ +import type { APIRoute } from 'astro'; +import { createSupabaseServerClient } from '../../../lib/supabase-ssr'; + +export const GET: APIRoute = async ({ request, cookies }) => { + try { + // Create server-side Supabase client for admin operations + const supabase = createSupabaseServerClient(cookies); + + // Get the current session + const { data: { session }, error: sessionError } = await supabase.auth.getSession(); + + if (sessionError || !session) { + return new Response( + JSON.stringify({ success: false, error: 'Authentication required' }), + { + status: 401, + headers: { 'Content-Type': 'application/json' } + } + ); + } + + // Check if user is admin + const { data: userRecord, error: userError } = await supabase + .from('users') + .select('role') + .eq('id', session.user.id) + .single(); + + if (userError || !userRecord || userRecord.role !== 'admin') { + return new Response( + JSON.stringify({ success: false, error: 'Admin access required' }), + { + status: 403, + headers: { 'Content-Type': 'application/json' } + } + ); + } + + // Use service role client for admin queries to bypass RLS + const { createSupabaseAdmin } = await import('../../../lib/supabase-admin'); + const serviceClient = createSupabaseAdmin(); + + // Get all users with organization details + const { data: users, error } = await serviceClient + .from('users') + .select(` + *, + organizations(name) + `) + .order('created_at', { ascending: false }); + + if (error) { + console.error('Error fetching users:', error); + return new Response( + JSON.stringify({ success: false, error: 'Failed to fetch users' }), + { + status: 500, + headers: { 'Content-Type': 'application/json' } + } + ); + } + + return new Response( + JSON.stringify({ success: true, data: users || [] }), + { + status: 200, + headers: { 'Content-Type': 'application/json' } + } + ); + + } catch (error) { + console.error('Admin users API error:', error); + return new Response( + JSON.stringify({ success: false, error: 'Internal server error' }), + { + status: 500, + headers: { 'Content-Type': 'application/json' } + } + ); + } +}; \ No newline at end of file