Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 14 additions & 20 deletions src/pages/auth/Auth.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React, { useState, useEffect, useRef, useCallback } from 'react';
import { PageSEO } from '@/components/seo/PageSEO';
import { useNavigate, useSearchParams, useLocation } from 'react-router-dom';
import { consumePostLoginRedirect } from '@/lib/auth/post-login-redirect';
import { resolveRedirectTarget } from '@/lib/auth/resolve-redirect-target';
import { resolveOAuthError, type OAuthErrorCopy } from '@/lib/auth/oauth-error-messages';

Expand All @@ -11,7 +10,6 @@ import {
Eye,
EyeOff,
Loader2,
Gift,
Mail,
Lock,
ShieldAlert,
Expand All @@ -27,7 +25,7 @@ import {
Rocket,
} from 'lucide-react';
import { motion, AnimatePresence } from 'framer-motion';
import { AuthBrandingPanel, Starfield, SpaceScene } from "@/pages/auth/AuthBranding";
import { AuthBrandingPanel, SpaceScene } from "@/pages/auth/AuthBranding";

import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
Expand Down Expand Up @@ -184,7 +182,7 @@ export default function Auth() {
} else {
setDbStatus(prev => ({ ...prev, external: { ok: false, loading: false } }));
}
} catch (err) {
} catch {
setDbStatus(prev => ({ ...prev, external: { ok: false, loading: false } }));
}
};
Expand Down Expand Up @@ -240,8 +238,8 @@ export default function Auth() {
navigate(resolveRedirectTargetCb(), { replace: true });
}, 600);
return true;
} catch (error) {
console.error('Validation error:', error);
} catch {
console.warn('Validation failed during post-login access checks; continuing with fail-open redirect');
navigate(resolveRedirectTargetCb(), { replace: true }); // Fail-open
return true;
}
Expand All @@ -252,11 +250,10 @@ export default function Auth() {
setIpBlocked(false);

try {
console.log(`[AUTH_START] Tentativa de login para: ${data.email}`);
const { error, data: signInData } = await signIn(data.email, data.password);
const { error } = await signIn(data.email, data.password);

if (error) {
console.error(`[AUTH_FAILED] Erro de autenticação para ${data.email}:`, error);
console.warn('[AUTH_FAILED] Authentication failed', { status: error.status ?? 'unknown' });
await logLoginAttempt(data.email, null, false, error.message);

let description = error.message;
Expand Down Expand Up @@ -307,8 +304,6 @@ export default function Auth() {
return;
}

console.log(`[AUTH_OK] Login bem-sucedido para ${data.email}. Validando sessão...`);

// Credential Management API — pede ao navegador para salvar email/senha
// após login bem-sucedido (Chrome/Edge/Brave). Silencioso se não suportado.
try {
Expand All @@ -319,8 +314,8 @@ export default function Auth() {
const cred = new CredCtor({ id: data.email, password: data.password, name: data.email });
await navigator.credentials.store(cred);
}
} catch (credErr) {
console.warn('[AUTH_CRED_STORE] Não foi possível salvar credenciais:', credErr);
} catch {
console.warn('[AUTH_CRED_STORE] Credential store failed');
}


Expand All @@ -340,15 +335,16 @@ export default function Auth() {
}

// 1. Verificação detalhada de Perfil (is_active)
console.log(`[AUTH_SESSION] Sessão iniciada para ${userId}. Carregando perfil...`);
const { data: profileData, error: profileError } = await supabase
.from('profiles')
.select('is_active, role')
.eq('user_id', userId)
.single();

if (profileError) {
console.error(`[AUTH_PROFILE_FAILED] Erro ao buscar perfil para ${userId}:`, profileError);
console.error('[AUTH_PROFILE_FAILED] Failed to load authenticated profile', {
code: profileError.code ?? 'unknown',
});
const isRLSError = profileError.code === 'PGRST301' || profileError.code === '42501';
toast({
variant: 'destructive',
Expand Down Expand Up @@ -382,17 +378,15 @@ export default function Auth() {
.eq('user_id', userId);

if (rolesError || !rolesData || rolesData.length === 0) {
console.warn(`[AUTH_RBAC_WARN] Usuário ${userId} sem papéis (roles) atribuídos.`);
} else {
console.log(`[AUTH_RBAC_OK] Usuário ${userId} possui ${rolesData.length} roles.`);
console.warn('[AUTH_RBAC_WARN] Authenticated user has no assigned roles');
}


// 3. Validação final de IP e Redirecionamento
await validateAndRedirect(userId, data.email);

} catch (err) {
console.error('Login exception:', err);
} catch {
console.error('Login exception');
toast({
variant: 'destructive',
title: 'Erro inesperado',
Expand Down
28 changes: 14 additions & 14 deletions supabase/functions/crm-db-bridge/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,6 @@ export function logInsertResultIfAnomalous(
shape: diagnostic.shape,
rowCount: diagnostic.rowCount,
errorMessage: diagnostic.errorMessage,
preview: diagnostic.preview,
};

const icon = diagnostic.shape === "generic-string-error" ? "🚨" : "⚠️";
Expand Down Expand Up @@ -464,7 +463,7 @@ async function authenticateRequest(req: Request): Promise<AuthResult> {
.from("user_roles").select("role").eq("user_id", user.id).single();

const userRole = roleData?.role || "vendedor";
console.log(`Request from user: ${user.id}, role: ${userRole}`);
console.log(`Authenticated CRM request role=${userRole}`);
return { userId: user.id, userRole };
}

Expand Down Expand Up @@ -667,7 +666,7 @@ async function handleInsert(crm: SupabaseClient, body: CrmQuery): Promise<Respon
if (isOptionalQuoteTable(table) && isMissingTableError(error, table)) {
return createOptionalWriteError(table);
}
console.error("CRM insert error:", error);
console.error("CRM insert error:", { code: error.code ?? "unknown", message: error.message });
return jsonResponse({ error: error.message }, 500);
}
return jsonResponse({ data: result, count: result?.length || 0 });
Expand Down Expand Up @@ -697,7 +696,7 @@ async function handleUpdate(crm: SupabaseClient, body: CrmQuery): Promise<Respon
if (isOptionalQuoteTable(table) && isMissingTableError(error, table)) {
return createOptionalWriteError(table);
}
console.error("CRM update error:", error);
console.error("CRM update error:", { code: error.code ?? "unknown", message: error.message });
return jsonResponse({ error: error.message }, 500);
}
return jsonResponse({ data: result, count: result?.length || 0 });
Expand All @@ -720,7 +719,7 @@ async function handleDelete(crm: SupabaseClient, body: CrmQuery): Promise<Respon
if (isOptionalQuoteTable(table) && isMissingTableError(error, table)) {
return createOptionalWriteError(table);
}
console.error("CRM delete error:", error);
console.error("CRM delete error:", { code: error.code ?? "unknown", message: error.message });
return jsonResponse({ error: error.message }, 500);
}
return jsonResponse({ data: null, success: true });
Expand All @@ -730,14 +729,17 @@ async function handleSelect(crm: SupabaseClient, body: CrmQuery): Promise<Respon
const { table, id, filters, select, orderBy, limit, offset, search, relations } = body;
const selectFields = select || (relations ? `${select || "*"}, ${relations}` : "*");

console.log(`[SELECT] table=${table}, selectFields=${selectFields}, filters=${JSON.stringify(filters)}, limit=${limit}`);
console.log(
`[SELECT] table=${table}, select_fields=${selectFields.split(',').length}, ` +
`filter_keys=${Object.keys(filters ?? {}).length}, limit=${limit ?? 'default'}`
);

let query = crm.from(table).select(selectFields);

if (id) {
const { data, error } = await query.eq("id", id).single();
if (error) {
console.error(`[SELECT] single error: code=${error.code}, message=${error.message}, details=${JSON.stringify(error)}`);
console.error(`[SELECT] single error: code=${error.code}, message=${error.message}`);
if (isOptionalQuoteTable(table) && isMissingTableError(error, table)) {
return createOptionalSelectFallback(table, true);
}
Expand All @@ -753,7 +755,10 @@ async function handleSelect(crm: SupabaseClient, body: CrmQuery): Promise<Respon
if (offset) query = query.range(offset, offset + (limit || 50) - 1);

const { data, error, count, status, statusText } = await query;
console.log(`[SELECT] result: status=${status}, statusText=${statusText}, dataLength=${(data || []).length}, error=${error ? JSON.stringify(error) : 'none'}, count=${count}`);
console.log(
`[SELECT] result: status=${status}, statusText=${statusText}, ` +
`dataLength=${(data || []).length}, hasError=${!!error}, count=${count}`
);

if (error) {
if (isOptionalQuoteTable(table) && isMissingTableError(error, table)) {
Expand Down Expand Up @@ -890,14 +895,9 @@ Deno.serve((req) => {
evt: "crm-creds-resolved",
url_source: urlRes.source,
url_via_alias: urlRes.resolved_name !== "EXTERNAL_CRM_URL",
url_prefix: CRM_URL.substring(0, 30),
using,
key_source: keySource,
key_len: CRM_KEY.length,
anon_len: CRM_ANON.length,
keys_match: CRM_SERVICE_KEY === CRM_ANON,
svc_last4: CRM_KEY.slice(-4),
anon_last4: CRM_ANON.slice(-4),
}));
}

Expand Down Expand Up @@ -996,4 +996,4 @@ Deno.serve((req) => {
return jsonResponse({ error: error instanceof Error ? error.message : "Internal error" }, 500);
}
});
});
});
38 changes: 25 additions & 13 deletions supabase/functions/external-db-bridge/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,12 @@ function safeStringify(v: unknown): string {
try { return JSON.stringify(v); } catch { return String(v); }
}

function summarizeFilterViolations(
violations: Array<{ field: string; reason: string; receivedType: string }>,
) {
return violations.map(({ field, reason, receivedType }) => ({ field, reason, receivedType }));
}

export function applyFilters(
query: any,
filters: Record<string, unknown>,
Expand Down Expand Up @@ -590,7 +596,10 @@ async function handleBatch(body: any, req: Request, corsHeaders: Record<string,
// Reject malformed filters (objects, NaN, functions) per-batch-item BEFORE building the query.
const batchFilterViolations = validateFilters(qFilters);
if (batchFilterViolations.length > 0) {
console.warn(`[batch] Query ${idx} (${qTable}) rejected — invalid filters:`, batchFilterViolations);
console.warn(
`[batch] Query ${idx} (${qTable}) rejected — invalid filters:`,
summarizeFilterViolations(batchFilterViolations),
);
return {
success: false,
error: 'Invalid filter values',
Expand Down Expand Up @@ -722,7 +731,7 @@ async function handleRpc(body: any, corsHeaders: Record<string, string>) {
const externalSupabase = await getExternalClient(corsHeaders);
if (externalSupabase instanceof Response) return externalSupabase;

console.log(`RPC: ${rpcName}`, rpcParams);
console.log(`RPC: ${rpcName} params_keys=${Object.keys(rpcParams || {}).length}`);
const rpcStart = performance.now();
const { data: rpcDataRaw, error: rpcError } = await externalSupabase.rpc(rpcName, rpcParams || {});
const rpcDuration = Math.round(performance.now() - rpcStart);
Expand Down Expand Up @@ -990,7 +999,10 @@ async function handleSelect(externalSupabase: any, table: string, opts: any) {
// Reject malformed filters (objects, NaN, functions) BEFORE building the query.
const filterViolations = validateFilters(filters as Record<string, unknown> | undefined);
if (filterViolations.length > 0) {
console.warn(`[external-db-bridge] Rejecting select on "${table}" — ${filterViolations.length} invalid filter(s):`, filterViolations);
console.warn(
`[external-db-bridge] Rejecting select on "${table}" — ${filterViolations.length} invalid filter(s):`,
summarizeFilterViolations(filterViolations),
);
return jsonResponse({
error: 'Invalid filter values',
details: filterViolations,
Expand Down Expand Up @@ -1232,7 +1244,7 @@ async function handleSelect(externalSupabase: any, table: string, opts: any) {
console.warn(
`[external-db-bridge] orderBy fallback: column "${orderColumn}" failed on table "${table}" ` +
`(code=${errCode || 'n/a'}, msg="${errMsg}"). Retrying without orderBy. ` +
`Caller origin: select="${effectiveSelect.slice(0, 80)}..." filters=${JSON.stringify(filters ?? {}).slice(0, 200)}`
`Caller origin: select_fields=${effectiveSelect.split(',').length} filters_keys=${Object.keys(filters ?? {}).length}`
);

const retryStart = performance.now();
Expand Down Expand Up @@ -1333,7 +1345,7 @@ async function handleInsert(externalSupabase: any, table: string, opts: any) {
const virtualRecord = buildVirtualRecords(productId, updatedAreas)
.find((r) => r.area_id === areaId && r.technique_id === techniqueId);
const result = virtualRecord || { id: `${productId}::${areaId}::${techniqueId}`, product_id: productId, area_id: areaId, technique_id: techniqueId };
console.log(`${alreadyLinked ? 'Already linked' : 'Inserted virtual record'} in ${table}:`, (result as any).id);
console.log(`${alreadyLinked ? 'Already linked' : 'Inserted virtual record'} in ${table}`);
return result;
}

Expand All @@ -1346,15 +1358,15 @@ async function handleInsert(externalSupabase: any, table: string, opts: any) {
});
if (canInjectCreatedAt && !insertData.created_at) insertData.created_at = new Date().toISOString();

console.log(`Inserting into ${table}:`, JSON.stringify(insertData).substring(0, 500));
console.log(`Inserting into ${table}: field_count=${Object.keys(insertData).length}`);
const { data: insertResult, error: insertError } = await externalSupabase.from(table).insert(insertData).select().single();

if (insertError) {
console.error('Insert error:', insertError.message, insertError.details, insertError.hint);
return jsonResponse({ error: insertError.message, details: insertError.details, hint: insertError.hint }, 400, corsHeaders);
}

console.log(`Inserted record in ${table}:`, insertResult?.id);
console.log(`Inserted record in ${table}`);
return insertResult;
}

Expand All @@ -1377,7 +1389,7 @@ async function handleUpdate(externalSupabase: any, table: string, opts: any) {
...(canInjectUpdatedAt ? { updated_at: new Date().toISOString() } : {}),
});

console.log(`Updating ${table} id=${id}:`, JSON.stringify(updateData).substring(0, 500));
console.log(`Updating ${table}: field_count=${Object.keys(updateData).length}`);
const { data: updateResult, error: updateError } = await externalSupabase.from(table).update(updateData).eq('id', id).select().maybeSingle();

if (updateError) {
Expand All @@ -1386,7 +1398,7 @@ async function handleUpdate(externalSupabase: any, table: string, opts: any) {
}
if (!updateResult) return jsonResponse({ error: `Registro não encontrado em '${table}' com id='${id}'` }, 404, corsHeaders);

console.log(`Updated record in ${table}:`, id);
console.log(`Updated record in ${table}`);
return updateResult;
}

Expand Down Expand Up @@ -1419,7 +1431,7 @@ async function handleDelete(externalSupabase: any, table: string, opts: any) {
if (updateError) return jsonResponse({ error: updateError.message, details: updateError.details }, 400, corsHeaders);
if (!updatedProduct) return jsonResponse({ error: `Produto '${parsedId.productId}' não encontrado para atualização` }, 404, corsHeaders);

console.log(`Deleted virtual record from ${table}:`, id);
console.log(`Deleted virtual record from ${table}`);
return { success: true, deleted_id: id };
}

Expand All @@ -1432,7 +1444,7 @@ async function handleDelete(externalSupabase: any, table: string, opts: any) {
}
if (!deleteResult) return jsonResponse({ error: `Registro não encontrado em '${table}' com id='${id}'` }, 404, corsHeaders);

console.log(`Deleted record from ${table}:`, id);
console.log(`Deleted record from ${table}`);
return { success: true, deleted_id: id };
}

Expand All @@ -1455,7 +1467,7 @@ async function handleUpsert(externalSupabase: any, table: string, opts: any) {
// Default merge on 'sku' for products, 'id' for others
const onConflict = table === 'products' ? 'sku' : 'id';

console.log(`Upserting into ${table} (onConflict=${onConflict}):`, JSON.stringify(upsertData).substring(0, 500));
console.log(`Upserting into ${table} (onConflict=${onConflict}): field_count=${Object.keys(upsertData).length}`);
const { data: result, error } = await externalSupabase
.from(table)
.upsert(upsertData, { onConflict })
Expand All @@ -1467,7 +1479,7 @@ async function handleUpsert(externalSupabase: any, table: string, opts: any) {
return jsonResponse({ error: error.message, details: error.details, hint: error.hint }, 400, corsHeaders);
}

console.log(`Upserted record in ${table}:`, result?.id);
console.log(`Upserted record in ${table}`);
return result;
}

Expand Down
Loading