const http = require('http'); const PORT = process.env.PORT || 9091; const HOST = '0.0.0.0'; // Escuta em todas as interfaces (incluindo VPN) const GITEA_API_URL = process.env.GITEA_API_URL || 'http://10.99.0.3:3000/api/v1'; const GITEA_TOKEN = process.env.GITEA_TOKEN || 'ada47ca2fb196319dd10245cc216a327dac857ec'; const GITEA_REPO_OWNER = process.env.GITEA_REPO_OWNER || 'ruicesar'; const GITEA_REPO_NAME = process.env.GITEA_REPO_NAME || 'clube67_newwhats.local'; function log(message) { console.log(`[${new Date().toISOString()}] ${message}`); } // Envia a Issue para o Gitea function createGiteaIssue(title, body, repoName) { const postData = JSON.stringify({ title: title, body: body, assignees: [GITEA_REPO_OWNER] }); const url = new URL(`${GITEA_API_URL}/repos/${GITEA_REPO_OWNER}/${repoName}/issues`); const options = { hostname: url.hostname, port: url.port, path: url.pathname, method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'token ' + GITEA_TOKEN, 'Content-Length': Buffer.byteLength(postData) } }; const req = http.request(options, (res) => { let responseData = ''; res.on('data', chunk => responseData += chunk); res.on('end', () => { if (res.statusCode === 201) { log(`Sucesso: Issue criada no Gitea para o repositório ${repoName}`); } else { log(`Erro ao criar Issue no Gitea no repositório ${repoName} (HTTP ${res.statusCode}): ${responseData}`); } }); }); req.on('error', (e) => { log(`Erro na requisição para o Gitea: ${e.message}`); }); req.write(postData); req.end(); } const server = http.createServer((req, res) => { if (req.method !== 'POST' || req.url !== '/alerts') { res.writeHead(404, { 'Content-Type': 'application/json' }); return res.end(JSON.stringify({ error: 'Endpoint não encontrado' })); } let body = ''; req.on('data', chunk => { body += chunk.toString(); }); req.on('end', () => { try { const payload = JSON.parse(body); log(`Recebido payload de alerta do Grafana.`); // Trata múltiplos alertas enviados no mesmo payload pelo Grafana const alerts = payload.alerts || []; if (alerts.length === 0) { res.writeHead(200, { 'Content-Type': 'application/json' }); return res.end(JSON.stringify({ status: 'ignored', reason: 'Nenhum alerta ativo' })); } alerts.forEach(alert => { const status = alert.status; // firing ou resolved if (status !== 'firing') return; // Ignora alertas resolvidos no momento const alertName = alert.labels.alertname || 'Alerta Sem Nome'; const vpsName = alert.labels.vps || 'Desconhecida'; const repoName = alert.labels.repo || GITEA_REPO_NAME; const summary = alert.annotations.summary || alert.annotations.description || 'Nenhum sumário fornecido.'; const fullLogs = alert.annotations.logs || 'Sem logs anexados.'; const issueTitle = `🚨 [AUTO-BUG] ${alertName} detectado na ${vpsName}`; const issueBody = `### 📟 RELATÓRIO DE ERRO AUTOMÁTICO (SELF-HEALING) O monitoramento centralizado (Grafana/Loki) detectou uma falha crítica em tempo de execução. * **Falha:** \`${alertName}\` * **Origem:** \`${vpsName}\` * **Estado:** \`Disparando\` * **Hora do Evento:** \`${alert.startsAt || new Date().toISOString()}\` --- #### 📝 Detalhes & Logs Extraídos: \`\`\`logs ${summary} ${fullLogs} \`\`\` --- > [!IMPORTANT] > **Ação Recomendada para o Agente de IA:** > 1. Analise a stack trace ou a mensagem de erro acima. > 2. Localize o arquivo de código fonte correspondente no repositório. > 3. Implemente a correção, realize os testes locais e execute o commit/push. `; createGiteaIssue(issueTitle, issueBody, repoName); }); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ status: 'processed' })); } catch (e) { log(`Erro ao processar payload: ${e.message}`); res.writeHead(400, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Payload inválido' })); } }); }); server.listen(PORT, HOST, () => { log(`Grafana-Gitea Bridge ativa em http://${HOST}:${PORT}/alerts`); });