Files
blackcanyontickets/src/pages/api/auth/login.ts
dzinesco dbf4b11e81 fix: Implement comprehensive edit event button functionality and resolve authentication issues
Major fixes and improvements:
- Fixed edit event button functionality with proper event handlers and DOM ready state checking
- Added status column to tickets table via Supabase migration to resolve 500 API errors
- Updated stats API to correctly calculate revenue from decimal price values
- Resolved authentication redirect loops by fixing cookie configuration for Docker environment
- Fixed Permissions-Policy header syntax errors
- Added comprehensive debugging and error handling for event management
- Implemented modal-based event editing with form validation and API integration
- Enhanced event data loading with proper error handling and user feedback

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-14 18:49:49 -06:00

153 lines
4.9 KiB
TypeScript

import type { APIRoute } from 'astro';
import { createSupabaseServerClient } from '../../../lib/supabase-ssr';
// Helper function to retry authentication with exponential backoff
async function retryAuth(supabase: any, email: string, password: string, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const { data, error } = await supabase.auth.signInWithPassword({
email,
password,
});
// If successful or non-rate-limit error, return immediately
if (!error || (!error.message.includes('over_request_rate_limit') && error.status !== 429)) {
return { data, error };
}
// If rate limited and not final attempt, wait and retry
if (attempt < maxRetries) {
const delay = Math.pow(2, attempt) * 1000; // Exponential backoff: 2s, 4s, 8s
console.log(`[LOGIN] Rate limited, waiting ${delay}ms before retry ${attempt + 1}`);
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
// Final attempt failed, return the error
return { data, error };
} catch (err) {
// Non-auth errors, return immediately
return { data: null, error: err };
}
}
}
export const POST: APIRoute = async ({ request, cookies }) => {
try {
const formData = await request.json();
const { email, password } = formData;
console.log('[LOGIN] Attempting login for:', email);
if (!email || !password) {
return new Response(JSON.stringify({
error: 'Email and password are required'
}), {
status: 400,
headers: { 'Content-Type': 'application/json' }
});
}
const supabase = createSupabaseServerClient(cookies);
console.log('[LOGIN] Created Supabase client');
const { data, error } = await retryAuth(supabase, email, password);
console.log('[LOGIN] Supabase response:', {
hasUser: !!data?.user,
hasSession: !!data?.session,
error: error?.message
});
if (error) {
console.log('[LOGIN] Authentication failed:', error.message);
// Handle specific error types
if (error.message.includes('over_request_rate_limit') || error.status === 429) {
return new Response(JSON.stringify({
error: 'Too many login attempts. Please wait 5 minutes and try again.',
code: 'RATE_LIMITED'
}), {
status: 429,
headers: { 'Content-Type': 'application/json' }
});
}
// Handle invalid credentials
if (error.message.includes('Invalid login credentials') || error.message.includes('Email not confirmed')) {
return new Response(JSON.stringify({
error: 'Invalid email or password. Please check your credentials.',
code: 'INVALID_CREDENTIALS'
}), {
status: 401,
headers: { 'Content-Type': 'application/json' }
});
}
// Generic error fallback
return new Response(JSON.stringify({
error: error.message || 'Authentication failed',
code: 'AUTH_ERROR'
}), {
status: 401,
headers: { 'Content-Type': 'application/json' }
});
}
// Get user organization
console.log('[LOGIN] Looking up user data for ID:', data.user.id);
const { data: userData, error: userError } = await supabase
.from('users')
.select('organization_id, role')
.eq('id', data.user.id)
.single();
console.log('[LOGIN] User data lookup:', {
userData,
userError: userError?.message,
hasOrganization: !!userData?.organization_id,
userId: data.user.id
});
// If user lookup failed, log detailed error and return error response
if (userError) {
console.error('[LOGIN] User lookup failed:', {
error: userError,
userId: data.user.id,
userEmail: data.user.email
});
return new Response(JSON.stringify({
error: 'Failed to retrieve user profile. Please try again.'
}), {
status: 500,
headers: { 'Content-Type': 'application/json' }
});
}
const redirectTo = !userData?.organization_id
? '/onboarding/organization'
: '/dashboard';
console.log('[LOGIN] Redirecting to:', redirectTo);
return new Response(JSON.stringify({
success: true,
user: data.user,
organizationId: userData?.organization_id,
isAdmin: userData?.role === 'admin',
isSuperAdmin: false, // Super admin logic can be added later if needed
redirectTo
}), {
status: 200,
headers: { 'Content-Type': 'application/json' }
});
} catch (error) {
console.error('Login error:', error);
return new Response(JSON.stringify({
error: 'An error occurred during login'
}), {
status: 500,
headers: { 'Content-Type': 'application/json' }
});
}
};