diff --git a/susconecta/app/api/signin-user/route.ts b/susconecta/app/api/signin-user/route.ts new file mode 100644 index 0000000..10f0789 --- /dev/null +++ b/susconecta/app/api/signin-user/route.ts @@ -0,0 +1,46 @@ +import { NextResponse } from 'next/server'; +import { ENV_CONFIG } from '@/lib/env-config'; + +/** + * Proxy server-side route (App Router) to call Supabase OpenAPI /auth/v1/signin + * This keeps the Supabase anon key on the server and avoids CORS from browsers. + */ +export async function POST(req: Request) { + try { + const payload = await req.json(); + + // Lightweight, non-sensitive debug logging to verify the proxy is hit at runtime. + try { + console.log('[api/signin-user] POST received', { + url: typeof (req as any).url === 'string' ? (req as any).url : undefined, + email: payload?.email ?? null, + }); + } catch (e) { + // never throw from logging + } + + const url = `${ENV_CONFIG.SUPABASE_URL}/auth/v1/signin`; + + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + apikey: ENV_CONFIG.SUPABASE_ANON_KEY, + }, + body: JSON.stringify(payload), + }); + + const text = await response.text(); + let data: any = null; + try { + data = text ? JSON.parse(text) : null; + } catch (e) { + data = text; + } + + return NextResponse.json(data, { status: response.status }); + } catch (error) { + console.error('[api/signin-user] Unexpected error', error); + return NextResponse.json({ error: 'Internal proxy error' }, { status: 500 }); + } +} diff --git a/susconecta/lib/auth.ts b/susconecta/lib/auth.ts index e21a67d..88c5186 100644 --- a/susconecta/lib/auth.ts +++ b/susconecta/lib/auth.ts @@ -89,7 +89,10 @@ export async function loginUser( password: string, userType: 'profissional' | 'paciente' | 'administrador' ): Promise { - let url = AUTH_ENDPOINTS.LOGIN; + // Use server-side AUTH_ENDPOINTS.LOGIN by default. When running in the browser + // prefer the local proxy that forwards to the OpenAPI signin: `/api/signin-user`. + const isBrowser = typeof window !== 'undefined'; + const url = isBrowser ? '/api/signin-user' : AUTH_ENDPOINTS.LOGIN; const payload = { email, @@ -104,9 +107,9 @@ export async function loginUser( timestamp: new Date().toLocaleTimeString() }); - console.log('🔑 [AUTH-API] Credenciais sendo usadas no login:'); + // Log only non-sensitive info; never log passwords + console.log('🔑 [AUTH-API] Credenciais sendo usadas no login (redacted):'); console.log('📧 Email:', email); - console.log('🔐 Senha:', password); console.log('👤 UserType:', userType); // Delay para visualizar na aba Network @@ -118,11 +121,48 @@ export async function loginUser( // Debug: Log request sem credenciais sensíveis debugRequest('POST', url, getLoginHeaders(), payload); - const response = await fetch(url, { - method: 'POST', - headers: getLoginHeaders(), - body: JSON.stringify(payload), - }); + // Helper to perform a login fetch and return response (no processing here) + async function doLoginFetch(targetUrl: string) { + try { + return await fetch(targetUrl, { + method: 'POST', + headers: getLoginHeaders(), + body: JSON.stringify(payload), + }); + } catch (err) { + // bubble up the error to the caller + throw err; + } + } + + let response: Response; + try { + response = await doLoginFetch(url); + } catch (networkError) { + console.warn('[AUTH-API] Network error when calling', url, networkError); + // Try fallback to server endpoints if available + const fallback1 = AUTH_ENDPOINTS.LOGIN; + const fallback2 = `${ENV_CONFIG.SUPABASE_URL}/auth/v1/signin`; + let tried = [] as string[]; + + try { + tried.push(fallback1); + response = await doLoginFetch(fallback1); + } catch (e1) { + console.warn('[AUTH-API] Fallback1 failed', fallback1, e1); + try { + tried.push(fallback2); + response = await doLoginFetch(fallback2); + } catch (e2) { + console.error('[AUTH-API] All fallbacks failed', { tried, e1, e2 }); + throw new AuthenticationError( + 'Não foi possível contatar o serviço de autenticação (todos os caminhos falharam)', + 'AUTH_NETWORK_ERROR', + { tried } + ); + } + } + } console.log(`[AUTH-API] Login response: ${response.status} ${response.statusText}`, { url: response.url, @@ -130,6 +170,34 @@ export async function loginUser( timestamp: new Date().toLocaleTimeString() }); + // If proxy returned 404, try direct fallbacks (in case the proxy route is missing) + if (response.status === 404) { + console.warn('[AUTH-API] Proxy returned 404, attempting direct login fallbacks'); + const fallback1 = AUTH_ENDPOINTS.LOGIN; + const fallback2 = `${ENV_CONFIG.SUPABASE_URL}/auth/v1/signin`; + let fallbackResponse: Response | null = null; + try { + fallbackResponse = await doLoginFetch(fallback1); + } catch (e) { + console.warn('[AUTH-API] fallback1 failed', fallback1, e); + } + + if (!fallbackResponse || fallbackResponse.status === 404) { + try { + fallbackResponse = await doLoginFetch(fallback2); + } catch (e) { + console.warn('[AUTH-API] fallback2 failed', fallback2, e); + } + } + + if (fallbackResponse) { + response = fallbackResponse; + console.log('[AUTH-API] Used fallback response', { url: response.url, status: response.status }); + } else { + console.error('[AUTH-API] No fallback produced a valid response'); + } + } + // Se falhar, mostrar detalhes do erro if (!response.ok) { try { @@ -148,6 +216,16 @@ export async function loginUser( // Delay adicional para ver status code await new Promise(resolve => setTimeout(resolve, 50)); + // If after trying fallbacks we still have a 404, make the error explicit and actionable + if (response.status === 404) { + console.error('[AUTH-API] Final response was 404 (Not Found). Likely the local proxy route is missing or Next dev server is not running.', { url: response.url }); + throw new AuthenticationError( + 'Signin endpoint not found (404). Ensure Next.js dev server is running and the route `/api/signin-user` exists.', + 'SIGNIN_NOT_FOUND', + { url: response.url } + ); + } + const data = await processResponse(response); console.log('[AUTH] Dados recebidos da API:', data); diff --git a/susconecta/lib/debug-utils.ts b/susconecta/lib/debug-utils.ts index b08ee4c..fdfd64d 100644 --- a/susconecta/lib/debug-utils.ts +++ b/susconecta/lib/debug-utils.ts @@ -9,7 +9,6 @@ export function debugRequest( body?: any ) { if (process.env.NODE_ENV !== 'development') return; - const headersWithoutSensitive = Object.keys(headers).reduce((acc, key) => { // Não logar valores sensíveis, apenas nomes if (key.toLowerCase().includes('apikey') || key.toLowerCase().includes('authorization')) { @@ -20,15 +19,35 @@ export function debugRequest( return acc; }, {} as Record); - const bodyShape = body ? Object.keys(typeof body === 'string' ? JSON.parse(body) : body) : []; + const bodyShape = body ? Object.keys(typeof body === 'string' ? (() => { + try { return JSON.parse(body); } catch { return {}; } + })() : body) : []; - console.log('[DEBUG] Request Preview:', { - method, - path: new URL(url).pathname, - query: new URL(url).search, - headerNames: Object.keys(headers), - headers: headersWithoutSensitive, - bodyShape, - timestamp: new Date().toISOString(), - }); + // Support relative URLs (e.g. '/api/signin-user') by providing a base when needed. + try { + let urlObj: URL; + try { + urlObj = new URL(url); + } catch (e) { + // Fallbacks: browser origin or localhost for server-side dev + const base = (typeof window !== 'undefined' && window.location && window.location.origin) + ? window.location.origin + : 'http://localhost'; + urlObj = new URL(url, base); + } + + console.log('[DEBUG] Request Preview:', { + method, + path: urlObj.pathname, + query: urlObj.search, + headerNames: Object.keys(headers), + headers: headersWithoutSensitive, + bodyShape, + timestamp: new Date().toISOString(), + }); + } catch (err) { + // Never throw from debug tooling; keep best-effort logging + // eslint-disable-next-line no-console + console.warn('[DEBUG] debugRequest failed to parse URL or body', { url, error: err }); + } } \ No newline at end of file diff --git a/susconecta/pages/api/signin-user.ts b/susconecta/pages/api/signin-user.ts new file mode 100644 index 0000000..3a93296 --- /dev/null +++ b/susconecta/pages/api/signin-user.ts @@ -0,0 +1,39 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; +import { ENV_CONFIG } from '@/lib/env-config'; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + if (req.method !== 'POST') { + res.setHeader('Allow', 'POST'); + return res.status(405).json({ error: 'Method not allowed' }); + } + + try { + const payload = req.body; + + // Lightweight debug log to confirm the pages API route is invoked. + try { + console.log('[pages/api/signin-user] POST received', { url: req.url, email: payload?.email ?? null }); + } catch (e) { + // ignore logging errors + } + const url = `${ENV_CONFIG.SUPABASE_URL}/auth/v1/signin`; + + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + apikey: ENV_CONFIG.SUPABASE_ANON_KEY, + }, + body: JSON.stringify(payload), + }); + + const text = await response.text(); + let data: any = null; + try { data = text ? JSON.parse(text) : null; } catch { data = text; } + + return res.status(response.status).json(data); + } catch (error) { + console.error('[pages/api/signin-user] Unexpected error', error); + return res.status(500).json({ error: 'Internal proxy error' }); + } +}