diff --git a/MEDICONNECT 2/netlify/functions/appointments.ts b/MEDICONNECT 2/netlify/functions/appointments.ts deleted file mode 100644 index 0eaab64df..000000000 --- a/MEDICONNECT 2/netlify/functions/appointments.ts +++ /dev/null @@ -1,163 +0,0 @@ -import type { Handler, HandlerEvent } from "@netlify/functions"; - -const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co"; -const SUPABASE_ANON_KEY = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ"; - -export const handler: Handler = async (event: HandlerEvent) => { - const headers = { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "Content-Type, Authorization", - "Access-Control-Allow-Methods": "GET, POST, PATCH, DELETE, OPTIONS", - }; - - if (event.httpMethod === "OPTIONS") { - return { statusCode: 200, headers, body: "" }; - } - - try { - const authHeader = - event.headers.authorization || event.headers.Authorization; - if (!authHeader) { - return { - statusCode: 401, - headers, - body: JSON.stringify({ error: "Token n�o fornecido" }), - }; - } - - const pathParts = event.path.split("/"); - const appointmentId = - pathParts[pathParts.length - 1] !== "appointments" - ? pathParts[pathParts.length - 1] - : null; - - if (event.httpMethod === "GET") { - let url = `${SUPABASE_URL}/rest/v1/appointments`; - if (appointmentId && appointmentId !== "appointments") { - url += `?id=eq.${appointmentId}&select=*`; - } else if (event.queryStringParameters) { - const params = new URLSearchParams( - event.queryStringParameters as Record - ); - url += `?${params.toString()}`; - if (!params.has("select")) { - url += url.includes("?") ? "&select=*" : "?select=*"; - } - } else { - url += "?select=*"; - } - - const response = await fetch(url, { - method: "GET", - headers: { apikey: SUPABASE_ANON_KEY, Authorization: authHeader }, - }); - let data = await response.json(); - if ( - appointmentId && - appointmentId !== "appointments" && - Array.isArray(data) && - data.length > 0 - ) { - data = data[0]; - } - return { - statusCode: response.status, - headers: { ...headers, "Content-Type": "application/json" }, - body: JSON.stringify(data), - }; - } - - if (event.httpMethod === "POST") { - const body = JSON.parse(event.body || "{}"); - if (!body.patient_id || !body.doctor_id || !body.scheduled_at) { - return { - statusCode: 400, - headers, - body: JSON.stringify({ - error: "Campos obrigat�rios: patient_id, doctor_id, scheduled_at", - }), - }; - } - const response = await fetch(`${SUPABASE_URL}/rest/v1/appointments`, { - method: "POST", - headers: { - apikey: SUPABASE_ANON_KEY, - Authorization: authHeader, - "Content-Type": "application/json", - Prefer: "return=representation", - }, - body: JSON.stringify(body), - }); - let data = await response.json(); - if (Array.isArray(data) && data.length > 0) data = data[0]; - return { - statusCode: response.status, - headers: { ...headers, "Content-Type": "application/json" }, - body: JSON.stringify(data), - }; - } - - if (event.httpMethod === "PATCH") { - if (!appointmentId || appointmentId === "appointments") { - return { - statusCode: 400, - headers, - body: JSON.stringify({ error: "ID do agendamento � obrigat�rio" }), - }; - } - const body = JSON.parse(event.body || "{}"); - const response = await fetch( - `${SUPABASE_URL}/rest/v1/appointments?id=eq.${appointmentId}`, - { - method: "PATCH", - headers: { - apikey: SUPABASE_ANON_KEY, - Authorization: authHeader, - "Content-Type": "application/json", - Prefer: "return=representation", - }, - body: JSON.stringify(body), - } - ); - let data = await response.json(); - if (Array.isArray(data) && data.length > 0) data = data[0]; - return { - statusCode: response.status, - headers: { ...headers, "Content-Type": "application/json" }, - body: JSON.stringify(data), - }; - } - - if (event.httpMethod === "DELETE") { - if (!appointmentId || appointmentId === "appointments") { - return { - statusCode: 400, - headers, - body: JSON.stringify({ error: "ID do agendamento � obrigat�rio" }), - }; - } - const response = await fetch( - `${SUPABASE_URL}/rest/v1/appointments?id=eq.${appointmentId}`, - { - method: "DELETE", - headers: { apikey: SUPABASE_ANON_KEY, Authorization: authHeader }, - } - ); - return { statusCode: response.status, headers, body: "" }; - } - - return { - statusCode: 405, - headers, - body: JSON.stringify({ error: "Method Not Allowed" }), - }; - } catch (error) { - console.error("Erro:", error); - return { - statusCode: 500, - headers, - body: JSON.stringify({ error: "Erro interno" }), - }; - } -}; diff --git a/MEDICONNECT 2/netlify/functions/assignments.ts b/MEDICONNECT 2/netlify/functions/assignments.ts deleted file mode 100644 index 1869ebcbf..000000000 --- a/MEDICONNECT 2/netlify/functions/assignments.ts +++ /dev/null @@ -1,153 +0,0 @@ -/** - * Netlify Function: Listar Atribuições - * GET /rest/v1/patient_assignments - */ - -import type { Handler, HandlerEvent } from "@netlify/functions"; - -const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co"; -const SUPABASE_ANON_KEY = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ"; - -export const handler: Handler = async (event: HandlerEvent) => { - const headers = { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "Content-Type, Authorization", - "Access-Control-Allow-Methods": "GET, POST, OPTIONS", - }; - - if (event.httpMethod === "OPTIONS") { - return { - statusCode: 200, - headers, - body: "", - }; - } - - // GET - Listar atribuições - if (event.httpMethod === "GET") { - try { - const authHeader = - event.headers.authorization || event.headers.Authorization; - - if (!authHeader) { - return { - statusCode: 401, - headers, - body: JSON.stringify({ error: "Token não fornecido" }), - }; - } - - // Monta URL com query params (se houver) - const queryString = event.queryStringParameters - ? "?" + - new URLSearchParams( - event.queryStringParameters as Record - ).toString() - : ""; - - const response = await fetch( - `${SUPABASE_URL}/rest/v1/patient_assignments${queryString}`, - { - method: "GET", - headers: { - apikey: SUPABASE_ANON_KEY, - Authorization: authHeader, - }, - } - ); - - const data = await response.json(); - - return { - statusCode: response.status, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }; - } catch (error) { - console.error("Erro ao listar atribuições:", error); - - return { - statusCode: 500, - headers, - body: JSON.stringify({ - error: "Erro interno no servidor", - message: error instanceof Error ? error.message : "Erro desconhecido", - }), - }; - } - } - - // POST - Criar atribuição - if (event.httpMethod === "POST") { - try { - const authHeader = - event.headers.authorization || event.headers.Authorization; - - if (!authHeader) { - return { - statusCode: 401, - headers, - body: JSON.stringify({ error: "Token não fornecido" }), - }; - } - - const body = JSON.parse(event.body || "{}"); - - if (!body.patient_id || !body.user_id || !body.role) { - return { - statusCode: 400, - headers, - body: JSON.stringify({ - error: "patient_id, user_id e role são obrigatórios", - }), - }; - } - - const response = await fetch( - `${SUPABASE_URL}/rest/v1/patient_assignments`, - { - method: "POST", - headers: { - apikey: SUPABASE_ANON_KEY, - Authorization: authHeader, - "Content-Type": "application/json", - Prefer: "return=representation", - }, - body: JSON.stringify(body), - } - ); - - const data = await response.json(); - - return { - statusCode: response.status, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }; - } catch (error) { - console.error("Erro ao criar atribuição:", error); - - return { - statusCode: 500, - headers, - body: JSON.stringify({ - error: "Erro interno no servidor", - message: error instanceof Error ? error.message : "Erro desconhecido", - }), - }; - } - } - - return { - statusCode: 405, - headers, - body: JSON.stringify({ error: "Method Not Allowed" }), - }; -}; diff --git a/MEDICONNECT 2/netlify/functions/auth-login.ts b/MEDICONNECT 2/netlify/functions/auth-login.ts deleted file mode 100644 index b4f76a5ab..000000000 --- a/MEDICONNECT 2/netlify/functions/auth-login.ts +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Netlify Function: Login - * Faz proxy seguro para API Supabase com apikey protegida - */ - -import type { Handler, HandlerEvent } from "@netlify/functions"; - -// Constantes da API (protegidas no backend) -const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co"; -const SUPABASE_ANON_KEY = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ"; - -interface LoginRequest { - email: string; - password: string; -} - -export const handler: Handler = async (event: HandlerEvent) => { - // CORS headers - const headers = { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "Content-Type, Authorization", - "Access-Control-Allow-Methods": "POST, OPTIONS", - }; - - // Handle preflight - if (event.httpMethod === "OPTIONS") { - return { - statusCode: 200, - headers, - body: "", - }; - } - - // Apenas POST é permitido - if (event.httpMethod !== "POST") { - return { - statusCode: 405, - headers, - body: JSON.stringify({ error: "Method Not Allowed" }), - }; - } - - try { - // Parse body - const body: LoginRequest = JSON.parse(event.body || "{}"); - - if (!body.email || !body.password) { - return { - statusCode: 400, - headers, - body: JSON.stringify({ error: "Email e senha são obrigatórios" }), - }; - } - - // Faz requisição para API Supabase COM a apikey protegida - const response = await fetch( - `${SUPABASE_URL}/auth/v1/token?grant_type=password`, - { - method: "POST", - headers: { - "Content-Type": "application/json", - apikey: SUPABASE_ANON_KEY, - }, - body: JSON.stringify({ - email: body.email, - password: body.password, - }), - } - ); - - const data = await response.json(); - - // Repassa a resposta para o frontend - return { - statusCode: response.status, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }; - } catch (error) { - console.error("Erro no login:", error); - - return { - statusCode: 500, - headers, - body: JSON.stringify({ - error: "Erro interno no servidor", - message: error instanceof Error ? error.message : "Erro desconhecido", - }), - }; - } -}; diff --git a/MEDICONNECT 2/netlify/functions/auth-logout.ts b/MEDICONNECT 2/netlify/functions/auth-logout.ts deleted file mode 100644 index 1854837c9..000000000 --- a/MEDICONNECT 2/netlify/functions/auth-logout.ts +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Netlify Function: Logout - * Invalida a sessão do usuário no Supabase - */ - -import type { Handler, HandlerEvent } from "@netlify/functions"; - -const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co"; -const SUPABASE_ANON_KEY = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ"; - -export const handler: Handler = async (event: HandlerEvent) => { - const headers = { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "Content-Type, Authorization", - "Access-Control-Allow-Methods": "POST, OPTIONS", - }; - - if (event.httpMethod === "OPTIONS") { - return { - statusCode: 200, - headers, - body: "", - }; - } - - if (event.httpMethod !== "POST") { - return { - statusCode: 405, - headers, - body: JSON.stringify({ error: "Method Not Allowed" }), - }; - } - - try { - // Pega o Bearer token do header - const authHeader = - event.headers.authorization || event.headers.Authorization; - - if (!authHeader) { - return { - statusCode: 401, - headers, - body: JSON.stringify({ error: "Token não fornecido" }), - }; - } - - // Faz logout no Supabase - const response = await fetch(`${SUPABASE_URL}/auth/v1/logout`, { - method: "POST", - headers: { - "Content-Type": "application/json", - apikey: SUPABASE_ANON_KEY, - Authorization: authHeader, - }, - }); - - // Logout retorna 204 No Content (sem body) - if (response.status === 204) { - return { - statusCode: 204, - headers, - body: "", - }; - } - - // Se não for 204, retorna o body da resposta - const data = await response.text(); - - return { - statusCode: response.status, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: data || "{}", - }; - } catch (error) { - console.error("Erro no logout:", error); - - return { - statusCode: 500, - headers, - body: JSON.stringify({ - error: "Erro interno no servidor", - message: error instanceof Error ? error.message : "Erro desconhecido", - }), - }; - } -}; diff --git a/MEDICONNECT 2/netlify/functions/auth-magic-link.ts b/MEDICONNECT 2/netlify/functions/auth-magic-link.ts deleted file mode 100644 index bf2d32bf4..000000000 --- a/MEDICONNECT 2/netlify/functions/auth-magic-link.ts +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Netlify Function: Magic Link - * Envia link de autenticação sem senha por email - */ - -import type { Handler, HandlerEvent } from "@netlify/functions"; - -// Constantes da API (protegidas no backend) -const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co"; -const SUPABASE_ANON_KEY = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ"; - -interface MagicLinkRequest { - email: string; - redirect_url?: string; -} - -export const handler: Handler = async (event: HandlerEvent) => { - // CORS headers - const headers = { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "Content-Type, Authorization", - "Access-Control-Allow-Methods": "POST, OPTIONS", - }; - - // Handle preflight - if (event.httpMethod === "OPTIONS") { - return { - statusCode: 200, - headers, - body: "", - }; - } - - // Apenas POST é permitido - if (event.httpMethod !== "POST") { - return { - statusCode: 405, - headers, - body: JSON.stringify({ error: "Method Not Allowed" }), - }; - } - - try { - // Parse body - const body: MagicLinkRequest = JSON.parse(event.body || "{}"); - - if (!body.email) { - return { - statusCode: 400, - headers, - body: JSON.stringify({ error: "Email é obrigatório" }), - }; - } - - // Faz requisição para API Supabase COM a apikey protegida - const response = await fetch(`${SUPABASE_URL}/auth/v1/otp`, { - method: "POST", - headers: { - "Content-Type": "application/json", - apikey: SUPABASE_ANON_KEY, - }, - body: JSON.stringify({ - email: body.email, - options: { - emailRedirectTo: - body.redirect_url || - "https://mediconnectbrasil.netlify.app/auth/callback", - }, - }), - }); - - const data = await response.json(); - - // Repassa a resposta para o frontend - return { - statusCode: response.status, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }; - } catch (error) { - console.error("[auth-magic-link] Erro:", error); - return { - statusCode: 500, - headers, - body: JSON.stringify({ - error: "Erro ao enviar magic link", - details: error instanceof Error ? error.message : "Erro desconhecido", - }), - }; - } -}; diff --git a/MEDICONNECT 2/netlify/functions/auth-refresh.ts b/MEDICONNECT 2/netlify/functions/auth-refresh.ts deleted file mode 100644 index 84eca6a30..000000000 --- a/MEDICONNECT 2/netlify/functions/auth-refresh.ts +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Netlify Function: Refresh Token - * Renova o access token usando o refresh token - */ - -import type { Handler, HandlerEvent } from "@netlify/functions"; - -const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co"; -const SUPABASE_ANON_KEY = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ"; - -interface RefreshTokenRequest { - refresh_token: string; -} - -export const handler: Handler = async (event: HandlerEvent) => { - const headers = { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "Content-Type, Authorization", - "Access-Control-Allow-Methods": "POST, OPTIONS", - }; - - if (event.httpMethod === "OPTIONS") { - return { - statusCode: 200, - headers, - body: "", - }; - } - - if (event.httpMethod !== "POST") { - return { - statusCode: 405, - headers, - body: JSON.stringify({ error: "Method Not Allowed" }), - }; - } - - try { - const body: RefreshTokenRequest = JSON.parse(event.body || "{}"); - - if (!body.refresh_token) { - return { - statusCode: 400, - headers, - body: JSON.stringify({ error: "Refresh token é obrigatório" }), - }; - } - - // Faz requisição para renovar token no Supabase - const response = await fetch( - `${SUPABASE_URL}/auth/v1/token?grant_type=refresh_token`, - { - method: "POST", - headers: { - "Content-Type": "application/json", - apikey: SUPABASE_ANON_KEY, - }, - body: JSON.stringify({ - refresh_token: body.refresh_token, - }), - } - ); - - const data = await response.json(); - - return { - statusCode: response.status, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }; - } catch (error) { - console.error("Erro ao renovar token:", error); - - return { - statusCode: 500, - headers, - body: JSON.stringify({ - error: "Erro interno no servidor", - message: error instanceof Error ? error.message : "Erro desconhecido", - }), - }; - } -}; diff --git a/MEDICONNECT 2/netlify/functions/auth-user.ts b/MEDICONNECT 2/netlify/functions/auth-user.ts deleted file mode 100644 index 1203887e5..000000000 --- a/MEDICONNECT 2/netlify/functions/auth-user.ts +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Netlify Function: Auth User - * GET /auth/v1/user - Retorna dados do usuário autenticado - */ - -import type { Handler, HandlerEvent } from "@netlify/functions"; - -const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co"; -const SUPABASE_ANON_KEY = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ"; - -export const handler: Handler = async (event: HandlerEvent) => { - const headers = { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "Content-Type, Authorization", - "Access-Control-Allow-Methods": "GET, OPTIONS", - }; - - if (event.httpMethod === "OPTIONS") { - return { - statusCode: 200, - headers, - body: "", - }; - } - - try { - const authHeader = - event.headers.authorization || event.headers.Authorization; - - if (!authHeader) { - return { - statusCode: 401, - headers, - body: JSON.stringify({ error: "Token não fornecido" }), - }; - } - - if (event.httpMethod === "GET") { - const response = await fetch(`${SUPABASE_URL}/auth/v1/user`, { - method: "GET", - headers: { - apikey: SUPABASE_ANON_KEY, - Authorization: authHeader, - }, - }); - - const data = await response.json(); - - return { - statusCode: response.status, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }; - } - - return { - statusCode: 405, - headers, - body: JSON.stringify({ error: "Method Not Allowed" }), - }; - } catch (error) { - console.error("Erro na API de auth user:", error); - - return { - statusCode: 500, - headers, - body: JSON.stringify({ - error: "Erro interno no servidor", - message: error instanceof Error ? error.message : "Erro desconhecido", - }), - }; - } -}; diff --git a/MEDICONNECT 2/netlify/functions/avatars-delete.ts b/MEDICONNECT 2/netlify/functions/avatars-delete.ts deleted file mode 100644 index 13e18cb94..000000000 --- a/MEDICONNECT 2/netlify/functions/avatars-delete.ts +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Netlify Function: Delete Avatar - * DELETE /storage/v1/object/avatars/{userId}/avatar - */ - -import type { Handler, HandlerEvent } from "@netlify/functions"; - -const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co"; -const SUPABASE_ANON_KEY = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ"; - -export const handler: Handler = async (event: HandlerEvent) => { - const headers = { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "Content-Type, Authorization", - "Access-Control-Allow-Methods": "DELETE, OPTIONS", - }; - - if (event.httpMethod === "OPTIONS") { - return { - statusCode: 200, - headers, - body: "", - }; - } - - if (event.httpMethod !== "DELETE") { - return { - statusCode: 405, - headers, - body: JSON.stringify({ error: "Method Not Allowed" }), - }; - } - - try { - const authHeader = - event.headers.authorization || event.headers.Authorization; - - if (!authHeader) { - return { - statusCode: 401, - headers, - body: JSON.stringify({ error: "Token não fornecido" }), - }; - } - - const userId = event.queryStringParameters?.userId; - - if (!userId) { - return { - statusCode: 400, - headers, - body: JSON.stringify({ error: "userId é obrigatório" }), - }; - } - - const response = await fetch( - `${SUPABASE_URL}/storage/v1/object/avatars/${userId}/avatar`, - { - method: "DELETE", - headers: { - apikey: SUPABASE_ANON_KEY, - Authorization: authHeader, - }, - } - ); - - // DELETE pode retornar 200 com body vazio - const contentType = response.headers.get("content-type"); - const data = contentType?.includes("application/json") - ? await response.json() - : {}; - - return { - statusCode: response.status, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }; - } catch (error) { - console.error("Erro ao deletar avatar:", error); - - return { - statusCode: 500, - headers, - body: JSON.stringify({ - error: "Erro interno no servidor", - message: error instanceof Error ? error.message : "Erro desconhecido", - }), - }; - } -}; diff --git a/MEDICONNECT 2/netlify/functions/avatars-upload.ts b/MEDICONNECT 2/netlify/functions/avatars-upload.ts deleted file mode 100644 index 854ee40cb..000000000 --- a/MEDICONNECT 2/netlify/functions/avatars-upload.ts +++ /dev/null @@ -1,145 +0,0 @@ -/** - * Netlify Function: Upload Avatar - * POST /storage/v1/object/avatars/{userId}/avatar - * - * Aceita JSON com base64 para simplificar o upload via Netlify Functions - */ - -import type { Handler, HandlerEvent } from "@netlify/functions"; - -const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co"; -const SUPABASE_ANON_KEY = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ"; - -export const handler: Handler = async (event: HandlerEvent) => { - const headers = { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "Content-Type, Authorization", - "Access-Control-Allow-Methods": "POST, OPTIONS", - }; - - if (event.httpMethod === "OPTIONS") { - return { - statusCode: 200, - headers, - body: "", - }; - } - - if (event.httpMethod !== "POST") { - return { - statusCode: 405, - headers, - body: JSON.stringify({ error: "Method Not Allowed" }), - }; - } - - try { - const authHeader = - event.headers.authorization || event.headers.Authorization; - - if (!authHeader) { - return { - statusCode: 401, - headers, - body: JSON.stringify({ error: "Token não fornecido" }), - }; - } - - // Extrai userId do query string - const userId = event.queryStringParameters?.userId; - - if (!userId) { - return { - statusCode: 400, - headers, - body: JSON.stringify({ error: "userId é obrigatório" }), - }; - } - - // Parse JSON body com base64 - let fileData: string; - let contentType: string; - - try { - const body = JSON.parse(event.body || "{}"); - fileData = body.fileData; // base64 string - contentType = body.contentType || "image/jpeg"; - - if (!fileData) { - return { - statusCode: 400, - headers, - body: JSON.stringify({ error: "fileData (base64) é obrigatório" }), - }; - } - } catch { - return { - statusCode: 400, - headers, - body: JSON.stringify({ - error: "Body deve ser JSON válido com fileData em base64", - }), - }; - } - - // Converte base64 para Buffer - const buffer = Buffer.from(fileData, "base64"); - - // Upload para Supabase Storage - const response = await fetch( - `${SUPABASE_URL}/storage/v1/object/avatars/${userId}/avatar`, - { - method: "POST", - headers: { - apikey: SUPABASE_ANON_KEY, - Authorization: authHeader, - "Content-Type": contentType, - "x-upsert": "true", // Sobrescreve se já existir - }, - body: buffer, - } - ); - - const data = await response.json(); - - if (!response.ok) { - console.error("Erro do Supabase:", data); - return { - statusCode: response.status, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - error: data.error || "Erro ao fazer upload no Supabase", - details: data, - }), - }; - } - - return { - statusCode: 200, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - message: "Upload realizado com sucesso", - path: data.Key || data.path, - fullPath: data.Key || data.path, - }), - }; - } catch (error) { - console.error("Erro no upload do avatar:", error); - - return { - statusCode: 500, - headers, - body: JSON.stringify({ - error: "Erro interno no servidor", - message: error instanceof Error ? error.message : "Erro desconhecido", - }), - }; - } -}; diff --git a/MEDICONNECT 2/netlify/functions/consultas.ts b/MEDICONNECT 2/netlify/functions/consultas.ts deleted file mode 100644 index b22e39ab9..000000000 --- a/MEDICONNECT 2/netlify/functions/consultas.ts +++ /dev/null @@ -1,163 +0,0 @@ -import type { Handler, HandlerEvent } from "@netlify/functions"; - -const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co"; -const SUPABASE_ANON_KEY = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ"; - -export const handler: Handler = async (event: HandlerEvent) => { - const headers = { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "Content-Type, Authorization", - "Access-Control-Allow-Methods": "GET, POST, PATCH, DELETE, OPTIONS", - }; - - if (event.httpMethod === "OPTIONS") { - return { statusCode: 200, headers, body: "" }; - } - - try { - const authHeader = - event.headers.authorization || event.headers.Authorization; - if (!authHeader) { - return { - statusCode: 401, - headers, - body: JSON.stringify({ error: "Token n�o fornecido" }), - }; - } - - const pathParts = event.path.split("/"); - const appointmentId = - pathParts[pathParts.length - 1] !== "consultas" - ? pathParts[pathParts.length - 1] - : null; - - if (event.httpMethod === "GET") { - let url = `${SUPABASE_URL}/rest/v1/appointments`; - if (appointmentId && appointmentId !== "consultas") { - url += `?id=eq.${appointmentId}&select=*`; - } else if (event.queryStringParameters) { - const params = new URLSearchParams( - event.queryStringParameters as Record - ); - url += `?${params.toString()}`; - if (!params.has("select")) { - url += url.includes("?") ? "&select=*" : "?select=*"; - } - } else { - url += "?select=*"; - } - - const response = await fetch(url, { - method: "GET", - headers: { apikey: SUPABASE_ANON_KEY, Authorization: authHeader }, - }); - let data = await response.json(); - if ( - appointmentId && - appointmentId !== "consultas" && - Array.isArray(data) && - data.length > 0 - ) { - data = data[0]; - } - return { - statusCode: response.status, - headers: { ...headers, "Content-Type": "application/json" }, - body: JSON.stringify(data), - }; - } - - if (event.httpMethod === "POST") { - const body = JSON.parse(event.body || "{}"); - if (!body.patient_id || !body.doctor_id || !body.scheduled_at) { - return { - statusCode: 400, - headers, - body: JSON.stringify({ - error: "Campos obrigat�rios: patient_id, doctor_id, scheduled_at", - }), - }; - } - const response = await fetch(`${SUPABASE_URL}/rest/v1/appointments`, { - method: "POST", - headers: { - apikey: SUPABASE_ANON_KEY, - Authorization: authHeader, - "Content-Type": "application/json", - Prefer: "return=representation", - }, - body: JSON.stringify(body), - }); - let data = await response.json(); - if (Array.isArray(data) && data.length > 0) data = data[0]; - return { - statusCode: response.status, - headers: { ...headers, "Content-Type": "application/json" }, - body: JSON.stringify(data), - }; - } - - if (event.httpMethod === "PATCH") { - if (!appointmentId || appointmentId === "consultas") { - return { - statusCode: 400, - headers, - body: JSON.stringify({ error: "ID do agendamento � obrigat�rio" }), - }; - } - const body = JSON.parse(event.body || "{}"); - const response = await fetch( - `${SUPABASE_URL}/rest/v1/appointments?id=eq.${appointmentId}`, - { - method: "PATCH", - headers: { - apikey: SUPABASE_ANON_KEY, - Authorization: authHeader, - "Content-Type": "application/json", - Prefer: "return=representation", - }, - body: JSON.stringify(body), - } - ); - let data = await response.json(); - if (Array.isArray(data) && data.length > 0) data = data[0]; - return { - statusCode: response.status, - headers: { ...headers, "Content-Type": "application/json" }, - body: JSON.stringify(data), - }; - } - - if (event.httpMethod === "DELETE") { - if (!appointmentId || appointmentId === "consultas") { - return { - statusCode: 400, - headers, - body: JSON.stringify({ error: "ID do agendamento � obrigat�rio" }), - }; - } - const response = await fetch( - `${SUPABASE_URL}/rest/v1/appointments?id=eq.${appointmentId}`, - { - method: "DELETE", - headers: { apikey: SUPABASE_ANON_KEY, Authorization: authHeader }, - } - ); - return { statusCode: response.status, headers, body: "" }; - } - - return { - statusCode: 405, - headers, - body: JSON.stringify({ error: "Method Not Allowed" }), - }; - } catch (error) { - console.error("Erro:", error); - return { - statusCode: 500, - headers, - body: JSON.stringify({ error: "Erro interno" }), - }; - } -}; diff --git a/MEDICONNECT 2/netlify/functions/create-doctor.ts b/MEDICONNECT 2/netlify/functions/create-doctor.ts deleted file mode 100644 index a8ecad447..000000000 --- a/MEDICONNECT 2/netlify/functions/create-doctor.ts +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Netlify Function: Create Doctor - * POST /create-doctor - Cria registro de médico com validações - * Não cria auth user - apenas registro na tabela doctors - */ - -import type { Handler, HandlerEvent } from "@netlify/functions"; - -const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co"; -const SUPABASE_ANON_KEY = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ"; - -export const handler: Handler = async (event: HandlerEvent) => { - const headers = { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "Content-Type, Authorization", - "Access-Control-Allow-Methods": "POST, OPTIONS", - }; - - if (event.httpMethod === "OPTIONS") { - return { - statusCode: 200, - headers, - body: "", - }; - } - - if (event.httpMethod !== "POST") { - return { - statusCode: 405, - headers, - body: JSON.stringify({ error: "Method Not Allowed" }), - }; - } - - try { - const authHeader = - event.headers.authorization || event.headers.Authorization; - - if (!authHeader) { - return { - statusCode: 401, - headers, - body: JSON.stringify({ error: "Token não fornecido" }), - }; - } - - const body = JSON.parse(event.body || "{}"); - - // Validação dos campos obrigatórios - if ( - !body.email || - !body.full_name || - !body.cpf || - !body.crm || - !body.crm_uf - ) { - return { - statusCode: 400, - headers, - body: JSON.stringify({ - error: "Campos obrigatórios: email, full_name, cpf, crm, crm_uf", - }), - }; - } - - // Chama a Edge Function do Supabase para criar médico - const response = await fetch(`${SUPABASE_URL}/functions/v1/create-doctor`, { - method: "POST", - headers: { - apikey: SUPABASE_ANON_KEY, - Authorization: authHeader, - "Content-Type": "application/json", - }, - body: JSON.stringify(body), - }); - - const data = await response.json(); - - return { - statusCode: response.status, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }; - } catch (error) { - console.error("Erro na API de create doctor:", error); - - return { - statusCode: 500, - headers, - body: JSON.stringify({ - error: "Erro interno no servidor", - message: error instanceof Error ? error.message : "Erro desconhecido", - }), - }; - } -}; diff --git a/MEDICONNECT 2/netlify/functions/create-patient.ts b/MEDICONNECT 2/netlify/functions/create-patient.ts deleted file mode 100644 index cc447906f..000000000 --- a/MEDICONNECT 2/netlify/functions/create-patient.ts +++ /dev/null @@ -1,102 +0,0 @@ -/** - * Netlify Function: Create Patient - * POST /create-patient - Cria registro de paciente diretamente - * Não cria auth user - apenas registro na tabela patients - */ - -import type { Handler, HandlerEvent } from "@netlify/functions"; - -const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co"; -const SUPABASE_ANON_KEY = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ"; - -export const handler: Handler = async (event: HandlerEvent) => { - const headers = { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "Content-Type, Authorization", - "Access-Control-Allow-Methods": "POST, OPTIONS", - }; - - if (event.httpMethod === "OPTIONS") { - return { - statusCode: 200, - headers, - body: "", - }; - } - - if (event.httpMethod !== "POST") { - return { - statusCode: 405, - headers, - body: JSON.stringify({ error: "Method Not Allowed" }), - }; - } - - try { - const authHeader = - event.headers.authorization || event.headers.Authorization; - - if (!authHeader) { - return { - statusCode: 401, - headers, - body: JSON.stringify({ error: "Token não fornecido" }), - }; - } - - const body = JSON.parse(event.body || "{}"); - - // Validação dos campos obrigatórios - if ( - !body.full_name || - !body.cpf || - !body.email || - !body.phone_mobile || - !body.created_by - ) { - return { - statusCode: 400, - headers, - body: JSON.stringify({ - error: - "Campos obrigatórios: full_name, cpf, email, phone_mobile, created_by", - }), - }; - } - - // Chama REST API do Supabase para criar paciente diretamente - const response = await fetch(`${SUPABASE_URL}/rest/v1/patients`, { - method: "POST", - headers: { - apikey: SUPABASE_ANON_KEY, - Authorization: authHeader, - "Content-Type": "application/json", - Prefer: "return=representation", - }, - body: JSON.stringify(body), - }); - - const data = await response.json(); - - return { - statusCode: response.status, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }; - } catch (error) { - console.error("Erro na API de create patient:", error); - - return { - statusCode: 500, - headers, - body: JSON.stringify({ - error: "Erro interno no servidor", - message: error instanceof Error ? error.message : "Erro desconhecido", - }), - }; - } -}; diff --git a/MEDICONNECT 2/netlify/functions/create-user-with-password.ts b/MEDICONNECT 2/netlify/functions/create-user-with-password.ts deleted file mode 100644 index 882d3ece2..000000000 --- a/MEDICONNECT 2/netlify/functions/create-user-with-password.ts +++ /dev/null @@ -1,223 +0,0 @@ -/** - * Netlify Function: Create User With Password - * POST /create-user-with-password - Cria usuário com senha - * Usa Edge Function do Supabase (não Admin API) - * Requer permissão de admin, gestor ou secretaria - */ - -import type { Handler, HandlerEvent } from "@netlify/functions"; - -const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co"; -const SUPABASE_ANON_KEY = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ"; - -export const handler: Handler = async (event: HandlerEvent) => { - const headers = { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "Content-Type, Authorization, apikey", - "Access-Control-Allow-Methods": "POST, OPTIONS", - }; - - if (event.httpMethod === "OPTIONS") { - return { - statusCode: 200, - headers, - body: "", - }; - } - - if (event.httpMethod !== "POST") { - return { - statusCode: 405, - headers, - body: JSON.stringify({ error: "Method Not Allowed" }), - }; - } - - try { - const authHeader = - event.headers.authorization || event.headers.Authorization; - - if (!authHeader) { - console.error("[create-user-with-password] Token não fornecido!"); - return { - statusCode: 401, - headers, - body: JSON.stringify({ - error: "Token de autenticação é obrigatório", - }), - }; - } - - const body = JSON.parse(event.body || "{}"); - - console.log( - "[create-user-with-password] Recebido:", - JSON.stringify({ ...body, password: "***" }, null, 2) - ); - - // Validações - if (!body.email || !body.password || !body.full_name) { - return { - statusCode: 400, - headers, - body: JSON.stringify({ - error: "Campos obrigatórios: email, password, full_name", - }), - }; - } - - if (body.password.length < 6) { - return { - statusCode: 400, - headers, - body: JSON.stringify({ - error: "Senha deve ter no mínimo 6 caracteres", - }), - }; - } - - // 1. Criar usuário via Edge Function do Supabase - console.log( - "[create-user-with-password] Chamando Edge Function do Supabase..." - ); - console.log( - "[create-user-with-password] URL:", - `${SUPABASE_URL}/functions/v1/create-user` - ); - console.log("[create-user-with-password] Payload:", { - email: body.email, - has_password: !!body.password, - full_name: body.full_name, - }); - - const createUserResponse = await fetch( - `${SUPABASE_URL}/functions/v1/create-user`, - { - method: "POST", - headers: { - apikey: SUPABASE_ANON_KEY, - Authorization: authHeader, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - email: body.email, - password: body.password, - full_name: body.full_name, - phone: body.phone || null, - role: body.role || "user", - }), - } - ); - - console.log( - "[create-user-with-password] Status da resposta:", - createUserResponse.status - ); - console.log( - "[create-user-with-password] Status text:", - createUserResponse.statusText - ); - - // Sempre tenta ler a resposta como JSON - let responseData; - try { - responseData = await createUserResponse.json(); - console.log( - "[create-user-with-password] Resposta JSON:", - JSON.stringify(responseData, null, 2) - ); - } catch (error) { - const responseText = await createUserResponse.text(); - console.error( - "[create-user-with-password] Resposta não é JSON:", - responseText - ); - console.error("[create-user-with-password] Erro ao parsear JSON:", error); - return { - statusCode: 500, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - error: "Erro ao processar resposta do Supabase", - details: responseText, - }), - }; - } - - if (!createUserResponse.ok) { - console.error( - "[create-user-with-password] Erro ao criar usuário:", - JSON.stringify(responseData, null, 2) - ); - return { - statusCode: createUserResponse.status, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - error: - responseData.msg || responseData.message || "Erro ao criar usuário", - details: responseData, - }), - }; - } - - // Verificar se a Edge Function retornou sucesso - if (!responseData.success) { - console.error( - "[create-user-with-password] Edge Function retornou erro:", - JSON.stringify(responseData, null, 2) - ); - return { - statusCode: 400, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - error: responseData.error || "Erro ao criar usuário", - details: responseData, - }), - }; - } - - const userData = responseData.user; - console.log( - "[create-user-with-password] Usuário criado com sucesso:", - userData.id - ); - console.log( - "[create-user-with-password] Resposta completa:", - JSON.stringify(responseData, null, 2) - ); - - // A Edge Function já cria o perfil e atribui a role automaticamente - // Retornar sucesso - return { - statusCode: 201, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - success: true, - user: userData, - message: responseData.message || "Usuário criado com sucesso", - }), - }; - } catch (error) { - console.error("[create-user-with-password] Erro:", error); - return { - statusCode: 500, - headers, - body: JSON.stringify({ - error: "Erro interno no servidor", - message: error instanceof Error ? error.message : "Erro desconhecido", - }), - }; - } -}; diff --git a/MEDICONNECT 2/netlify/functions/create-user.ts b/MEDICONNECT 2/netlify/functions/create-user.ts deleted file mode 100644 index eec83df16..000000000 --- a/MEDICONNECT 2/netlify/functions/create-user.ts +++ /dev/null @@ -1,120 +0,0 @@ -/** - * Netlify Function: Create User - * POST /create-user - Cria novo usuário no sistema - * Requer permissão de admin, gestor ou secretaria - * Envia magic link automaticamente para o email - */ - -import type { Handler, HandlerEvent } from "@netlify/functions"; - -const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co"; -const SUPABASE_ANON_KEY = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ"; - -export const handler: Handler = async (event: HandlerEvent) => { - const headers = { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "Content-Type, Authorization", - "Access-Control-Allow-Methods": "POST, OPTIONS", - }; - - if (event.httpMethod === "OPTIONS") { - return { - statusCode: 200, - headers, - body: "", - }; - } - - try { - const authHeader = - event.headers.authorization || event.headers.Authorization; - - // create-user pode ser chamado SEM autenticação (para auto-registro) - // Se houver token, será usado; se não houver, usa apenas anon key - - if (event.httpMethod === "POST") { - const body = JSON.parse(event.body || "{}"); - - console.log( - "[create-user] Recebido body:", - JSON.stringify(body, null, 2) - ); - console.log("[create-user] Auth header presente?", !!authHeader); - - // Validação dos campos obrigatórios - if (!body.email || !body.full_name) { - return { - statusCode: 400, - headers, - body: JSON.stringify({ - error: "Campos obrigatórios: email, full_name", - }), - }; - } - - if (!body.role && (!body.roles || body.roles.length === 0)) { - return { - statusCode: 400, - headers, - body: JSON.stringify({ - error: "É necessário fornecer role ou roles", - }), - }; - } - - // Chama a Edge Function do Supabase para criar usuário - const fetchHeaders: Record = { - apikey: SUPABASE_ANON_KEY, - "Content-Type": "application/json", - // Se houver token de usuário autenticado, usa ele; senão usa anon key - Authorization: authHeader || `Bearer ${SUPABASE_ANON_KEY}`, - }; - - console.log("[create-user] Chamando Supabase com headers:", { - hasAuthHeader: !!authHeader, - hasApikey: !!fetchHeaders.apikey, - authType: authHeader ? "User Token" : "Anon Key", - }); - - const response = await fetch(`${SUPABASE_URL}/functions/v1/create-user`, { - method: "POST", - headers: fetchHeaders, - body: JSON.stringify(body), - }); - - const data = await response.json(); - - console.log("[create-user] Resposta do Supabase:", { - status: response.status, - data: JSON.stringify(data, null, 2), - }); - - return { - statusCode: response.status, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }; - } - - return { - statusCode: 405, - headers, - body: JSON.stringify({ error: "Method Not Allowed" }), - }; - } catch (error) { - console.error("Erro na API de create user:", error); - - return { - statusCode: 500, - headers, - body: JSON.stringify({ - error: "Erro interno no servidor", - message: error instanceof Error ? error.message : "Erro desconhecido", - }), - }; - } -}; diff --git a/MEDICONNECT 2/netlify/functions/delete-user.ts b/MEDICONNECT 2/netlify/functions/delete-user.ts deleted file mode 100644 index 972efa383..000000000 --- a/MEDICONNECT 2/netlify/functions/delete-user.ts +++ /dev/null @@ -1,127 +0,0 @@ -/** - * Netlify Function: Delete User (Hard Delete) - * POST /delete-user - Deleta usuário permanentemente - * ⚠️ OPERAÇÃO IRREVERSÍVEL - Use com extremo cuidado! - * Requer permissão de admin ou gestor - * Usa Admin API do Supabase - */ - -import type { Handler, HandlerEvent } from "@netlify/functions"; - -const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co"; -const SUPABASE_SERVICE_ROLE_KEY = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTc1NDk1NDM2OSwiZXhwIjoyMDcwNTMwMzY5fQ.Dez8PQkV8vWv7VkL_fZe-lY-Xs9P5VptNvRRnhkxoXw"; - -export const handler: Handler = async (event: HandlerEvent) => { - const headers = { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "Content-Type, Authorization, apikey", - "Access-Control-Allow-Methods": "POST, OPTIONS", - }; - - if (event.httpMethod === "OPTIONS") { - return { - statusCode: 200, - headers, - body: "", - }; - } - - if (event.httpMethod !== "POST") { - return { - statusCode: 405, - headers, - body: JSON.stringify({ error: "Method Not Allowed" }), - }; - } - - try { - const authHeader = - event.headers.authorization || event.headers.Authorization; - - if (!authHeader) { - return { - statusCode: 401, - headers, - body: JSON.stringify({ - error: "Token de autenticação é obrigatório", - }), - }; - } - - const body = JSON.parse(event.body || "{}"); - - console.log("[delete-user] ATENÇÃO: Tentativa de hard delete:", { - userId: body.userId, - requestedBy: "via Netlify Function", - }); - - // Validação - if (!body.userId) { - return { - statusCode: 400, - headers, - body: JSON.stringify({ - error: "userId é obrigatório", - }), - }; - } - - // TODO: Aqui deveria verificar se o usuário tem permissão de admin/gestor - // Verificando o token JWT que foi passado - - // Deletar usuário via Admin API do Supabase - const response = await fetch( - `${SUPABASE_URL}/auth/v1/admin/users/${body.userId}`, - { - method: "DELETE", - headers: { - apikey: SUPABASE_SERVICE_ROLE_KEY, - Authorization: `Bearer ${SUPABASE_SERVICE_ROLE_KEY}`, - "Content-Type": "application/json", - }, - } - ); - - if (response.ok) { - console.log("[delete-user] Usuário deletado com sucesso:", body.userId); - return { - statusCode: 200, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - success: true, - message: "Usuário deletado permanentemente", - userId: body.userId, - }), - }; - } - - const errorData = await response.json(); - console.error("[delete-user] Erro ao deletar:", errorData); - - return { - statusCode: response.status, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - error: errorData.msg || errorData.message || "Erro ao deletar usuário", - details: errorData, - }), - }; - } catch (error) { - console.error("[delete-user] Erro:", error); - return { - statusCode: 500, - headers, - body: JSON.stringify({ - error: "Erro interno no servidor", - message: error instanceof Error ? error.message : "Erro desconhecido", - }), - }; - } -}; diff --git a/MEDICONNECT 2/netlify/functions/doctor-availability.ts b/MEDICONNECT 2/netlify/functions/doctor-availability.ts deleted file mode 100644 index e793b27d3..000000000 --- a/MEDICONNECT 2/netlify/functions/doctor-availability.ts +++ /dev/null @@ -1,217 +0,0 @@ -/** - * Netlify Function: doctor-availability - * - * Proxy para operações de disponibilidade dos médicos - * GET: Lista disponibilidades - * POST: Criar disponibilidade - * PATCH: Atualizar disponibilidade - * DELETE: Deletar disponibilidade - */ - -const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co"; -const SUPABASE_API_KEY = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ"; - -export default async (req: Request) => { - // Permitir CORS - if (req.method === "OPTIONS") { - return new Response(null, { - status: 204, - headers: { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Methods": "GET, POST, PATCH, DELETE, OPTIONS", - "Access-Control-Allow-Headers": "Content-Type, Authorization", - }, - }); - } - - try { - const url = new URL(req.url); - const authHeader = req.headers.get("Authorization"); - - // Extrair ID do path se existir - const pathParts = url.pathname.split("/"); - const availabilityId = pathParts[pathParts.length - 1]; - - // GET: Listar disponibilidades - if (req.method === "GET") { - const select = url.searchParams.get("select") || "*"; - const doctor_id = url.searchParams.get("doctor_id"); - const active = url.searchParams.get("active"); - - const queryParams = new URLSearchParams(); - queryParams.append("select", select); - if (doctor_id) queryParams.append("doctor_id", `eq.${doctor_id}`); - if (active !== null) queryParams.append("active", `eq.${active}`); - - const supabaseUrl = `${SUPABASE_URL}/rest/v1/doctor_availability?${queryParams}`; - - const headers: HeadersInit = { - apikey: SUPABASE_API_KEY, - "Content-Type": "application/json", - }; - - if (authHeader) { - headers["Authorization"] = authHeader; - } - - const response = await fetch(supabaseUrl, { - method: "GET", - headers, - }); - - const data = await response.json(); - - return new Response(JSON.stringify(data), { - status: response.status, - headers: { - "Content-Type": "application/json", - "Access-Control-Allow-Origin": "*", - }, - }); - } - - // POST: Criar disponibilidade - if (req.method === "POST") { - const body = await req.json(); - - const supabaseUrl = `${SUPABASE_URL}/rest/v1/doctor_availability`; - - const headers: HeadersInit = { - apikey: SUPABASE_API_KEY, - "Content-Type": "application/json", - Prefer: "return=representation", - }; - - if (authHeader) { - headers["Authorization"] = authHeader; - } - - const response = await fetch(supabaseUrl, { - method: "POST", - headers, - body: JSON.stringify(body), - }); - - const data = await response.json(); - - return new Response(JSON.stringify(data), { - status: response.status, - headers: { - "Content-Type": "application/json", - "Access-Control-Allow-Origin": "*", - }, - }); - } - - // PATCH: Atualizar disponibilidade - if (req.method === "PATCH") { - if (!availabilityId || availabilityId === "doctor-availability") { - return new Response( - JSON.stringify({ error: "Availability ID is required" }), - { - status: 400, - headers: { - "Content-Type": "application/json", - "Access-Control-Allow-Origin": "*", - }, - } - ); - } - - const body = await req.json(); - - const supabaseUrl = `${SUPABASE_URL}/rest/v1/doctor_availability?id=eq.${availabilityId}`; - - const headers: HeadersInit = { - apikey: SUPABASE_API_KEY, - "Content-Type": "application/json", - Prefer: "return=representation", - }; - - if (authHeader) { - headers["Authorization"] = authHeader; - } - - const response = await fetch(supabaseUrl, { - method: "PATCH", - headers, - body: JSON.stringify(body), - }); - - const data = await response.json(); - const result = Array.isArray(data) && data.length > 0 ? data[0] : data; - - return new Response(JSON.stringify(result), { - status: response.status, - headers: { - "Content-Type": "application/json", - "Access-Control-Allow-Origin": "*", - }, - }); - } - - // DELETE: Deletar disponibilidade - if (req.method === "DELETE") { - if (!availabilityId || availabilityId === "doctor-availability") { - return new Response( - JSON.stringify({ error: "Availability ID is required" }), - { - status: 400, - headers: { - "Content-Type": "application/json", - "Access-Control-Allow-Origin": "*", - }, - } - ); - } - - const supabaseUrl = `${SUPABASE_URL}/rest/v1/doctor_availability?id=eq.${availabilityId}`; - - const headers: HeadersInit = { - apikey: SUPABASE_API_KEY, - "Content-Type": "application/json", - }; - - if (authHeader) { - headers["Authorization"] = authHeader; - } - - const response = await fetch(supabaseUrl, { - method: "DELETE", - headers, - }); - - return new Response(null, { - status: response.status, - headers: { - "Access-Control-Allow-Origin": "*", - }, - }); - } - - // Método não suportado - return new Response(JSON.stringify({ error: "Method not allowed" }), { - status: 405, - headers: { - "Content-Type": "application/json", - "Access-Control-Allow-Origin": "*", - }, - }); - } catch (error) { - console.error("Error in doctor-availability function:", error); - return new Response( - JSON.stringify({ - error: "Internal server error", - details: error instanceof Error ? error.message : "Unknown error", - }), - { - status: 500, - headers: { - "Content-Type": "application/json", - "Access-Control-Allow-Origin": "*", - }, - } - ); - } -}; diff --git a/MEDICONNECT 2/netlify/functions/doctor-exceptions.ts b/MEDICONNECT 2/netlify/functions/doctor-exceptions.ts deleted file mode 100644 index b5dc31541..000000000 --- a/MEDICONNECT 2/netlify/functions/doctor-exceptions.ts +++ /dev/null @@ -1,169 +0,0 @@ -/** - * Netlify Function: doctor-exceptions - * - * Proxy para operações de exceções na agenda dos médicos - * GET: Lista exceções - * POST: Criar exceção - * DELETE: Deletar exceção - */ - -const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co"; -const SUPABASE_API_KEY = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ"; - -export default async (req: Request) => { - // Permitir CORS - if (req.method === "OPTIONS") { - return new Response(null, { - status: 204, - headers: { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Methods": "GET, POST, PATCH, DELETE, OPTIONS", - "Access-Control-Allow-Headers": "Content-Type, Authorization", - }, - }); - } - - try { - const url = new URL(req.url); - const authHeader = req.headers.get("Authorization"); - - // Extrair ID do path se existir - const pathParts = url.pathname.split("/"); - const exceptionId = pathParts[pathParts.length - 1]; - - // GET: Listar exceções - if (req.method === "GET") { - const select = url.searchParams.get("select") || "*"; - const doctor_id = url.searchParams.get("doctor_id"); - const date = url.searchParams.get("date"); - - const queryParams = new URLSearchParams(); - queryParams.append("select", select); - if (doctor_id) queryParams.append("doctor_id", `eq.${doctor_id}`); - if (date) queryParams.append("date", `eq.${date}`); - - const supabaseUrl = `${SUPABASE_URL}/rest/v1/doctor_exceptions?${queryParams}`; - - const headers: HeadersInit = { - apikey: SUPABASE_API_KEY, - "Content-Type": "application/json", - }; - - if (authHeader) { - headers["Authorization"] = authHeader; - } - - const response = await fetch(supabaseUrl, { - method: "GET", - headers, - }); - - const data = await response.json(); - - return new Response(JSON.stringify(data), { - status: response.status, - headers: { - "Content-Type": "application/json", - "Access-Control-Allow-Origin": "*", - }, - }); - } - - // POST: Criar exceção - if (req.method === "POST") { - const body = await req.json(); - - const supabaseUrl = `${SUPABASE_URL}/rest/v1/doctor_exceptions`; - - const headers: HeadersInit = { - apikey: SUPABASE_API_KEY, - "Content-Type": "application/json", - Prefer: "return=representation", - }; - - if (authHeader) { - headers["Authorization"] = authHeader; - } - - const response = await fetch(supabaseUrl, { - method: "POST", - headers, - body: JSON.stringify(body), - }); - - const data = await response.json(); - - return new Response(JSON.stringify(data), { - status: response.status, - headers: { - "Content-Type": "application/json", - "Access-Control-Allow-Origin": "*", - }, - }); - } - - // DELETE: Deletar exceção - if (req.method === "DELETE") { - if (!exceptionId || exceptionId === "doctor-exceptions") { - return new Response( - JSON.stringify({ error: "Exception ID is required" }), - { - status: 400, - headers: { - "Content-Type": "application/json", - "Access-Control-Allow-Origin": "*", - }, - } - ); - } - - const supabaseUrl = `${SUPABASE_URL}/rest/v1/doctor_exceptions?id=eq.${exceptionId}`; - - const headers: HeadersInit = { - apikey: SUPABASE_API_KEY, - "Content-Type": "application/json", - }; - - if (authHeader) { - headers["Authorization"] = authHeader; - } - - const response = await fetch(supabaseUrl, { - method: "DELETE", - headers, - }); - - return new Response(null, { - status: response.status, - headers: { - "Access-Control-Allow-Origin": "*", - }, - }); - } - - // Método não suportado - return new Response(JSON.stringify({ error: "Method not allowed" }), { - status: 405, - headers: { - "Content-Type": "application/json", - "Access-Control-Allow-Origin": "*", - }, - }); - } catch (error) { - console.error("Error in doctor-exceptions function:", error); - return new Response( - JSON.stringify({ - error: "Internal server error", - details: error instanceof Error ? error.message : "Unknown error", - }), - { - status: 500, - headers: { - "Content-Type": "application/json", - "Access-Control-Allow-Origin": "*", - }, - } - ); - } -}; diff --git a/MEDICONNECT 2/netlify/functions/doctors.ts b/MEDICONNECT 2/netlify/functions/doctors.ts deleted file mode 100644 index 3527f18c3..000000000 --- a/MEDICONNECT 2/netlify/functions/doctors.ts +++ /dev/null @@ -1,237 +0,0 @@ -/** - * Netlify Function: Doctors CRUD - * GET /rest/v1/doctors - Lista médicos - * GET /rest/v1/doctors/{id} - Busca por ID - * POST /rest/v1/doctors - Cria médico - * PATCH /rest/v1/doctors/{id} - Atualiza médico - * DELETE /rest/v1/doctors/{id} - Deleta médico - */ - -import type { Handler, HandlerEvent } from "@netlify/functions"; - -const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co"; -const SUPABASE_ANON_KEY = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ"; - -export const handler: Handler = async (event: HandlerEvent) => { - const headers = { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "Content-Type, Authorization", - "Access-Control-Allow-Methods": "GET, POST, PATCH, DELETE, OPTIONS", - }; - - if (event.httpMethod === "OPTIONS") { - return { - statusCode: 200, - headers, - body: "", - }; - } - - try { - const authHeader = - event.headers.authorization || event.headers.Authorization; - - if (!authHeader) { - return { - statusCode: 401, - headers, - body: JSON.stringify({ error: "Token não fornecido" }), - }; - } - - // Extrai ID da URL se houver (doctors/123 ou doctors?id=123) - const pathParts = event.path.split("/"); - const doctorId = - pathParts[pathParts.length - 1] !== "doctors" - ? pathParts[pathParts.length - 1] - : null; - - // GET - Listar ou buscar por ID - if (event.httpMethod === "GET") { - let url = `${SUPABASE_URL}/rest/v1/doctors`; - - if (doctorId && doctorId !== "doctors") { - // Buscar por ID específico - url += `?id=eq.${doctorId}&select=*`; - } else if (event.queryStringParameters) { - // Adiciona filtros da query string - const params = new URLSearchParams( - event.queryStringParameters as Record - ); - url += `?${params.toString()}`; - - // Adiciona select=* se não tiver - if (!params.has("select")) { - url += url.includes("?") ? "&select=*" : "?select=*"; - } - } else { - url += "?select=*"; - } - - const response = await fetch(url, { - method: "GET", - headers: { - apikey: SUPABASE_ANON_KEY, - Authorization: authHeader, - }, - }); - - let data = await response.json(); - - // Se buscar por ID, retorna o objeto diretamente (não array) - if ( - doctorId && - doctorId !== "doctors" && - Array.isArray(data) && - data.length > 0 - ) { - data = data[0]; - } - - return { - statusCode: response.status, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }; - } - - // POST - Criar médico - if (event.httpMethod === "POST") { - const body = JSON.parse(event.body || "{}"); - - if ( - !body.crm || - !body.crm_uf || - !body.full_name || - !body.cpf || - !body.email - ) { - return { - statusCode: 400, - headers, - body: JSON.stringify({ - error: "Campos obrigatórios: crm, crm_uf, full_name, cpf, email", - }), - }; - } - - const response = await fetch(`${SUPABASE_URL}/rest/v1/doctors`, { - method: "POST", - headers: { - apikey: SUPABASE_ANON_KEY, - Authorization: authHeader, - "Content-Type": "application/json", - Prefer: "return=representation", - }, - body: JSON.stringify(body), - }); - - let data = await response.json(); - - // Supabase retorna array, pega o primeiro - if (Array.isArray(data) && data.length > 0) { - data = data[0]; - } - - return { - statusCode: response.status, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }; - } - - // PATCH - Atualizar médico - if (event.httpMethod === "PATCH") { - if (!doctorId || doctorId === "doctors") { - return { - statusCode: 400, - headers, - body: JSON.stringify({ error: "ID do médico é obrigatório" }), - }; - } - - const body = JSON.parse(event.body || "{}"); - - const response = await fetch( - `${SUPABASE_URL}/rest/v1/doctors?id=eq.${doctorId}`, - { - method: "PATCH", - headers: { - apikey: SUPABASE_ANON_KEY, - Authorization: authHeader, - "Content-Type": "application/json", - Prefer: "return=representation", - }, - body: JSON.stringify(body), - } - ); - - let data = await response.json(); - - if (Array.isArray(data) && data.length > 0) { - data = data[0]; - } - - return { - statusCode: response.status, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }; - } - - // DELETE - Deletar médico - if (event.httpMethod === "DELETE") { - if (!doctorId || doctorId === "doctors") { - return { - statusCode: 400, - headers, - body: JSON.stringify({ error: "ID do médico é obrigatório" }), - }; - } - - const response = await fetch( - `${SUPABASE_URL}/rest/v1/doctors?id=eq.${doctorId}`, - { - method: "DELETE", - headers: { - apikey: SUPABASE_ANON_KEY, - Authorization: authHeader, - }, - } - ); - - return { - statusCode: response.status, - headers, - body: "", - }; - } - - return { - statusCode: 405, - headers, - body: JSON.stringify({ error: "Method Not Allowed" }), - }; - } catch (error) { - console.error("Erro na API de médicos:", error); - - return { - statusCode: 500, - headers, - body: JSON.stringify({ - error: "Erro interno no servidor", - message: error instanceof Error ? error.message : "Erro desconhecido", - }), - }; - } -}; diff --git a/MEDICONNECT 2/netlify/functions/get-available-slots.ts b/MEDICONNECT 2/netlify/functions/get-available-slots.ts deleted file mode 100644 index 4cbba71eb..000000000 --- a/MEDICONNECT 2/netlify/functions/get-available-slots.ts +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Netlify Function: Get Available Slots - * POST /functions/v1/get-available-slots - Busca horários disponíveis - */ - -import type { Handler, HandlerEvent } from "@netlify/functions"; - -const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co"; -const SUPABASE_ANON_KEY = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ"; - -export const handler: Handler = async (event: HandlerEvent) => { - const headers = { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "Content-Type, Authorization", - "Access-Control-Allow-Methods": "POST, OPTIONS", - }; - - if (event.httpMethod === "OPTIONS") { - return { - statusCode: 200, - headers, - body: "", - }; - } - - try { - const authHeader = - event.headers.authorization || event.headers.Authorization; - - if (!authHeader) { - return { - statusCode: 401, - headers, - body: JSON.stringify({ error: "Token não fornecido" }), - }; - } - - if (event.httpMethod === "POST") { - const body = JSON.parse(event.body || "{}"); - - // Validação dos campos obrigatórios - if (!body.doctor_id || !body.start_date || !body.end_date) { - return { - statusCode: 400, - headers, - body: JSON.stringify({ - error: "Campos obrigatórios: doctor_id, start_date, end_date", - }), - }; - } - - const response = await fetch( - `${SUPABASE_URL}/functions/v1/get-available-slots`, - { - method: "POST", - headers: { - apikey: SUPABASE_ANON_KEY, - Authorization: authHeader, - "Content-Type": "application/json", - }, - body: JSON.stringify(body), - } - ); - - const data = await response.json(); - - return { - statusCode: response.status, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }; - } - - return { - statusCode: 405, - headers, - body: JSON.stringify({ error: "Method Not Allowed" }), - }; - } catch (error) { - console.error("Erro na API de available slots:", error); - - return { - statusCode: 500, - headers, - body: JSON.stringify({ - error: "Erro interno no servidor", - message: error instanceof Error ? error.message : "Erro desconhecido", - }), - }; - } -}; diff --git a/MEDICONNECT 2/netlify/functions/patients.ts b/MEDICONNECT 2/netlify/functions/patients.ts deleted file mode 100644 index 73bb18a08..000000000 --- a/MEDICONNECT 2/netlify/functions/patients.ts +++ /dev/null @@ -1,226 +0,0 @@ -/** - * Netlify Function: Patients CRUD - * GET /rest/v1/patients - Lista pacientes - * GET /rest/v1/patients/{id} - Busca por ID - * POST /rest/v1/patients - Cria paciente - * PATCH /rest/v1/patients/{id} - Atualiza paciente - * DELETE /rest/v1/patients/{id} - Deleta paciente - */ - -import type { Handler, HandlerEvent } from "@netlify/functions"; - -const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co"; -const SUPABASE_ANON_KEY = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ"; - -export const handler: Handler = async (event: HandlerEvent) => { - const headers = { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "Content-Type, Authorization", - "Access-Control-Allow-Methods": "GET, POST, PATCH, DELETE, OPTIONS", - }; - - if (event.httpMethod === "OPTIONS") { - return { - statusCode: 200, - headers, - body: "", - }; - } - - try { - const authHeader = - event.headers.authorization || event.headers.Authorization; - - if (!authHeader) { - return { - statusCode: 401, - headers, - body: JSON.stringify({ error: "Token não fornecido" }), - }; - } - - // Extrai ID da URL se houver - const pathParts = event.path.split("/"); - const patientId = - pathParts[pathParts.length - 1] !== "patients" - ? pathParts[pathParts.length - 1] - : null; - - // GET - Listar ou buscar por ID - if (event.httpMethod === "GET") { - let url = `${SUPABASE_URL}/rest/v1/patients`; - - if (patientId && patientId !== "patients") { - url += `?id=eq.${patientId}&select=*`; - } else if (event.queryStringParameters) { - const params = new URLSearchParams( - event.queryStringParameters as Record - ); - url += `?${params.toString()}`; - - if (!params.has("select")) { - url += url.includes("?") ? "&select=*" : "?select=*"; - } - } else { - url += "?select=*"; - } - - const response = await fetch(url, { - method: "GET", - headers: { - apikey: SUPABASE_ANON_KEY, - Authorization: authHeader, - }, - }); - - let data = await response.json(); - - if ( - patientId && - patientId !== "patients" && - Array.isArray(data) && - data.length > 0 - ) { - data = data[0]; - } - - return { - statusCode: response.status, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }; - } - - // POST - Criar paciente - if (event.httpMethod === "POST") { - const body = JSON.parse(event.body || "{}"); - - if (!body.full_name || !body.cpf || !body.email || !body.phone_mobile) { - return { - statusCode: 400, - headers, - body: JSON.stringify({ - error: "Campos obrigatórios: full_name, cpf, email, phone_mobile", - }), - }; - } - - const response = await fetch(`${SUPABASE_URL}/rest/v1/patients`, { - method: "POST", - headers: { - apikey: SUPABASE_ANON_KEY, - Authorization: authHeader, - "Content-Type": "application/json", - Prefer: "return=representation", - }, - body: JSON.stringify(body), - }); - - let data = await response.json(); - - if (Array.isArray(data) && data.length > 0) { - data = data[0]; - } - - return { - statusCode: response.status, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }; - } - - // PATCH - Atualizar paciente - if (event.httpMethod === "PATCH") { - if (!patientId || patientId === "patients") { - return { - statusCode: 400, - headers, - body: JSON.stringify({ error: "ID do paciente é obrigatório" }), - }; - } - - const body = JSON.parse(event.body || "{}"); - - const response = await fetch( - `${SUPABASE_URL}/rest/v1/patients?id=eq.${patientId}`, - { - method: "PATCH", - headers: { - apikey: SUPABASE_ANON_KEY, - Authorization: authHeader, - "Content-Type": "application/json", - Prefer: "return=representation", - }, - body: JSON.stringify(body), - } - ); - - let data = await response.json(); - - if (Array.isArray(data) && data.length > 0) { - data = data[0]; - } - - return { - statusCode: response.status, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }; - } - - // DELETE - Deletar paciente - if (event.httpMethod === "DELETE") { - if (!patientId || patientId === "patients") { - return { - statusCode: 400, - headers, - body: JSON.stringify({ error: "ID do paciente é obrigatório" }), - }; - } - - const response = await fetch( - `${SUPABASE_URL}/rest/v1/patients?id=eq.${patientId}`, - { - method: "DELETE", - headers: { - apikey: SUPABASE_ANON_KEY, - Authorization: authHeader, - }, - } - ); - - return { - statusCode: response.status, - headers, - body: "", - }; - } - - return { - statusCode: 405, - headers, - body: JSON.stringify({ error: "Method Not Allowed" }), - }; - } catch (error) { - console.error("Erro na API de pacientes:", error); - - return { - statusCode: 500, - headers, - body: JSON.stringify({ - error: "Erro interno no servidor", - message: error instanceof Error ? error.message : "Erro desconhecido", - }), - }; - } -}; diff --git a/MEDICONNECT 2/netlify/functions/profiles.ts b/MEDICONNECT 2/netlify/functions/profiles.ts deleted file mode 100644 index 4c8c02d06..000000000 --- a/MEDICONNECT 2/netlify/functions/profiles.ts +++ /dev/null @@ -1,155 +0,0 @@ -/** - * Netlify Function: Profiles - * GET /rest/v1/profiles - Lista perfis - * GET /rest/v1/profiles/{id} - Busca por ID - * PATCH /rest/v1/profiles/{id} - Atualiza avatar_url - */ - -import type { Handler, HandlerEvent } from "@netlify/functions"; - -const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co"; -const SUPABASE_ANON_KEY = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ"; - -export const handler: Handler = async (event: HandlerEvent) => { - const headers = { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "Content-Type, Authorization", - "Access-Control-Allow-Methods": "GET, PATCH, OPTIONS", - }; - - if (event.httpMethod === "OPTIONS") { - return { - statusCode: 200, - headers, - body: "", - }; - } - - try { - const authHeader = - event.headers.authorization || event.headers.Authorization; - - if (!authHeader) { - return { - statusCode: 401, - headers, - body: JSON.stringify({ error: "Token não fornecido" }), - }; - } - - // Extrai ID da URL se houver - const pathParts = event.path.split("/"); - const profileId = - pathParts[pathParts.length - 1] !== "profiles" - ? pathParts[pathParts.length - 1] - : null; - - // GET - Listar ou buscar por ID - if (event.httpMethod === "GET") { - let url = `${SUPABASE_URL}/rest/v1/profiles`; - - if (profileId && profileId !== "profiles") { - url += `?id=eq.${profileId}&select=*`; - } else if (event.queryStringParameters) { - const params = new URLSearchParams( - event.queryStringParameters as Record - ); - url += `?${params.toString()}`; - - if (!params.has("select")) { - url += url.includes("?") ? "&select=*" : "?select=*"; - } - } else { - url += "?select=*"; - } - - const response = await fetch(url, { - method: "GET", - headers: { - apikey: SUPABASE_ANON_KEY, - Authorization: authHeader, - }, - }); - - let data = await response.json(); - - if ( - profileId && - profileId !== "profiles" && - Array.isArray(data) && - data.length > 0 - ) { - data = data[0]; - } - - return { - statusCode: response.status, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }; - } - - // PATCH - Atualizar avatar_url - if (event.httpMethod === "PATCH") { - if (!profileId || profileId === "profiles") { - return { - statusCode: 400, - headers, - body: JSON.stringify({ error: "ID do perfil é obrigatório" }), - }; - } - - const body = JSON.parse(event.body || "{}"); - - const response = await fetch( - `${SUPABASE_URL}/rest/v1/profiles?id=eq.${profileId}`, - { - method: "PATCH", - headers: { - apikey: SUPABASE_ANON_KEY, - Authorization: authHeader, - "Content-Type": "application/json", - Prefer: "return=representation", - }, - body: JSON.stringify(body), - } - ); - - let data = await response.json(); - - if (Array.isArray(data) && data.length > 0) { - data = data[0]; - } - - return { - statusCode: response.status, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }; - } - - return { - statusCode: 405, - headers, - body: JSON.stringify({ error: "Method Not Allowed" }), - }; - } catch (error) { - console.error("Erro na API de perfis:", error); - - return { - statusCode: 500, - headers, - body: JSON.stringify({ - error: "Erro interno no servidor", - message: error instanceof Error ? error.message : "Erro desconhecido", - }), - }; - } -}; diff --git a/MEDICONNECT 2/netlify/functions/register-patient.ts b/MEDICONNECT 2/netlify/functions/register-patient.ts deleted file mode 100644 index 304c8f196..000000000 --- a/MEDICONNECT 2/netlify/functions/register-patient.ts +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Netlify Function: Register Patient (Public) - * POST /register-patient - Registro público de paciente - * Não requer autenticação - função pública - * Validações rigorosas (CPF, rate limiting, rollback) - */ - -import type { Handler, HandlerEvent } from "@netlify/functions"; - -const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co"; -const SUPABASE_ANON_KEY = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ"; - -export const handler: Handler = async (event: HandlerEvent) => { - const headers = { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "Content-Type, Authorization", - "Access-Control-Allow-Methods": "POST, OPTIONS", - }; - - if (event.httpMethod === "OPTIONS") { - return { - statusCode: 200, - headers, - body: "", - }; - } - - try { - if (event.httpMethod === "POST") { - const body = JSON.parse(event.body || "{}"); - - console.log( - "[register-patient] Recebido body:", - JSON.stringify(body, null, 2) - ); - - // Validação dos campos obrigatórios - if (!body.email || !body.full_name || !body.cpf || !body.phone_mobile) { - return { - statusCode: 400, - headers, - body: JSON.stringify({ - error: "Campos obrigatórios: email, full_name, cpf, phone_mobile", - }), - }; - } - - // Chama a Edge Function pública do Supabase - const response = await fetch( - `${SUPABASE_URL}/functions/v1/register-patient`, - { - method: "POST", - headers: { - apikey: SUPABASE_ANON_KEY, - Authorization: `Bearer ${SUPABASE_ANON_KEY}`, - "Content-Type": "application/json", - }, - body: JSON.stringify(body), - } - ); - - const data = await response.json(); - - console.log("[register-patient] Resposta do Supabase:", { - status: response.status, - data: JSON.stringify(data, null, 2), - }); - - return { - statusCode: response.status, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }; - } - - return { - statusCode: 405, - headers, - body: JSON.stringify({ error: "Method Not Allowed" }), - }; - } catch (error) { - console.error("[register-patient] Erro na API:", error); - - return { - statusCode: 500, - headers, - body: JSON.stringify({ - error: "Erro interno do servidor", - message: error instanceof Error ? error.message : "Erro desconhecido", - }), - }; - } -}; diff --git a/MEDICONNECT 2/netlify/functions/reports.ts b/MEDICONNECT 2/netlify/functions/reports.ts deleted file mode 100644 index 23323f5d8..000000000 --- a/MEDICONNECT 2/netlify/functions/reports.ts +++ /dev/null @@ -1,197 +0,0 @@ -/** - * Netlify Function: Reports - * GET /rest/v1/reports - Lista relatórios - * GET /rest/v1/reports/{id} - Busca por ID - * POST /rest/v1/reports - Cria relatório - * PATCH /rest/v1/reports/{id} - Atualiza relatório - */ - -import type { Handler, HandlerEvent } from "@netlify/functions"; - -const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co"; -const SUPABASE_ANON_KEY = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ"; - -export const handler: Handler = async (event: HandlerEvent) => { - const headers = { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "Content-Type, Authorization", - "Access-Control-Allow-Methods": "GET, POST, PATCH, OPTIONS", - }; - - if (event.httpMethod === "OPTIONS") { - return { - statusCode: 200, - headers, - body: "", - }; - } - - try { - const authHeader = - event.headers.authorization || event.headers.Authorization; - - if (!authHeader) { - return { - statusCode: 401, - headers, - body: JSON.stringify({ error: "Token não fornecido" }), - }; - } - - // Extrai ID da URL se houver - const pathParts = event.path.split("/"); - const reportId = - pathParts[pathParts.length - 1] !== "reports" - ? pathParts[pathParts.length - 1] - : null; - - // GET - Listar ou buscar por ID - if (event.httpMethod === "GET") { - let url = `${SUPABASE_URL}/rest/v1/reports`; - - if (reportId && reportId !== "reports") { - url += `?id=eq.${reportId}&select=*`; - } else if (event.queryStringParameters) { - const params = new URLSearchParams( - event.queryStringParameters as Record - ); - url += `?${params.toString()}`; - - if (!params.has("select")) { - url += url.includes("?") ? "&select=*" : "?select=*"; - } - } else { - url += "?select=*"; - } - - const response = await fetch(url, { - method: "GET", - headers: { - apikey: SUPABASE_ANON_KEY, - Authorization: authHeader, - }, - }); - - let data = await response.json(); - - if ( - reportId && - reportId !== "reports" && - Array.isArray(data) && - data.length > 0 - ) { - data = data[0]; - } - - return { - statusCode: response.status, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }; - } - - // POST - Criar relatório - if (event.httpMethod === "POST") { - const body = JSON.parse(event.body || "{}"); - - if (!body.patient_id) { - return { - statusCode: 400, - headers, - body: JSON.stringify({ - error: "Campo obrigatório: patient_id", - }), - }; - } - - const response = await fetch(`${SUPABASE_URL}/rest/v1/reports`, { - method: "POST", - headers: { - apikey: SUPABASE_ANON_KEY, - Authorization: authHeader, - "Content-Type": "application/json", - Prefer: "return=representation", - }, - body: JSON.stringify(body), - }); - - let data = await response.json(); - - if (Array.isArray(data) && data.length > 0) { - data = data[0]; - } - - return { - statusCode: response.status, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }; - } - - // PATCH - Atualizar relatório - if (event.httpMethod === "PATCH") { - if (!reportId || reportId === "reports") { - return { - statusCode: 400, - headers, - body: JSON.stringify({ error: "ID do relatório é obrigatório" }), - }; - } - - const body = JSON.parse(event.body || "{}"); - - const response = await fetch( - `${SUPABASE_URL}/rest/v1/reports?id=eq.${reportId}`, - { - method: "PATCH", - headers: { - apikey: SUPABASE_ANON_KEY, - Authorization: authHeader, - "Content-Type": "application/json", - Prefer: "return=representation", - }, - body: JSON.stringify(body), - } - ); - - let data = await response.json(); - - if (Array.isArray(data) && data.length > 0) { - data = data[0]; - } - - return { - statusCode: response.status, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }; - } - - return { - statusCode: 405, - headers, - body: JSON.stringify({ error: "Method Not Allowed" }), - }; - } catch (error) { - console.error("Erro na API de relatórios:", error); - - return { - statusCode: 500, - headers, - body: JSON.stringify({ - error: "Erro interno no servidor", - message: error instanceof Error ? error.message : "Erro desconhecido", - }), - }; - } -}; diff --git a/MEDICONNECT 2/netlify/functions/request-password-reset.ts b/MEDICONNECT 2/netlify/functions/request-password-reset.ts deleted file mode 100644 index b100d2725..000000000 --- a/MEDICONNECT 2/netlify/functions/request-password-reset.ts +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Netlify Function: Request Password Reset - * POST /request-password-reset - Solicita reset de senha via email (público) - * Não requer autenticação - endpoint público - */ - -import type { Handler, HandlerEvent } from "@netlify/functions"; - -const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co"; -const SUPABASE_ANON_KEY = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ"; - -interface PasswordResetRequest { - email: string; - redirect_url?: string; -} - -export const handler: Handler = async (event: HandlerEvent) => { - const headers = { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "Content-Type, Authorization", - "Access-Control-Allow-Methods": "POST, OPTIONS", - }; - - if (event.httpMethod === "OPTIONS") { - return { - statusCode: 200, - headers, - body: "", - }; - } - - if (event.httpMethod !== "POST") { - return { - statusCode: 405, - headers, - body: JSON.stringify({ error: "Method Not Allowed" }), - }; - } - - try { - const body: PasswordResetRequest = JSON.parse(event.body || "{}"); - - console.log("[request-password-reset] Recebido:", { - email: body.email, - hasRedirectUrl: !!body.redirect_url, - }); - - if (!body.email) { - return { - statusCode: 400, - headers, - body: JSON.stringify({ error: "Email é obrigatório" }), - }; - } - - // Chama a API do Supabase para enviar email de reset - const response = await fetch(`${SUPABASE_URL}/auth/v1/recover`, { - method: "POST", - headers: { - "Content-Type": "application/json", - apikey: SUPABASE_ANON_KEY, - }, - body: JSON.stringify({ - email: body.email, - options: { - redirectTo: - body.redirect_url || - "https://mediconnectbrasil.netlify.app/reset-password", - }, - }), - }); - - const data = await response.json(); - - console.log("[request-password-reset] Resposta Supabase:", { - status: response.status, - data, - }); - - // Supabase sempre retorna 200 mesmo se o email não existir (por segurança) - if (response.ok) { - return { - statusCode: 200, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify({ - success: true, - message: - "Email de reset de senha enviado com sucesso. Verifique sua caixa de entrada.", - }), - }; - } - - return { - statusCode: response.status, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }; - } catch (error) { - console.error("[request-password-reset] Erro:", error); - return { - statusCode: 500, - headers, - body: JSON.stringify({ - error: "Erro ao solicitar reset de senha", - details: error instanceof Error ? error.message : "Erro desconhecido", - }), - }; - } -}; diff --git a/MEDICONNECT 2/netlify/functions/send-sms.ts b/MEDICONNECT 2/netlify/functions/send-sms.ts deleted file mode 100644 index 57e76a4ab..000000000 --- a/MEDICONNECT 2/netlify/functions/send-sms.ts +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Netlify Function: Send SMS - * POST /functions/v1/send-sms - Envia SMS via Twilio - */ - -import type { Handler, HandlerEvent } from "@netlify/functions"; - -const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co"; -const SUPABASE_ANON_KEY = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ"; - -export const handler: Handler = async (event: HandlerEvent) => { - const headers = { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "Content-Type, Authorization", - "Access-Control-Allow-Methods": "POST, OPTIONS", - }; - - if (event.httpMethod === "OPTIONS") { - return { - statusCode: 200, - headers, - body: "", - }; - } - - try { - const authHeader = - event.headers.authorization || event.headers.Authorization; - - if (!authHeader) { - return { - statusCode: 401, - headers, - body: JSON.stringify({ error: "Token não fornecido" }), - }; - } - - if (event.httpMethod === "POST") { - const body = JSON.parse(event.body || "{}"); - - // Validação dos campos obrigatórios - if (!body.phone_number || !body.message) { - return { - statusCode: 400, - headers, - body: JSON.stringify({ - error: "Campos obrigatórios: phone_number, message", - }), - }; - } - - // Chama a função Supabase de enviar SMS - const response = await fetch(`${SUPABASE_URL}/functions/v1/send-sms`, { - method: "POST", - headers: { - apikey: SUPABASE_ANON_KEY, - Authorization: authHeader, - "Content-Type": "application/json", - }, - body: JSON.stringify(body), - }); - - const data = await response.json(); - - return { - statusCode: response.status, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }; - } - - return { - statusCode: 405, - headers, - body: JSON.stringify({ error: "Method Not Allowed" }), - }; - } catch (error) { - console.error("Erro na API de SMS:", error); - - return { - statusCode: 500, - headers, - body: JSON.stringify({ - error: "Erro interno no servidor", - message: error instanceof Error ? error.message : "Erro desconhecido", - }), - }; - } -}; diff --git a/MEDICONNECT 2/netlify/functions/user-info-by-id.ts b/MEDICONNECT 2/netlify/functions/user-info-by-id.ts deleted file mode 100644 index 4f3a4e2b5..000000000 --- a/MEDICONNECT 2/netlify/functions/user-info-by-id.ts +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Netlify Function: User Info By ID - * POST /user-info-by-id - Retorna dados de usuário específico (apenas admin/gestor) - */ - -import type { Handler, HandlerEvent } from "@netlify/functions"; - -const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co"; -const SUPABASE_ANON_KEY = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ"; - -export const handler: Handler = async (event: HandlerEvent) => { - const headers = { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "Content-Type, Authorization", - "Access-Control-Allow-Methods": "POST, OPTIONS", - }; - - if (event.httpMethod === "OPTIONS") { - return { - statusCode: 200, - headers, - body: "", - }; - } - - if (event.httpMethod !== "POST") { - return { - statusCode: 405, - headers, - body: JSON.stringify({ error: "Method Not Allowed" }), - }; - } - - try { - const authHeader = - event.headers.authorization || event.headers.Authorization; - - if (!authHeader) { - return { - statusCode: 401, - headers, - body: JSON.stringify({ error: "Token não fornecido" }), - }; - } - - const body = JSON.parse(event.body || "{}"); - - if (!body.user_id) { - return { - statusCode: 400, - headers, - body: JSON.stringify({ error: "Campo obrigatório: user_id" }), - }; - } - - // Chama a Edge Function do Supabase - const response = await fetch( - `${SUPABASE_URL}/functions/v1/user-info-by-id`, - { - method: "POST", - headers: { - apikey: SUPABASE_ANON_KEY, - Authorization: authHeader, - "Content-Type": "application/json", - }, - body: JSON.stringify(body), - } - ); - - const data = await response.json(); - - return { - statusCode: response.status, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }; - } catch (error) { - console.error("Erro na API de user-info-by-id:", error); - - return { - statusCode: 500, - headers, - body: JSON.stringify({ - error: "Erro interno no servidor", - message: error instanceof Error ? error.message : "Erro desconhecido", - }), - }; - } -}; diff --git a/MEDICONNECT 2/netlify/functions/user-info.ts b/MEDICONNECT 2/netlify/functions/user-info.ts deleted file mode 100644 index e197758fe..000000000 --- a/MEDICONNECT 2/netlify/functions/user-info.ts +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Netlify Function: User Info - * GET /functions/v1/user-info - Retorna informações completas do usuário autenticado - * Inclui: user, profile, roles e permissions calculadas - */ - -import type { Handler, HandlerEvent } from "@netlify/functions"; - -const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co"; -const SUPABASE_ANON_KEY = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ"; - -export const handler: Handler = async (event: HandlerEvent) => { - const headers = { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "Content-Type, Authorization", - "Access-Control-Allow-Methods": "POST, GET, OPTIONS", - }; - - if (event.httpMethod === "OPTIONS") { - return { - statusCode: 200, - headers, - body: "", - }; - } - - try { - const authHeader = - event.headers.authorization || event.headers.Authorization; - - if (!authHeader) { - return { - statusCode: 401, - headers, - body: JSON.stringify({ error: "Token não fornecido" }), - }; - } - - // Aceita tanto POST quanto GET para compatibilidade - if (event.httpMethod === "POST" || event.httpMethod === "GET") { - // Chama a Edge Function do Supabase (POST conforme doc 21/10/2025) - const response = await fetch(`${SUPABASE_URL}/functions/v1/user-info`, { - method: "POST", - headers: { - apikey: SUPABASE_ANON_KEY, - Authorization: authHeader, - "Content-Type": "application/json", - }, - }); - - const data = await response.json(); - - return { - statusCode: response.status, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }; - } - - return { - statusCode: 405, - headers, - body: JSON.stringify({ error: "Method Not Allowed" }), - }; - } catch (error) { - console.error("Erro na API de user-info:", error); - - return { - statusCode: 500, - headers, - body: JSON.stringify({ - error: "Erro interno no servidor", - message: error instanceof Error ? error.message : "Erro desconhecido", - }), - }; - } -}; diff --git a/MEDICONNECT 2/netlify/functions/user-roles.ts b/MEDICONNECT 2/netlify/functions/user-roles.ts deleted file mode 100644 index b05b6f5a7..000000000 --- a/MEDICONNECT 2/netlify/functions/user-roles.ts +++ /dev/null @@ -1,161 +0,0 @@ -/** - * Netlify Function: User Roles - * GET /rest/v1/user_roles - Lista roles de usuários - * POST /rest/v1/user_roles - Adiciona role a um usuário - * DELETE /rest/v1/user_roles - Remove role de um usuário - */ - -import type { Handler, HandlerEvent } from "@netlify/functions"; - -const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co"; -const SUPABASE_ANON_KEY = - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1YW5xZnN3aGJlcmtvZXZ0bWZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTQ5NTQzNjksImV4cCI6MjA3MDUzMDM2OX0.g8Fm4XAvtX46zifBZnYVH4tVuQkqUH6Ia9CXQj4DztQ"; - -export const handler: Handler = async (event: HandlerEvent) => { - const headers = { - "Access-Control-Allow-Origin": "*", - "Access-Control-Allow-Headers": "Content-Type, Authorization", - "Access-Control-Allow-Methods": "GET, POST, DELETE, OPTIONS", - }; - - if (event.httpMethod === "OPTIONS") { - return { - statusCode: 200, - headers, - body: "", - }; - } - - try { - const authHeader = - event.headers.authorization || event.headers.Authorization; - - if (!authHeader) { - return { - statusCode: 401, - headers, - body: JSON.stringify({ error: "Token não fornecido" }), - }; - } - - if (event.httpMethod === "GET") { - let url = `${SUPABASE_URL}/rest/v1/user_roles?select=*`; - - if (event.queryStringParameters) { - const params = new URLSearchParams( - event.queryStringParameters as Record - ); - const paramsStr = params.toString(); - if (paramsStr) { - url += `&${paramsStr}`; - } - } - - const response = await fetch(url, { - method: "GET", - headers: { - apikey: SUPABASE_ANON_KEY, - Authorization: authHeader, - }, - }); - - const data = await response.json(); - - return { - statusCode: response.status, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }; - } - - if (event.httpMethod === "POST") { - // Adicionar nova role para um usuário - const body = JSON.parse(event.body || "{}"); - - if (!body.user_id || !body.role) { - return { - statusCode: 400, - headers, - body: JSON.stringify({ error: "user_id e role são obrigatórios" }), - }; - } - - const response = await fetch(`${SUPABASE_URL}/rest/v1/user_roles`, { - method: "POST", - headers: { - apikey: SUPABASE_ANON_KEY, - Authorization: authHeader, - "Content-Type": "application/json", - Prefer: "return=representation", - }, - body: JSON.stringify({ - user_id: body.user_id, - role: body.role, - }), - }); - - const data = await response.json(); - - return { - statusCode: response.status, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }; - } - - if (event.httpMethod === "DELETE") { - // Remover role de um usuário - const params = event.queryStringParameters; - - if (!params?.user_id || !params?.role) { - return { - statusCode: 400, - headers, - body: JSON.stringify({ error: "user_id e role são obrigatórios" }), - }; - } - - const url = `${SUPABASE_URL}/rest/v1/user_roles?user_id=eq.${params.user_id}&role=eq.${params.role}`; - - const response = await fetch(url, { - method: "DELETE", - headers: { - apikey: SUPABASE_ANON_KEY, - Authorization: authHeader, - }, - }); - - return { - statusCode: response.status, - headers: { - ...headers, - "Content-Type": "application/json", - }, - body: JSON.stringify({ success: true }), - }; - } - - return { - statusCode: 405, - headers, - body: JSON.stringify({ error: "Method Not Allowed" }), - }; - } catch (error) { - console.error("Erro na API de user roles:", error); - - return { - statusCode: 500, - headers, - body: JSON.stringify({ - error: "Erro interno no servidor", - message: error instanceof Error ? error.message : "Erro desconhecido", - }), - }; - } -}; diff --git a/MEDICONNECT 2/src/components/Chatbot.tsx b/MEDICONNECT 2/src/components/Chatbot.tsx new file mode 100644 index 000000000..36cbe82bb --- /dev/null +++ b/MEDICONNECT 2/src/components/Chatbot.tsx @@ -0,0 +1,234 @@ +import { useState, useRef, useEffect } from 'react'; +import { MessageCircle, X, Send } from 'lucide-react'; + +interface Message { + id: number; + text: string; + sender: 'user' | 'bot'; + timestamp: Date; +} + +export function Chatbot() { + const [isOpen, setIsOpen] = useState(false); + const [messages, setMessages] = useState([ + { + id: 1, + text: 'Olá! Como posso ajudar você hoje?', + sender: 'bot', + timestamp: new Date(), + }, + ]); + const [inputMessage, setInputMessage] = useState(''); + const [isTyping, setIsTyping] = useState(false); + const messagesEndRef = useRef(null); + + const scrollToBottom = () => { + messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); + }; + + useEffect(() => { + scrollToBottom(); + }, [messages]); + + const handleSend = async () => { + if (!inputMessage.trim()) return; + + const userMessage: Message = { + id: messages.length + 1, + text: inputMessage, + sender: 'user', + timestamp: new Date(), + }; + + setMessages((prev) => [...prev, userMessage]); + setInputMessage(''); + setIsTyping(true); + + // Simular resposta do bot + setTimeout(() => { + const botResponse = getBotResponse(inputMessage.toLowerCase()); + const botMessage: Message = { + id: messages.length + 2, + text: botResponse, + sender: 'bot', + timestamp: new Date(), + }; + setMessages((prev) => [...prev, botMessage]); + setIsTyping(false); + }, 1000); + }; + + const getBotResponse = (input: string): string => { + if (input.includes('agendar') || input.includes('consulta') || input.includes('marcar')) { + return 'Para agendar uma consulta, acesse o painel do paciente e clique em "Agendar Consulta". Você poderá escolher o médico, data e horário disponível.'; + } + + if (input.includes('cancelar') || input.includes('remarcar')) { + return 'Para cancelar ou remarcar uma consulta, acesse "Minhas Consultas" no painel do paciente e clique na consulta desejada.'; + } + + if (input.includes('pagamento') || input.includes('pagar')) { + return 'Aceitamos pagamento via PIX, cartão de crédito e débito. O pagamento pode ser realizado no momento da consulta ou através do nosso sistema online.'; + } + + if (input.includes('senha') || input.includes('esqueci')) { + return 'Para redefinir sua senha, clique em "Esqueci minha senha" na tela de login e siga as instruções enviadas para seu e-mail.'; + } + + if (input.includes('exame') || input.includes('resultado')) { + return 'Os resultados de exames ficam disponíveis no menu "Meus Laudos" do painel do paciente. Você receberá uma notificação quando estiverem prontos.'; + } + + if (input.includes('horário') || input.includes('funciona')) { + return 'Nosso atendimento funciona de segunda a sexta das 8h às 18h, e sábados das 8h às 12h.'; + } + + if (input.includes('telemedicina') || input.includes('online')) { + return 'Sim, oferecemos consultas por telemedicina! Ao agendar, selecione a opção "Teleconsulta" e você receberá o link para a videochamada.'; + } + + if (input.includes('prontuário') || input.includes('histórico')) { + return 'Seu histórico médico completo está disponível no menu "Meu Prontuário" no painel do paciente.'; + } + + if (input.includes('suporte') || input.includes('ajuda') || input.includes('contato')) { + return 'Para suporte adicional, entre em contato conosco:\n📞 Telefone: (11) 1234-5678\n📧 Email: suporte@mediconnect.com.br\n💬 WhatsApp: (11) 98765-4321'; + } + + return 'Desculpe, não entendi sua pergunta. Você pode perguntar sobre:\n• Agendar consultas\n• Cancelar/remarcar consultas\n• Pagamentos\n• Resultados de exames\n• Redefinir senha\n• Horário de funcionamento\n• Telemedicina\n• Contato/suporte'; + }; + + const quickReplies = [ + 'Como agendar consulta?', + 'Horário de funcionamento', + 'Resultados de exames', + 'Esqueci minha senha', + ]; + + return ( + <> + {/* Botão flutuante */} + {!isOpen && ( + + )} + + {/* Janela do chat */} + {isOpen && ( +
+ {/* Header */} +
+
+
+ +
+
+

Assistente Virtual

+

Online

+
+
+ +
+ + {/* Mensagens */} +
+ {messages.map((message) => ( +
+
+

{message.text}

+

+ {message.timestamp.toLocaleTimeString('pt-BR', { + hour: '2-digit', + minute: '2-digit', + })} +

+
+
+ ))} + + {isTyping && ( +
+
+
+ + + +
+
+
+ )} + +
+
+ + {/* Respostas rápidas */} + {messages.length === 1 && ( +
+

Perguntas frequentes:

+
+ {quickReplies.map((reply, index) => ( + + ))} +
+
+ )} + + {/* Input */} +
+
+ setInputMessage(e.target.value)} + onKeyPress={(e) => e.key === 'Enter' && handleSend()} + placeholder="Digite sua mensagem..." + className="flex-1 border border-gray-300 rounded-full px-4 py-2 focus:outline-none focus:ring-2 focus:ring-[#00a8a8] focus:border-transparent text-sm" + /> + +
+
+
+ )} + + ); +} diff --git a/MEDICONNECT 2/src/pages/CentralAjuda.tsx b/MEDICONNECT 2/src/pages/CentralAjuda.tsx index 8c7c4d83e..d45e96e40 100644 --- a/MEDICONNECT 2/src/pages/CentralAjuda.tsx +++ b/MEDICONNECT 2/src/pages/CentralAjuda.tsx @@ -14,6 +14,7 @@ import { Headphones, ArrowLeft, } from "lucide-react"; +import { Chatbot } from "../components/Chatbot"; interface FAQ { question: string; @@ -404,6 +405,9 @@ const CentralAjuda: React.FC = () => {
+ + {/* Chatbot */} + ); }; diff --git a/MEDICONNECT 2/src/pages/CentralAjudaMedico.tsx b/MEDICONNECT 2/src/pages/CentralAjudaMedico.tsx index 6154029ca..184c54810 100644 --- a/MEDICONNECT 2/src/pages/CentralAjudaMedico.tsx +++ b/MEDICONNECT 2/src/pages/CentralAjudaMedico.tsx @@ -14,6 +14,7 @@ import { Headphones, ArrowLeft, } from "lucide-react"; +import { Chatbot } from "../components/Chatbot"; interface FAQ { question: string; @@ -408,6 +409,9 @@ const CentralAjudaMedico: React.FC = () => { + + {/* Chatbot */} + ); }; diff --git a/MEDICONNECT 2/src/pages/LoginPaciente.tsx b/MEDICONNECT 2/src/pages/LoginPaciente.tsx index e61c15969..de6a9b889 100644 --- a/MEDICONNECT 2/src/pages/LoginPaciente.tsx +++ b/MEDICONNECT 2/src/pages/LoginPaciente.tsx @@ -22,7 +22,7 @@ const LoginPaciente: React.FC = () => { const navigate = useNavigate(); - const { loginPaciente } = useAuth(); + const { loginComEmailSenha } = useAuth(); const handleLogin = async (e: React.FormEvent) => { e.preventDefault(); @@ -31,48 +31,19 @@ const LoginPaciente: React.FC = () => { try { console.log("[LoginPaciente] Fazendo login com email:", formData.email); - // Fazer login via API Supabase - await authService.login({ - email: formData.email, - password: formData.senha, - }); + const ok = await loginComEmailSenha(formData.email, formData.senha); - console.log("[LoginPaciente] Login bem-sucedido!"); - - // Buscar dados do paciente da API - const pacientes = await patientService.list(); - const paciente = pacientes.find((p: any) => p.email === formData.email); - - console.log("[LoginPaciente] Paciente encontrado:", paciente); - - if (paciente) { - console.log("[LoginPaciente] Paciente encontrado:", { - id: paciente.id, - nome: paciente.full_name, - email: paciente.email, - }); - const ok = await loginPaciente({ - id: paciente.id, - nome: paciente.full_name, - email: paciente.email, - }); - - if (ok) { - console.log("[LoginPaciente] Navegando para /acompanhamento"); - navigate("/acompanhamento"); - } else { - console.error("[LoginPaciente] loginPaciente retornou false"); - toast.error("Erro ao processar login"); - } + if (ok) { + console.log("[LoginPaciente] Login bem-sucedido! Navegando para /acompanhamento"); + toast.success("Login realizado com sucesso!"); + navigate("/acompanhamento"); } else { - console.log("[LoginPaciente] Paciente não encontrado na lista"); - toast.error( - "Dados do paciente não encontrados. Entre em contato com o suporte." - ); + console.error("[LoginPaciente] loginComEmailSenha retornou false"); + toast.error("Credenciais inválidas ou usuário sem permissão"); } } catch (error) { console.error("[LoginPaciente] Erro no login:", error); - toast.error("Erro ao fazer login. Tente novamente."); + toast.error("Erro ao fazer login. Verifique suas credenciais."); } finally { setLoading(false); } @@ -158,68 +129,6 @@ const LoginPaciente: React.FC = () => { } }; - // Login LOCAL: cria uma sessão de paciente sem chamar a API - const handleLoginLocal = async () => { - const email = formData.email.trim(); - const senha = formData.senha; - - console.log("[LoginPaciente] Login local - tentando com API primeiro"); - - // Tentar fazer login via API mesmo no modo "local" - setLoading(true); - try { - // Fazer login via API Supabase - await authService.login({ - email: email, - password: senha, - }); - - console.log("[LoginPaciente] Login via API bem-sucedido!"); - - // Buscar dados do paciente da API - const pacientes = await patientService.list(); - const paciente = pacientes.find((p: any) => p.email === email); - - if (paciente) { - console.log( - "[LoginPaciente] Paciente encontrado na API:", - paciente.full_name - ); - const ok = await loginPaciente({ - id: paciente.id, - nome: paciente.full_name, - email: paciente.email, - }); - - if (ok) { - navigate("/acompanhamento"); - } else { - toast.error("Erro ao processar login"); - } - } else { - console.log( - "[LoginPaciente] Paciente não encontrado na API, usando dados locais" - ); - const ok = await loginPaciente({ - id: email, - nome: email.split("@")[0], - email: email, - }); - - if (ok) { - navigate("/acompanhamento"); - } else { - toast.error("Erro ao processar login"); - } - } - } catch (err) { - console.error("[LoginPaciente] Erro no login:", err); - toast.error("Erro ao fazer login"); - } finally { - setLoading(false); - } - }; - return (
@@ -335,8 +244,7 @@ const LoginPaciente: React.FC = () => { **/}