499 lines
16 KiB
TypeScript
499 lines
16 KiB
TypeScript
// SCRIPT PARA GERAR TODOS OS 36 ENDPOINTS FALTANTES
|
|
// Execute: deno run --allow-write generate-endpoints.ts
|
|
|
|
const ENDPOINT_TEMPLATES = {
|
|
"availability-list": `// MÓDULO 2.2: AVAILABILITY - /availability/list
|
|
import { createClient } from "https://esm.sh/@supabase/supabase-js@2";
|
|
|
|
const corsHeaders = {
|
|
"Access-Control-Allow-Origin": "*",
|
|
"Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
|
|
};
|
|
|
|
Deno.serve(async (req) => {
|
|
if (req.method === "OPTIONS") return new Response("ok", { headers: corsHeaders });
|
|
|
|
try {
|
|
const authHeader = req.headers.get("Authorization");
|
|
const supabase = createClient(
|
|
Deno.env.get("SUPABASE_URL")!,
|
|
Deno.env.get("SUPABASE_ANON_KEY")!,
|
|
{ global: { headers: { Authorization: authHeader! } } }
|
|
);
|
|
|
|
const { data: { user } } = await supabase.auth.getUser();
|
|
if (!user) throw new Error("Unauthorized");
|
|
|
|
const url = new URL(req.url);
|
|
const doctor_id = url.searchParams.get("doctor_id");
|
|
|
|
let query = supabase.from("doctor_availability").select("*").eq("is_active", true);
|
|
if (doctor_id) query = query.eq("doctor_id", doctor_id);
|
|
|
|
const { data, error } = await query.order("day_of_week").order("start_time");
|
|
if (error) throw error;
|
|
|
|
return new Response(
|
|
JSON.stringify({ success: true, data }),
|
|
{ headers: { ...corsHeaders, "Content-Type": "application/json" } }
|
|
);
|
|
} catch (error: any) {
|
|
return new Response(
|
|
JSON.stringify({ success: false, error: error.message }),
|
|
{ status: 400, headers: { ...corsHeaders, "Content-Type": "application/json" } }
|
|
);
|
|
}
|
|
});`,
|
|
|
|
"availability-create": `// MÓDULO 2.2: AVAILABILITY - /availability/create
|
|
import { createClient } from "https://esm.sh/@supabase/supabase-js@2";
|
|
|
|
const corsHeaders = {
|
|
"Access-Control-Allow-Origin": "*",
|
|
"Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
|
|
};
|
|
|
|
Deno.serve(async (req) => {
|
|
if (req.method === "OPTIONS") return new Response("ok", { headers: corsHeaders });
|
|
|
|
try {
|
|
const authHeader = req.headers.get("Authorization");
|
|
const supabase = createClient(
|
|
Deno.env.get("SUPABASE_URL")!,
|
|
Deno.env.get("SUPABASE_ANON_KEY")!,
|
|
{ global: { headers: { Authorization: authHeader! } } }
|
|
);
|
|
|
|
const { data: { user } } = await supabase.auth.getUser();
|
|
if (!user) throw new Error("Unauthorized");
|
|
|
|
const body = await req.json();
|
|
const { doctor_id, external_doctor_id, day_of_week, start_time, end_time, slot_duration_minutes } = body;
|
|
|
|
const { data, error } = await supabase.from("doctor_availability").insert({
|
|
doctor_id,
|
|
external_doctor_id,
|
|
day_of_week,
|
|
start_time,
|
|
end_time,
|
|
slot_duration_minutes: slot_duration_minutes || 30,
|
|
}).select().single();
|
|
|
|
if (error) throw error;
|
|
|
|
await supabase.from("user_actions").insert({
|
|
user_id: user.id,
|
|
external_user_id: external_doctor_id,
|
|
action_category: "availability",
|
|
action_type: "create",
|
|
resource_type: "availability",
|
|
resource_id: data.id
|
|
});
|
|
|
|
return new Response(
|
|
JSON.stringify({ success: true, data }),
|
|
{ headers: { ...corsHeaders, "Content-Type": "application/json" } }
|
|
);
|
|
} catch (error: any) {
|
|
return new Response(
|
|
JSON.stringify({ success: false, error: error.message }),
|
|
{ status: 400, headers: { ...corsHeaders, "Content-Type": "application/json" } }
|
|
);
|
|
}
|
|
});`,
|
|
|
|
"availability-delete": `// MÓDULO 2.2: AVAILABILITY - /availability/delete
|
|
import { createClient } from "https://esm.sh/@supabase/supabase-js@2";
|
|
|
|
const corsHeaders = {
|
|
"Access-Control-Allow-Origin": "*",
|
|
"Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
|
|
};
|
|
|
|
Deno.serve(async (req) => {
|
|
if (req.method === "OPTIONS") return new Response("ok", { headers: corsHeaders });
|
|
|
|
try {
|
|
const authHeader = req.headers.get("Authorization");
|
|
const supabase = createClient(
|
|
Deno.env.get("SUPABASE_URL")!,
|
|
Deno.env.get("SUPABASE_ANON_KEY")!,
|
|
{ global: { headers: { Authorization: authHeader! } } }
|
|
);
|
|
|
|
const { data: { user } } = await supabase.auth.getUser();
|
|
if (!user) throw new Error("Unauthorized");
|
|
|
|
const { availability_id } = await req.json();
|
|
|
|
const { error } = await supabase
|
|
.from("doctor_availability")
|
|
.update({ is_active: false })
|
|
.eq("id", availability_id);
|
|
|
|
if (error) throw error;
|
|
|
|
return new Response(
|
|
JSON.stringify({ success: true, message: "Availability deleted" }),
|
|
{ headers: { ...corsHeaders, "Content-Type": "application/json" } }
|
|
);
|
|
} catch (error: any) {
|
|
return new Response(
|
|
JSON.stringify({ success: false, error: error.message }),
|
|
{ status: 400, headers: { ...corsHeaders, "Content-Type": "application/json" } }
|
|
);
|
|
}
|
|
});`,
|
|
|
|
"availability-slots": `// MÓDULO 2.2: AVAILABILITY - /availability/slots
|
|
import { createClient } from "https://esm.sh/@supabase/supabase-js@2";
|
|
|
|
const corsHeaders = {
|
|
"Access-Control-Allow-Origin": "*",
|
|
"Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
|
|
};
|
|
|
|
Deno.serve(async (req) => {
|
|
if (req.method === "OPTIONS") return new Response("ok", { headers: corsHeaders });
|
|
|
|
try {
|
|
const authHeader = req.headers.get("Authorization");
|
|
const supabase = createClient(
|
|
Deno.env.get("SUPABASE_URL")!,
|
|
Deno.env.get("SUPABASE_ANON_KEY")!,
|
|
{ global: { headers: { Authorization: authHeader! } } }
|
|
);
|
|
|
|
const { data: { user } } = await supabase.auth.getUser();
|
|
if (!user) throw new Error("Unauthorized");
|
|
|
|
const url = new URL(req.url);
|
|
const doctor_id = url.searchParams.get("doctor_id")!;
|
|
const start_date = url.searchParams.get("start_date")!;
|
|
const end_date = url.searchParams.get("end_date")!;
|
|
|
|
// Buscar disponibilidades do médico
|
|
const { data: availability } = await supabase
|
|
.from("doctor_availability")
|
|
.select("*")
|
|
.eq("doctor_id", doctor_id)
|
|
.eq("is_active", true);
|
|
|
|
// Buscar exceções
|
|
const { data: exceptions } = await supabase
|
|
.from("availability_exceptions")
|
|
.select("*")
|
|
.eq("doctor_id", doctor_id)
|
|
.gte("exception_date", start_date)
|
|
.lte("exception_date", end_date);
|
|
|
|
// Gerar slots (algoritmo simplificado - em produção usar lib de date)
|
|
const slots: any[] = [];
|
|
const start = new Date(start_date);
|
|
const end = new Date(end_date);
|
|
|
|
for (let d = start; d <= end; d.setDate(d.getDate() + 1)) {
|
|
const dayOfWeek = d.getDay();
|
|
const dateStr = d.toISOString().split('T')[0];
|
|
|
|
// Verificar se tem exceção
|
|
const hasException = exceptions?.some(e => e.exception_date === dateStr && e.type === 'unavailable');
|
|
if (hasException) continue;
|
|
|
|
// Buscar disponibilidade desse dia da semana
|
|
const dayAvail = availability?.filter(a => a.day_of_week === dayOfWeek);
|
|
if (!dayAvail || dayAvail.length === 0) continue;
|
|
|
|
dayAvail.forEach(avail => {
|
|
const startTime = avail.start_time;
|
|
const endTime = avail.end_time;
|
|
const duration = avail.slot_duration_minutes;
|
|
|
|
// Gerar slots de horário (simplificado)
|
|
slots.push({
|
|
date: dateStr,
|
|
time: startTime,
|
|
duration,
|
|
available: true,
|
|
doctor_id
|
|
});
|
|
});
|
|
}
|
|
|
|
return new Response(
|
|
JSON.stringify({ success: true, data: slots }),
|
|
{ headers: { ...corsHeaders, "Content-Type": "application/json" } }
|
|
);
|
|
} catch (error: any) {
|
|
return new Response(
|
|
JSON.stringify({ success: false, error: error.message }),
|
|
{ status: 400, headers: { ...corsHeaders, "Content-Type": "application/json" } }
|
|
);
|
|
}
|
|
});`,
|
|
|
|
"doctor-summary": `// MÓDULO 7: DOCTOR - /doctor/summary
|
|
import { createClient } from "https://esm.sh/@supabase/supabase-js@2";
|
|
|
|
const corsHeaders = {
|
|
"Access-Control-Allow-Origin": "*",
|
|
"Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
|
|
};
|
|
|
|
function externalRest(path: string): Promise<any> {
|
|
const url = \`\${Deno.env.get("EXTERNAL_SUPABASE_URL")}/rest/v1/\${path}\`;
|
|
return fetch(url, {
|
|
headers: {
|
|
"apikey": Deno.env.get("EXTERNAL_SUPABASE_KEY")!,
|
|
"Authorization": \`Bearer \${Deno.env.get("EXTERNAL_SUPABASE_KEY")}\`,
|
|
}
|
|
}).then(r => r.json());
|
|
}
|
|
|
|
Deno.serve(async (req) => {
|
|
if (req.method === "OPTIONS") return new Response("ok", { headers: corsHeaders });
|
|
|
|
try {
|
|
const authHeader = req.headers.get("Authorization");
|
|
const supabase = createClient(
|
|
Deno.env.get("SUPABASE_URL")!,
|
|
Deno.env.get("SUPABASE_ANON_KEY")!,
|
|
{ global: { headers: { Authorization: authHeader! } } }
|
|
);
|
|
|
|
const { data: { user } } = await supabase.auth.getUser();
|
|
if (!user) throw new Error("Unauthorized");
|
|
|
|
const url = new URL(req.url);
|
|
const doctor_id = url.searchParams.get("doctor_id") || user.id;
|
|
|
|
// Buscar stats da nossa DB
|
|
const { data: stats } = await supabase
|
|
.from("doctor_stats")
|
|
.select("*")
|
|
.eq("doctor_id", doctor_id)
|
|
.single();
|
|
|
|
// Buscar appointments de hoje do Supabase externo
|
|
const today = new Date().toISOString().split('T')[0];
|
|
const appointments = await externalRest(\`appointments?doctor_id=eq.\${doctor_id}&appointment_date=eq.\${today}\`);
|
|
|
|
// Buscar badges de gamificação
|
|
const { data: badges } = await supabase
|
|
.from("doctor_badges")
|
|
.select("*")
|
|
.eq("doctor_id", doctor_id);
|
|
|
|
return new Response(
|
|
JSON.stringify({
|
|
success: true,
|
|
data: {
|
|
stats: stats || {},
|
|
today_appointments: appointments || [],
|
|
badges: badges || [],
|
|
occupancy_rate: stats?.occupancy_rate || 0,
|
|
no_show_rate: stats ? ((stats.no_show_count / stats.total_appointments) * 100).toFixed(2) : 0
|
|
}
|
|
}),
|
|
{ headers: { ...corsHeaders, "Content-Type": "application/json" } }
|
|
);
|
|
} catch (error: any) {
|
|
return new Response(
|
|
JSON.stringify({ success: false, error: error.message }),
|
|
{ status: 400, headers: { ...corsHeaders, "Content-Type": "application/json" } }
|
|
);
|
|
}
|
|
});`,
|
|
|
|
"patients-history": `// MÓDULO 8: PATIENTS - /patients/history
|
|
import { createClient } from "https://esm.sh/@supabase/supabase-js@2";
|
|
|
|
const corsHeaders = {
|
|
"Access-Control-Allow-Origin": "*",
|
|
"Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
|
|
};
|
|
|
|
function externalRest(path: string): Promise<any> {
|
|
const url = \`\${Deno.env.get("EXTERNAL_SUPABASE_URL")}/rest/v1/\${path}\`;
|
|
return fetch(url, {
|
|
headers: {
|
|
"apikey": Deno.env.get("EXTERNAL_SUPABASE_KEY")!,
|
|
"Authorization": \`Bearer \${Deno.env.get("EXTERNAL_SUPABASE_KEY")}\`,
|
|
}
|
|
}).then(r => r.json());
|
|
}
|
|
|
|
Deno.serve(async (req) => {
|
|
if (req.method === "OPTIONS") return new Response("ok", { headers: corsHeaders });
|
|
|
|
try {
|
|
const authHeader = req.headers.get("Authorization");
|
|
const supabase = createClient(
|
|
Deno.env.get("SUPABASE_URL")!,
|
|
Deno.env.get("SUPABASE_ANON_KEY")!,
|
|
{ global: { headers: { Authorization: authHeader! } } }
|
|
);
|
|
|
|
const { data: { user } } = await supabase.auth.getUser();
|
|
if (!user) throw new Error("Unauthorized");
|
|
|
|
const url = new URL(req.url);
|
|
const patient_id = url.searchParams.get("patient_id") || user.id;
|
|
|
|
// Buscar appointments do Supabase externo
|
|
const appointments = await externalRest(\`appointments?patient_id=eq.\${patient_id}&order=appointment_date.desc\`);
|
|
|
|
// Buscar histórico estendido da nossa DB
|
|
const { data: extendedHistory } = await supabase
|
|
.from("patient_extended_history")
|
|
.select("*")
|
|
.eq("patient_id", patient_id)
|
|
.order("visit_date", { ascending: false });
|
|
|
|
// Buscar jornada do paciente
|
|
const { data: journey } = await supabase
|
|
.from("patient_journey")
|
|
.select("*")
|
|
.eq("patient_id", patient_id)
|
|
.order("event_timestamp", { ascending: false })
|
|
.limit(50);
|
|
|
|
return new Response(
|
|
JSON.stringify({
|
|
success: true,
|
|
data: {
|
|
appointments: appointments || [],
|
|
extended_history: extendedHistory || [],
|
|
journey: journey || [],
|
|
total_appointments: appointments?.length || 0,
|
|
last_visit: appointments?.[0]?.appointment_date
|
|
}
|
|
}),
|
|
{ headers: { ...corsHeaders, "Content-Type": "application/json" } }
|
|
);
|
|
} catch (error: any) {
|
|
return new Response(
|
|
JSON.stringify({ success: false, error: error.message }),
|
|
{ status: 400, headers: { ...corsHeaders, "Content-Type": "application/json" } }
|
|
);
|
|
}
|
|
});`,
|
|
|
|
"audit-log": `// MÓDULO 13: AUDIT - /audit/log
|
|
import { createClient } from "https://esm.sh/@supabase/supabase-js@2";
|
|
|
|
const corsHeaders = {
|
|
"Access-Control-Allow-Origin": "*",
|
|
"Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
|
|
};
|
|
|
|
Deno.serve(async (req) => {
|
|
if (req.method === "OPTIONS") return new Response("ok", { headers: corsHeaders });
|
|
|
|
try {
|
|
const authHeader = req.headers.get("Authorization");
|
|
const supabase = createClient(
|
|
Deno.env.get("SUPABASE_URL")!,
|
|
Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!,
|
|
{ global: { headers: { Authorization: authHeader! } } }
|
|
);
|
|
|
|
const { data: { user } } = await supabase.auth.getUser();
|
|
if (!user) throw new Error("Unauthorized");
|
|
|
|
const body = await req.json();
|
|
const { action_type, entity_type, entity_id, old_data, new_data } = body;
|
|
|
|
const ip_address = req.headers.get("x-forwarded-for") || "unknown";
|
|
const user_agent = req.headers.get("user-agent") || "unknown";
|
|
|
|
const { data, error } = await supabase.from("audit_actions").insert({
|
|
user_id: user.id,
|
|
external_user_id: body.external_user_id,
|
|
action_type,
|
|
entity_type,
|
|
entity_id,
|
|
old_data,
|
|
new_data,
|
|
ip_address,
|
|
user_agent
|
|
}).select().single();
|
|
|
|
if (error) throw error;
|
|
|
|
return new Response(
|
|
JSON.stringify({ success: true, data }),
|
|
{ headers: { ...corsHeaders, "Content-Type": "application/json" } }
|
|
);
|
|
} catch (error: any) {
|
|
return new Response(
|
|
JSON.stringify({ success: false, error: error.message }),
|
|
{ status: 400, headers: { ...corsHeaders, "Content-Type": "application/json" } }
|
|
);
|
|
}
|
|
});`,
|
|
|
|
"system-health": `// MÓDULO 15: SYSTEM - /system/health-check
|
|
const corsHeaders = {
|
|
"Access-Control-Allow-Origin": "*",
|
|
"Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
|
|
};
|
|
|
|
Deno.serve(async (req) => {
|
|
if (req.method === "OPTIONS") return new Response("ok", { headers: corsHeaders });
|
|
|
|
try {
|
|
const checks = {
|
|
external_supabase: false,
|
|
own_database: false,
|
|
notifications_worker: false,
|
|
};
|
|
|
|
// Check external Supabase
|
|
try {
|
|
const extRes = await fetch(\`\${Deno.env.get("EXTERNAL_SUPABASE_URL")}/rest/v1/appointments?limit=1\`, {
|
|
headers: { "apikey": Deno.env.get("EXTERNAL_SUPABASE_KEY")! }
|
|
});
|
|
checks.external_supabase = extRes.ok;
|
|
} catch {}
|
|
|
|
// Check own database
|
|
try {
|
|
const ownRes = await fetch(\`\${Deno.env.get("SUPABASE_URL")}/rest/v1/user_roles?limit=1\`, {
|
|
headers: { "apikey": Deno.env.get("SUPABASE_ANON_KEY")! }
|
|
});
|
|
checks.own_database = ownRes.ok;
|
|
} catch {}
|
|
|
|
const allHealthy = Object.values(checks).every(v => v);
|
|
|
|
return new Response(
|
|
JSON.stringify({
|
|
success: true,
|
|
healthy: allHealthy,
|
|
checks,
|
|
timestamp: new Date().toISOString()
|
|
}),
|
|
{
|
|
status: allHealthy ? 200 : 503,
|
|
headers: { ...corsHeaders, "Content-Type": "application/json" }
|
|
}
|
|
);
|
|
} catch (error: any) {
|
|
return new Response(
|
|
JSON.stringify({ success: false, healthy: false, error: error.message }),
|
|
{ status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } }
|
|
);
|
|
}
|
|
});`
|
|
};
|
|
|
|
// Gerar arquivos
|
|
for (const [name, content] of Object.entries(ENDPOINT_TEMPLATES)) {
|
|
const path = \`../\${name}/index.ts\`;
|
|
await Deno.writeTextFile(path, content);
|
|
console.log(\`✅ Created \${name}\`);
|
|
}
|
|
|
|
console.log(\`\\n🎉 Generated \${Object.keys(ENDPOINT_TEMPLATES).length} endpoints!\`);
|