{
  "name": "Custo AWS",
  "nodes": [
    {
      "parameters": {
        "jsCode": "// Calculate date range (yesterday)\nconst today = new Date();\nconst yesterday = new Date(today);\nyesterday.setDate(yesterday.getDate() - 1);\n\nconst startDate = yesterday.toISOString().split('T')[0];\nconst endDate = today.toISOString().split('T')[0];\n\nconsole.log('Date range:', startDate, 'to', endDate);\n\nreturn [{\n  json: {\n    start_date: startDate,\n    end_date: endDate,\n    timestamp: new Date().toISOString()\n  }\n}];"
      },
      "id": "b9da2034-b433-4bfb-ac52-6db4012ed1a7",
      "name": "Calculate Dates",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -400,
        800
      ]
    },
    {
      "parameters": {
        "command": "=aws ce get-cost-and-usage --time-period Start={{ $json.start_date }},End={{ $json.end_date }} --granularity DAILY --metrics UnblendedCost UsageQuantity --group-by Type=DIMENSION,Key=SERVICE Type=DIMENSION,Key=REGION --output json"
      },
      "id": "fa295a65-9162-4e51-adeb-933f249fdaf8",
      "name": "Fetch AWS Costs (CLI)",
      "type": "n8n-nodes-base.executeCommand",
      "typeVersion": 1,
      "position": [
        -176,
        800
      ]
    },
    {
      "parameters": {
        "jsCode": "const stdout = $input.first().json.stdout;\nconst awsResponse = JSON.parse(stdout);\n\nconst results = [];\n\nfor (const timeResult of awsResponse.ResultsByTime) {\n  const date = timeResult.TimePeriod.Start;\n  \n  for (const group of timeResult.Groups || []) {\n    const serviceName = group.Keys[0];\n    const region = group.Keys[1] || 'global';\n    const cost = parseFloat(group.Metrics.UnblendedCost.Amount);\n    const usage = parseFloat(group.Metrics.UsageQuantity?.Amount || 0);\n    \n    // Mudei para > 0 ou remova o if completamente\n    if (cost > 0 || usage > 0) {\n      results.push({\n        json: {\n          date: date,\n          service_name: serviceName,\n          region: region,\n          amount: cost,\n          usage_quantity: usage,\n          unit: group.Metrics.UnblendedCost.Unit\n        }\n      });\n    }\n  }\n}\n\nif (results.length === 0) {\n  return [];\n}\n\nreturn results;"
      },
      "id": "b448d2fa-8fa7-46c7-9bb7-dcbe66aa26ae",
      "name": "Parse CLI Output",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        48,
        800
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT \n  service_name,\n  SUM(amount) as total_cost\nFROM daily_costs\nWHERE date = CURRENT_DATE - INTERVAL '1 day'\nGROUP BY service_name\nORDER BY total_cost DESC\nLIMIT 5;",
        "options": {}
      },
      "id": "7b6c21c4-f014-4d4b-9957-d0d2146681ac",
      "name": "Top 5 Services",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.4,
      "position": [
        496,
        800
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT \n  SUM(amount) as mtd_total\nFROM daily_costs\nWHERE date >= DATE_TRUNC('month', CURRENT_DATE);",
        "options": {}
      },
      "id": "8109d16b-bbaf-41c5-b109-6b8c36420021",
      "name": "Month-to-Date Total",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.4,
      "position": [
        496,
        992
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Pegar todos os inputs do Merge\nconst allInputs = $input.all();\n\n// Identificar cada resultado pelos campos que retornam\nlet dailyTotal = { total_cost: 0, service_count: 0 };\nlet topServices = [];\nlet monthlyData = { mtd_total: 0 };\n\n// Processar cada input\nfor (const input of allInputs) {\n  const data = input.json;\n  \n  // Identificar Calculate Daily Total (tem date, total_cost, service_count)\n  if (data.date && data.total_cost !== undefined && data.service_count !== undefined) {\n    dailyTotal = data;\n  }\n  \n  // Identificar Month-to-Date (tem mtd_total)\n  if (data.mtd_total !== undefined) {\n    monthlyData = data;\n  }\n  \n  // Identificar Top 5 Services (tem service_name)\n  if (data.service_name && data.total_cost !== undefined) {\n    topServices.push(data);\n  }\n}\n\n// Pegar configura\u00e7\u00e3o\nconst config = $('Configuration1').item.json;\n\nconst dailyCost = parseFloat(dailyTotal.total_cost || 0);\nconst mtdCost = parseFloat(monthlyData.mtd_total || 0);\nconst budgetLimit = parseFloat(config.budget_limit_monthly);\nconst warningThreshold = parseFloat(config.budget_alert_threshold_warning);\nconst criticalThreshold = parseFloat(config.budget_alert_threshold_critical);\n\nconst budgetPercentage = (mtdCost / budgetLimit) * 100;\n\nlet alertLevel = 'normal';\nlet alertEmoji = '\ud83d\udcb0';\n\nif (budgetPercentage >= criticalThreshold) {\n  alertLevel = 'critical';\n  alertEmoji = '\ud83d\udea8';\n} else if (budgetPercentage >= warningThreshold) {\n  alertLevel = 'warning';\n  alertEmoji = '\u26a0\ufe0f';\n}\n\nlet topServicesText = '';\nif (topServices.length > 0) {\n  topServicesText = topServices.map((svc, idx) => {\n    const cost = parseFloat(svc.total_cost).toFixed(2);\n    return `${idx + 1}. ${svc.service_name}: $${cost}`;\n  }).join('\\n');\n} else {\n  topServicesText = 'No data available';\n}\n\nconst daysInMonth = new Date(new Date().getFullYear(), new Date().getMonth() + 1, 0).getDate();\nconst dayOfMonth = new Date().getDate();\nconst projectedCost = dayOfMonth > 0 ? (mtdCost / dayOfMonth) * daysInMonth : 0;\n\nreturn [{\n  json: {\n    daily_cost: dailyCost,\n    mtd_cost: mtdCost,\n    budget_limit: budgetLimit,\n    budget_percentage: budgetPercentage,\n    budget_remaining: budgetLimit - mtdCost,\n    projected_monthly_cost: projectedCost,\n    alert_level: alertLevel,\n    alert_emoji: alertEmoji,\n    top_services: topServicesText,\n    service_count: dailyTotal.service_count || 0,\n    date: new Date().toISOString().split('T')[0]\n  }\n}];"
      },
      "id": "88ff903a-f808-41b7-bf5b-a39036fd7e0c",
      "name": "Analyze Budget",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        800,
        640
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 1
          },
          "conditions": [
            {
              "leftValue": "={{ $json.alert_level }}",
              "rightValue": "normal",
              "operator": {
                "type": "string",
                "operation": "notEquals"
              },
              "id": "0cf1ab3d-af4d-4037-8eb6-cf1c50fe0934"
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "f3204255-2d66-4521-9ce8-792a40dc30cf",
      "name": "Should Alert?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        976,
        736
      ]
    },
    {
      "parameters": {
        "jsCode": "const data = $json;\n\nlet msg = `${data.alert_emoji} *AWS Cost Report*\\n\\n`;\nmsg += `\ud83d\udcc5 *Daily Summary*\\n`;\nmsg += `Date: ${data.date}\\n`;\nmsg += `Cost: $${data.daily_cost.toFixed(2)}\\n`;\nmsg += `Services: ${data.service_count}\\n\\n`;\n\nmsg += `\ud83d\udcca *Monthly Budget*\\n`;\nmsg += `MTD: $${data.mtd_cost.toFixed(2)}\\n`;\nmsg += `Limit: $${data.budget_limit.toFixed(2)}\\n`;\nmsg += `Used: ${data.budget_percentage.toFixed(1)}%\\n`;\nmsg += `Remaining: $${data.budget_remaining.toFixed(2)}\\n\\n`;\n\nmsg += `\ud83d\udcc8 *Projection*\\n`;\nmsg += `Est. End: $${data.projected_monthly_cost.toFixed(2)}\\n`;\n\nif (data.projected_monthly_cost > data.budget_limit) {\n  const overrun = data.projected_monthly_cost - data.budget_limit;\n  msg += `\u26a0\ufe0f Overrun: $${overrun.toFixed(2)}\\n`;\n}\nmsg += `\\n`;\n\nmsg += `\ud83c\udfc6 *Top 5 Services*\\n`;\nmsg += data.top_services + '\\n\\n';\n\nif (data.alert_level === 'critical') {\n  msg += `\ud83d\udea8 *CRITICAL*: Budget >80%!\\n`;\n} else if (data.alert_level === 'warning') {\n  msg += `\u26a0\ufe0f *WARNING*: Budget >50%\\n`;\n}\n\nmsg += `\\n---\\n\ud83e\udd16 _AWS Cost Management_`;\n\nreturn [{ json: { ...data, telegram_message: msg } }];"
      },
      "id": "21e749c5-3271-49b8-bb03-c22a49068980",
      "name": "Format Message",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1184,
        608
      ]
    },
    {
      "parameters": {
        "chatId": "={{ $env.ID_FIN_BOT }}",
        "text": "={{ $json.telegram_message }}",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "id": "10870833-510b-4946-b3fd-2b357d11ce3d",
      "name": "Send Telegram",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        1360,
        720
      ],
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours"
            }
          ]
        }
      },
      "id": "598aed8f-8406-4db5-8f72-cc47e24d004e",
      "name": "Daily Cost Collection - 8AM1",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1,
      "position": [
        -848,
        800
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "CREATE TABLE IF NOT EXISTS daily_costs (\n  id SERIAL PRIMARY KEY,\n  date DATE NOT NULL,\n  service_name VARCHAR(100) NOT NULL,\n  region VARCHAR(50),\n  amount DECIMAL(10,2) NOT NULL,\n  usage_quantity DECIMAL(15,4),\n  unit VARCHAR(50),\n  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n  UNIQUE(date, service_name, region)\n);\n\nINSERT INTO daily_costs (date, service_name, region, amount, usage_quantity, unit)\nVALUES (\n  '{{ $json.date }}',\n  '{{ $json.service_name }}',\n  '{{ $json.region }}',\n  {{ $json.amount }},\n  {{ $json.usage_quantity }},\n  '{{ $json.unit }}'\n)\nON CONFLICT (date, service_name, region)\nDO UPDATE SET\n  amount = EXCLUDED.amount,\n  usage_quantity = EXCLUDED.usage_quantity,\n  created_at = CURRENT_TIMESTAMP;",
        "options": {}
      },
      "id": "2082894a-3743-4ec5-b3e0-b65412b606e1",
      "name": "Save to PostgreSQL1",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.4,
      "position": [
        272,
        800
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT \n  date,\n  SUM(amount) as total_cost,\n  COUNT(DISTINCT service_name) as service_count\nFROM daily_costs\nWHERE date = CURRENT_DATE - INTERVAL '1 day'\nGROUP BY date;",
        "options": {}
      },
      "id": "67607dcc-3a0e-434d-b868-beec7bd9fb58",
      "name": "Calculate Daily Total1",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.4,
      "position": [
        496,
        640
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "CREATE TABLE IF NOT EXISTS budget_alerts (\n  id SERIAL PRIMARY KEY,\n  date DATE NOT NULL,\n  budget_limit DECIMAL(10,2),\n  mtd_cost DECIMAL(10,2),\n  budget_percentage DECIMAL(5,2),\n  alert_level VARCHAR(20),\n  projected_monthly_cost DECIMAL(10,2),\n  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n\nINSERT INTO budget_alerts (date, budget_limit, mtd_cost, budget_percentage, alert_level, projected_monthly_cost)\nVALUES ('{{ $json.date }}', {{ $json.budget_limit }}, {{ $json.mtd_cost }}, {{ $json.budget_percentage }}, '{{ $json.alert_level }}', {{ $json.projected_monthly_cost }});",
        "options": {}
      },
      "id": "03ecc107-4c1b-4163-b165-9d8f38f2e569",
      "name": "Save Alert History1",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.4,
      "position": [
        1360,
        880
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "fields": {
          "values": [
            {
              "name": "budget_limit_monthly",
              "stringValue": "10"
            },
            {
              "name": "budget_alert_threshold_warning",
              "stringValue": "50"
            },
            {
              "name": "budget_alert_threshold_critical",
              "stringValue": "80"
            },
            {
              "name": "telegram_chat_id",
              "stringValue": "={{ $env.ID_FIN_BOT }}"
            }
          ]
        },
        "options": {}
      },
      "id": "aa3d2603-2c98-468c-97fe-001d5fdeb3b2",
      "name": "Configuration1",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3,
      "position": [
        -592,
        784
      ]
    },
    {
      "parameters": {
        "numberInputs": 3
      },
      "type": "n8n-nodes-base.merge",
      "typeVersion": 3.2,
      "position": [
        672,
        784
      ],
      "id": "30629398-a4f6-4623-8a10-6c51ef7c00b6",
      "name": "Merge"
    }
  ],
  "connections": {
    "Calculate Dates": {
      "main": [
        [
          {
            "node": "Fetch AWS Costs (CLI)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch AWS Costs (CLI)": {
      "main": [
        [
          {
            "node": "Parse CLI Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse CLI Output": {
      "main": [
        [
          {
            "node": "Save to PostgreSQL1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Analyze Budget": {
      "main": [
        [
          {
            "node": "Should Alert?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Should Alert?": {
      "main": [
        [
          {
            "node": "Format Message",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Format Message": {
      "main": [
        [
          {
            "node": "Send Telegram",
            "type": "main",
            "index": 0
          },
          {
            "node": "Save Alert History1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Daily Cost Collection - 8AM1": {
      "main": [
        [
          {
            "node": "Configuration1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save to PostgreSQL1": {
      "main": [
        [
          {
            "node": "Calculate Daily Total1",
            "type": "main",
            "index": 0
          },
          {
            "node": "Top 5 Services",
            "type": "main",
            "index": 0
          },
          {
            "node": "Month-to-Date Total",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Daily Total1": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Configuration1": {
      "main": [
        [
          {
            "node": "Calculate Dates",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Month-to-Date Total": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 2
          }
        ]
      ]
    },
    "Top 5 Services": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Merge": {
      "main": [
        [
          {
            "node": "Analyze Budget",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": true,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "ef3477e5-5663-4a0a-89a6-42ad2fcdb259",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "id": "FY9kIZOJlGWDb2vi",
  "tags": []
}