diff --git a/self-healing/README.md b/self-healing/README.md new file mode 100644 index 0000000..57bfc7d --- /dev/null +++ b/self-healing/README.md @@ -0,0 +1,62 @@ +# 🔄 Loop de Auto-Cura (Self-Healing DevOps) com Gitea + Grafana + Loki + +Este diretório contém a infraestrutura e os scripts necessários para integrar o seu sistema de monitoramento centralizado (**Grafana + Loki**) ao **Gitea**, permitindo que erros de runtime nos containers de produção abram Issues automaticamente para serem corrigidas pelo Agente de IA. + +--- + +## 🗺️ Visão Geral da Arquitetura + +```mermaid +graph TD + A[Docker Container na VPS 1] -->|Gera Logs de Erro| B[Promtail / Loki] + B -->|Métricas & Alertas| C[Grafana Alerting] + C -->|Webhook de Alerta| D[Grafana-Gitea Bridge] + D -->|API POST /issues| E[Gitea Server - VPS 3] + E -->|Abre Issue de Bug| F[Agente de IA] + F -->|Corrige, Comita e Dá Push| A +``` + +Como o formato de payload de alertas do Grafana é diferente do formato que o Gitea espera na API de Issues, o **`bridge.js`** atua como o tradutor inteligente desse fluxo, formatando o alerta em um relatório em Markdown limpo e amigável para o Agente. + +--- + +## 🛠️ Como Configurar e Ativar o Fluxo + +### Passo 1: Iniciar a Bridge (`bridge.js`) +A Bridge pode rodar na **VPS 4 (Dev/Builder)** ou na **VPS 1 (Produção/SOC)**. Ela escuta na porta `9091` por padrão. + +Para rodá-la em segundo plano com PM2 ou Node: +```bash +# Iniciar a bridge informando o Token do Gitea +GITEA_TOKEN="ada47ca2fb196319dd10245cc216a327dac857ec" node bridge.js +``` + +### Passo 2: Configurar o Canal de Alerta no Grafana (VPS 1) +1. Acesse o seu Grafana em `http://10.99.0.1:3000`. +2. Vá em **Alerting** -> **Contact points** -> **New contact point**. +3. Escolha um nome (ex: `Gitea-Bridge`). +4. Selecione a integração **Webhook**. +5. No campo **URL**, aponte para a Bridge: `http://10.99.0.4:9091/alerts` (ou o IP onde você subiu a bridge). +6. Clique em **Save contact point**. + +### Passo 3: Criar a Regra de Alerta no Grafana (Loki Query) +1. Vá em **Alerting** -> **Alert rules** -> **New alert rule**. +2. Defina uma consulta LogQL no Loki para monitorar erros no seu container WhatsApp. Exemplo: + ```logql + sum(rate({vps="vps4_whatsapp", job="varlogs"} |= "error" or "Exception" or "Uncaught"[5m])) > 0 + ``` +3. Defina as anotações do Alerta: + * **Summary:** `Foi detectado um erro crítico de execução no container.` + * **Description:** `{{ $values.var_name }} erros detectados nos últimos 5 minutos.` +4. Na seção **Notifications**, vincule o alerta ao Contact Point `Gitea-Bridge` que criamos. +5. Salve a regra. + +--- + +## 🎯 Testando o Fluxo de Auto-Cura + +Quando ocorrer qualquer erro no container, o fluxo será disparado: +1. O Grafana detectará e enviará o alerta para a Bridge. +2. A Bridge criará uma Issue no Gitea com o título: `🚨 [AUTO-BUG] Docker Container Error detectado na vps4_whatsapp`. +3. O corpo da Issue conterá a stack trace do erro. +4. O Agente de IA poderá ler a Issue e realizar o reparo autônomo do código! diff --git a/self-healing/bridge.js b/self-healing/bridge.js new file mode 100644 index 0000000..49eb9e5 --- /dev/null +++ b/self-healing/bridge.js @@ -0,0 +1,127 @@ +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) { + const postData = JSON.stringify({ + title: title, + body: body, + assignees: [GITEA_REPO_OWNER] + }); + + const url = new URL(`${GITEA_API_URL}/repos/${GITEA_REPO_OWNER}/${GITEA_REPO_NAME}/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 alerta.`); + } else { + log(`Erro ao criar Issue no Gitea (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 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); + }); + + 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`); +});