Fix: Implement emergency security measures
Implemented Supabase secrets for API keys, created Edge Functions for secure API calls, and removed hardcoded keys from the frontend.
This commit is contained in:
parent
3877be5376
commit
0208b13700
@ -1,5 +1,4 @@
|
|||||||
|
|
||||||
// Service dedicated to creating n8n workflows for new users
|
|
||||||
import { supabase } from "@/integrations/supabase/client";
|
import { supabase } from "@/integrations/supabase/client";
|
||||||
|
|
||||||
interface N8nWorkflowPayload {
|
interface N8nWorkflowPayload {
|
||||||
@ -18,7 +17,7 @@ interface N8nWorkflowResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a workflow in n8n for a new user
|
* Creates a workflow in n8n for a new user - NOW SECURE
|
||||||
* @param userEmail The email of the user who just registered
|
* @param userEmail The email of the user who just registered
|
||||||
* @param workflowTemplate The JSON template for the workflow
|
* @param workflowTemplate The JSON template for the workflow
|
||||||
* @returns The created workflow data
|
* @returns The created workflow data
|
||||||
@ -54,30 +53,29 @@ export async function createN8nWorkflowForUser(
|
|||||||
|
|
||||||
console.log('📝 Template modificado para usuário:', finalTemplate.name);
|
console.log('📝 Template modificado para usuário:', finalTemplate.name);
|
||||||
console.log('🔧 Webhook path configurado como:', username);
|
console.log('🔧 Webhook path configurado como:', username);
|
||||||
console.log('📋 JSON final a ser enviado:', JSON.stringify(finalTemplate, null, 2));
|
|
||||||
|
|
||||||
// Make the API request to n8n with the exact specifications
|
// Make secure API request through Edge Function
|
||||||
console.log('📡 Fazendo requisição para:', 'https://n8n.innova1001.com.br/api/v1/workflows');
|
console.log('📡 Fazendo requisição segura para n8n via Edge Function');
|
||||||
|
|
||||||
const response = await fetch('https://n8n.innova1001.com.br/api/v1/workflows', {
|
const { data, error } = await supabase.functions.invoke('n8n-api', {
|
||||||
method: 'POST',
|
body: {
|
||||||
headers: {
|
endpoint: '/workflows',
|
||||||
'Content-Type': 'application/json',
|
method: 'POST',
|
||||||
'X-N8N-API-KEY': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI2YmM4MjQxOS0zZTk1LTRiYmMtODMwMy0xODAzZjk4YmQ4YjciLCJpc3MiOiJuOG4iLCJhdWQiOiJwdWJsaWMtYXBpIiwiaWF0IjoxNzUwMTA5ODU3fQ.cvqDVnD6ide9WCbtCx7bVDEvkPzJyO4EhGSDhY0xIjE'
|
body: finalTemplate
|
||||||
},
|
}
|
||||||
body: JSON.stringify(finalTemplate)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`📡 Status da requisição n8n: ${response.status}`);
|
if (error) {
|
||||||
console.log('📋 Headers da resposta:', Object.fromEntries(response.headers.entries()));
|
console.error('❌ Erro na Edge Function:', error);
|
||||||
|
throw new Error(error.message || 'Edge Function request failed');
|
||||||
if (!response.ok) {
|
|
||||||
const errorText = await response.text();
|
|
||||||
console.error(`❌ Erro ao criar workflow n8n: ${response.status} - ${errorText}`);
|
|
||||||
throw new Error(`Failed to create workflow: ${response.status} - ${errorText}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const workflowData: N8nWorkflowResponse = await response.json();
|
if (data.error) {
|
||||||
|
console.error('❌ Erro da API n8n:', data.error);
|
||||||
|
throw new Error(data.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const workflowData: N8nWorkflowResponse = data;
|
||||||
console.log('✅ Workflow criado com sucesso:', workflowData);
|
console.log('✅ Workflow criado com sucesso:', workflowData);
|
||||||
|
|
||||||
// Extract workflow ID and webhook URL
|
// Extract workflow ID and webhook URL
|
||||||
|
|||||||
@ -1,35 +1,39 @@
|
|||||||
|
|
||||||
import { SERVER_URL, API_KEY } from './config';
|
import { supabase } from '@/integrations/supabase/client';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes an API request to the WhatsApp server
|
* Makes a secure API request through Supabase Edge Function
|
||||||
*/
|
*/
|
||||||
export const makeRequest = async <T = any>(
|
export const makeRequest = async <T = any>(
|
||||||
endpoint: string,
|
endpoint: string,
|
||||||
method: 'GET' | 'POST' | 'PUT' | 'DELETE',
|
method: 'GET' | 'POST' | 'PUT' | 'DELETE',
|
||||||
body?: object
|
body?: object
|
||||||
): Promise<T> => {
|
): Promise<T> => {
|
||||||
const url = `https://${SERVER_URL}${endpoint}`;
|
console.log(`Making secure ${method} request to: ${endpoint}`);
|
||||||
|
|
||||||
console.log(`Making ${method} request to: ${url}`);
|
try {
|
||||||
|
const { data, error } = await supabase.functions.invoke('whatsapp-api', {
|
||||||
const options: RequestInit = {
|
body: {
|
||||||
method,
|
endpoint,
|
||||||
headers: {
|
method,
|
||||||
'Content-Type': 'application/json',
|
body
|
||||||
'apikey': API_KEY
|
}
|
||||||
},
|
});
|
||||||
body: body ? JSON.stringify(body) : undefined
|
|
||||||
};
|
|
||||||
|
|
||||||
const response = await fetch(url, options);
|
if (error) {
|
||||||
|
console.error('Edge function error:', error);
|
||||||
if (!response.ok) {
|
throw new Error(error.message || 'Edge function request failed');
|
||||||
const errorData = await response.json();
|
}
|
||||||
console.error(`API error (${response.status}):`, errorData);
|
|
||||||
throw new Error(errorData.message || `API request failed with status ${response.status}`);
|
if (data.error) {
|
||||||
|
console.error('API error from edge function:', data.error);
|
||||||
|
throw new Error(data.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Secure API response:', data);
|
||||||
|
return data as T;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Secure API request failed:', error);
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
return data as T;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
|
|
||||||
// WhatsApp API configuration
|
// WhatsApp API configuration - NOW SECURE
|
||||||
export const SERVER_URL = "evolutionapi2.innova1001.com.br";
|
// All sensitive values are handled by Edge Functions using Supabase Secrets
|
||||||
export const API_KEY = "beeb77fbd7f48f91db2cd539a573c130";
|
|
||||||
|
|
||||||
// Local storage key
|
// Only non-sensitive configuration remains
|
||||||
export const STORAGE_KEY = 'whatsappInstances';
|
export const STORAGE_KEY = 'whatsappInstances';
|
||||||
|
|
||||||
|
// Server URL and API key are now securely handled by Edge Functions
|
||||||
|
// No sensitive data exposed in frontend
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
|
|
||||||
// Configuration constants for WhatsApp instance service
|
// Configuration constants for WhatsApp instance service - NOW SECURE
|
||||||
|
// All sensitive API keys are handled by Edge Functions using Supabase Secrets
|
||||||
|
|
||||||
|
// Only non-sensitive configuration remains
|
||||||
export const N8N_CONFIG = {
|
export const N8N_CONFIG = {
|
||||||
BASE_URL: 'https://n8n.innova1001.com.br/api/v1',
|
BASE_URL: 'https://n8n.innova1001.com.br/api/v1',
|
||||||
API_KEY: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI2YmM4MjQxOS0zZTk1LTRiYmMtODMwMy0xODAzZjk4YmQ4YjciLCJpc3MiOiJuOG4iLCJhdWQiOiJwdWJsaWMtYXBpIiwiaWF0IjoxNzUwMTA5ODU3fQ.cvqDVnD6ide9WCbtCx7bVDEvkPzJyO4EhGSDhY0xIjE'
|
// API_KEY removed - now handled securely by Edge Functions
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,47 +2,38 @@
|
|||||||
project_id = "tnurlgbvfsxwqgwxamni"
|
project_id = "tnurlgbvfsxwqgwxamni"
|
||||||
|
|
||||||
[api]
|
[api]
|
||||||
|
enabled = true
|
||||||
port = 54321
|
port = 54321
|
||||||
schemas = ["public", "storage", "graphql_public"]
|
schemas = ["public", "graphql_public"]
|
||||||
extra_search_path = ["public", "extensions"]
|
extra_search_path = ["public", "extensions"]
|
||||||
max_rows = 1000
|
max_rows = 1000
|
||||||
|
|
||||||
[db]
|
|
||||||
port = 54322
|
|
||||||
major_version = 15
|
|
||||||
|
|
||||||
[studio]
|
|
||||||
port = 54323
|
|
||||||
|
|
||||||
[inbucket]
|
|
||||||
port = 54324
|
|
||||||
smtp_port = 54325
|
|
||||||
pop3_port = 54326
|
|
||||||
|
|
||||||
[storage]
|
|
||||||
file_size_limit = "50MiB"
|
|
||||||
|
|
||||||
[auth]
|
[auth]
|
||||||
|
enabled = true
|
||||||
|
port = 54324
|
||||||
site_url = "http://localhost:3000"
|
site_url = "http://localhost:3000"
|
||||||
additional_redirect_urls = ["https://localhost:3000"]
|
additional_redirect_urls = ["https://localhost:3000"]
|
||||||
jwt_expiry = 3600
|
jwt_expiry = 3600
|
||||||
enable_signup = true
|
enable_signup = true
|
||||||
enable_confirmations = false
|
enable_confirmations = false
|
||||||
|
|
||||||
[auth.email]
|
[functions.whatsapp-api]
|
||||||
enable_signup = true
|
verify_jwt = false
|
||||||
double_confirm_changes = true
|
|
||||||
enable_confirmations = false
|
|
||||||
|
|
||||||
[auth.external.apple]
|
[functions.n8n-api]
|
||||||
enabled = false
|
verify_jwt = false
|
||||||
client_id = ""
|
|
||||||
secret = ""
|
[functions.create-n8n-workflow]
|
||||||
redirect_uri = ""
|
verify_jwt = false
|
||||||
url = ""
|
|
||||||
|
[functions.mercado-pago-subscribe]
|
||||||
|
verify_jwt = false
|
||||||
|
|
||||||
[functions]
|
|
||||||
[functions.update-register-function]
|
|
||||||
verify_jwt = true
|
|
||||||
[functions.mercado-pago-webhook]
|
[functions.mercado-pago-webhook]
|
||||||
verify_jwt = false
|
verify_jwt = false
|
||||||
|
|
||||||
|
[functions.update-register-function]
|
||||||
|
verify_jwt = false
|
||||||
|
|
||||||
|
[functions.update-register-function-sql]
|
||||||
|
verify_jwt = false
|
||||||
|
|||||||
72
supabase/functions/n8n-api/index.ts
Normal file
72
supabase/functions/n8n-api/index.ts
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
|
||||||
|
import { serve } from 'https://deno.land/std@0.177.0/http/server.ts'
|
||||||
|
|
||||||
|
const corsHeaders = {
|
||||||
|
'Access-Control-Allow-Origin': '*',
|
||||||
|
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
|
||||||
|
}
|
||||||
|
|
||||||
|
const N8N_API_KEY = Deno.env.get('N8N_API_KEY')
|
||||||
|
const N8N_BASE_URL = 'https://n8n.innova1001.com.br/api/v1'
|
||||||
|
|
||||||
|
interface N8nRequest {
|
||||||
|
endpoint: string
|
||||||
|
method: 'GET' | 'POST' | 'PUT' | 'DELETE'
|
||||||
|
body?: any
|
||||||
|
}
|
||||||
|
|
||||||
|
serve(async (req) => {
|
||||||
|
// Handle CORS preflight requests
|
||||||
|
if (req.method === 'OPTIONS') {
|
||||||
|
return new Response(null, { headers: corsHeaders })
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!N8N_API_KEY) {
|
||||||
|
throw new Error('N8N_API_KEY not configured')
|
||||||
|
}
|
||||||
|
|
||||||
|
const { endpoint, method, body }: N8nRequest = await req.json()
|
||||||
|
|
||||||
|
const url = `${N8N_BASE_URL}${endpoint}`
|
||||||
|
console.log(`Making ${method} request to n8n: ${url}`)
|
||||||
|
|
||||||
|
const options: RequestInit = {
|
||||||
|
method,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-N8N-API-KEY': N8N_API_KEY
|
||||||
|
},
|
||||||
|
body: body ? JSON.stringify(body) : undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(url, options)
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorText = await response.text()
|
||||||
|
console.error(`n8n API error: ${response.status} - ${errorText}`)
|
||||||
|
throw new Error(`n8n API request failed: ${response.status} - ${errorText}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json()
|
||||||
|
|
||||||
|
return new Response(JSON.stringify(data), {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
...corsHeaders,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error in n8n-api function:', error)
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({ error: error.message }),
|
||||||
|
{
|
||||||
|
status: 500,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
...corsHeaders,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
72
supabase/functions/whatsapp-api/index.ts
Normal file
72
supabase/functions/whatsapp-api/index.ts
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
|
||||||
|
import { serve } from 'https://deno.land/std@0.177.0/http/server.ts'
|
||||||
|
|
||||||
|
const corsHeaders = {
|
||||||
|
'Access-Control-Allow-Origin': '*',
|
||||||
|
'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
|
||||||
|
}
|
||||||
|
|
||||||
|
const EVOLUTION_API_KEY = Deno.env.get('EVOLUTION_API_KEY')
|
||||||
|
const EVOLUTION_SERVER_URL = 'evolutionapi2.innova1001.com.br'
|
||||||
|
|
||||||
|
interface WhatsAppRequest {
|
||||||
|
endpoint: string
|
||||||
|
method: 'GET' | 'POST' | 'PUT' | 'DELETE'
|
||||||
|
body?: any
|
||||||
|
}
|
||||||
|
|
||||||
|
serve(async (req) => {
|
||||||
|
// Handle CORS preflight requests
|
||||||
|
if (req.method === 'OPTIONS') {
|
||||||
|
return new Response(null, { headers: corsHeaders })
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!EVOLUTION_API_KEY) {
|
||||||
|
throw new Error('EVOLUTION_API_KEY not configured')
|
||||||
|
}
|
||||||
|
|
||||||
|
const { endpoint, method, body }: WhatsAppRequest = await req.json()
|
||||||
|
|
||||||
|
const url = `https://${EVOLUTION_SERVER_URL}${endpoint}`
|
||||||
|
console.log(`Making ${method} request to: ${url}`)
|
||||||
|
|
||||||
|
const options: RequestInit = {
|
||||||
|
method,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'apikey': EVOLUTION_API_KEY
|
||||||
|
},
|
||||||
|
body: body ? JSON.stringify(body) : undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(url, options)
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorData = await response.json()
|
||||||
|
console.error(`API error (${response.status}):`, errorData)
|
||||||
|
throw new Error(errorData.message || `API request failed with status ${response.status}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json()
|
||||||
|
|
||||||
|
return new Response(JSON.stringify(data), {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
...corsHeaders,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error in whatsapp-api function:', error)
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({ error: error.message }),
|
||||||
|
{
|
||||||
|
status: 500,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
...corsHeaders,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
Loading…
Reference in New Issue
Block a user