mika-agent-assist/supabase/functions/configure-telegram-webhook/index.ts
gpt-engineer-app[bot] 262ff41baa Changes
Co-authored-by: domfelipe <53182096+domfelipe@users.noreply.github.com>
2026-05-23 17:55:11 +00:00

140 lines
4.5 KiB
TypeScript

// configure-telegram-webhook
// Mantém compatibilidade com o wizard: garante que o webhook do Telegram fique desligado,
// pois o Hermes responde via polling/runtime.
import { createClient } from "https://esm.sh/@supabase/supabase-js@2.57.4";
import { corsHeaders } from "../_shared/cors.ts";
import { telegramApi } from "../_shared/telegram.ts";
function jsonResponse(body: unknown, status = 200): Response {
return new Response(JSON.stringify(body), {
status,
headers: { ...corsHeaders, "Content-Type": "application/json" },
});
}
// deno-lint-ignore no-explicit-any
async function getDecryptedSecret(
admin: any,
secretId: string,
): Promise<string | null> {
const { data, error } = await admin
.rpc("vault_decrypt_secret", { secret_id: secretId })
.single();
if (!error && data) {
// deno-lint-ignore no-explicit-any
return (data as any).decrypted_secret ?? (data as unknown as string);
}
return null;
}
Deno.serve(async (req) => {
if (req.method === "OPTIONS") {
return new Response(null, { headers: corsHeaders });
}
try {
const supabaseUrl = Deno.env.get("SUPABASE_URL")!;
const serviceKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!;
const anonKey = Deno.env.get("SUPABASE_ANON_KEY")!;
const authHeader = req.headers.get("Authorization") ?? "";
const userClient = createClient(supabaseUrl, anonKey, {
global: { headers: { Authorization: authHeader } },
});
const { data: userData, error: userErr } = await userClient.auth.getUser();
if (userErr || !userData.user) {
return jsonResponse({ error: "Não autenticado" }, 401);
}
const userId = userData.user.id;
const admin = createClient(supabaseUrl, serviceKey);
const { data: agent, error: agentErr } = await admin
.from("agent_instances")
.select("id, uuid_tenant, telegram_bot_token_vault_id, status")
.eq("user_id", userId)
.maybeSingle();
if (agentErr || !agent) {
return jsonResponse({ error: "Agente não encontrado." }, 404);
}
if (!agent.telegram_bot_token_vault_id) {
return jsonResponse(
{ error: "Conecte um bot antes de configurar o webhook." },
400,
);
}
const token = await getDecryptedSecret(admin, agent.telegram_bot_token_vault_id);
if (!token) {
return jsonResponse(
{ error: "Token corrompido. Desconecte e reconecte o bot." },
500,
);
}
const deleteRes = await telegramApi(token, "deleteWebhook", {
drop_pending_updates: false,
});
if (!deleteRes.ok) {
console.error("deleteWebhook failed", deleteRes);
return jsonResponse(
{ error: "Falha ao liberar o Telegram para o runtime do agente." },
500,
);
}
const { error: updErr } = await admin
.from("agent_instances")
.update({
telegram_webhook_secret: null,
telegram_webhook_configured: false,
updated_at: new Date().toISOString(),
})
.eq("id", agent.id);
if (updErr) {
console.error("agent update error", updErr);
return jsonResponse({ error: "Webhook configurado, mas falha ao salvar." }, 500);
}
// Auto-provisionamento: agora que temos token + webhook, disparamos provision-agent
// de forma assíncrona. Não bloqueamos a resposta — falhas são tratadas via retry/admin.
if (agent.status === "provisioning") {
try {
const provisionUrl = `${supabaseUrl}/functions/v1/provision-agent`;
console.log(
`auto-provision: disparando para agent ${agent.id} (user ${userId})`,
);
// Fire-and-forget: não fazemos await para não bloquear o cliente
fetch(provisionUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${serviceKey}`,
"X-Internal-Secret": Deno.env.get("INTERNAL_FUNCTION_SECRET") ?? "",
},
body: JSON.stringify({
agent_instance_id: agent.id,
user_id: userId,
}),
}).catch((err) =>
console.error("auto-provision fetch error:", err)
);
} catch (err) {
console.error("auto-provision trigger error:", err);
// Não re-throw — wizard do Telegram deve retornar sucesso mesmo se provision falhar
}
}
return jsonResponse({ success: true });
} catch (err) {
console.error("configure-telegram-webhook fatal", err);
return jsonResponse(
{ error: err instanceof Error ? err.message : "Erro inesperado" },
500,
);
}
});