[
  {
    "path": ".dockerignore",
    "content": "node_modules\nnpm-debug.log\n.git\n.gitignore\nREADME.md\n.env\n.nyc_output\ncoverage\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\n\n# Client specific\nclient/node_modules\nclient/build\nclient/dist\nclient/.env\n\n# Docker\nDockerfile\ndocker-compose.yml\n\n# IDE\n.vscode\n.idea\n*.swp\n*.swo\n\n# OS\n.DS_Store\nThumbs.db "
  },
  {
    "path": ".gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n\n\n\n#React node_modules\n/client/node_modules\n\n#React production project\n/client/build\n/.pnp\n.pnp.js\n\n# testing\n/coverage\n\n# production\n/build\n\n# misc\n.DS_Store\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\n\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n"
  },
  {
    "path": ".kiro/agents/bia.json",
    "content": "{\n    \"name\": \"bia\",\n    \"description\": \"Agente especialista em DevOps e Cloud AWS do projeto BIA da Formação AWS.\",\n    \"prompt\": \"Você é um DevOps Engineer especialista em AWS Cloud. Você é parte do time de desenvolvimento do projeto BIA da Formação AWS. Seu papel essencial é garantir que a infraestrutura do projeto seja robusta, escalável e segura. Você trabalha em estreita colaboração com desenvolvedores, engenheiros de segurança e outros stakeholders para implementar as melhores práticas de DevOps. Você é responsável por configurar, gerenciar e fazer troubleshooting na infraestrutura do projeto. Você está configurado dentro de uma EC2 e vai acessar os serviços utilizando a role da instância.\",\n    \"tools\": [\n      \"*\"\n    ],\n    \"resources\": [\n      \"file://AmazonQ.md\",\n      \"file://README.md\",\n      \"file://.kiro/rules/**/*.md\"\n    ]\n  }"
  },
  {
    "path": ".kiro/mcp-db.json",
    "content": "{\n  \"mcpServers\": {\n    \"postgres\": {\n      \"command\": \"docker\",\n      \"args\": [\n        \"run\",\n        \"-i\",\n        \"--rm\",\n        \"--network=bia_default\",\n        \"-e\",\n        \"DATABASE_URI\",\n        \"crystaldba/postgres-mcp\",\n        \"--access-mode=unrestricted\"\n      ],\n      \"env\": {\n        \"DATABASE_URI\": \"postgresql://postgres:postgres@database:5432/bia\"\n      }\n    }\n  }\n}\n//Source: https://github.com/crystaldba/postgres-mcp"
  },
  {
    "path": ".kiro/mcp-ecs.json",
    "content": "{\n  \"mcpServers\": {\n    \"awslabs.ecs-mcp-server\": {\n      \"command\": \"uvx\",\n      \"args\": [\"--from\", \"awslabs-ecs-mcp-server\", \"ecs-mcp-server\"],\n      \"env\": {\n        \"FASTMCP_LOG_LEVEL\": \"ERROR\",\n        \"FASTMCP_LOG_FILE\": \"/path/to/ecs-mcp-server.log\",\n        \"ALLOW_WRITE\": \"false\",\n        \"ALLOW_SENSITIVE_DATA\": \"false\"\n      }\n    }\n  }\n}\n//Source: https://awslabs.github.io/mcp/servers/ecs-mcp-server/"
  },
  {
    "path": ".kiro/rules/dockerfile.md",
    "content": "# Regras para Dockerfile - Projeto BIA\n\n## Filosofia de Desenvolvimento\n- **Público-alvo:** Alunos em aprendizado\n- **Abordagem:** Simplicidade acima de complexidade\n- **Objetivo:** Facilitar compreensão de quem que está na etapa inicial da jornada\n\n## Regras Obrigatórias para Dockerfiles\n\n### 1. Análise Prévia Obrigatória\n- **SEMPRE** verificar o package.json do projeto raiz\n- **SEMPRE** verificar o package.json do client (se existir)\n- **IDENTIFICAR** versão do Node.js necessária\n- **IDENTIFICAR** todas as tecnologias envolvidas (React, Vite, etc.)\n- **VERIFICAR** scripts de build e dependências\n- **VERIFICAR** variáveis de ambiente necessárias (ex: VITE_API_URL)\n- **ANALISAR** o Dockerfile original para configurações específicas\n\n### 2. Configurações Importantes (NÃO IGNORAR)\n- **Imagem base:** Sempre usar ECR (`public.ecr.aws/docker/library/node:XX-slim`)\n- **Upgrade do npm:** Busque incluir, para estarmos sempre mais atualizados\n- **WORKDIR:** Trabalhar com (`/usr/src/app`)\n- **Instalação do curl:** Incluir para health checks\n- **Flags do npm:** Manter `--loglevel=error`, `--legacy-peer-deps` se necessário\n\n### 3. Single Stage Sempre\n- **NUNCA** usar multi-stage builds\n- **NUNCA** sugerir otimizações complexas\n- Manter uma única etapa de build\n\n### 4. Simplicidade Máxima\n- **EVITAR** mudanças de permissões (chmod, chown)\n- **EVITAR** criação de usuários não-root\n- **EVITAR** otimizações avançadas de camadas\n- Usar comandos básicos e diretos\n\n### 5. Processo de Validação\n- **SEMPRE** perguntar se é para testar o Dockerfile\n- **QUANDO TESTAR:** Executar o projeto e verificar a rota de health check\n- **ROTA DE HEALTH:** Confirmar que `/api/versao` está respondendo corretamente\n\n### 6. Criação de Arquivos\n- **NUNCA** sobrescrever Dockerfile existente\n- **SEMPRE** avisar ao usuário onde o novo Dockerfile está sendo criado\n- **SUGERIR** nome alternativo se já existir (ex: Dockerfile.new, Dockerfile.backup)\n\n## Comandos de Teste\n```bash\n# Build da imagem\ndocker build -t bia-app .\n\n# Execução do container\ndocker run -p 3001:8080 bia-app\n\n# Teste do health check\ncurl http://localhost:3000/api/versao\n```\n\n## O que NÃO fazer\n- ❌ Multi-stage builds\n- ❌ Otimizações de segurança complexas\n- ❌ Mudanças de usuário/permissões\n- ❌ Configurações avançadas de networking\n\n## O que SEMPRE fazer\n- ✅ Single stage\n- ✅ Comandos simples e claros\n- ✅ Perguntar sobre teste\n- ✅ Validar health check quando testando\n"
  },
  {
    "path": ".kiro/rules/infraestrutura.md",
    "content": "# Regras de Infraestrutura - Projeto BIA\n\n## Arquitetura Base\n- **Plataforma:** ECS com cluster de instâncias EC2\n- **Evolução:** Iniciar sem ALB → Evoluir para incluir ALB\n\n## Tipos de Instância\n- **RDS (Database):** t3.micro\n- **EC2 (ECS Cluster):** t3.micro\n\n## Filosofia de Simplicidade\n- **Público-alvo:** Alunos em aprendizado\n- **Abordagem:** Simplicidade acima de complexidade\n- **Objetivo:** Facilitar compreensão de quem está na etapa inicial da jornada\n\n## Recursos Avançados - NÃO INCLUIR\n- **Secrets Manager:** É um estágio mais avançado do aprendizado\n- **Multi-AZ deployments:** Manter configuração simples\n- **Auto Scaling complexo:** Usar configurações básicas\n\n## Padrão de Nomenclatura\n\n### Prefixo Padrão\n- **Prefixo:** `bia` (nome do projeto)\n\n### Nomenclatura de Recursos ECS\n- **Cluster com ALB:** `cluster-bia-alb`\n- **Cluster sem ALB:** `cluster-bia`\n- **Task Definition com ALB:** `task-def-bia-alb` (prefixo task-def e sufixo -alb)\n- **Task Definition sem ALB:** `task-def-bia` (prefixo task-def)\n- **Service:** `service-bia` (sem alb)\n- **Service:** `service-bia-alb` (com alb)\n\n### Sufixos dos Security Groups\n\n#### Cenário 1: Sem ALB (Inicial)\n- **Database (RDS):** `bia-db`\n- **EC2 (ECS Cluster):** `bia-web`\n\n#### Cenário 2: Com ALB (Evolução)\n- **Database (RDS):** `bia-db`\n- **Application Load Balancer:** `bia-alb`\n- **EC2 (ECS Cluster):** `bia-ec2`\n\n## Regras de Security Groups\n\n### Padrão de Descrição das Inbound Rules\n- **Formato obrigatório:** \"acesso vindo de (nome do security group)\"\n- **Exemplo:** \"acesso vindo de bia-dev\"\n- **Aplicação:** APENAS para inbound rules\n\n### Database (bia-db)\n**Inbound Rules:**\n- **Porta:** 5432 (PostgreSQL)\n- **Sources:** \n  - `bia-dev` → Descrição: \"acesso vindo de bia-dev\"\n  - `bia-ec2` (quando com ALB) → Descrição: \"acesso vindo de bia-ec2\"\n  - `bia-web` (quando sem ALB) → Descrição: \"acesso vindo de bia-web\"\n\n### EC2 com ALB (bia-ec2)\n**Inbound Rules:**\n- **Protocolo:** All TCP\n- **Source:** `bia-alb` → Descrição: \"acesso vindo de bia-alb\"\n- **Motivo:** Portas aleatórias do ECS Service\n\n### Application Load Balancer (bia-alb)\n**Inbound Rules:**\n- **Porta:** 80/443\n- **Source:** 0.0.0.0/0 → Descrição: \"acesso público HTTP/HTTPS\"\n\n## Banco de Dados\n- **Aproveitamento:** Usar banco existente na infraestrutura\n- **Não criar:** Novos recursos RDS nos templates\n- **Security Group:** Manter `bia-db` preparado para conexão\n\n### Observações\n- As regras seguem o princípio de menor privilégio\n- Security Groups referenciam outros Security Groups para maior flexibilidade\n- Configuração permite evolução da arquitetura sem grandes mudanças\n"
  },
  {
    "path": ".kiro/rules/pipeline.md",
    "content": "# Regras de Pipeline - Projeto BIA\n\n## Definição de Pipeline\nSempre que falarmos de **pipeline** para este projeto, estamos nos referindo à combinação de:\n- **AWS CodePipeline** (orquestração)\n- **AWS CodeBuild** (build e deploy)\n\n## Arquitetura do Pipeline\n\n### Componentes Principais\n1. **Source Stage:** GitHub como repositório de código\n2. **Build Stage:** CodeBuild para build da aplicação\n3. **Deploy Stage:** Deploy automático para ECS\n\n### Configuração Base\n- **Buildspec:** Já configurado no arquivo `buildspec.yml` na raiz do projeto\n- **ECR:** Registry para armazenar imagens Docker\n- **ECS:** Target de deploy da aplicação\n\n## Fluxo do Pipeline\n\n### 1. Source (GitHub)\n- Trigger automático em push para branch principal\n- Webhook configurado para detectar mudanças\n\n### 2. Build (CodeBuild)\n- Executa comandos definidos no `buildspec.yml`\n- Build da imagem Docker\n- Push da imagem para ECR\n\n### 3. Deploy (ECS)\n- Deploy automático da nova imagem\n- Rolling update do serviço ECS\n- Health check da aplicação\n\n## Configurações Importantes\n\n### Variáveis de Ambiente\n- Configuradas no CodeBuild project\n- Variáveis específicas por ambiente (dev/prod)\n\n### Permissões IAM\n- Role do CodeBuild com permissões para:\n  - ECR (push/pull de imagens)\n  - ECS (deploy de serviços)\n\n### Monitoramento\n- CloudWatch Logs para logs do build\n- Métricas de build e deploy\n\n## Boas Práticas\n\n### Performance\n- Cache de dependências do npm\n- Otimização de layers do Docker\n\n### Confiabilidade\n- Health checks após deploy\n- Rollback automático em caso de falha\n\n## Troubleshooting Comum\n\n### Build Failures\n- Verificar logs no CloudWatch\n- Validar permissões IAM\n- Confirmar configuração do buildspec.yml\n\n### Deploy Issues\n- Verificar health checks do ECS\n- Validar configuração do service\n- Confirmar conectividade com RDS\n\n## Evolução do Pipeline\n\n### Fase Inicial\n- Pipeline simples: Source → Build → Deploy\n- Deploy direto para ECS\n\n### Fase Avançada\n- Múltiplos ambientes (dev/staging/prod)\n- Aprovações manuais para produção\n"
  },
  {
    "path": ".sequelizerc",
    "content": "const path = require('path');\n\nmodule.exports = {\n  'config': path.resolve('config', 'database.js'),\n  'models-path': path.resolve('app', 'models'),\n  'seeders-path': path.resolve('database', 'seeders'),\n  'migrations-path': path.resolve('database', 'migrations'),\n};"
  },
  {
    "path": ".vscode/launch.json",
    "content": "{\n    \"version\": \"0.2.0\", \n    \"configurations\": [\n        {\n            \"name\": \"Testar DB com secrets\",\n            \"request\": \"launch\",\n            \"skipFiles\": [\n                \"<node_internals>/**\"\n            ],\n            \"type\": \"node\",\n            \"env\": {\n                \"DB_SECRET_NAME\": \"\",\n                \"DB_REGION\": \"us-east-1\",\n                \"AWS_ACCESS_KEY_ID\": \"\",\n                \"AWS_SECRET_ACCESS_KEY\": \"\",\n                \"DEBUG_SECRET\": \"true\"\n            },\n            \"program\": \"${workspaceFolder}/config/database.js\"\n        },\n        {\n            \"name\": \"Debug Server\",\n            \"request\": \"launch\",\n            \"skipFiles\": [\n                \"<node_internals>/**\"\n            ],\n            \"type\": \"node\",\n            \"env\": {\n                \"DB_SECRET_NAME\": \"\",\n                \"DB_REGION\": \"us-east-1\",\n                \"AWS_ACCESS_KEY_ID\": \"\",\n                \"AWS_SECRET_ACCESS_KEY\": \"\",\n                \"DEBUG_SECRET\": \"true\"\n            },\n            // \"program\": \"${workspaceFolder}/server.js\"\n            \"program\": \"${workspaceFolder}/server.js\"\n        }\n    ]\n}"
  },
  {
    "path": "AmazonQ.md",
    "content": "# Projeto BIA - Contexto e Análise\n\n## Visão Geral do Projeto\n**Nome:** BIA  \n**Versão:** 4.2.0  \n**Período da Imersão AWS & IA:** 23/05 a 24/05/2026 (Online e ao Vivo das 9h30 às 17h30)  \n**Repositório:** https://github.com/henrylle/bia\n\n## Impressões Iniciais do Desenvolvedor\nO projeto da BIA é um projeto educacional criado pelo Henrylle Maia (@henryllemaia) para ser usado nos eventos que ele realiza e servir de base para o treinamento Formação AWS.\n\nÉ um projeto concebido no ano de 2021 e que vem evoluindo usando as melhores práticas dentro da AWS.\n\nO foco base dele é fornecer uma estrutura educacional em que o aluno possa evoluir gradualmente, desde problemas simples até situações mais complexas.\n\n\n---\n\n## Análise Técnica (Amazon Q)\n\n### Arquitetura Identificada\n- **Frontend:** React 17.0.2 com Vite para build\n- **Backend:** Node.js com Express 4.17.1\n- **Banco de Dados:** PostgreSQL 16.1\n- **ORM:** Sequelize 6.6.5\n- **Containerização:** Docker com Docker Compose\n\n### Stack Tecnológica\n**Frontend:**\n- React com React Router DOM\n- React Icons para ícones\n- Vite como bundler (configurado no Dockerfile)\n\n**Backend:**\n- Express.js como framework web\n- Sequelize como ORM\n- Morgan para logging\n- CORS habilitado\n- Express Session para gerenciamento de sessões\n- EJS e HBS como template engines\n\n**Infraestrutura:**\n- Docker containerizado\n- AWS SDK integrado (Secrets Manager, STS)\n- PostgreSQL como banco principal\n- Suporte a variáveis de ambiente\n\n### Estrutura do Projeto\n```\n/bia\n├── api/                 # APIs do backend\n├── client/             # Aplicação React\n├── config/             # Configurações\n├── database/           # Migrations e seeds\n├── scripts/            # Scripts auxiliares\n├── tests/              # Testes unitários (Jest)\n├── docs/               # Documentação\n├── compose.yml         # Docker Compose\n├── Dockerfile          # Container da aplicação\n├── buildspec.yml       # AWS CodeBuild\n└── package.json        # Dependências Node.js\n```\n\n### Recursos AWS Identificados\n- **ECR:** Registry para imagens Docker (configurado no buildspec.yml)\n- **CodeBuild:** Pipeline de CI/CD já configurado\n- **Secrets Manager:** Gerenciamento de credenciais\n- **STS:** Tokens temporários de acesso\n\n### Pontos de Atenção\n1. **Segurança:** Credenciais hardcoded no compose.yml (apenas para desenvolvimento)\n2. **Escalabilidade:** Aplicação monolítica, mas bem estruturada\n3. **Monitoramento:** Healthcheck comentado no Docker Compose\n4. **Testes:** Estrutura de testes presente com Jest\n\n### Rotas da API para Testes\n- **`/api/versao`:** Retorna versão da aplicação (não usa banco)\n- **`/api/tarefas`:** Retorna dados do banco PostgreSQL (ideal para testar conectividade com RDS)"
  },
  {
    "path": "Dockerfile",
    "content": "FROM public.ecr.aws/docker/library/node:22.22.1-slim\nRUN npm install -g npm@11 --loglevel=error\n\n# Instalando curl\nRUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*\n\nWORKDIR /usr/src/app\n\n# Copiar package.json raiz primeiro\nCOPY package*.json ./\nRUN npm install --loglevel=error\n\n# Copiar package.json do client e instalar dependências (incluindo devDependencies para build)\nCOPY client/package*.json ./client/\nRUN cd client && npm install --legacy-peer-deps --loglevel=error\n\n# Copiar todos os arquivos\nCOPY . .\n\n# Build do front-end com Vite\nRUN cd client && VITE_API_URL=http://localhost:3001 npm run build\n\n# Limpeza das dependências de desenvolvimento do client para reduzir tamanho\nRUN cd client && npm prune --production && rm -rf node_modules/.cache\n\nEXPOSE 8080\n\nCMD [ \"npm\", \"start\" ]\n"
  },
  {
    "path": "Dockerfile_checkdisponibilidade",
    "content": "FROM alpine\n\n# Define o fuso horário como São Paulo\nENV TZ=America/Sao_Paulo\n\n# Endereço para teste\nENV URL=https://www.google.com.br\n\n# Instala curl e configura timezone\nRUN apk add --no-cache curl tzdata \\\n    && cp /usr/share/zoneinfo/$TZ /etc/localtime \\\n    && echo \"$TZ\" > /etc/timezone\n\n# Script de monitoramento\nENTRYPOINT echo \"Checando Site: $URL\"; \\\n    while sleep 5; do \\\n        curl -o /dev/null -s -w \"Status: %{http_code} - Hora: $(date +%H:%M:%S)\\n\" $URL; \\\n    done\n"
  },
  {
    "path": "README.md",
    "content": "## Projeto base para o evento Imersão AWS & IA que irei realizar.\n\n### Período do evento: 23/05 e 24/05/2026 (Online e ao Vivo das 9h30 às 17h30)\n\n[>> Página de Inscrição do evento](https://org.imersaoaws.com.br/github/readme)\n\n#### Para rodar as migrations no container ####\n```\ndocker compose exec server bash -c 'npx sequelize db:migrate'\n```\n\n"
  },
  {
    "path": "api/controllers/tarefas.js",
    "content": "const initializeModels = require(\"../models\");\n\nmodule.exports = () => {\n  const controller = {};\n\n  controller.create = async (req, res) => {\n    try {\n      const { Tarefas } = await initializeModels();\n      let tarefa = {\n        titulo: req.body.titulo,\n        dia_atividade: req.body.dia_atividade,\n        importante: req.body.importante,\n      };\n\n      const data = await Tarefas.create(tarefa);\n      res.send(data);\n    } catch (err) {\n      res.status(500).send({\n        message: err.message || \"Deu ruim.\",\n      });\n    }\n  };\n\n  controller.find = async (req, res) => {\n    try {\n      const { Tarefas } = await initializeModels();\n      let uuid = req.params.uuid;\n      const data = await Tarefas.findByPk(uuid);\n      if (data) {\n        res.send(data);\n      } else {\n        res.status(404).send({\n          message: \"Tarefa não encontrada.\",\n        });\n      }\n    } catch (err) {\n      res.status(500).send({\n        message: err.message || \"Deu ruim.\",\n      });\n    }\n  };\n\n  controller.delete = async (req, res) => {\n    try {\n      const { Tarefas } = await initializeModels();\n      let { uuid } = req.params;\n      const result = await Tarefas.destroy({\n        where: {\n          uuid: uuid,\n        },\n      });\n\n      if (result) {\n        res.send({ message: \"Tarefa deletada com sucesso.\" });\n      } else {\n        res.status(404).send({\n          message: \"Tarefa não encontrada.\",\n        });\n      }\n    } catch (err) {\n      res.status(500).send({\n        message: err.message || \"Deu ruim.\",\n      });\n    }\n  };\n\n  controller.update_priority = async (req, res) => {\n    try {\n      const { Tarefas } = await initializeModels();\n      let { uuid } = req.params;\n      await Tarefas.update(req.body, {\n        where: {\n          uuid: uuid,\n        },\n      });\n\n      const data = await Tarefas.findByPk(uuid);\n      if (data) {\n        res.send(data);\n      } else {\n        res.status(404).send({\n          message: \"Tarefa não encontrada.\",\n        });\n      }\n    } catch (err) {\n      res.status(500).send({\n        message: err.message || \"Deu ruim.\",\n      });\n    }\n  };\n\n  controller.findAll = async (req, res) => {\n    try {\n      const { Tarefas } = await initializeModels();\n      const data = await Tarefas.findAll();\n      res.send(data);\n    } catch (err) {\n      res.status(500).send({\n        message: err.message || \"Deu ruim.\",\n      });\n    }\n  };\n\n  return controller;\n};\n"
  },
  {
    "path": "api/controllers/versao.js",
    "content": "module.exports = () => {\n  const controller = {};\n\n  controller.get = async (req, res) => {\n    const responseString = `Bia ${process.env.VERSAO_API || \"4.2.0\"}`;\n    res.send(responseString);\n  };\n\n  return controller;\n};\n"
  },
  {
    "path": "api/data/tarefas.json",
    "content": "{\n  \"tasks\": [{\n      \"id\": 1,\n      \"title\": \"Doctr Appoiment\",\n      \"day\": \"Feb 5th at 2:30pm\",\n      \"reminder\": true\n    },\n    {\n      \"title\": \"Party 2\",\n      \"day\": \"16 de abril\",\n      \"reminder\": true,\n      \"id\": 4\n    },\n    {\n      \"title\": \"Party 3\",\n      \"day\": \"18 de Abril\",\n      \"reminder\": true,\n      \"id\": 5\n    }\n  ]\n}"
  },
  {
    "path": "api/models/index.js",
    "content": "\"use strict\";\n\nconst fs = require(\"fs\");\nconst path = require(\"path\");\nconst Sequelize = require(\"sequelize\");\nconst basename = path.basename(__filename);\nconst getConfig = require(\"../../config/database.js\");\n\nconst db = {};\n\nconst initializeModels = async () => {  \n  const resolvedConfig = await getConfig();\n  const sequelize = new Sequelize(resolvedConfig);\n  try {\n    const files = await fs.promises.readdir(__dirname);\n\n    for (const file of files) {\n      if (file !== basename && file.slice(-3) === \".js\") {\n        const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);\n        db[model.name] = model;\n      }\n    }\n\n    Object.keys(db).forEach(modelName => {\n      if (db[modelName].associate) {\n        db[modelName].associate(db);\n      }\n    });\n\n    db.sequelize = sequelize;\n    db.Sequelize = Sequelize;\n\n    return db;\n  } catch (error) {\n    console.error(\"Erro ao inicializar os modelos:\", error);\n    throw error;\n  }\n};\n\nmodule.exports = initializeModels;\n"
  },
  {
    "path": "api/models/tarefas.js",
    "content": "module.exports = (sequelize, DataTypes) => {\n  const Tarefas = sequelize.define(\"Tarefas\", {\n    uuid: {\n      type: DataTypes.UUID,\n      defaultValue: DataTypes.UUIDV1,\n      primaryKey: true,\n    },\n    titulo: DataTypes.STRING,\n    dia_atividade: DataTypes.STRING,\n    importante: DataTypes.BOOLEAN,\n  });\n\n  return Tarefas;\n};\n"
  },
  {
    "path": "api/routes/ping.js",
    "content": "module.exports = (app) => {\n  app.route(\"/api/ping\").get((req, res) => {\n    res.json(\"Rota funcionando. Pong!\");\n  });\n};\n"
  },
  {
    "path": "api/routes/tarefas.js",
    "content": "module.exports = (app) => {\n  const controllerFactory = require(\"../controllers/tarefas\");\n  const controller = controllerFactory();\n\n  app.route(\"/api/tarefas\")\n    .get(async (req, res, next) => {\n      try {\n        await controller.findAll(req, res);\n      } catch (err) {\n        next(err);\n      }\n    })\n    .post(async (req, res, next) => {\n      try {\n        await controller.create(req, res);\n      } catch (err) {\n        next(err);\n      }\n    });\n\n  app.route(\"/api/tarefas/:uuid\")\n    .get(async (req, res, next) => {\n      try {\n        await controller.find(req, res);\n      } catch (err) {\n        next(err);\n      }\n    });\n\n  app.route(\"/api/tarefas/update_priority/:uuid\")\n    .put(async (req, res, next) => {\n      try {\n        await controller.update_priority(req, res);\n      } catch (err) {\n        next(err);\n      }\n    });\n\n  app.route(\"/api/tarefas/:uuid\")\n    .delete(async (req, res, next) => {\n      try {\n        await controller.delete(req, res);\n      } catch (err) {\n        next(err);\n      }\n    });\n};\n"
  },
  {
    "path": "api/routes/versao.js",
    "content": "module.exports = (app) => {\n  const controller = require(\"../controllers/versao\")();\n\n  app.route(\"/api/versao\").get(controller.get);\n};\n"
  },
  {
    "path": "buildspec.yml",
    "content": "version: 0.2\n\nphases:\n  pre_build:\n    commands:\n      - echo Fazendo login no ECR...      \n      - aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 380278406175.dkr.ecr.us-east-1.amazonaws.com\n      - REPOSITORY_URI=380278406175.dkr.ecr.us-east-1.amazonaws.com/bia\n      - COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)\n      - IMAGE_TAG=${COMMIT_HASH:=latest}\n  build:\n    commands:\n      - echo Build iniciado em `date`\n      - echo Gerando imagem da BIA...\n      - docker build -t $REPOSITORY_URI:latest .\n      - docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG\n  post_build:\n    commands:\n      - echo Build finalizado com sucesso em `date`\n      - echo Fazendo push da imagem para o ECR...\n      - docker push $REPOSITORY_URI:latest\n      - docker push $REPOSITORY_URI:$IMAGE_TAG\n      - echo Gerando artefato da imagem para o ECS\n      - printf '[{\"name\":\"bia\",\"imageUri\":\"%s\"}]' $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json\nartifacts:\n  files: imagedefinitions.json\n"
  },
  {
    "path": "client/db.json",
    "content": "{\n  \"tasks\": [\n    {\n      \"id\": 1,\n      \"title\": \"Doctor Appoiment\",\n      \"day\": \"Feb 5th at 2:30pm\",\n      \"reminder\": true\n    },\n    {\n      \"title\": \"Party 2\",\n      \"day\": \"16 de abril\",\n      \"reminder\": true,\n      \"id\": 4\n    },\n    {\n      \"title\": \"Party 3\",\n      \"day\": \"18 de Abril\",\n      \"reminder\": true,\n      \"id\": 5\n    }\n  ]\n}"
  },
  {
    "path": "client/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <link rel=\"icon\" href=\"/favicon-simple.svg\" type=\"image/svg+xml\" />\n    <link rel=\"icon\" href=\"/favicon.ico\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <meta name=\"theme-color\" content=\"#000000\" />\n    <meta\n      name=\"description\"\n      content=\"Web site created using create-react-app\"\n    />\n    <link rel=\"apple-touch-icon\" href=\"/logo192.png\" />\n    <link rel=\"manifest\" href=\"/manifest.json\" />\n    <title>BIA + IA - 2026</title>\n  </head>\n  <body>\n    <noscript>You need to enable JavaScript to run this app.</noscript>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"/src/main.jsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "client/package.json",
    "content": "{\n  \"name\": \"react-task-tracker\",\n  \"version\": \"0.1.0\",\n  \"type\": \"module\",\n  \"private\": true,\n  \"dependencies\": {\n    \"@testing-library/jest-dom\": \"^6.5.0\",\n    \"@testing-library/react\": \"^16.0.1\",\n    \"@testing-library/user-event\": \"^14.5.2\",\n    \"json-server\": \"^1.0.0-beta.3\",\n    \"react\": \"^18.3.1\",\n    \"react-dom\": \"^18.3.1\",\n    \"react-icons\": \"^5.3.0\",\n    \"react-router-dom\": \"^6.28.0\",\n    \"web-vitals\": \"^4.2.4\"\n  },\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build\",\n    \"preview\": \"vite preview\",\n    \"server\": \"json-server --watch db.json --port 5000\"\n  },\n  \"eslintConfig\": {\n    \"extends\": [\n      \"react-app\",\n      \"react-app/jest\"\n    ]\n  },\n  \"browserslist\": {\n    \"production\": [\n      \">0.2%\",\n      \"not dead\",\n      \"not op_mini all\"\n    ],\n    \"development\": [\n      \"last 1 chrome version\",\n      \"last 1 firefox version\",\n      \"last 1 safari version\"\n    ]\n  },\n  \"devDependencies\": {\n    \"@vitejs/plugin-react\": \"^4.5.2\",\n    \"vite\": \"^5.4.19\"\n  }\n}\n"
  },
  {
    "path": "client/public/manifest.json",
    "content": "{\n  \"short_name\": \"BIA\",\n  \"name\": \"BIA - Bot Inteligente de Atividades\",\n  \"icons\": [\n    {\n      \"src\": \"favicon.ico\",\n      \"sizes\": \"64x64 32x32 24x24 16x16\",\n      \"type\": \"image/x-icon\"\n    },\n    {\n      \"src\": \"logo192.png\",\n      \"type\": \"image/png\",\n      \"sizes\": \"192x192\"\n    },\n    {\n      \"src\": \"logo512.png\",\n      \"type\": \"image/png\",\n      \"sizes\": \"512x512\"\n    }\n  ],\n  \"start_url\": \".\",\n  \"display\": \"standalone\",\n  \"theme_color\": \"#000000\",\n  \"background_color\": \"#ffffff\"\n}\n"
  },
  {
    "path": "client/public/robots.txt",
    "content": "# https://www.robotstxt.org/robotstxt.html\nUser-agent: *\nDisallow:\n"
  },
  {
    "path": "client/src/App.jsx",
    "content": "import React, { useState, useEffect } from \"react\";\nimport { BrowserRouter as Router, Routes, Route } from \"react-router-dom\";\nimport { ThemeProvider } from \"./contexts/ThemeContext.jsx\";\nimport { LogProvider, useLog } from \"./contexts/LogContext.jsx\";\nimport Header from \"./components/Header.jsx\";\nimport Footer from \"./components/Footer.jsx\";\nimport Tasks from \"./components/Tasks.jsx\";\nimport AddTask from \"./components/AddTask.jsx\";\nimport About from \"./components/About.jsx\";\nimport DebugLogs from \"./components/DebugLogs.jsx\";\n\nconst apiUrl = import.meta.env.VITE_API_URL || \"http://localhost:8080\";\n\nfunction AppContent() {\n  const [tasks, setTasks] = useState([]);\n  const { logApiRequest, logApiResponse, logApiError, addLog } = useLog();\n\n  useEffect(() => {\n    addLog('INFO', 'Aplicação iniciada', `API URL configurada: ${apiUrl}`);\n    getTasks();\n  }, []);\n\n  const getTasks = async () => {\n    try {\n      const tasksFromServer = await fetchTasks();\n      setTasks(tasksFromServer);\n    } catch (error) {\n      addLog('ERROR', 'Falha ao carregar tarefas', error.message);\n    }\n  };\n\n  //Listar Tarefas\n  const fetchTasks = async () => {\n    const url = `${apiUrl}/api/tarefas`;\n    logApiRequest('GET', url);\n    \n    try {\n      const res = await fetch(url);\n      const data = await res.json();\n      \n      logApiResponse('GET', url, res.status, data);\n      \n      if (!res.ok) {\n        throw new Error(`HTTP ${res.status}: ${res.statusText}`);\n      }\n      \n      return data;\n    } catch (error) {\n      logApiError('GET', url, error);\n      throw error;\n    }\n  };\n\n  //Listar Tarefa\n  const fetchTask = async (uuid) => {\n    const url = `${apiUrl}/api/tarefas/${uuid}`;\n    logApiRequest('GET', url);\n    \n    try {\n      const res = await fetch(url);\n      const data = await res.json();\n      \n      logApiResponse('GET', url, res.status, data);\n      \n      if (!res.ok) {\n        throw new Error(`HTTP ${res.status}: ${res.statusText}`);\n      }\n      \n      return data;\n    } catch (error) {\n      logApiError('GET', url, error);\n      throw error;\n    }\n  };\n\n  //Alternar Importante\n  const toggleReminder = async (uuid) => {\n    try {\n      const taskToToggle = await fetchTask(uuid);\n      const updatedTask = {\n        ...taskToToggle,\n        importante: !taskToToggle.importante,\n      };\n\n      const url = `${apiUrl}/api/tarefas/update_priority/${uuid}`;\n      logApiRequest('PUT', url, updatedTask);\n\n      const res = await fetch(url, {\n        method: \"PUT\",\n        headers: {\n          \"Content-type\": \"application/json\",\n        },\n        body: JSON.stringify(updatedTask),\n      });\n      \n      const data = await res.json();\n      \n      logApiResponse('PUT', url, res.status, data);\n      \n      if (!res.ok) {\n        throw new Error(`HTTP ${res.status}: ${res.statusText}`);\n      }\n      \n      setTasks(\n        tasks.map((task) =>\n          task.uuid === uuid ? { ...task, importante: data.importante } : task\n        )\n      );\n      \n      addLog('SUCCESS', 'Prioridade alterada', `Tarefa ${uuid} - Importante: ${data.importante}`);\n    } catch (error) {\n      addLog('ERROR', 'Falha ao alterar prioridade', error.message);\n    }\n  };\n\n  //Adicionar Tarefa\n  const addTask = async (task) => {\n    const url = `${apiUrl}/api/tarefas`;\n    logApiRequest('POST', url, task);\n    \n    try {\n      const res = await fetch(url, {\n        method: \"POST\",\n        headers: {\n          \"Content-type\": \"application/json\",\n        },\n        body: JSON.stringify(task),\n      });\n      \n      const data = await res.json();\n      \n      logApiResponse('POST', url, res.status, data);\n      \n      if (!res.ok) {\n        throw new Error(`HTTP ${res.status}: ${res.statusText}`);\n      }\n      \n      setTasks([...tasks, data]);\n      addLog('SUCCESS', 'Tarefa criada', `\"${task.titulo}\" adicionada com sucesso`);\n    } catch (error) {\n      logApiError('POST', url, error);\n      addLog('ERROR', 'Falha ao criar tarefa', error.message);\n    }\n  };\n\n  //Remover tarefa\n  const deleteTask = async (uuid) => {\n    const url = `${apiUrl}/api/tarefas/${uuid}`;\n    logApiRequest('DELETE', url);\n    \n    try {\n      const res = await fetch(url, {\n        method: \"DELETE\",\n      });\n      \n      logApiResponse('DELETE', url, res.status);\n      \n      if (!res.ok) {\n        throw new Error(`HTTP ${res.status}: ${res.statusText}`);\n      }\n      \n      setTasks(tasks.filter((task) => task.uuid !== uuid));\n      addLog('SUCCESS', 'Tarefa removida', `Tarefa ${uuid} excluída com sucesso`);\n    } catch (error) {\n      logApiError('DELETE', url, error);\n      addLog('ERROR', 'Falha ao excluir tarefa', error.message);\n    }\n  };\n\n  // Componente para página principal\n  const HomePage = () => (\n    <>\n      <AddTask onAdd={addTask} />\n      {tasks.length > 0 ? (\n        <Tasks\n          tasks={tasks}\n          onDelete={deleteTask}\n          onToggle={toggleReminder}\n        />\n      ) : (\n        <div className=\"empty-state\">\n          <h3>Nenhuma tarefa por aqui 📝</h3>\n          <p>Adicione sua primeira tarefa usando o formulário acima!</p>\n        </div>\n      )}\n    </>\n  );\n\n  return (\n    <div className=\"app\">\n      <Router>\n        <div className=\"container\">\n          <Header />\n\n          <Routes>\n            <Route path=\"/\" element={<HomePage />} />\n            <Route path=\"/about\" element={<About />} />\n          </Routes>\n          <Footer />\n        </div>\n        <DebugLogs />\n      </Router>\n    </div>\n  );\n}\n\nfunction App() {\n  return (\n    <ThemeProvider>\n      <LogProvider>\n        <AppContent />\n      </LogProvider>\n    </ThemeProvider>\n  );\n}\n\nexport default App;\n"
  },
  {
    "path": "client/src/components/About.js",
    "content": "import React from \"react\";\nimport { Link } from \"react-router-dom\";\nimport DadosHenrylle from \"./DadosHenrylle\";\nconst About = () => {\n  return (\n    <div>\n      <h4>Versão 4.2.0</h4>\n      <h5>BIA 23/05 e 24/05/2026</h5>\n      <Link to=\"/\">Voltar</Link>\n      <DadosHenrylle />\n    </div>\n  );\n};\n\nexport default About;\n"
  },
  {
    "path": "client/src/components/About.jsx",
    "content": "import React from \"react\";\nimport { Link } from \"react-router-dom\";\nimport DadosHenrylle from \"./DadosHenrylle.jsx\";\n\nconst About = () => {\n  return (\n    <div className=\"about-page\">\n      <div className=\"about-content\">\n        <div className=\"feature-grid\">\n          <div className=\"feature-card highlight\">\n            <h3>Próximo Evento</h3>\n            <h4>AWS & IA</h4>\n            <p><strong>23/05 e 24/05/2026</strong><br/>Formação AWS</p>\n          </div>\n        </div>\n\n        <DadosHenrylle />\n      </div>\n\n      <div className=\"about-footer\">\n        <Link to=\"/\" className=\"back-button\">\n          ← Voltar\n        </Link>\n      </div>\n    </div>\n  );\n};\n\nexport default About;\n"
  },
  {
    "path": "client/src/components/AddTask.jsx",
    "content": "import React, { useState } from \"react\";\nimport Modal from \"./Modal\";\n\nconst AddTask = ({ onAdd }) => {\n  const [titulo, setTitulo] = useState(\"\");\n  const [dia, setDia] = useState(\"\");\n  const [importante, setImportante] = useState(false);\n  const [showModal, setShowModal] = useState(false);\n\n  const onSubmit = (e) => {\n    e.preventDefault();\n\n    if (!titulo.trim()) {\n      setShowModal(true);\n      return;\n    }\n\n    onAdd({ \n      titulo: titulo.trim(), \n      dia_atividade: dia || new Date().toLocaleDateString('pt-BR'), \n      importante \n    });\n\n    setTitulo(\"\");\n    setDia(\"\");\n    setImportante(false);\n  };\n\n  return (\n    <form className=\"add-form\" onSubmit={onSubmit}>\n      <div className=\"form-control\">\n        <label>Tarefa</label>\n        <input\n          type=\"text\"\n          placeholder=\"O que você precisa fazer?\"\n          value={titulo}\n          onChange={(e) => setTitulo(e.target.value)}\n        />\n      </div>\n      \n      <div className=\"form-control\">\n        <label>Data/Prazo</label>\n        <input\n          type=\"text\"\n          placeholder=\"Quando?\"\n          value={dia}\n          onChange={(e) => setDia(e.target.value)}\n        />\n      </div>\n      \n      <div className=\"form-control-check\">\n        <input\n          type=\"checkbox\"\n          id=\"importante\"\n          checked={importante}\n          onChange={(e) => setImportante(e.target.checked)}\n        />\n        <label htmlFor=\"importante\">Importante</label>\n      </div>\n      \n      <button type=\"submit\" className=\"btn btn-block success\">\n        Add New Task\n      </button>\n      \n      <Modal\n        isOpen={showModal}\n        onClose={() => setShowModal(false)}\n        title=\"Campo obrigatório\"\n        message=\"Por favor, adicione uma descrição para a tarefa\"\n        type=\"warning\"\n      />\n    </form>\n  );\n};\n\nexport default AddTask;\n"
  },
  {
    "path": "client/src/components/Button.jsx",
    "content": "import React from \"react\";\n\nconst Button = ({ color, text, onClick }) => {\n  return (\n    <button\n      onClick={onClick}\n      className={`btn ${color ? color : ''}`}\n    >\n      {text}\n    </button>\n  );\n};\n\nButton.defaultProps = {\n  color: \"\",\n};\n\nexport default Button;\n"
  },
  {
    "path": "client/src/components/DadosHenrylle.jsx",
    "content": "import React from \"react\";\n\nconst DadosHenrylle = () => {\n  return (\n    <div className=\"dados-henrylle\">\n      <h3>Links Importantes</h3>\n      <div className=\"links-grid\">\n        <a\n          href=\"https://inscricao.formacaoaws.com.br/suporte\"\n          target=\"_blank\"\n          rel=\"noopener noreferrer\"\n          className=\"link-card\"\n        >\n          <h4>💬 Suporte</h4>\n          <p>Formação AWS</p>\n        </a>\n        \n        <a\n          href=\"https://instagram.com/henryllemaia\"\n          target=\"_blank\"\n          rel=\"noopener noreferrer\"\n          className=\"link-card\"\n        >\n          <h4>📸 Instagram</h4>\n          <p>Henrylle Maia</p>\n        </a>\n        \n        <a\n          href=\"https://www.youtube.com/@henryllemaia\"\n          target=\"_blank\"\n          rel=\"noopener noreferrer\"\n          className=\"link-card\"\n        >\n          <h4>🎥 YouTube</h4>\n          <p>Canal oficial</p>\n        </a>\n        \n        <a\n          href=\"https://www.linkedin.com/in/henrylle/recent-activity/all/\"\n          target=\"_blank\"\n          rel=\"noopener noreferrer\"\n          className=\"link-card\"\n        >\n          <h4>💼 LinkedIn</h4>\n          <p>Desafio Labs AWS</p>\n        </a>\n      </div>\n    </div>\n  );\n};\n\nexport default DadosHenrylle;\n"
  },
  {
    "path": "client/src/components/DebugLogs.jsx",
    "content": "import React from 'react';\nimport { FaBug, FaTimes, FaTrash, FaChevronDown, FaChevronUp } from 'react-icons/fa';\nimport { useLog } from '../contexts/LogContext.jsx';\n\nconst DebugLogs = () => {\n  const { logs, isLogVisible, debugMode, clearLogs, toggleLogVisibility } = useLog();\n\n  if (!debugMode) return null;\n\n  const getLogIcon = (type) => {\n    switch (type) {\n      case 'ERROR': return '🔴';\n      case 'SUCCESS': return '🟢';\n      case 'WARNING': return '🟡';\n      default: return '🔵';\n    }\n  };\n\n  const getLogClass = (type) => {\n    switch (type) {\n      case 'ERROR': return 'log-error';\n      case 'SUCCESS': return 'log-success';\n      case 'WARNING': return 'log-warning';\n      default: return 'log-info';\n    }\n  };\n\n  return (\n    <div className=\"debug-logs\">\n      <div className=\"debug-header\">\n        <button \n          className=\"debug-toggle\"\n          onClick={toggleLogVisibility}\n          title={isLogVisible ? \"Ocultar logs\" : \"Mostrar logs\"}\n        >\n          <FaBug />\n          <span>Debug ({logs.length})</span>\n          {isLogVisible ? <FaChevronUp /> : <FaChevronDown />}\n        </button>\n        \n        {isLogVisible && (\n          <button \n            className=\"debug-clear\"\n            onClick={clearLogs}\n            title=\"Limpar logs\"\n          >\n            <FaTrash />\n          </button>\n        )}\n      </div>\n\n      {isLogVisible && (\n        <div className=\"debug-content\">\n          <div className=\"debug-info\">\n            <h4>🔧 Área de Debug</h4>\n            <p>Esta área mostra logs da API para facilitar o debug durante o desenvolvimento.</p>\n            <p><strong>URL da API:</strong> {import.meta.env.VITE_API_URL || \"http://localhost:8080\"}</p>\n          </div>\n          \n          <div className=\"logs-container\">\n            {logs.length === 0 ? (\n              <div className=\"no-logs\">\n                <p>Nenhum log ainda. Interaja com a aplicação para ver os logs aparecerem aqui.</p>\n              </div>\n            ) : (\n              logs.map((log) => (\n                <div key={log.id} className={`log-entry ${getLogClass(log.type)}`}>\n                  <div className=\"log-header\">\n                    <span className=\"log-icon\">{getLogIcon(log.type)}</span>\n                    <span className=\"log-time\">{log.timestamp}</span>\n                    <span className=\"log-type\">{log.type}</span>\n                    <span className=\"log-message\">{log.message}</span>\n                  </div>\n                  {log.details && (\n                    <div className=\"log-details\">\n                      {log.details}\n                    </div>\n                  )}\n                </div>\n              ))\n            )}\n          </div>\n        </div>\n      )}\n    </div>\n  );\n};\n\nexport default DebugLogs; "
  },
  {
    "path": "client/src/components/Footer.jsx",
    "content": "import React from \"react\";\nimport { Link } from \"react-router-dom\";\n\nconst Footer = () => {\n  return (\n    <footer>\n      <div className=\"footer-content\">\n        <p>Formação AWS 2026</p>\n        <Link to=\"/about\" className=\"footer-link\">\n          Sobre a BIA\n        </Link>\n      </div>\n    </footer>\n  );\n};\n\nexport default Footer;\n"
  },
  {
    "path": "client/src/components/Header.jsx",
    "content": "import React from \"react\";\nimport { FaSun, FaMoon } from \"react-icons/fa\";\nimport { useTheme } from \"../contexts/ThemeContext.jsx\";\nimport VersionInfo from \"./VersionInfo\";\n\nconst Header = ({ title }) => {\n  const { isDarkMode, toggleTheme } = useTheme();\n  \n  return (\n    <header className=\"header\">\n      <h1>{title}</h1>\n      <div className=\"header-controls\">\n        <VersionInfo />\n        <button \n          className=\"theme-toggle\" \n          onClick={toggleTheme}\n          title={isDarkMode ? \"Tema claro\" : \"Tema escuro\"}\n        >\n          {isDarkMode ? <FaSun /> : <FaMoon />}\n        </button>\n      </div>\n    </header>\n  );\n};\n\nHeader.defaultProps = {\n  title: \"BIA 2026\",\n};\n\nexport default Header;\n"
  },
  {
    "path": "client/src/components/Modal.jsx",
    "content": "import React from 'react';\n\nconst Modal = ({ isOpen, onClose, title, message, type = 'info' }) => {\n  if (!isOpen) return null;\n\n  const getIcon = () => {\n    switch (type) {\n      case 'error':\n        return '⚠️';\n      case 'success':\n        return '✅';\n      case 'warning':\n        return '⚠️';\n      default:\n        return 'ℹ️';\n    }\n  };\n\n  const getTypeClass = () => {\n    switch (type) {\n      case 'error':\n        return 'modal-error';\n      case 'success':\n        return 'modal-success';\n      case 'warning':\n        return 'modal-warning';\n      default:\n        return 'modal-info';\n    }\n  };\n\n  return (\n    <div className=\"modal-overlay\" onClick={onClose}>\n      <div className={`modal-content ${getTypeClass()}`} onClick={(e) => e.stopPropagation()}>\n        <div className=\"modal-header\">\n          <span className=\"modal-icon\">{getIcon()}</span>\n          <h3 className=\"modal-title\">{title || 'Atenção'}</h3>\n        </div>\n        <div className=\"modal-body\">\n          <p>{message}</p>\n        </div>\n        <div className=\"modal-footer\">\n          <button className=\"modal-btn\" onClick={onClose}>\n            OK\n          </button>\n        </div>\n      </div>\n    </div>\n  );\n};\n\nexport default Modal; "
  },
  {
    "path": "client/src/components/Task.jsx",
    "content": "import React from \"react\";\nimport { FaTimes, FaStar, FaRegStar } from \"react-icons/fa\";\n\nconst Task = ({ task, onDelete, onToggle }) => {\n  return (\n    <div\n      className={`task ${task.importante ? \"reminder\" : \"\"}`}\n      onDoubleClick={() => onToggle(task.uuid)}\n    >\n      <div className=\"task-content\">\n        <h3>{task.titulo}</h3>\n        <p className=\"task-date\">\n          📅 {task.dia_atividade || \"Sem data definida\"}\n        </p>\n      </div>\n      <div className=\"task-actions\">\n        <button\n          className=\"task-priority\"\n          onClick={() => onToggle(task.uuid)}\n          title={task.importante ? \"Remover importante\" : \"Marcar importante\"}\n        >\n          {task.importante ? <FaStar /> : <FaRegStar />}\n        </button>\n        <button\n          className=\"task-delete\"\n          onClick={() => onDelete(task.uuid)}\n          title=\"Excluir\"\n        >\n          <FaTimes />\n        </button>\n      </div>\n    </div>\n  );\n};\n\nexport default Task;\n"
  },
  {
    "path": "client/src/components/Tasks.jsx",
    "content": "import React, { useState, useEffect } from \"react\";\nimport Task from \"./Task.jsx\";\n\nconst Tasks = ({ tasks, onDelete, onToggle }) => {\n  const [currentPage, setCurrentPage] = useState(1);\n  const tasksPerPage = 5; // Mostrar 5 tarefas por página\n\n  // Calcular tarefas da página atual\n  const indexOfLastTask = currentPage * tasksPerPage;\n  const indexOfFirstTask = indexOfLastTask - tasksPerPage;\n  const currentTasks = tasks.slice(indexOfFirstTask, indexOfLastTask);\n  \n  // Calcular total de páginas\n  const totalPages = Math.ceil(tasks.length / tasksPerPage);\n\n  // Resetar para primeira página quando tasks mudam\n  useEffect(() => {\n    if (currentPage > totalPages && totalPages > 0) {\n      setCurrentPage(1);\n    }\n  }, [tasks.length, totalPages, currentPage]);\n\n  // Funções de navegação\n  const goToPage = (pageNumber) => {\n    setCurrentPage(pageNumber);\n  };\n\n  const goToPrevious = () => {\n    if (currentPage > 1) {\n      setCurrentPage(currentPage - 1);\n    }\n  };\n\n  const goToNext = () => {\n    if (currentPage < totalPages) {\n      setCurrentPage(currentPage + 1);\n    }\n  };\n\n  // Se não há tarefas, não mostrar nada\n  if (tasks.length === 0) {\n    return null;\n  }\n\n  return (\n    <div className=\"tasks-container\">\n      {/* Lista de tarefas da página atual */}\n      <div className=\"tasks-list\">\n        {currentTasks.map((task) => (\n          <Task\n            key={task.uuid}\n            task={task}\n            onDelete={onDelete}\n            onToggle={onToggle}\n          />\n        ))}\n      </div>\n\n      {/* Controles de paginação */}\n      {totalPages > 1 && (\n        <div className=\"pagination\">\n          <div className=\"pagination-info\">\n            <span>\n              Mostrando {indexOfFirstTask + 1}-{Math.min(indexOfLastTask, tasks.length)} de {tasks.length} tarefas\n            </span>\n          </div>\n          \n          <div className=\"pagination-controls\">\n            <button \n              className=\"pagination-btn\"\n              onClick={goToPrevious}\n              disabled={currentPage === 1}\n              title=\"Página anterior\"\n            >\n              ‹\n            </button>\n            \n            {/* Números das páginas */}\n            {Array.from({ length: totalPages }, (_, index) => {\n              const pageNumber = index + 1;\n              \n              // Mostrar sempre primeira, última e páginas próximas da atual\n              if (\n                pageNumber === 1 ||\n                pageNumber === totalPages ||\n                (pageNumber >= currentPage - 1 && pageNumber <= currentPage + 1)\n              ) {\n                return (\n                  <button\n                    key={pageNumber}\n                    className={`pagination-btn ${currentPage === pageNumber ? 'active' : ''}`}\n                    onClick={() => goToPage(pageNumber)}\n                  >\n                    {pageNumber}\n                  </button>\n                );\n              }\n              \n              // Mostrar reticências\n              if (\n                pageNumber === currentPage - 2 ||\n                pageNumber === currentPage + 2\n              ) {\n                return <span key={pageNumber} className=\"pagination-dots\">...</span>;\n              }\n              \n              return null;\n            })}\n            \n            <button \n              className=\"pagination-btn\"\n              onClick={goToNext}\n              disabled={currentPage === totalPages}\n              title=\"Próxima página\"\n            >\n              ›\n            </button>\n          </div>\n        </div>\n      )}\n    </div>\n  );\n};\n\nexport default Tasks;\n"
  },
  {
    "path": "client/src/components/VersionInfo.jsx",
    "content": "import React, { useState, useEffect } from 'react';\n\nconst VersionInfo = () => {\n  const [showVersion, setShowVersion] = useState(false);\n  const [apiStatus, setApiStatus] = useState('checking'); // 'checking', 'online', 'offline'\n  const [apiVersion, setApiVersion] = useState('4.0.0');\n\n  const getApiUrl = () => {\n    // Se estiver definido no ambiente (Docker/Produção)\n    if (import.meta.env.VITE_API_URL) {\n      return import.meta.env.VITE_API_URL;\n    }\n    \n    // Se estiver rodando no mesmo domínio (produção integrada)\n    if (window.location.port === '8080') {\n      return window.location.origin;\n    }\n    \n    // Desenvolvimento local - inferir porta 8080\n    return 'http://localhost:8080';\n  };\n\n  const checkApiHealth = async () => {\n    setApiStatus('checking');\n    try {\n      const apiUrl = getApiUrl();\n      const controller = new AbortController();\n      const timeoutId = setTimeout(() => controller.abort(), 5000); // 5s timeout\n      \n      const response = await fetch(`${apiUrl}/api/versao`, {\n        signal: controller.signal,\n        method: 'GET',\n        cache: 'no-cache'\n      });\n      \n      clearTimeout(timeoutId);\n      \n      if (response.ok) {\n        const versionText = await response.text();\n        setApiVersion(versionText);\n        setApiStatus('online');\n      } else {\n        setApiStatus('offline');\n      }\n    } catch (error) {\n      console.warn('API Health Check falhou:', error.message);\n      setApiStatus('offline');\n    }\n  };\n\n  useEffect(() => {\n    checkApiHealth();\n    // Recheck a cada 30 segundos\n    const interval = setInterval(checkApiHealth, 30000);\n    return () => clearInterval(interval);\n  }, []);\n\n  const handleVersionClick = () => {\n    setShowVersion(!showVersion);\n    if (!showVersion) {\n      // Recheca quando abre o tooltip\n      checkApiHealth();\n    }\n  };\n\n  const openVersionEndpoint = () => {\n    const apiUrl = getApiUrl();\n    window.open(`${apiUrl}/api/versao`, '_blank');\n  };\n\n  const getStatusIcon = () => {\n    switch (apiStatus) {\n      case 'online': return '🟢';\n      case 'offline': return '🔴';\n      case 'checking': return '🟡';\n      default: return '⚪';\n    }\n  };\n\n  const getStatusText = () => {\n    switch (apiStatus) {\n      case 'online': return 'Online';\n      case 'offline': return 'Offline';\n      case 'checking': return 'Verificando...';\n      default: return 'Desconhecido';\n    }\n  };\n\n  const getEnvironmentInfo = () => {\n    const { protocol, hostname, port } = window.location;\n    \n    // Detectar tipo de ambiente\n    if (hostname === 'localhost' || hostname === '127.0.0.1') {\n      return {\n        type: 'local',\n        icon: '🏠',\n        label: 'Local',\n        description: `${hostname}:${port}`,\n        color: '#3b82f6' // azul\n      };\n    }\n    \n    // IP direto sem HTTPS\n    if (/^\\d+\\.\\d+\\.\\d+\\.\\d+$/.test(hostname) && protocol === 'http:') {\n      return {\n        type: 'ip-http',\n        icon: '🌐',\n        label: 'IP Direto',\n        description: `${hostname}${port ? ':' + port : ''}`,\n        color: '#f59e0b' // amarelo/laranja\n      };\n    }\n    \n    // ALB/Load Balancer sem HTTPS\n    if (protocol === 'http:' && hostname.includes('.elb.')) {\n      return {\n        type: 'alb-http',\n        icon: '⚖️',\n        label: 'ALB HTTP',\n        description: hostname,\n        color: '#ef4444' // vermelho\n      };\n    }\n    \n    // Domínio com HTTPS (produção)\n    if (protocol === 'https:') {\n      return {\n        type: 'domain-https',\n        icon: '🔒',\n        label: 'Produção',\n        description: hostname,\n        color: '#22c55e' // verde\n      };\n    }\n    \n    // Outros casos\n    return {\n      type: 'other',\n      icon: '❓',\n      label: 'Outro',\n      description: `${hostname}${port ? ':' + port : ''}`,\n      color: '#6b7280' // cinza\n    };\n  };\n\n  return (\n    <div className=\"version-info\">\n      <button \n        className={`version-trigger ${apiStatus} ${getEnvironmentInfo().type}`}\n        onClick={handleVersionClick}\n        title={`${getEnvironmentInfo().icon} ${getEnvironmentInfo().label} | API: ${getStatusText()}`}\n        style={{\n          borderColor: apiStatus === 'online' ? getEnvironmentInfo().color : \n                      apiStatus === 'offline' ? '#ef4444' : \n                      '#f59e0b'\n        }}\n      >\n        {getStatusIcon()}\n      </button>\n             {showVersion && (\n         <div className=\"version-tooltip\">\n           <div className=\"version-content\">\n             <strong>{apiVersion}</strong>\n             <div className=\"version-details\">\n               <small>\n                 <span className=\"status-indicator\">{getStatusIcon()}</span>\n                 Status: {getStatusText()}\n               </small>\n               <small>\n                 <span \n                   className=\"env-indicator\" \n                   style={{ color: getEnvironmentInfo().color }}\n                 >\n                   {getEnvironmentInfo().icon}\n                 </span>\n                 Ambiente: {getEnvironmentInfo().label}\n               </small>\n               <small>Local: {getEnvironmentInfo().description}</small>\n               <small>API: {getApiUrl()}</small>\n               <small>\n                 <button \n                   className=\"version-link\" \n                   onClick={openVersionEndpoint}\n                   title=\"Abrir endpoint de versão\"\n                 >\n                   🔗 /api/versao\n                 </button>\n               </small>\n               <small>\n                 <button \n                   className=\"version-link refresh-btn\" \n                   onClick={checkApiHealth}\n                   title=\"Verificar status da API\"\n                   disabled={apiStatus === 'checking'}\n                 >\n                   🔄 {apiStatus === 'checking' ? 'Verificando...' : 'Atualizar'}\n                 </button>\n               </small>\n             </div>\n           </div>\n         </div>\n       )}\n    </div>\n  );\n};\n\nexport default VersionInfo; "
  },
  {
    "path": "client/src/contexts/LogContext.jsx",
    "content": "import React, { createContext, useContext, useState, useEffect } from 'react';\n\nconst LogContext = createContext();\n\nexport const useLog = () => {\n  const context = useContext(LogContext);\n  if (!context) {\n    throw new Error('useLog deve ser usado dentro de um LogProvider');\n  }\n  return context;\n};\n\nexport const LogProvider = ({ children }) => {\n  const [logs, setLogs] = useState([]);\n  const [isLogVisible, setIsLogVisible] = useState(false);\n  const [debugMode, setDebugMode] = useState(false);\n\n  useEffect(() => {\n    // Verifica se o modo debug está habilitado via environment variable\n    const debugEnabled = import.meta.env.VITE_DEBUG_MODE === 'true';\n    setDebugMode(debugEnabled);\n    \n    if (debugEnabled) {\n      addLog('INFO', 'Modo debug habilitado', 'Sistema inicializado com logs visíveis');\n    }\n  }, []);\n\n  const addLog = (type, message, details = null) => {\n    const timestamp = new Date().toLocaleTimeString('pt-BR');\n    const logEntry = {\n      id: Date.now() + Math.random(),\n      timestamp,\n      type, // INFO, ERROR, SUCCESS, WARNING\n      message,\n      details,\n    };\n\n    setLogs(prev => [logEntry, ...prev].slice(0, 50)); // Mantém apenas os 50 logs mais recentes\n    \n    // Log no console também\n    const logMethod = type === 'ERROR' ? 'error' : type === 'WARNING' ? 'warn' : 'log';\n    console[logMethod](`[${timestamp}] ${type}: ${message}`, details || '');\n  };\n\n  const clearLogs = () => {\n    setLogs([]);\n    addLog('INFO', 'Logs limpos', 'Histórico de logs foi resetado');\n  };\n\n  const toggleLogVisibility = () => {\n    setIsLogVisible(prev => !prev);\n  };\n\n  // Função para interceptar e logar requests da API\n  const logApiRequest = (method, url, payload = null) => {\n    addLog('INFO', `API ${method}`, `${url}${payload ? ' | Payload: ' + JSON.stringify(payload) : ''}`);\n  };\n\n  const logApiResponse = (method, url, status, data = null) => {\n    const type = status >= 200 && status < 300 ? 'SUCCESS' : 'ERROR';\n    const message = `API ${method} - ${status}`;\n    const details = `${url} | Response: ${data ? JSON.stringify(data).substring(0, 100) : 'Sem dados'}`;\n    addLog(type, message, details);\n  };\n\n  const logApiError = (method, url, error) => {\n    addLog('ERROR', `API ${method} FALHOU`, `${url} | Erro: ${error.message}`);\n  };\n\n  return (\n    <LogContext.Provider value={{\n      logs,\n      isLogVisible,\n      debugMode,\n      addLog,\n      clearLogs,\n      toggleLogVisibility,\n      logApiRequest,\n      logApiResponse,\n      logApiError,\n    }}>\n      {children}\n    </LogContext.Provider>\n  );\n}; "
  },
  {
    "path": "client/src/contexts/ThemeContext.jsx",
    "content": "import React, { createContext, useContext, useState, useEffect } from 'react';\n\nconst ThemeContext = createContext();\n\nexport const useTheme = () => {\n  const context = useContext(ThemeContext);\n  if (!context) {\n    throw new Error('useTheme deve ser usado dentro de um ThemeProvider');\n  }\n  return context;\n};\n\nexport const ThemeProvider = ({ children }) => {\n  const [isDarkMode, setIsDarkMode] = useState(() => {\n    const savedTheme = localStorage.getItem('theme');\n    return savedTheme === 'dark' || \n           (!savedTheme && window.matchMedia('(prefers-color-scheme: dark)').matches);\n  });\n\n  useEffect(() => {\n    localStorage.setItem('theme', isDarkMode ? 'dark' : 'light');\n    document.documentElement.setAttribute('data-theme', isDarkMode ? 'dark' : 'light');\n  }, [isDarkMode]);\n\n  const toggleTheme = () => {\n    setIsDarkMode(prev => !prev);\n  };\n\n  return (\n    <ThemeContext.Provider value={{ isDarkMode, toggleTheme }}>\n      {children}\n    </ThemeContext.Provider>\n  );\n}; "
  },
  {
    "path": "client/src/index.css",
    "content": "@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap');\n\n/* Variáveis de tema */\n:root {\n  /* Tema Light */\n  --bg-primary: #ffffff;\n  --bg-secondary: #f8fafc;\n  --bg-card: #ffffff;\n  --text-primary: #1f2937;\n  --text-secondary: #6b7280;\n  --border-color: #e5e7eb;\n  --accent-primary: #3b82f6;\n  --accent-success: #10b981;\n  --accent-danger: #ef4444;\n  --shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);\n}\n\n[data-theme=\"dark\"] {\n  /* Tema Dark */\n  --bg-primary: #111827;\n  --bg-secondary: #1f2937;\n  --bg-card: #1f2937;\n  --text-primary: #f9fafb;\n  --text-secondary: #d1d5db;\n  --border-color: #374151;\n  --accent-primary: #60a5fa;\n  --accent-success: #34d399;\n  --accent-danger: #f87171;\n  --shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.3);\n}\n\n* {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n\nbody {\n  font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;\n  background: var(--bg-primary);\n  color: var(--text-primary);\n  line-height: 1.5;\n  transition: background-color 0.2s ease, color 0.2s ease;\n}\n\n.app {\n  min-height: 100vh;\n  background: var(--bg-primary);\n  padding: 1rem;\n}\n\n.container {\n  max-width: 480px;\n  margin: 0 auto;\n  background: var(--bg-card);\n  border-radius: 8px;\n  box-shadow: var(--shadow);\n  border: 1px solid var(--border-color);\n  overflow: hidden;\n}\n\n/* Header */\n.header {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  padding: 1rem 1.5rem;\n  border-bottom: 1px solid var(--border-color);\n  background: var(--bg-card);\n}\n\n.header h1 {\n  font-size: 1.5rem;\n  font-weight: 600;\n  color: var(--text-primary);\n}\n\n.theme-controls {\n  display: flex;\n  align-items: center;\n  gap: 0.5rem;\n}\n\n.theme-toggle {\n  background: var(--bg-secondary);\n  border: 1px solid var(--border-color);\n  border-radius: 6px;\n  padding: 0.5rem;\n  cursor: pointer;\n  transition: all 0.2s ease;\n  color: var(--text-secondary);\n}\n\n.theme-toggle:hover {\n  background: var(--bg-primary);\n  color: var(--text-primary);\n}\n\n/* Buttons */\n.btn {\n  display: inline-flex;\n  align-items: center;\n  gap: 0.5rem;\n  background: var(--accent-primary);\n  color: white;\n  border: none;\n  padding: 0.5rem 1rem;\n  border-radius: 6px;\n  cursor: pointer;\n  font-size: 0.875rem;\n  font-weight: 500;\n  transition: all 0.2s ease;\n}\n\n.btn:hover {\n  opacity: 0.9;\n}\n\n.btn.danger {\n  background: var(--accent-danger);\n}\n\n.btn.success {\n  background: var(--accent-success);\n}\n\n.btn-block {\n  width: 100%;\n  justify-content: center;\n}\n\n/* Tasks */\n.task {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  padding: 0.75rem 1.5rem;\n  border-bottom: 1px solid var(--border-color);\n  background: var(--bg-card);\n  transition: background-color 0.2s ease;\n}\n\n.task:hover {\n  background: var(--bg-secondary);\n}\n\n.task.reminder {\n  border-left: 3px solid var(--accent-success);\n}\n\n.task-content {\n  flex: 1;\n}\n\n.task h3 {\n  font-size: 0.875rem;\n  font-weight: 500;\n  color: var(--text-primary);\n  margin-bottom: 0.25rem;\n}\n\n.task p {\n  font-size: 0.75rem;\n  color: var(--text-secondary);\n}\n\n.task-actions {\n  display: flex;\n  gap: 0.25rem;\n}\n\n.task-priority,\n.task-delete {\n  background: none;\n  border: none;\n  cursor: pointer;\n  padding: 0.25rem;\n  border-radius: 4px;\n  font-size: 0.875rem;\n  transition: all 0.2s ease;\n}\n\n.task-priority {\n  color: var(--text-secondary);\n}\n\n.task-priority:hover,\n.task.reminder .task-priority {\n  color: var(--accent-success);\n}\n\n.task-delete {\n  color: var(--text-secondary);\n}\n\n.task-delete:hover {\n  color: var(--accent-danger);\n}\n\n/* Add Form */\n.add-form {\n  padding: 1.5rem;\n  background: var(--bg-secondary);\n  border-bottom: 1px solid var(--border-color);\n}\n\n.form-control {\n  margin-bottom: 1rem;\n}\n\n.form-control:last-child {\n  margin-bottom: 0;\n}\n\n.form-control label {\n  display: block;\n  font-size: 0.875rem;\n  font-weight: 500;\n  color: var(--text-primary);\n  margin-bottom: 0.25rem;\n}\n\n.form-control input {\n  width: 100%;\n  padding: 0.5rem;\n  border: 1px solid var(--border-color);\n  border-radius: 6px;\n  font-size: 0.875rem;\n  background: var(--bg-card);\n  color: var(--text-primary);\n  transition: border-color 0.2s ease;\n}\n\n.form-control input:focus {\n  outline: none;\n  border-color: var(--accent-primary);\n}\n\n.form-control-check {\n  display: flex;\n  align-items: center;\n  gap: 0.5rem;\n  margin-bottom: 1rem;\n}\n\n.form-control-check input[type=\"checkbox\"] {\n  width: auto;\n  margin: 0;\n}\n\n.form-control-check label {\n  margin: 0;\n  font-size: 0.875rem;\n  cursor: pointer;\n}\n\n/* Footer */\nfooter {\n  padding: 1rem 1.5rem;\n  text-align: center;\n  border-top: 1px solid var(--border-color);\n  background: var(--bg-card);\n}\n\n.footer-content {\n  display: flex;\n  flex-direction: column;\n  gap: 0.5rem;\n  align-items: center;\n}\n\n.footer-content p {\n  font-size: 0.75rem;\n  color: var(--text-secondary);\n  margin: 0;\n}\n\n.footer-link {\n  font-size: 0.75rem;\n  color: var(--accent-primary);\n  text-decoration: none;\n  font-weight: 500;\n}\n\n.footer-link:hover {\n  text-decoration: underline;\n}\n\n/* Empty State */\n.empty-state {\n  text-align: center;\n  padding: 2rem 1.5rem;\n  color: var(--text-secondary);\n}\n\n.empty-state h3 {\n  font-size: 1rem;\n  font-weight: 500;\n  color: var(--text-primary);\n  margin-bottom: 0.5rem;\n}\n\n.empty-state p {\n  font-size: 0.875rem;\n}\n\n/* About Page */\n.about-page {\n  padding: 1.5rem;\n  display: flex;\n  flex-direction: column;\n  min-height: 100%;\n}\n\n.about-content {\n  flex: 1;\n}\n\n.about-footer {\n  margin-top: 2rem;\n  padding-top: 1rem;\n  border-top: 1px solid var(--border-color);\n}\n\n.about-header {\n  text-align: center;\n  margin-bottom: 2rem;\n}\n\n.about-hero {\n  background: linear-gradient(135deg, var(--accent-primary), var(--accent-success));\n  color: white;\n  padding: 2rem;\n  border-radius: 8px;\n  margin-bottom: 1.5rem;\n}\n\n.about-hero h2 {\n  font-size: 1.5rem;\n  font-weight: 600;\n  margin-bottom: 0.5rem;\n}\n\n.about-subtitle {\n  font-size: 0.875rem;\n  opacity: 0.9;\n}\n\n.feature-grid {\n  display: grid;\n  grid-template-columns: 1fr;\n  gap: 1rem;\n  margin-bottom: 2rem;\n}\n\n.feature-card {\n  background: var(--bg-card);\n  border: 1px solid var(--border-color);\n  border-radius: 8px;\n  padding: 1.5rem;\n  text-align: center;\n}\n\n.feature-card.highlight {\n  border-color: var(--accent-primary);\n  background: var(--bg-secondary);\n}\n\n.feature-card h3 {\n  font-size: 1rem;\n  font-weight: 600;\n  color: var(--text-primary);\n  margin-bottom: 0.5rem;\n}\n\n.feature-card h4 {\n  font-size: 1.25rem;\n  font-weight: 600;\n  color: var(--accent-primary);\n  margin-bottom: 0.5rem;\n}\n\n.feature-card p {\n  font-size: 0.875rem;\n  color: var(--text-secondary);\n  line-height: 1.4;\n}\n\n.back-button {\n  display: inline-flex;\n  align-items: center;\n  gap: 0.5rem;\n  color: var(--accent-primary);\n  text-decoration: none;\n  font-weight: 500;\n  padding: 0.75rem 1.25rem;\n  border: 1px solid var(--accent-primary);\n  border-radius: 8px;\n  font-size: 0.875rem;\n  transition: all 0.2s ease;\n  align-self: flex-start;\n}\n\n.back-button:hover {\n  background: var(--accent-primary);\n  color: white;\n}\n\n/* Links Cards */\n.dados-henrylle {\n  margin-top: 2rem;\n}\n\n.dados-henrylle h3 {\n  font-size: 1rem;\n  font-weight: 600;\n  color: var(--text-primary);\n  margin-bottom: 1rem;\n  text-align: center;\n}\n\n.links-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n  gap: 0.75rem;\n}\n\n.link-card {\n  display: block;\n  background: var(--bg-card);\n  border: 1px solid var(--border-color);\n  border-radius: 8px;\n  padding: 1rem;\n  text-decoration: none;\n  transition: all 0.2s ease;\n  text-align: center;\n}\n\n.link-card:hover {\n  border-color: var(--accent-primary);\n  background: var(--bg-secondary);\n  transform: translateY(-1px);\n  box-shadow: var(--shadow);\n}\n\n.link-card h4 {\n  font-size: 0.875rem;\n  font-weight: 600;\n  color: var(--text-primary);\n  margin-bottom: 0.25rem;\n}\n\n.link-card p {\n  font-size: 0.75rem;\n  color: var(--text-secondary);\n  margin: 0;\n}\n\n/* Responsive */\n@media (max-width: 640px) {\n  .app {\n    padding: 0.5rem;\n  }\n  \n  .container {\n    border-radius: 0;\n    border-left: none;\n    border-right: none;\n  }\n  \n  .header {\n    padding: 1rem;\n  }\n  \n  .header h1 {\n    font-size: 1.25rem;\n  }\n  \n  .links-grid {\n    grid-template-columns: 1fr;\n  }\n}\n\n/* Header Controls */\n.header-controls {\n  display: flex;\n  align-items: center;\n  gap: 0.5rem;\n}\n\n/* Version Info */\n.version-info {\n  position: relative;\n}\n\n.version-trigger {\n  background: var(--bg-secondary);\n  border: 1px solid var(--border-color);\n  border-radius: 50%;\n  width: 24px;\n  height: 24px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  cursor: pointer;\n  font-size: 0.75rem;\n  font-weight: 500;\n  color: var(--text-secondary);\n  transition: all 0.2s ease;\n}\n\n.version-trigger.online {\n  border-color: #22c55e;\n}\n\n.version-trigger.offline {\n  border-color: #ef4444;\n}\n\n.version-trigger.checking {\n  border-color: #f59e0b;\n}\n\n.version-trigger:hover {\n  background: var(--bg-primary);\n  color: var(--text-primary);\n  border-color: var(--accent-primary);\n}\n\n.version-tooltip {\n  position: absolute;\n  top: 100%;\n  right: 0;\n  margin-top: 0.5rem;\n  background: var(--bg-card);\n  border: 1px solid var(--border-color);\n  border-radius: 6px;\n  padding: 0.75rem;\n  box-shadow: var(--shadow);\n  min-width: 160px;\n  z-index: 1000;\n}\n\n.version-content strong {\n  display: block;\n  color: var(--text-primary);\n  font-size: 0.875rem;\n  margin-bottom: 0.5rem;\n}\n\n.version-details {\n  display: flex;\n  flex-direction: column;\n  gap: 0.25rem;\n}\n\n.version-details small {\n  color: var(--text-secondary);\n  font-size: 0.75rem;\n}\n\n.version-link {\n  background: none;\n  border: none;\n  color: var(--accent-primary);\n  cursor: pointer;\n  font-size: 0.75rem;\n  text-decoration: underline;\n  padding: 0;\n}\n\n.version-link:hover {\n  opacity: 0.8;\n}\n\n.version-link.refresh-btn:disabled {\n  opacity: 0.5;\n  cursor: not-allowed;\n}\n\n.version-details .status-indicator {\n  margin-right: 0.25rem;\n}\n\n.version-details .env-indicator {\n  margin-right: 0.25rem;\n  font-weight: 600;\n}\n\n/* Modal */\n.modal-overlay {\n  position: fixed;\n  top: 0;\n  left: 0;\n  right: 0;\n  bottom: 0;\n  background: rgba(0, 0, 0, 0.5);\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  z-index: 1000;\n  backdrop-filter: blur(4px);\n}\n\n.modal-content {\n  background: var(--bg-card);\n  border-radius: 8px;\n  box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);\n  border: 1px solid var(--border-color);\n  max-width: 400px;\n  width: 90%;\n  max-height: 90vh;\n  overflow: hidden;\n  animation: modalSlideIn 0.2s ease-out;\n}\n\n@keyframes modalSlideIn {\n  from {\n    opacity: 0;\n    transform: translateY(-20px) scale(0.95);\n  }\n  to {\n    opacity: 1;\n    transform: translateY(0) scale(1);\n  }\n}\n\n.modal-header {\n  display: flex;\n  align-items: center;\n  gap: 0.75rem;\n  padding: 1rem 1.5rem;\n  border-bottom: 1px solid var(--border-color);\n}\n\n.modal-icon {\n  font-size: 1.25rem;\n}\n\n.modal-title {\n  font-size: 1rem;\n  font-weight: 600;\n  color: var(--text-primary);\n  margin: 0;\n}\n\n.modal-body {\n  padding: 1rem 1.5rem;\n}\n\n.modal-body p {\n  color: var(--text-secondary);\n  font-size: 0.875rem;\n  line-height: 1.5;\n  margin: 0;\n}\n\n.modal-footer {\n  padding: 1rem 1.5rem;\n  border-top: 1px solid var(--border-color);\n  display: flex;\n  justify-content: flex-end;\n}\n\n.modal-btn {\n  background: var(--accent-primary);\n  color: white;\n  border: none;\n  padding: 0.5rem 1rem;\n  border-radius: 6px;\n  cursor: pointer;\n  font-size: 0.875rem;\n  font-weight: 500;\n  transition: all 0.2s ease;\n}\n\n.modal-btn:hover {\n  opacity: 0.9;\n}\n\n.modal-error .modal-header {\n  border-bottom-color: var(--accent-danger);\n}\n\n.modal-error .modal-btn {\n  background: var(--accent-danger);\n}\n\n.modal-success .modal-header {\n  border-bottom-color: var(--accent-success);\n}\n\n.modal-success .modal-btn {\n  background: var(--accent-success);\n}\n\n.modal-warning .modal-header {\n  border-bottom-color: #f59e0b;\n}\n\n.modal-warning .modal-btn {\n  background: #f59e0b;\n}\n\n/* Paginação */\n.tasks-container {\n  display: flex;\n  flex-direction: column;\n}\n\n.tasks-list {\n  flex: 1;\n}\n\n.pagination {\n  padding: 1rem 1.5rem;\n  border-top: 1px solid var(--border-color);\n  background: var(--bg-secondary);\n  display: flex;\n  flex-direction: column;\n  gap: 0.75rem;\n}\n\n.pagination-info {\n  text-align: center;\n}\n\n.pagination-info span {\n  font-size: 0.75rem;\n  color: var(--text-secondary);\n}\n\n.pagination-controls {\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  gap: 0.25rem;\n}\n\n.pagination-btn {\n  background: var(--bg-card);\n  border: 1px solid var(--border-color);\n  color: var(--text-primary);\n  padding: 0.5rem 0.75rem;\n  border-radius: 6px;\n  cursor: pointer;\n  font-size: 0.875rem;\n  font-weight: 500;\n  transition: all 0.2s ease;\n  min-width: 2.5rem;\n  height: 2.5rem;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}\n\n.pagination-btn:hover:not(:disabled) {\n  background: var(--bg-primary);\n  border-color: var(--accent-primary);\n  color: var(--accent-primary);\n}\n\n/* Feedback visual para toque em mobile */\n.pagination-btn:active:not(:disabled) {\n  transform: scale(0.95);\n  background: var(--accent-primary);\n  color: white;\n  transition: all 0.1s ease;\n}\n\n/* Melhor contraste para toque */\n@media (hover: none) and (pointer: coarse) {\n  .pagination-btn {\n    border-width: 2px;\n  }\n  \n  .pagination-btn:not(.active):not(:disabled) {\n    background: var(--bg-primary);\n    border-color: var(--border-color);\n  }\n  \n  .pagination-btn:active:not(:disabled) {\n    background: var(--accent-primary);\n    border-color: var(--accent-primary);\n    color: white;\n  }\n}\n\n.pagination-btn.active {\n  background: var(--accent-primary);\n  color: white;\n  border-color: var(--accent-primary);\n}\n\n.pagination-btn:disabled {\n  opacity: 0.4;\n  cursor: not-allowed;\n}\n\n.pagination-dots {\n  padding: 0.5rem 0.25rem;\n  color: var(--text-secondary);\n  font-weight: 500;\n}\n\n@media (max-width: 640px) {\n  .pagination {\n    padding: 0.75rem 1rem;\n    gap: 1rem;\n  }\n  \n  .pagination-controls {\n    gap: 0.5rem;\n  }\n  \n  .pagination-btn {\n    padding: 0.75rem;\n    min-width: 3rem;\n    height: 3rem;\n    font-size: 0.875rem;\n    border-radius: 8px;\n    /* Área de toque maior para mobile */\n    touch-action: manipulation;\n  }\n  \n  /* Botões de navegação (anterior/próximo) maiores no mobile */\n  .pagination-btn:first-child,\n  .pagination-btn:last-child {\n    min-width: 3.5rem;\n    font-size: 1.25rem;\n    font-weight: 600;\n  }\n  \n  .pagination-info span {\n    font-size: 0.75rem;\n  }\n  \n  /* Esconder números de página no mobile muito pequeno */\n  @media (max-width: 480px) {\n    .pagination-btn:not(:first-child):not(:last-child):not(.active) {\n      display: none;\n    }\n    \n    .pagination-dots {\n      display: none;\n    }\n    \n    /* Mostrar apenas página atual entre os botões de navegação */\n    .pagination-controls {\n      justify-content: space-between;\n      padding: 0 0.5rem;\n    }\n    \n    .pagination-btn.active {\n      order: 0;\n      position: absolute;\n      left: 50%;\n      transform: translateX(-50%);\n    }\n  }\n}"
  },
  {
    "path": "client/src/main.jsx",
    "content": "import React from 'react';\nimport { createRoot } from 'react-dom/client';\nimport './index.css';\nimport App from './App.jsx';\nimport reportWebVitals from './reportWebVitals';\n\nconst container = document.getElementById('root');\nconst root = createRoot(container);\n\nroot.render(\n  <React.StrictMode>\n    <App />\n  </React.StrictMode>\n);\n\n// If you want to start measuring performance in your app, pass a function\n// to log results (for example: reportWebVitals(console.log))\n// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals\nreportWebVitals();\n"
  },
  {
    "path": "client/src/reportWebVitals.js",
    "content": "const reportWebVitals = onPerfEntry => {\n  if (onPerfEntry && onPerfEntry instanceof Function) {\n    import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {\n      getCLS(onPerfEntry);\n      getFID(onPerfEntry);\n      getFCP(onPerfEntry);\n      getLCP(onPerfEntry);\n      getTTFB(onPerfEntry);\n    });\n  }\n};\n\nexport default reportWebVitals;\n"
  },
  {
    "path": "client/vite.config.js",
    "content": "import { defineConfig } from 'vite';\nimport react from '@vitejs/plugin-react';\n\nexport default defineConfig({\n  plugins: [react()],\n  server: {\n    port: 3001,\n    open: true,\n  },\n  build: {\n    outDir: 'build',\n    sourcemap: true,\n  },\n  define: {\n    'process.env': {},\n  },\n}); "
  },
  {
    "path": "compose.yml",
    "content": "services:\n  server:\n    build: .\n    container_name: bia\n    ports:\n      - 3001:8080\n    depends_on:\n      - database\n    environment:\n      DB_USER: postgres\n      DB_PWD: postgres\n      DB_HOST: database\n      DB_PORT: 5432\n      ## NAO PRECISA NO BOOTCAMP DAQUI PRA BAIXO ##\n      # DB_SECRET_NAME: \n      # DB_REGION: \n      # AWS_ACCESS_KEY_ID: \n      # AWS_SECRET_ACCESS_KEY:\n      # DEBUG_SECRET: \n      # IS_LOCAL: true \n    # healthcheck:\n    #   test: [\"CMD\", \"curl\", \"-f\", \"http://localhost:8080/api/versao\"]\n    #   interval: 10s\n    #   timeout: 5s\n    #   retries: 3\n    #   start_period: 5s     \n  database:\n    image: postgres:17.1\n    container_name: database\n    environment:\n      - \"POSTGRES_USER=postgres\"\n      - \"POSTGRES_PASSWORD=postgres\"\n      - \"POSTGRES_DB=bia\"\n    ports:\n      - 5433:5432\n    volumes:\n      - db:/var/lib/postgresql/data\nvolumes:\n  db:\n"
  },
  {
    "path": "config/database.js",
    "content": "const { SecretsManagerClient, GetSecretValueCommand } = require(\"@aws-sdk/client-secrets-manager\");\nconst { fromIni, fromEnv } = require(\"@aws-sdk/credential-providers\");\nconst { STSClient, GetCallerIdentityCommand } = require(\"@aws-sdk/client-sts\");\n\nasync function isLocalConnection() {\n  // Lógica para determinar se a conexão é local\n  return (\n    process.env.DB_HOST === undefined ||\n    process.env.DB_HOST === \"database\" ||\n    process.env.DB_HOST === \"127.0.0.1\" ||\n    process.env.DB_HOST === \"localhost\"\n  );\n}\n\nasync function getRemoteDialectOptions() {\n  // Configurações específicas para conexões remotas (útil a partir do pg 15)\n  return {\n    ssl: {\n      require: true,\n      rejectUnauthorized: false,\n    },\n  };\n}\n\nasync function getConfig(){\n  let dbConfig = {\n    username: process.env.DB_USER || \"postgres\",\n    password: process.env.DB_PWD || \"postgres\",\n    database: \"bia\",\n    host: process.env.DB_HOST || \"127.0.0.1\",\n    port: process.env.DB_PORT || 5433,\n    dialect: \"postgres\",\n    dialectOptions: await isLocalConnection() ? {} : await getRemoteDialectOptions(),\n  };\n\n  if(process.env.DB_SECRET_NAME && process.env.DB_SECRET_NAME.trim() !== '' ){\n    const secretsManagerClient = await createSecretsManagerClient();\n    const secrets = await getSecrets(secretsManagerClient);\n\n    if(secrets){\n      dbConfig.username = secrets.username;\n      dbConfig.password = secrets.password;\n\n      await imprimirSecrets(secrets);\n    }\n  }\n  return dbConfig;\n}\n\nasync function createSecretsManagerClient() {\n  // Verifica se a variável de ambiente está definida e não está vazia\n  let credentials;\n  \n  if (process.env.IS_LOCAL === \"true\") {\n    credentials = fromEnv();\n    //credentials = fromIni({ profile: \"SEU_PROFILE\" });\n  }\n  \n  if (process.env.DB_SECRET_NAME) {\n    // Instancia o cliente do Secrets Manager\n    const client = new SecretsManagerClient({\n      region: process.env.DB_REGION,\n      credentials\n    });\n\n  if(process.env.DEBUG_SECRET === \"true\"){\n    const stsClient = new STSClient({\n      region: process.env.DB_REGION,\n      credentials\n    });\n\n    try {\n      const identity = await stsClient.send(new GetCallerIdentityCommand({}));\n      console.log('Credenciais carregadas com sucesso:', identity);\n      console.log('Account ID:', identity.Account);\n    } catch (error) {\n      console.error('Erro ao carregar credenciais:', error);\n    }\n  }\n    return client;\n  } else {\n    console.log('DB_SECRET_NAME não está definida. Se for usar secrets, informe também DB_REGION.');\n    return null;\n  }\n}\n\nasync function imprimirSecrets(secrets){\n  if(process.env.DEBUG_SECRET === \"true\")\n    console.log(secrets);\n}\n\nasync function getSecrets(secretsManagerClient) {\n  try {\n    if (!secretsManagerClient) {\n      console.error('O cliente do Secrets Manager não foi instanciado.');\n      return;\n    }\n    console.log(`Vou trabalhar com o secrets ${process.env.DB_SECRET_NAME}`);\n    const command = new GetSecretValueCommand({ SecretId: process.env.DB_SECRET_NAME });\n    const data = await secretsManagerClient.send(command);\n\n    if ('SecretString' in data) {\n      return JSON.parse(data.SecretString);\n    } else {\n      return Buffer.from(data.SecretBinary, 'base64');\n    }\n  } catch (err) {\n    console.error('Erro ao recuperar as credenciais do Secrets Manager:', err);\n    throw err;\n  }\n}\n\nmodule.exports = getConfig;\n\n"
  },
  {
    "path": "config/default.json",
    "content": "{\n  \"server\": {\n    \"port\": 8080\n  }\n}"
  },
  {
    "path": "config/express.js",
    "content": "const express = require(\"express\");\nvar cors = require(\"cors\");\nvar path = require(\"path\");\nconst config = require(\"config\");\nvar bodyParser = require(\"body-parser\");\n\nmodule.exports = () => {\n  const app = express();\n\n  // SETANDO VARIÁVEIS DA APLICAÇÃO\n  app.set(\"port\", process.env.PORT || config.get(\"server.port\"));\n\n  //Setando react\n  app.use(express.static(path.join(__dirname, \"../\", \"client\", \"build\")));\n\n  // parse request bodies (req.body)\n  app.use(express.urlencoded({ extended: true }));\n  app.use(bodyParser.json());\n\n  app.use(cors());\n\n  require(\"../api/routes/tarefas\")(app);\n  require(\"../api/routes/versao\")(app);\n\n  // Fallback para React Router - serve index.html para todas as rotas não-API\n  app.get('*', (req, res) => {\n    res.sendFile(path.join(__dirname, \"../\", \"client\", \"build\", \"index.html\"));\n  });\n\n  return app;\n};\n"
  },
  {
    "path": "database/migrations/20210924000838-criar-tarefas.js",
    "content": "\"use strict\";\n\nmodule.exports = {\n  up: async (queryInterface, Sequelize) => {\n    return queryInterface.createTable(\"Tarefas\", {\n      uuid: {\n        allowNull: false,\n        primaryKey: true,\n        defaultValue: Sequelize.UUIDV1,\n        type: Sequelize.UUID,\n      },\n      titulo: {\n        allowNull: false,\n        type: Sequelize.STRING,\n      },\n      dia_atividade: {\n        allowNull: true,\n        type: Sequelize.STRING,\n      },\n      importante: {\n        allowNull: true,\n        defaultValue: false,\n        type: Sequelize.BOOLEAN,\n      },\n      createdAt: {\n        allowNull: false,\n        type: Sequelize.DATE,\n      },\n      updatedAt: {\n        allowNull: false,\n        type: Sequelize.DATE,\n      },\n    });\n  },\n\n  down: async (queryInterface, Sequelize) => {\n    await queryInterface.dropTable(\"Tarefas\");\n  },\n};\n"
  },
  {
    "path": "docs/README.md",
    "content": "# 📚 Documentação\n\n## 🏗️ Arquitetura AWS\n- [Diagrama ECS + EC2](./architecture/aws-ecs-diagram.html) - Visualização de uma das arquiteturas propostas para iniciar no treinamento e evoluir na Formação AWS\n"
  },
  {
    "path": "docs/architecture/aws-ecs-diagram.html",
    "content": "<!DOCTYPE html>\n<html lang=\"pt-BR\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Arquitetura AWS - Projeto BIA com ECS/EC2</title>\n    <style>\n        body {\n            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;\n            margin: 0;\n            padding: 20px;\n            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n            min-height: 100vh;\n        }\n        \n        .container {\n            max-width: 1400px;\n            margin: 0 auto;\n            background: white;\n            border-radius: 15px;\n            box-shadow: 0 20px 40px rgba(0,0,0,0.1);\n            overflow: hidden;\n        }\n        \n        .header {\n            background: linear-gradient(135deg, #FF9500 0%, #FF6B35 100%);\n            color: white;\n            padding: 30px;\n            text-align: center;\n        }\n        \n        .header h1 {\n            margin: 0;\n            font-size: 2.5em;\n            font-weight: 300;\n        }\n        \n        .header p {\n            margin: 10px 0 0 0;\n            opacity: 0.9;\n            font-size: 1.1em;\n        }\n        \n        .architecture-diagram {\n            padding: 40px;\n            background: #f8f9fa;\n        }\n        \n        .aws-region {\n            border: 3px dashed #FF9500;\n            border-radius: 15px;\n            padding: 30px;\n            margin: 20px 0;\n            background: white;\n            position: relative;\n        }\n        \n        .region-label {\n            position: absolute;\n            top: -15px;\n            left: 20px;\n            background: #FF9500;\n            color: white;\n            padding: 5px 15px;\n            border-radius: 20px;\n            font-weight: bold;\n            font-size: 0.9em;\n        }\n        \n        .vpc {\n            border: 2px solid #4CAF50;\n            border-radius: 10px;\n            padding: 25px;\n            margin: 20px 0;\n            background: #f0f8f0;\n            position: relative;\n        }\n        \n        .vpc-label {\n            position: absolute;\n            top: -12px;\n            left: 15px;\n            background: #4CAF50;\n            color: white;\n            padding: 3px 12px;\n            border-radius: 15px;\n            font-size: 0.8em;\n            font-weight: bold;\n        }\n        \n        .subnet-row {\n            display: flex;\n            gap: 20px;\n            margin: 20px 0;\n            flex-wrap: wrap;\n        }\n        \n        .subnet {\n            flex: 1;\n            min-width: 300px;\n            border: 2px solid #2196F3;\n            border-radius: 8px;\n            padding: 20px;\n            background: #e3f2fd;\n            position: relative;\n        }\n        \n        .subnet-label {\n            position: absolute;\n            top: -12px;\n            left: 15px;\n            background: #2196F3;\n            color: white;\n            padding: 3px 10px;\n            border-radius: 12px;\n            font-size: 0.75em;\n            font-weight: bold;\n        }\n        \n        .service-box {\n            background: white;\n            border-radius: 8px;\n            padding: 15px;\n            margin: 10px 0;\n            box-shadow: 0 4px 8px rgba(0,0,0,0.1);\n            border-left: 4px solid #FF6B35;\n            transition: transform 0.2s;\n        }\n        \n        .service-box:hover {\n            transform: translateY(-2px);\n            box-shadow: 0 6px 12px rgba(0,0,0,0.15);\n        }\n        \n        .service-title {\n            font-weight: bold;\n            color: #333;\n            margin-bottom: 8px;\n            display: flex;\n            align-items: center;\n            gap: 8px;\n        }\n        \n        .service-icon {\n            width: 24px;\n            height: 24px;\n            background: #FF6B35;\n            border-radius: 4px;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            color: white;\n            font-size: 12px;\n            font-weight: bold;\n        }\n        \n        .service-description {\n            color: #666;\n            font-size: 0.9em;\n            line-height: 1.4;\n        }\n        \n        .external-services {\n            display: flex;\n            gap: 20px;\n            margin: 30px 0;\n            flex-wrap: wrap;\n        }\n        \n        .external-service {\n            flex: 1;\n            min-width: 250px;\n            background: #fff3e0;\n            border: 2px solid #FF9500;\n            border-radius: 8px;\n            padding: 20px;\n            text-align: center;\n        }\n        \n        .flow-arrows {\n            text-align: center;\n            margin: 20px 0;\n            color: #666;\n            font-size: 1.2em;\n        }\n        \n        .tech-stack {\n            background: #f5f5f5;\n            padding: 30px;\n            margin-top: 20px;\n        }\n        \n        .tech-stack h3 {\n            color: #333;\n            margin-bottom: 20px;\n            text-align: center;\n        }\n        \n        .tech-grid {\n            display: grid;\n            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n            gap: 15px;\n        }\n        \n        .tech-item {\n            background: white;\n            padding: 15px;\n            border-radius: 8px;\n            text-align: center;\n            box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n        }\n        \n        .tech-category {\n            font-weight: bold;\n            color: #FF6B35;\n            margin-bottom: 8px;\n        }\n        \n        .benefits {\n            background: #e8f5e8;\n            padding: 30px;\n            margin-top: 20px;\n        }\n        \n        .benefits h3 {\n            color: #2e7d32;\n            margin-bottom: 20px;\n            text-align: center;\n        }\n        \n        .benefits-grid {\n            display: grid;\n            grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));\n            gap: 15px;\n        }\n        \n        .benefit-item {\n            background: white;\n            padding: 15px;\n            border-radius: 8px;\n            border-left: 4px solid #4CAF50;\n        }\n        \n        .benefit-title {\n            font-weight: bold;\n            color: #2e7d32;\n            margin-bottom: 5px;\n        }\n        \n        @media (max-width: 768px) {\n            .subnet-row {\n                flex-direction: column;\n            }\n            \n            .external-services {\n                flex-direction: column;\n            }\n            \n            .header h1 {\n                font-size: 2em;\n            }\n        }\n    </style>\n</head>\n<body>\n    <div class=\"container\">\n        <div class=\"header\">\n            <h1>🏗️ Arquitetura AWS - Projeto BIA</h1>\n            <p>ECS com EC2 + RDS PostgreSQL + CodePipeline</p>\n        </div>\n        \n        <div class=\"architecture-diagram\">\n            <!-- Serviços Externos -->\n            <div class=\"external-services\">\n                <div class=\"external-service\">\n                    <div class=\"service-title\">\n                        <div class=\"service-icon\">👥</div>\n                        Usuários\n                    </div>\n                    <div class=\"service-description\">\n                        Acesso via navegador web<br>\n                        Interface React (Vite)\n                    </div>\n                </div>\n                \n                <div class=\"external-service\">\n                    <div class=\"service-title\">\n                        <div class=\"service-icon\">🔧</div>\n                        GitHub\n                    </div>\n                    <div class=\"service-description\">\n                        Repositório de código<br>\n                        Trigger para CI/CD\n                    </div>\n                </div>\n            </div>\n            \n            <div class=\"flow-arrows\">⬇️ HTTPS / Git Webhook ⬇️</div>\n            \n            <!-- AWS Region -->\n            <div class=\"aws-region\">\n                <div class=\"region-label\">AWS Region (us-east-1)</div>\n                \n                <!-- Application Load Balancer -->\n                <div class=\"service-box\">\n                    <div class=\"service-title\">\n                        <div class=\"service-icon\">⚖️</div>\n                        Application Load Balancer (ALB)\n                    </div>\n                    <div class=\"service-description\">\n                        • Distribuição de tráfego entre instâncias ECS<br>\n                        • SSL/TLS termination<br>\n                        • Health checks automáticos<br>\n                        • Target Groups para containers\n                    </div>\n                </div>\n                \n                <div class=\"flow-arrows\">⬇️ Distribui tráfego ⬇️</div>\n                \n                <!-- VPC -->\n                <div class=\"vpc\">\n                    <div class=\"vpc-label\">VPC (10.0.0.0/16)</div>\n                    \n                    <!-- Public Subnets -->\n                    <div class=\"subnet-row\">\n                        <div class=\"subnet\">\n                            <div class=\"subnet-label\">Public Subnet A (10.0.1.0/24)</div>\n                            \n                            <div class=\"service-box\">\n                                <div class=\"service-title\">\n                                    <div class=\"service-icon\">🌐</div>\n                                    NAT Gateway\n                                </div>\n                                <div class=\"service-description\">\n                                    Acesso à internet para subnets privadas\n                                </div>\n                            </div>\n                        </div>\n                        \n                        <div class=\"subnet\">\n                            <div class=\"subnet-label\">Public Subnet B (10.0.2.0/24)</div>\n                            \n                            <div class=\"service-box\">\n                                <div class=\"service-title\">\n                                    <div class=\"service-icon\">🌐</div>\n                                    NAT Gateway\n                                </div>\n                                <div class=\"service-description\">\n                                    Redundância para alta disponibilidade\n                                </div>\n                            </div>\n                        </div>\n                    </div>\n                    \n                    <!-- Private Subnets -->\n                    <div class=\"subnet-row\">\n                        <div class=\"subnet\">\n                            <div class=\"subnet-label\">Private Subnet A (10.0.3.0/24)</div>\n                            \n                            <div class=\"service-box\">\n                                <div class=\"service-title\">\n                                    <div class=\"service-icon\">🐳</div>\n                                    ECS Cluster + EC2 Instances\n                                </div>\n                                <div class=\"service-description\">\n                                    • Auto Scaling Group<br>\n                                    • Instâncias t3.micro (2-4 instâncias)<br>\n                                    • ECS Agent rodando<br>\n                                    • Container BIA (Node.js + React)\n                                </div>\n                            </div>\n                            \n                            <div class=\"service-box\">\n                                <div class=\"service-title\">\n                                    <div class=\"service-icon\">📊</div>\n                                    CloudWatch Logs\n                                </div>\n                                <div class=\"service-description\">\n                                    Logs da aplicação e containers\n                                </div>\n                            </div>\n                        </div>\n                        \n                        <div class=\"subnet\">\n                            <div class=\"subnet-label\">Private Subnet B (10.0.4.0/24)</div>\n                            \n                            <div class=\"service-box\">\n                                <div class=\"service-title\">\n                                    <div class=\"service-icon\">🐳</div>\n                                    ECS Cluster + EC2 Instances\n                                </div>\n                                <div class=\"service-description\">\n                                    • Réplica para alta disponibilidade<br>\n                                    • Mesma configuração da Subnet A<br>\n                                    • Load balancing automático\n                                </div>\n                            </div>\n                            \n                            <div class=\"service-box\">\n                                <div class=\"service-title\">\n                                    <div class=\"service-icon\">🔐</div>\n                                    Secrets Manager\n                                </div>\n                                <div class=\"service-description\">\n                                    Credenciais do banco de dados\n                                </div>\n                            </div>\n                        </div>\n                    </div>\n                    \n                    <!-- Database Subnets -->\n                    <div class=\"subnet-row\">\n                        <div class=\"subnet\">\n                            <div class=\"subnet-label\">DB Subnet A (10.0.5.0/24)</div>\n                            \n                            <div class=\"service-box\">\n                                <div class=\"service-title\">\n                                    <div class=\"service-icon\">🗄️</div>\n                                    RDS PostgreSQL (Primary)\n                                </div>\n                                <div class=\"service-description\">\n                                    • PostgreSQL 16<br>\n                                    • db.t3.micro (para desenvolvimento)<br>\n                                    • Automated backups<br>\n                                    • Multi-AZ para produção\n                                </div>\n                            </div>\n                        </div>\n                        \n                        <div class=\"subnet\">\n                            <div class=\"subnet-label\">DB Subnet B (10.0.6.0/24)</div>\n                            \n                            <div class=\"service-box\">\n                                <div class=\"service-title\">\n                                    <div class=\"service-icon\">🗄️</div>\n                                    RDS PostgreSQL (Standby)\n                                </div>\n                                <div class=\"service-description\">\n                                    • Réplica síncrona (Multi-AZ)<br>\n                                    • Failover automático<br>\n                                    • Backup e recuperação\n                                </div>\n                            </div>\n                        </div>\n                    </div>\n                </div>\n                \n                <!-- CI/CD Pipeline -->\n                <div class=\"service-box\" style=\"margin-top: 30px;\">\n                    <div class=\"service-title\">\n                        <div class=\"service-icon\">🚀</div>\n                        CI/CD Pipeline\n                    </div>\n                    <div class=\"service-description\">\n                        <strong>CodePipeline:</strong> GitHub → CodeBuild → ECR → ECS Deploy<br>\n                        <strong>CodeBuild:</strong> Build da imagem Docker + Push para ECR<br>\n                        <strong>ECR:</strong> Registry privado para imagens Docker<br>\n                        <strong>ECS Service:</strong> Deploy automático com rolling updates\n                    </div>\n                </div>\n            </div>\n        </div>\n        \n        <!-- Tech Stack -->\n        <div class=\"tech-stack\">\n            <h3>🛠️ Stack Tecnológica Identificada</h3>\n            <div class=\"tech-grid\">\n                <div class=\"tech-item\">\n                    <div class=\"tech-category\">Frontend</div>\n                    React 18 + Vite<br>\n                    React Router DOM<br>\n                    React Icons\n                </div>\n                <div class=\"tech-item\">\n                    <div class=\"tech-category\">Backend</div>\n                    Node.js + Express<br>\n                    Sequelize ORM<br>\n                    Morgan (Logging)\n                </div>\n                <div class=\"tech-item\">\n                    <div class=\"tech-category\">Database</div>\n                    PostgreSQL 16<br>\n                    Sequelize Migrations<br>\n                    Connection Pooling\n                </div>\n                <div class=\"tech-item\">\n                    <div class=\"tech-category\">AWS Services</div>\n                    ECS + EC2<br>\n                    RDS PostgreSQL<br>\n                    Secrets Manager\n                </div>\n                <div class=\"tech-item\">\n                    <div class=\"tech-category\">DevOps</div>\n                    Docker<br>\n                    CodePipeline<br>\n                    CodeBuild + ECR\n                </div>\n                <div class=\"tech-item\">\n                    <div class=\"tech-category\">Monitoramento</div>\n                    CloudWatch Logs<br>\n                    Health Checks<br>\n                    Application Insights\n                </div>\n            </div>\n        </div>\n        \n        <!-- Benefits -->\n        <div class=\"benefits\">\n            <h3>✅ Benefícios desta Arquitetura</h3>\n            <div class=\"benefits-grid\">\n                <div class=\"benefit-item\">\n                    <div class=\"benefit-title\">Alta Disponibilidade</div>\n                    Multi-AZ deployment com failover automático\n                </div>\n                <div class=\"benefit-item\">\n                    <div class=\"benefit-title\">Escalabilidade</div>\n                    Auto Scaling baseado em CPU/memória\n                </div>\n                <div class=\"benefit-item\">\n                    <div class=\"benefit-title\">Segurança</div>\n                    Subnets privadas + Secrets Manager\n                </div>\n                <div class=\"benefit-item\">\n                    <div class=\"benefit-title\">CI/CD Automatizado</div>\n                    Deploy automático via CodePipeline\n                </div>\n                <div class=\"benefit-item\">\n                    <div class=\"benefit-title\">Monitoramento</div>\n                    CloudWatch integrado + Health checks\n                </div>\n                <div class=\"benefit-item\">\n                    <div class=\"benefit-title\">Custo Otimizado</div>\n                    EC2 Spot instances + RDS otimizado\n                </div>\n            </div>\n        </div>\n    </div>\n</body>\n</html>\n"
  },
  {
    "path": "generate-sts-token.bat",
    "content": "@echo off\nREM Script para Windows CMD que chama o script bash\nREM Uso: generate-sts-token.bat <profile-name> [duration-seconds] [--export-cmd]\n\nREM Verifica se Git Bash está disponível\nwhere bash >nul 2>nul\nif %ERRORLEVEL% NEQ 0 (\n    echo Erro: Git Bash nao encontrado. Instale o Git for Windows.\n    echo Download: https://git-scm.com/download/win\n    exit /b 1\n)\n\nREM Executa o script bash\nif \"%3\"==\"--export-cmd\" (\n    REM Modo export para CMD\n    bash \"%~dp0generate-sts-token.sh\" %1 %2 --export-cmd\n) else (\n    REM Modo normal\n    bash \"%~dp0generate-sts-token.sh\" %*\n)\n"
  },
  {
    "path": "generate-sts-token.sh",
    "content": "#!/bin/bash\n\n# Script universal para gerar token STS com base no profile AWS\n# Funciona em: Windows (Git Bash/WSL), macOS e Linux\n# Uso: ./generate-sts-token.sh <profile-name> [duration-seconds] [--export]\n\n# Detecta o sistema operacional\ndetect_os() {\n    case \"$(uname -s)\" in\n        CYGWIN*|MINGW*|MSYS*)\n            echo \"windows\"\n            ;;\n        Darwin*)\n            echo \"macos\"\n            ;;\n        Linux*)\n            echo \"linux\"\n            ;;\n        *)\n            echo \"unknown\"\n            ;;\n    esac\n}\n\n# Função para mostrar ajuda\nshow_help() {\n    local os_type=$(detect_os)\n    echo \"Uso: $0 <profile-name> [duration-seconds] [--export]\"\n    echo \"\"\n    echo \"Sistema detectado: $os_type\"\n    echo \"\"\n    echo \"Parâmetros:\"\n    echo \"  profile-name      Nome do profile AWS (obrigatório)\"\n    echo \"  duration-seconds  Duração em segundos (opcional, padrão: 3600)\"\n    echo \"  --export          Exporta as credenciais na sessão atual (opcional)\"\n    echo \"\"\n    echo \"Exemplos:\"\n    echo \"  $0 meu-profile                    # Token de 1 hora\"\n    echo \"  $0 meu-profile 7200               # Token de 2 horas\"\n    echo \"  $0 meu-profile 3600 --export      # Token de 1 hora + export automático\"\n    echo \"  $0 meu-profile --export           # Token de 1 hora + export automático\"\n    echo \"\"\n    \n    if [[ \"$os_type\" == \"windows\" ]]; then\n        echo \"Para Windows (Git Bash/PowerShell/CMD):\"\n        echo \"  # Git Bash:\"\n        echo \"  source <($0 meu-profile --export)\"\n        echo \"  # PowerShell:\"\n        echo \"  Invoke-Expression \\$(./$0 meu-profile --export-ps)\"\n        echo \"  # CMD:\"\n        echo \"  ./$0 meu-profile --export-cmd > temp.bat && temp.bat && del temp.bat\"\n    else\n        echo \"Para Unix/Linux/macOS:\"\n        echo \"  source <($0 meu-profile --export)\"\n    fi\n    \n    echo \"\"\n    echo \"Nota: Duração mínima é de 15 minutos (900 segundos)\"\n    echo \"      Duração máxima é de 12 horas (43200 segundos)\"\n}\n\n# Função para verificar dependências\ncheck_dependencies() {\n    local os_type=$(detect_os)\n    local missing_deps=()\n    \n    # Verifica AWS CLI\n    if ! command -v aws &> /dev/null; then\n        missing_deps+=(\"aws-cli\")\n    fi\n    \n    # Verifica jq\n    if ! command -v jq &> /dev/null; then\n        missing_deps+=(\"jq\")\n    fi\n    \n    if [[ ${#missing_deps[@]} -gt 0 ]]; then\n        echo \"Erro: Dependências não encontradas: ${missing_deps[*]}\"\n        echo \"\"\n        case \"$os_type\" in\n            \"windows\")\n                echo \"Para instalar no Windows:\"\n                echo \"  AWS CLI: https://aws.amazon.com/cli/\"\n                echo \"  jq: choco install jq (via Chocolatey) ou baixe de https://stedolan.github.io/jq/\"\n                ;;\n            \"macos\")\n                echo \"Para instalar no macOS:\"\n                echo \"  brew install awscli jq\"\n                ;;\n            \"linux\")\n                echo \"Para instalar no Linux:\"\n                echo \"  # Ubuntu/Debian:\"\n                echo \"  sudo apt-get install awscli jq\"\n                echo \"  # CentOS/RHEL:\"\n                echo \"  sudo yum install awscli jq\"\n                ;;\n        esac\n        exit 1\n    fi\n}\n\n# Inicializa variáveis\nPROFILE_NAME=\"\"\nDURATION=3600  # Default: 1 hora\nEXPORT_MODE=false\nEXPORT_PS=false\nEXPORT_CMD=false\n\n# Processa argumentos\nwhile [[ $# -gt 0 ]]; do\n    case $1 in\n        --export)\n            EXPORT_MODE=true\n            shift\n            ;;\n        --export-ps)\n            EXPORT_PS=true\n            shift\n            ;;\n        --export-cmd)\n            EXPORT_CMD=true\n            shift\n            ;;\n        --help|-h)\n            show_help\n            exit 0\n            ;;\n        *)\n            if [[ -z \"$PROFILE_NAME\" ]]; then\n                PROFILE_NAME=\"$1\"\n            elif [[ \"$1\" =~ ^[0-9]+$ ]]; then\n                DURATION=\"$1\"\n            else\n                echo \"Erro: Argumento inválido '$1'\"\n                show_help\n                exit 1\n            fi\n            shift\n            ;;\n    esac\ndone\n\n# Verifica se profile foi informado\nif [[ -z \"$PROFILE_NAME\" ]]; then\n    echo \"Erro: Profile AWS não informado\"\n    echo \"\"\n    show_help\n    exit 1\nfi\n\n# Verifica dependências\ncheck_dependencies\n\n# Valida duração mínima (15 minutos = 900 segundos)\nif [ \"$DURATION\" -lt 900 ]; then\n    echo \"Erro: Duração mínima é de 15 minutos (900 segundos)\"\n    echo \"Duração informada: ${DURATION} segundos\"\n    echo \"Use: $0 ${PROFILE_NAME} 900 (ou maior)\"\n    exit 1\nfi\n\n# Verifica se o profile existe\nPROFILES=$(aws configure list-profiles 2>/dev/null)\nif ! echo \"${PROFILES}\" | grep -q \"^${PROFILE_NAME}$\"; then\n    echo \"Erro: Profile '${PROFILE_NAME}' não encontrado\"\n    echo \"Profiles disponíveis:\"\n    echo \"${PROFILES}\"\n    exit 1\nfi\n\n# Detecta sistema operacional\nOS_TYPE=$(detect_os)\n\n# Mostra informações iniciais (exceto em modos de export)\nif [[ \"$EXPORT_MODE\" == false && \"$EXPORT_PS\" == false && \"$EXPORT_CMD\" == false ]]; then\n    echo \"Sistema: $OS_TYPE\"\n    echo \"Gerando token STS para o profile: ${PROFILE_NAME}\"\n    echo \"Duração: ${DURATION} segundos\"\n    echo \"----------------------------------------\"\nfi\n\n# Gera o token STS\nSTS_OUTPUT=$(aws sts get-session-token \\\n    --profile \"${PROFILE_NAME}\" \\\n    --duration-seconds \"${DURATION}\" \\\n    --output json 2>/dev/null)\n\nif [ $? -ne 0 ]; then\n    echo \"Erro ao gerar token STS. Verifique suas credenciais e permissões.\"\n    exit 1\nfi\n\n# Extrai as credenciais do output\nACCESS_KEY=$(echo \"${STS_OUTPUT}\" | jq -r '.Credentials.AccessKeyId')\nSECRET_KEY=$(echo \"${STS_OUTPUT}\" | jq -r '.Credentials.SecretAccessKey')\nSESSION_TOKEN=$(echo \"${STS_OUTPUT}\" | jq -r '.Credentials.SessionToken')\nEXPIRATION=$(echo \"${STS_OUTPUT}\" | jq -r '.Credentials.Expiration')\n\n# Saída baseada no modo e sistema operacional\nif [[ \"$EXPORT_MODE\" == true ]]; then\n    # Modo export Unix/Linux/macOS\n    echo \"export AWS_ACCESS_KEY_ID=${ACCESS_KEY}\"\n    echo \"export AWS_SECRET_ACCESS_KEY=${SECRET_KEY}\"\n    echo \"export AWS_SESSION_TOKEN=${SESSION_TOKEN}\"\n    echo \"# Credenciais exportadas para a sessão atual!\"\n    echo \"# Sistema: $OS_TYPE\"\n    echo \"# Expira em: ${EXPIRATION}\"\nelif [[ \"$EXPORT_PS\" == true ]]; then\n    # Modo export PowerShell\n    echo \"\\$env:AWS_ACCESS_KEY_ID='${ACCESS_KEY}'\"\n    echo \"\\$env:AWS_SECRET_ACCESS_KEY='${SECRET_KEY}'\"\n    echo \"\\$env:AWS_SESSION_TOKEN='${SESSION_TOKEN}'\"\n    echo \"# Credenciais exportadas para PowerShell!\"\n    echo \"# Expira em: ${EXPIRATION}\"\nelif [[ \"$EXPORT_CMD\" == true ]]; then\n    # Modo export CMD\n    echo \"set AWS_ACCESS_KEY_ID=${ACCESS_KEY}\"\n    echo \"set AWS_SECRET_ACCESS_KEY=${SECRET_KEY}\"\n    echo \"set AWS_SESSION_TOKEN=${SESSION_TOKEN}\"\n    echo \"REM Credenciais exportadas para CMD!\"\n    echo \"REM Expira em: ${EXPIRATION}\"\nelse\n    # Modo normal: mostra todas as informações\n    echo \"Token STS gerado com sucesso!\"\n    echo \"Expira em: ${EXPIRATION}\"\n    echo \"\"\n    echo \"=== Credenciais temporárias ===\"\n    echo \"AWS_ACCESS_KEY_ID=${ACCESS_KEY}\"\n    echo \"AWS_SECRET_ACCESS_KEY=${SECRET_KEY}\"\n    echo \"AWS_SESSION_TOKEN=${SESSION_TOKEN}\"\n    echo \"\"\n    \n    # Instruções específicas por sistema operacional\n    case \"$OS_TYPE\" in\n        \"windows\")\n            echo \"=== Para usar no Git Bash ===\"\n            echo \"export AWS_ACCESS_KEY_ID=${ACCESS_KEY}\"\n            echo \"export AWS_SECRET_ACCESS_KEY=${SECRET_KEY}\"\n            echo \"export AWS_SESSION_TOKEN=${SESSION_TOKEN}\"\n            echo \"\"\n            echo \"=== Para usar no PowerShell ===\"\n            echo \"\\$env:AWS_ACCESS_KEY_ID='${ACCESS_KEY}'\"\n            echo \"\\$env:AWS_SECRET_ACCESS_KEY='${SECRET_KEY}'\"\n            echo \"\\$env:AWS_SESSION_TOKEN='${SESSION_TOKEN}'\"\n            echo \"\"\n            echo \"=== Para usar no CMD ===\"\n            echo \"set AWS_ACCESS_KEY_ID=${ACCESS_KEY}\"\n            echo \"set AWS_SECRET_ACCESS_KEY=${SECRET_KEY}\"\n            echo \"set AWS_SESSION_TOKEN=${SESSION_TOKEN}\"\n            echo \"\"\n            echo \"=== Export automático ===\"\n            echo \"# Git Bash:\"\n            echo \"source <($0 ${PROFILE_NAME} ${DURATION} --export)\"\n            echo \"# PowerShell:\"\n            echo \"Invoke-Expression \\$(./$0 ${PROFILE_NAME} ${DURATION} --export-ps)\"\n            echo \"# CMD:\"\n            echo \"$0 ${PROFILE_NAME} ${DURATION} --export-cmd > temp.bat && temp.bat && del temp.bat\"\n            ;;\n        *)\n            echo \"=== Para usar no terminal ===\"\n            echo \"export AWS_ACCESS_KEY_ID=${ACCESS_KEY}\"\n            echo \"export AWS_SECRET_ACCESS_KEY=${SECRET_KEY}\"\n            echo \"export AWS_SESSION_TOKEN=${SESSION_TOKEN}\"\n            echo \"\"\n            echo \"=== Export automático ===\"\n            echo \"source <($0 ${PROFILE_NAME} ${DURATION} --export)\"\n            ;;\n    esac\n    \n    echo \"\"\n    echo \"=== Para usar em arquivo .env ===\"\n    echo \"AWS_ACCESS_KEY_ID=${ACCESS_KEY}\"\n    echo \"AWS_SECRET_ACCESS_KEY=${SECRET_KEY}\"\n    echo \"AWS_SESSION_TOKEN=${SESSION_TOKEN}\"\n    echo \"\"\n    echo \"=== Para salvar em arquivo ===\"\n    echo \"Para salvar as variáveis em um arquivo:\"\n    echo \"$0 ${PROFILE_NAME} ${DURATION} > sts-credentials.env\"\nfi\n"
  },
  {
    "path": "index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n    <head>\n        <meta charset=\"UTF-8\">\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n        \n        <title>Hello World Simple App</title>\n    </head>\n    <body>\n        <div>Hello World!</div>\n    </body>\n</html>"
  },
  {
    "path": "index.js",
    "content": "var express = require(\"express\");\nvar logger = require(\"morgan\");\nvar path = require(\"path\");\nvar session = require(\"express-session\");\nvar methodOverride = require(\"method-override\");\n\nvar app = express();\n\n// define a custom res.message() method\n// which stores messages in the session\napp.response.message = function (msg) {\n  // reference `req.session` via the `this.req` reference\n  var sess = this.req.session;\n  // simply add the msg to an array for later\n  sess.messages = sess.messages || [];\n  sess.messages.push(msg);\n  return this;\n};\n\n// log\napp.use(logger(\"dev\"));\n\n// serve static files\napp.use(express.static(path.join(__dirname, \"app\", \"public\")));\napp.use(express.static(path.join(__dirname, \"client\", \"build\")));\n\n// session support\napp.use(\n  session({\n    resave: false, // don't save session if unmodified\n    saveUninitialized: false, // don't create session until something stored\n    secret: \"some secret here\",\n  })\n);\n\n// parse request bodies (req.body)\napp.use(express.urlencoded({ extended: true }));\n\n// allow overriding methods in query (?_method=put)\napp.use(methodOverride(\"_method\"));\n\n// expose the \"messages\" local variable when views are rendered\napp.use(function (req, res, next) {\n  var msgs = req.session.messages || [];\n\n  // expose \"messages\" local variable\n  res.locals.messages = msgs;\n\n  // expose \"hasMessages\"\n  res.locals.hasMessages = !!msgs.length;\n\n  /* This is equivalent:\n    res.locals({\n      messages: msgs,\n      hasMessages: !! msgs.length\n    });\n   */\n\n  next();\n  // empty or \"flush\" the messages so they\n  // don't build up\n  req.session.messages = [];\n});\n\n// load controllers\nrequire(\"./lib/boot\")(app, { verbose: false });\n\napp.use(function (err, req, res, next) {\n  // log it\n  console.error(err.stack);\n\n  // error page\n  res.status(500).render(\"5xx\");\n});\n\n// assume 404 since no middleware responded\napp.use(function (req, res, next) {\n  res.status(404).render(\"404\", { url: req.originalUrl });\n});\n\napp.listen(3000);\nconsole.log(\"Express started on port 3000\");\n"
  },
  {
    "path": "lib/boot.js",
    "content": "/**\n * Module dependencies.\n */\n\nvar express = require(\"express\");\nvar fs = require(\"fs\");\nvar path = require(\"path\");\n\nmodule.exports = function (parent, options) {\n  var dir = path.join(__dirname, \"..\", \"app\", \"controllers\");\n  var verbose = options.verbose;\n  fs.readdirSync(dir).forEach(function (name) {\n    var file = path.join(dir, name);\n    if (!fs.statSync(file).isDirectory()) return;\n    verbose && console.log(\"\\n   %s:\", name);\n    var obj = require(file);\n    var name = obj.name || name;\n    var prefix = obj.prefix || \"\";\n    var app = express();\n    var handler;\n    var method;\n    var url;\n\n    // allow specifying the view engine\n    if (obj.engine) app.set(\"view engine\", obj.engine);\n    app.set(\n      \"views\",\n      path.join(__dirname, \"..\", \"app\", \"controllers\", name, \"views\")\n    );\n\n    // generate routes based\n    // on the exported methods\n    for (var key in obj) {\n      // \"reserved\" exports\n      if (~[\"name\", \"prefix\", \"engine\", \"before\"].indexOf(key)) continue;\n      // route exports\n      switch (key) {\n        case \"show\":\n          method = \"get\";\n          url = \"/\" + name + \"/:\" + name + \"_id\";\n          break;\n        case \"list\":\n          method = \"get\";\n          url = \"/\" + name + \"s\";\n          break;\n        case \"edit\":\n          method = \"get\";\n          url = \"/\" + name + \"/:\" + name + \"_id/edit\";\n          break;\n        case \"update\":\n          method = \"put\";\n          url = \"/\" + name + \"/:\" + name + \"_id\";\n          break;\n        case \"create\":\n          method = \"post\";\n          url = \"/\" + name;\n          break;\n        case \"index\":\n          method = \"get\";\n          url = \"/\";\n          break;\n        default:\n          /* istanbul ignore next */\n          throw new Error(\"unrecognized route: \" + name + \".\" + key);\n      }\n\n      // setup\n      handler = obj[key];\n      url = prefix + url;\n\n      // before middleware support\n      if (obj.before) {\n        app[method](url, obj.before, handler);\n        verbose &&\n          console.log(\n            \"     %s %s -> before -> %s\",\n            method.toUpperCase(),\n            url,\n            key\n          );\n      } else {\n        app[method](url, handler);\n        verbose &&\n          console.log(\"     %s %s -> %s\", method.toUpperCase(), url, key);\n      }\n    }\n\n    // mount the app\n    parent.use(app);\n  });\n};\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"bia\",\n  \"version\": \"4.2.0\",\n  \"description\": \"### Período do evento: 23/05 a 24/05/2026 (Online e ao vivo das 9h30 às 17h30)\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test\": \"jest tests/unit\",\n    \"start\": \"node server\",\n    \"start_db\": \"node config/database.js\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/henrylle/bia.git\"\n  },\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"bugs\": {\n    \"url\": \"https://github.com/henrylle/bia/issues\"\n  },\n  \"homepage\": \"https://github.com/henrylle/bia#readme\",\n  \"dependencies\": {\n    \"@aws-sdk/client-secrets-manager\": \"^3.583.0\",\n    \"@aws-sdk/client-sts\": \"^3.583.0\",\n    \"@aws-sdk/credential-providers\": \"^3.583.0\",\n    \"config\": \"^4.1.1\",\n    \"cors\": \"^2.8.5\",\n    \"ejs\": \"^3.1.6\",\n    \"express\": \"^4.17.1\",\n    \"express-session\": \"^1.17.2\",\n    \"hbs\": \"^4.1.2\",\n    \"json-server\": \"^0.17.4\",\n    \"method-override\": \"^3.0.0\",\n    \"morgan\": \"^1.10.0\",\n    \"pg\": \"^8.7.1\",\n    \"pg-hstore\": \"^2.3.4\",\n    \"sequelize\": \"^6.6.5\"\n  },\n  \"eslintConfig\": {\n    \"extends\": [\n      \"react-app\"\n    ]\n  },\n  \"devDependencies\": {\n    \"jest\": \"^27.5.1\",\n    \"sequelize-cli\": \"^6.2.0\"\n  }\n}\n"
  },
  {
    "path": "rodar_app_local_unix.sh",
    "content": "docker compose up -d database\nnpm install --loglevel=error\nnpm run build --prefix client --loglevel=error\nnpx sequelize db:migrate\nnpm start"
  },
  {
    "path": "rodar_app_local_windows.bat",
    "content": "@echo off\ndocker compose up -d database\nif %errorlevel% neq 0 exit /b %errorlevel%\ncall npm install -g npm@latest --loglevel=error\ncall npm install --loglevel=error\ncall npm run build --prefix client --loglevel=error\ncall npx sequelize db:migrate\nnpm start\n"
  },
  {
    "path": "scripts/criar_role_ssm.sh",
    "content": "role_name=\"role-acesso-ssm\"\npolicy_name=\"AmazonSSMManagedInstanceCore\"\n\nif aws iam get-role --role-name \"$role_name\" &> /dev/null; then\n    echo \"A IAM role $role_name já existe.\"\n    exit 1\nfi\n\naws iam create-role --role-name $role_name --assume-role-policy-document file://ec2_principal.json\n# Cria o perfil de instância\naws iam create-instance-profile --instance-profile-name $role_name\n\n# Adiciona a função IAM ao perfil de instância\naws iam add-role-to-instance-profile --instance-profile-name $role_name --role-name $role_name\n\naws iam attach-role-policy --role-name $role_name --policy-arn arn:aws:iam::aws:policy/$policy_name"
  },
  {
    "path": "scripts/ec2_principal.json",
    "content": "{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [{\n    \"Effect\": \"Allow\",\n    \"Principal\": {\n      \"Service\": \"ec2.amazonaws.com\"\n    },\n    \"Action\": \"sts:AssumeRole\"\n  }]\n}"
  },
  {
    "path": "scripts/ecs/unix/build.sh",
    "content": "ECR_REGISTRY=\"SEU_REGISTRY\"\naws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin $ECR_REGISTRY\ndocker build -t bia .\ndocker tag bia:latest $ECR_REGISTRY/bia:latest\ndocker push $ECR_REGISTRY/bia:latest\n"
  },
  {
    "path": "scripts/ecs/unix/check-disponibilidade.sh",
    "content": "url=\"https://www.uol.com.br\"\ndocker build -t check_disponibilidade -f Dockerfile_checkdisponibilidade .\ndocker run --rm -ti -e URL=$url check_disponibilidade\n"
  },
  {
    "path": "scripts/ecs/unix/deploy.sh",
    "content": "./build.sh\naws ecs update-service --cluster [SEU_CLUSTER] --service [SEU_SERVICE]  --force-new-deployment"
  },
  {
    "path": "scripts/ecs/unix/testar-latencia.sh",
    "content": "#URL=\"https://pages.formacaoaws.com.br/\"\nURL=\"https://pages.formacaoaws.com.br/bootcamp-imersao-aws-ga-qt-fu/\"\n#URL=\"https://pages.formacaoaws.com.br/wp-includes/images/blank.gif\"\nwhile true; do \n  START=$(date '+%Y-%m-%d %H:%M:%S')\n  START_MS=$(gdate +%s%3N)\n  \n  # Captura todos os cabeçalhos\n  HEADERS=$(curl -s -I -X GET $URL)\n  \n  # Extração dos valores desejados\n  X_CACHE=$(echo \"$HEADERS\" | grep -i x-cache | awk -F ': ' '{print $2}' | tr -d '\\r')\n  CACHE_CONTROL=$(echo \"$HEADERS\" | grep -i cache-control | awk -F ': ' '{print $2}' | tr -d '\\r')\n  \n  END_MS=$(gdate +%s%3N)\n  DURATION=$((END_MS - START_MS))\n  \n  # Adiciona o Cache-Control apenas se X-Cache contiver \"Miss from cloudfront\"\n  if [[ \"$X_CACHE\" == *\"Miss from cloudfront\"* ]]; then\n    X_CACHE=\"$X_CACHE ($CACHE_CONTROL)\"\n  fi\n  \n  # Exibe o log com os cabeçalhos desejados\n  echo \"$START - $DURATION ms - ${X_CACHE:-N/A}\"\n  \n  sleep 2;\ndone\n"
  },
  {
    "path": "scripts/ecs/windows/build.bat",
    "content": "aws ecr get-login-password --region us-east-1 --profile [SEU_PROFILE] | docker login --username AWS --password-stdin [SEU_ECR]\ndocker build -t bia .\ndocker tag bia:latest [SEU_ECR]/bia:latest\ndocker push [SEU_ECR]/bia:latest"
  },
  {
    "path": "scripts/ecs/windows/check-disponibilidade.bat",
    "content": "set url=\"https://www.uol.com.br\"\ndocker build -t check_disponibilidade -f Dockerfile_checkdisponibilidade .\ndocker run --rm -ti -e URL=%url% check_disponibilidade\n"
  },
  {
    "path": "scripts/ecs/windows/deploy.bat",
    "content": "call build.bat\naws ecs update-service --cluster [SEU_CLUSTER] --service [SEU_SERVICE]  --force-new-deployment --profile [SEU_PROFILE]\n"
  },
  {
    "path": "scripts/generate-sts-token.sh",
    "content": "#!/bin/bash\n\n# Script para gerar token STS com base no profile AWS\n# Uso: ./generate-sts-token.sh <profile-name> [duration-seconds] [--export]\n\n# Função para mostrar ajuda\nshow_help() {\n    echo \"Uso: $0 <profile-name> [duration-seconds] [--export]\"\n    echo \"\"\n    echo \"Parâmetros:\"\n    echo \"  profile-name      Nome do profile AWS (obrigatório)\"\n    echo \"  duration-seconds  Duração em segundos (opcional, padrão: 3600)\"\n    echo \"  --export          Exporta as credenciais na sessão atual (opcional)\"\n    echo \"\"\n    echo \"Exemplos:\"\n    echo \"  $0 meu-profile                    # Token de 1 hora\"\n    echo \"  $0 meu-profile 7200               # Token de 2 horas\"\n    echo \"  $0 meu-profile 3600 --export      # Token de 1 hora + export automático\"\n    echo \"  $0 meu-profile --export           # Token de 1 hora + export automático\"\n    echo \"  source <($0 meu-profile --export) # Alternativa para export\"\n    echo \"\"\n    echo \"Nota: Duração mínima é de 15 minutos (900 segundos)\"\n    echo \"      Duração máxima é de 12 horas (43200 segundos)\"\n}\n\n# Inicializa variáveis\nPROFILE_NAME=\"\"\nDURATION=3600  # Default: 1 hora\nEXPORT_MODE=false\n\n# Processa argumentos\nwhile [[ $# -gt 0 ]]; do\n    case $1 in\n        --export)\n            EXPORT_MODE=true\n            shift\n            ;;\n        --help|-h)\n            show_help\n            exit 0\n            ;;\n        *)\n            if [[ -z \"$PROFILE_NAME\" ]]; then\n                PROFILE_NAME=\"$1\"\n            elif [[ \"$1\" =~ ^[0-9]+$ ]]; then\n                DURATION=\"$1\"\n            else\n                echo \"Erro: Argumento inválido '$1'\"\n                show_help\n                exit 1\n            fi\n            shift\n            ;;\n    esac\ndone\n\n# Verifica se profile foi informado\nif [[ -z \"$PROFILE_NAME\" ]]; then\n    echo \"Erro: Profile AWS não informado\"\n    echo \"\"\n    show_help\n    exit 1\nfi\n\n# Valida duração mínima (15 minutos = 900 segundos)\nif [ \"$DURATION\" -lt 900 ]; then\n    echo \"Erro: Duração mínima é de 15 minutos (900 segundos)\"\n    echo \"Duração informada: ${DURATION} segundos\"\n    echo \"Use: $0 ${PROFILE_NAME} 900 (ou maior)\"\n    exit 1\nfi\n\n# Verifica se o profile existe\nPROFILES=$(aws configure list-profiles 2>/dev/null)\nif ! echo \"${PROFILES}\" | grep -q \"^${PROFILE_NAME}$\"; then\n    echo \"Erro: Profile '${PROFILE_NAME}' não encontrado\"\n    echo \"Profiles disponíveis:\"\n    echo \"${PROFILES}\"\n    exit 1\nfi\n\nif [ \"$EXPORT_MODE\" = false ]; then\n    echo \"Gerando token STS para o profile: ${PROFILE_NAME}\"\n    echo \"Duração: ${DURATION} segundos\"\n    echo \"----------------------------------------\"\nfi\n\n# Gera o token STS\nSTS_OUTPUT=$(aws sts get-session-token \\\n    --profile \"${PROFILE_NAME}\" \\\n    --duration-seconds \"${DURATION}\" \\\n    --output json 2>/dev/null)\n\nif [ $? -ne 0 ]; then\n    echo \"Erro ao gerar token STS. Verifique suas credenciais e permissões.\"\n    exit 1\nfi\n\n# Extrai as credenciais do output\nACCESS_KEY=$(echo \"${STS_OUTPUT}\" | jq -r '.Credentials.AccessKeyId')\nSECRET_KEY=$(echo \"${STS_OUTPUT}\" | jq -r '.Credentials.SecretAccessKey')\nSESSION_TOKEN=$(echo \"${STS_OUTPUT}\" | jq -r '.Credentials.SessionToken')\nEXPIRATION=$(echo \"${STS_OUTPUT}\" | jq -r '.Credentials.Expiration')\n\nif [ \"$EXPORT_MODE\" = true ]; then\n    # Modo export: só mostra os comandos export para serem executados via source\n    echo \"export AWS_ACCESS_KEY_ID=${ACCESS_KEY}\"\n    echo \"export AWS_SECRET_ACCESS_KEY=${SECRET_KEY}\"\n    echo \"export AWS_SESSION_TOKEN=${SESSION_TOKEN}\"\n    echo \"# Credenciais exportadas para a sessão atual!\"\n    echo \"# Expira em: ${EXPIRATION}\"\nelse\n    # Modo normal: mostra todas as informações\n    echo \"Token STS gerado com sucesso!\"\n    echo \"Expira em: ${EXPIRATION}\"\n    echo \"\"\n    echo \"=== Credenciais temporárias ===\"\n    echo \"AWS_ACCESS_KEY_ID=${ACCESS_KEY}\"\n    echo \"AWS_SECRET_ACCESS_KEY=${SECRET_KEY}\"\n    echo \"AWS_SESSION_TOKEN=${SESSION_TOKEN}\"\n    echo \"\"\n    echo \"=== Para usar no terminal ===\"\n    echo \"export AWS_ACCESS_KEY_ID=${ACCESS_KEY}\"\n    echo \"export AWS_SECRET_ACCESS_KEY=${SECRET_KEY}\"\n    echo \"export AWS_SESSION_TOKEN=${SESSION_TOKEN}\"\n    echo \"\"\n    echo \"=== Para usar em arquivo .env ===\"\n    echo \"AWS_ACCESS_KEY_ID=${ACCESS_KEY}\"\n    echo \"AWS_SECRET_ACCESS_KEY=${SECRET_KEY}\"\n    echo \"AWS_SESSION_TOKEN=${SESSION_TOKEN}\"\n    echo \"\"\n    echo \"=== Para exportar automaticamente ===\"\n    echo \"source <($0 ${PROFILE_NAME} ${DURATION} --export)\"\n    echo \"\"\n    echo \"=== Para salvar em arquivo ===\"\n    echo \"Para salvar as variáveis em um arquivo:\"\n    echo \"$0 ${PROFILE_NAME} ${DURATION} > sts-credentials.env\"\nfi\n"
  },
  {
    "path": "scripts/lancar_ec2_zona_a.sh",
    "content": "vpc_id=$(aws ec2 describe-vpcs --filters Name=isDefault,Values=true --query \"Vpcs[0].VpcId\" --output text)\nsubnet_id=$(aws ec2 describe-subnets --filters Name=vpc-id,Values=$vpc_id Name=availabilityZone,Values=us-east-1a --query \"Subnets[0].SubnetId\" --output text)\nsecurity_group_id=$(aws ec2 describe-security-groups --group-names \"bia-dev\" --query \"SecurityGroups[0].GroupId\" --output text 2>/dev/null)\n\nif [ -z \"$security_group_id\" ]; then\n    echo \">[ERRO] Security group bia-dev não foi criado na VPC $vpc_id\"\n    exit 1\nfi\n\naws ec2 run-instances --image-id ami-02f3f602d23f1659d --count 1 --instance-type t3.micro \\\n--security-group-ids $security_group_id --subnet-id $subnet_id --associate-public-ip-address \\\n--block-device-mappings '[{\"DeviceName\":\"/dev/xvda\",\"Ebs\":{\"VolumeSize\":15,\"VolumeType\":\"gp2\"}}]' \\\n--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=bia-dev}]' \\\n--iam-instance-profile Name=role-acesso-ssm --user-data file://user_data_ec2_zona_a.sh\n"
  },
  {
    "path": "scripts/ligar_bia_local.sh",
    "content": "nome=\"bia-dev\"\ninstance_id=$(aws ec2 describe-instances --query 'Reservations[].Instances[].InstanceId' --filters \"Name=tag:Name,Values=$nome\" --output text)\naws ec2 start-instances  --instance-ids $instance_id\n"
  },
  {
    "path": "scripts/parar_bia_local.sh",
    "content": "nome=\"bia-dev\"\ninstance_id=$(aws ec2 describe-instances --query 'Reservations[].Instances[].InstanceId' --filters \"Name=tag:Name,Values=$nome\" --output text)\naws ec2 stop-instances  --instance-ids $instance_id\n"
  },
  {
    "path": "scripts/setup_bia_dev_ubuntu_ui.sh",
    "content": "#!/bin/bash\nsudo apt-get -y update\n\n# INSTALANDO DOCKER\nsudo apt-get install -y ca-certificates curl gnupg lsb-release\ncurl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --batch --yes --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg\necho \\\n  \"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \\\n  $(lsb_release -cs) stable\" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null\nsudo apt-get -y update\nsudo apt-get install -y docker-ce docker-ce-cli containerd.io\n\n# Startando e habilitando docker para já iniciar ativo\nsudo systemctl enable docker.service\nsudo systemctl enable containerd.service\n\n# Instalando docker-compose\nsudo curl -L \"https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)\" -o /usr/local/bin/docker-compose\nsudo chmod +x /usr/local/bin/docker-compose\n\n# Instalando o Node\nsudo curl -fsSL https://deb.nodesource.com/setup_14.x | sudo -E bash -\nsudo apt-get install -y nodejs\n# Atualizando versao do NPM\nsudo npm install -g npm@latest --loglevel=error\n\n# Instalando AWS CLI\n  # Pre-requisito (unzip)\n  sudo apt-get install unzip -y\n\n  # AWS CLI (Install)\n  sudo curl \"https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip\" -o \"awscliv2.zip\"\n  sudo unzip awscliv2.zip\n  sudo ./aws/install\n\n# Configurando permissão no docker para não ter que ficar usando root\nsudo usermod -aG docker ubuntu\nnewgrp docker\n\n\n"
  },
  {
    "path": "scripts/setup_cloudshell_ssm.sh",
    "content": "curl \"https://s3.amazonaws.com/session-manager-downloads/plugin/latest/linux_64bit/session-manager-plugin.rpm\" -o \"session-manager-plugin.rpm\"\nsudo yum install -y session-manager-plugin.rpm"
  },
  {
    "path": "scripts/setup_ui_ubuntu.sh",
    "content": "# INSTALANDO UI\nsudo apt update -y && \\\nsudo apt upgrade -y && \\\nsudo apt install xfce4 xfce4-goodies -y \n\n# INSTALANDO CHROME REMOTE DESKTOP\nwget https://dl.google.com/linux/direct/chrome-remote-desktop_current_amd64.deb && \\\n  sudo apt install ./chrome-remote-desktop_current_amd64.deb -y\n  \n# DANDO PERMISSÕES NA PASTA  \nsudo chmod 777 /home/ubuntu/.config/"
  },
  {
    "path": "scripts/setup_vscode+chrome.sh",
    "content": "#!/bin/bash\n\n#Instalando VS CODE\nsudo snap install --classic code\n\n#Instalando Chrome\nwget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb\nsudo dpkg -i google-chrome-stable_current_amd64.deb"
  },
  {
    "path": "scripts/start-session-bash.sh",
    "content": "INSTANCE_ID=$1\necho \"Conectando na instancia $INSTANCE_ID\"\naws ssm start-session --target $INSTANCE_ID --document-name AWS-StartInteractiveCommand --parameters command=\"bash -l\""
  },
  {
    "path": "scripts/user_data_ec2_zona_a.sh",
    "content": "#!/bin/bash\n\n#Instalar Docker, Git, jq e AWS CLI\nsudo yum update -y\nsudo yum install git -y\nsudo yum install docker -y\nsudo yum install jq -y\n\n#Instalar AWS CLI v2\ncurl \"https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip\" -o \"awscliv2.zip\"\nsudo yum install unzip -y\nunzip awscliv2.zip\nsudo ./aws/install\nrm -rf awscliv2.zip aws/\nsudo usermod -a -G docker ec2-user\nsudo usermod -a -G docker ssm-user\nid ec2-user ssm-user\nsudo newgrp docker\n\n#Ativar docker\nsudo systemctl enable docker.service\nsudo systemctl start docker.service\n\n#Instalar docker compose 2\nsudo mkdir -p /usr/local/lib/docker/cli-plugins\nsudo curl -SL https://github.com/docker/compose/releases/download/v2.23.3/docker-compose-linux-x86_64 -o /usr/local/lib/docker/cli-plugins/docker-compose\nsudo chmod +x /usr/local/lib/docker/cli-plugins/docker-compose\n\n\n#Adicionar swap\nsudo dd if=/dev/zero of=/swapfile bs=128M count=32\nsudo chmod 600 /swapfile\nsudo mkswap /swapfile\nsudo swapon /swapfile\nsudo echo \"/swapfile swap swap defaults 0 0\" >> /etc/fstab\n\n\n#Instalar node e npm\ncurl -fsSL https://rpm.nodesource.com/setup_21.x | sudo bash -\nsudo yum install -y nodejs\n\n#Configurar python 3.11 e uv para uso com mcp servers da aws\nsudo dnf install python3.11 -y\nsudo ln -sf /usr/bin/python3.11 /usr/bin/python3\n\nsudo -u ec2-user bash -c 'curl -LsSf https://astral.sh/uv/install.sh | sh'\necho 'export PATH=\"$HOME/.local/bin:$PATH\"' >> /home/ec2-user/.bashrc\n"
  },
  {
    "path": "scripts/validar_recursos_zona_a.sh",
    "content": "vpc_id=$(aws ec2 describe-vpcs --filters Name=isDefault,Values=true --query \"Vpcs[0].VpcId\" --output text 2>/dev/null)\nif [ $? -eq 0 ]; then\n  echo \"[OK] Tudo certo com a VPC\"\nelse\n  echo \">[ERRO] Tenho um problema ao retornar a VPC default. Será se ela existe?\"\nfi\n\nsubnet_id=$(aws ec2 describe-subnets --filters Name=vpc-id,Values=$vpc_id Name=availabilityZone,Values=us-east-1a --query \"Subnets[0].SubnetId\" --output text 2>/dev/null)\nif [ $? -eq 0 ]; then\n  echo \"[OK] Tudo certo com a Subnet\"\nelse\n  echo \">[ERRO] Tenho um problema ao retornar a subnet da zona a. Será se existe uma subnet na zona A?\"\nfi\n\nsecurity_group_id=$(aws ec2 describe-security-groups --group-names \"bia-dev\" --query \"SecurityGroups[0].GroupId\" --output text 2>/dev/null)\nif [ $? -eq 0 ]; then\n  echo \"[OK] Security Group bia-dev foi criado\"\n  \n  # Validar inbound rule para o security group 'bia-dev'\n  inbound_rule=$(aws ec2 describe-security-groups --group-ids $security_group_id --filters \"Name=ip-permission.from-port,Values=3001\" --filters \"Name=ip-permission.cidr,Values=0.0.0.0/0\" --output text)\n\n  if [ -n \"$inbound_rule\" ]; then\n    echo \" [OK] Regra de entrada está ok\"\n  else\n    echo \" >[ERRO] Regra de entrada para a porta 3001 não encontrada ou não está aberta para o mundo todo. Reveja a aula do Henrylle\"\n  fi\n\n  # Validar outbound rule para o security group 'bia-dev'\n  outobund_rule=$(aws ec2 describe-security-groups --group-ids $security_group_id --query \"SecurityGroups[0].IpPermissionsEgress[?IpProtocol=='-1' && IpRanges[0].CidrIp=='0.0.0.0/0']\" --output text)\n  \n  if [ -n \"$outobund_rule\" ]; then\n    echo \" [OK] Regra de saída está correta\"\n  else\n    echo \" >[ERRO] Regra de saída para o mundo não encontrada. Reveja a aula do Henrylle\"\n  fi\nelse\n  echo \">[ERRO] Não achei o security group bia-dev. Ele foi criado?\"\nfi\n\nif aws iam get-role --role-name role-acesso-ssm &>/dev/null; then\n    echo \"[OK] Tudo certo com a role 'role-acesso-ssm'\"\nelse\n    echo \">[ERRO] A role 'role-acesso-ssm' não existe\"\nfi\n"
  },
  {
    "path": "scripts_evento/README.md",
    "content": "## Nova Localização do Conteúdo\n\nO conteúdo desta seção foi movido para [**scripts/ecs**](https://github.com/henrylle/bia/tree/main/scripts/ecs).\n"
  },
  {
    "path": "server.js",
    "content": "const app = require(\"./config/express\")();\nconst port = app.get(\"port\");\n\n// RODANDO NOSSA APLICAÇÃO NA PORTA SETADA\n\napp.listen(port, () => {\n  console.log(`Servidor rodando na porta ${port}`);\n});\n"
  },
  {
    "path": "tests/unit/controllers/tarefas.test.js",
    "content": "const tarefasController = require('../../../api/controllers/tarefas');\n\n// Mock do initializeModels\njest.mock('../../../api/models', () => {\n  return jest.fn();\n});\n\nconst initializeModels = require('../../../api/models');\n\ndescribe('Tarefas Controller', () => {\n  let req, res, mockTarefas;\n\n  beforeEach(() => {\n    req = {\n      body: {},\n      params: {}\n    };\n    res = {\n      send: jest.fn(),\n      status: jest.fn().mockReturnThis()\n    };\n\n    mockTarefas = {\n      create: jest.fn(),\n      findByPk: jest.fn(),\n      findAll: jest.fn(),\n      destroy: jest.fn(),\n      update: jest.fn()\n    };\n\n    initializeModels.mockResolvedValue({ Tarefas: mockTarefas });\n    jest.clearAllMocks();\n  });\n\n  describe('create', () => {\n    test('deve criar uma tarefa com sucesso', async () => {\n      const novaTarefa = {\n        titulo: 'Teste',\n        dia_atividade: '2026-01-26',\n        importante: true\n      };\n      req.body = novaTarefa;\n\n      const tarefaCriada = { uuid: '123', ...novaTarefa };\n      mockTarefas.create.mockResolvedValue(tarefaCriada);\n\n      const { create } = tarefasController();\n      await create(req, res);\n\n      expect(mockTarefas.create).toHaveBeenCalledWith(novaTarefa);\n      expect(res.send).toHaveBeenCalledWith(tarefaCriada);\n    });\n\n    test('deve retornar erro 500 ao falhar', async () => {\n      req.body = { titulo: 'Teste' };\n      mockTarefas.create.mockRejectedValue(new Error('Erro no banco'));\n\n      const { create } = tarefasController();\n      await create(req, res);\n\n      expect(res.status).toHaveBeenCalledWith(500);\n      expect(res.send).toHaveBeenCalledWith({\n        message: 'Erro no banco'\n      });\n    });\n  });\n\n  describe('find', () => {\n    test('deve retornar uma tarefa por uuid', async () => {\n      const tarefa = { uuid: '123', titulo: 'Teste' };\n      req.params.uuid = '123';\n      mockTarefas.findByPk.mockResolvedValue(tarefa);\n\n      const { find } = tarefasController();\n      await find(req, res);\n\n      expect(mockTarefas.findByPk).toHaveBeenCalledWith('123');\n      expect(res.send).toHaveBeenCalledWith(tarefa);\n    });\n\n    test('deve retornar 404 quando tarefa não existe', async () => {\n      req.params.uuid = '999';\n      mockTarefas.findByPk.mockResolvedValue(null);\n\n      const { find } = tarefasController();\n      await find(req, res);\n\n      expect(res.status).toHaveBeenCalledWith(404);\n      expect(res.send).toHaveBeenCalledWith({\n        message: 'Tarefa não encontrada.'\n      });\n    });\n\n    test('deve retornar erro 500 ao falhar', async () => {\n      req.params.uuid = '123';\n      mockTarefas.findByPk.mockRejectedValue(new Error('Erro no banco'));\n\n      const { find } = tarefasController();\n      await find(req, res);\n\n      expect(res.status).toHaveBeenCalledWith(500);\n      expect(res.send).toHaveBeenCalledWith({\n        message: 'Erro no banco'\n      });\n    });\n  });\n\n  describe('findAll', () => {\n    test('deve retornar todas as tarefas', async () => {\n      const tarefas = [\n        { uuid: '1', titulo: 'Tarefa 1' },\n        { uuid: '2', titulo: 'Tarefa 2' }\n      ];\n      mockTarefas.findAll.mockResolvedValue(tarefas);\n\n      const { findAll } = tarefasController();\n      await findAll(req, res);\n\n      expect(mockTarefas.findAll).toHaveBeenCalled();\n      expect(res.send).toHaveBeenCalledWith(tarefas);\n    });\n\n    test('deve retornar erro 500 ao falhar', async () => {\n      mockTarefas.findAll.mockRejectedValue(new Error('Erro no banco'));\n\n      const { findAll } = tarefasController();\n      await findAll(req, res);\n\n      expect(res.status).toHaveBeenCalledWith(500);\n      expect(res.send).toHaveBeenCalledWith({\n        message: 'Erro no banco'\n      });\n    });\n  });\n\n  describe('delete', () => {\n    test('deve deletar uma tarefa com sucesso', async () => {\n      req.params.uuid = '123';\n      mockTarefas.destroy.mockResolvedValue(1);\n\n      const { delete: deleteFn } = tarefasController();\n      await deleteFn(req, res);\n\n      expect(mockTarefas.destroy).toHaveBeenCalledWith({\n        where: { uuid: '123' }\n      });\n      expect(res.send).toHaveBeenCalledWith({\n        message: 'Tarefa deletada com sucesso.'\n      });\n    });\n\n    test('deve retornar 404 quando tarefa não existe', async () => {\n      req.params.uuid = '999';\n      mockTarefas.destroy.mockResolvedValue(0);\n\n      const { delete: deleteFn } = tarefasController();\n      await deleteFn(req, res);\n\n      expect(res.status).toHaveBeenCalledWith(404);\n      expect(res.send).toHaveBeenCalledWith({\n        message: 'Tarefa não encontrada.'\n      });\n    });\n\n    test('deve retornar erro 500 ao falhar', async () => {\n      req.params.uuid = '123';\n      mockTarefas.destroy.mockRejectedValue(new Error('Erro no banco'));\n\n      const { delete: deleteFn } = tarefasController();\n      await deleteFn(req, res);\n\n      expect(res.status).toHaveBeenCalledWith(500);\n      expect(res.send).toHaveBeenCalledWith({\n        message: 'Erro no banco'\n      });\n    });\n  });\n\n  describe('update_priority', () => {\n    test('deve atualizar uma tarefa com sucesso', async () => {\n      const tarefaAtualizada = { uuid: '123', importante: true };\n      req.params.uuid = '123';\n      req.body = { importante: true };\n      mockTarefas.update.mockResolvedValue([1]);\n      mockTarefas.findByPk.mockResolvedValue(tarefaAtualizada);\n\n      const { update_priority } = tarefasController();\n      await update_priority(req, res);\n\n      expect(mockTarefas.update).toHaveBeenCalledWith(\n        { importante: true },\n        { where: { uuid: '123' } }\n      );\n      expect(res.send).toHaveBeenCalledWith(tarefaAtualizada);\n    });\n\n    test('deve retornar 404 quando tarefa não existe', async () => {\n      req.params.uuid = '999';\n      req.body = { importante: true };\n      mockTarefas.update.mockResolvedValue([0]);\n      mockTarefas.findByPk.mockResolvedValue(null);\n\n      const { update_priority } = tarefasController();\n      await update_priority(req, res);\n\n      expect(res.status).toHaveBeenCalledWith(404);\n      expect(res.send).toHaveBeenCalledWith({\n        message: 'Tarefa não encontrada.'\n      });\n    });\n\n    test('deve retornar erro 500 ao falhar', async () => {\n      req.params.uuid = '123';\n      req.body = { importante: true };\n      mockTarefas.update.mockRejectedValue(new Error('Erro no banco'));\n\n      const { update_priority } = tarefasController();\n      await update_priority(req, res);\n\n      expect(res.status).toHaveBeenCalledWith(500);\n      expect(res.send).toHaveBeenCalledWith({\n        message: 'Erro no banco'\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "tests/unit/controllers/versao.test.js",
    "content": "const versaoController = require('../../../api/controllers/versao');\n\ndescribe('Versao Controller', () => {\n  // Mock para simular o objeto req e res\n  const req = {};\n  const res = {\n    send: jest.fn(),\n  };\n\n  beforeEach(() => {\n    jest.clearAllMocks();\n  });\n\n  test('get deve retornar a string de resposta correta', () => {\n    // Chama a função retornada pelo controller para obter o objeto controller\n    const { get } = versaoController();\n    // Chama o método get do objeto controller\n    get(req, res);\n\n    expect(res.send).toHaveBeenCalledWith('Bia 4.2.0');\n  });\n\n  test('get deve retornar a string de resposta correta quando VERSAO_API não está definido', () => {\n    // Simula o cenário onde VERSAO_API não está definido\n    delete process.env.VERSAO_API;\n\n    // Chama a função retornada pelo controller para obter o objeto controller\n    const { get } = versaoController();\n    // Chama o método get do objeto controller\n    get(req, res);\n\n    expect(res.send).toHaveBeenCalledWith('Bia 4.2.0');\n  });\n\n  test('get deve retornar a string de resposta correta quando VERSAO_API está definido', () => {\n    // Simula o cenário onde VERSAO_API está definido\n    process.env.VERSAO_API = '1.0.0';\n\n    // Chama a função retornada pelo controller para obter o objeto controller\n    const { get } = versaoController();\n    // Chama o método get do objeto controller\n    get(req, res);\n\n    expect(res.send).toHaveBeenCalledWith('Bia 1.0.0');\n  });\n});"
  }
]