chore: adiciona Docker, scripts e documentacao
- Adiciona Dockerfile e docker-compose para containerizacao - Adiciona docker-entrypoint.sh com inicializacao - Adiciona scripts/seed-admin.js para criar admin inicial - Adiciona docs/ com logos originais CambioReal - Atualiza README.md com instrucoes de uso - Atualiza queries.js com metricas de portfólio Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
34
.dockerignore
Normal file
34
.dockerignore
Normal file
@@ -0,0 +1,34 @@
|
||||
# Dependencies
|
||||
node_modules
|
||||
|
||||
# Data files (mounted as volume)
|
||||
data/
|
||||
|
||||
# Environment files
|
||||
.env
|
||||
.env.local
|
||||
|
||||
# Git
|
||||
.git
|
||||
.gitignore
|
||||
|
||||
# Docs
|
||||
docs/
|
||||
README.md
|
||||
*.md
|
||||
|
||||
# IDE
|
||||
.vscode
|
||||
.idea
|
||||
|
||||
# BMAD
|
||||
_bmad/
|
||||
_bmad-output/
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
@@ -1,8 +1,11 @@
|
||||
# RDS Read-Only
|
||||
# Netbird VPN (required for RDS access)
|
||||
NETBIRD_SETUP_KEY=14A782C8-24D2-46A9-B427-A422854E9B50
|
||||
|
||||
# RDS Read-Only (accessible via Netbird)
|
||||
MYSQL_URL=seu-host-rds.region.rds.amazonaws.com
|
||||
USER_MYSQL=usuario_readonly
|
||||
PW_MYSQL=senha_aqui
|
||||
|
||||
# Sessão
|
||||
# Sessao
|
||||
SESSION_SECRET=trocar-por-chave-secreta-aleatoria
|
||||
PORT=3080
|
||||
|
||||
29
Dockerfile
Normal file
29
Dockerfile
Normal file
@@ -0,0 +1,29 @@
|
||||
# BI Agentes - Dockerfile
|
||||
FROM node:20-alpine
|
||||
|
||||
# Install netbird client
|
||||
RUN apk add --no-cache curl bash iptables ip6tables \
|
||||
&& curl -fsSL https://pkgs.netbird.io/install.sh | sh
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files
|
||||
COPY package*.json ./
|
||||
|
||||
# Install dependencies
|
||||
RUN npm ci --only=production
|
||||
|
||||
# Copy application files
|
||||
COPY . .
|
||||
|
||||
# Create data directory for SQLite
|
||||
RUN mkdir -p /app/data
|
||||
|
||||
# Expose port
|
||||
EXPOSE 3080
|
||||
|
||||
# Start script will handle netbird connection
|
||||
COPY docker-entrypoint.sh /docker-entrypoint.sh
|
||||
RUN chmod +x /docker-entrypoint.sh
|
||||
|
||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||
260
README.md
260
README.md
@@ -1,109 +1,243 @@
|
||||
# BI Agentes — CambioReal
|
||||
# BI Agentes - CambioReal
|
||||
|
||||
Dashboard de transações para agentes CambioReal com autenticação local e dados ao vivo do RDS.
|
||||
Dashboard de Business Intelligence para agentes de cambio CambioReal. Monitoramento em tempo real de transacoes BRL-USD com dados do AWS RDS.
|
||||
|
||||
## Visao Geral
|
||||
|
||||
Sistema web que permite aos agentes de cambio visualizar suas transacoes, KPIs de performance e graficos analiticos. Cada agente ve apenas suas proprias transacoes, filtradas pelo `agente_id`.
|
||||
|
||||
**Principais recursos:**
|
||||
- Autenticacao segura (bcrypt + sessoes)
|
||||
- KPIs em tempo real (volume, spread, IOF, ticket medio)
|
||||
- Graficos interativos (Chart.js)
|
||||
- Filtros por periodo, fluxo e cliente
|
||||
- Exportacao de dados (em breve)
|
||||
|
||||
## Arquitetura
|
||||
|
||||
```
|
||||
┌─────────────────────────────────┐
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ BI Agentes (Express) │
|
||||
│ │
|
||||
│ SQLite (auth) Express/HTTP │
|
||||
│ agentes.db :3080 │
|
||||
│ email/senha /login │
|
||||
│ agente_id /dashboard │
|
||||
└───────────────────────┬─────────┘
|
||||
│ READ-ONLY
|
||||
┌──────▼──────┐
|
||||
│ cambio_db │
|
||||
│ (AWS RDS) │
|
||||
└─────────────┘
|
||||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||||
│ │ Login │───▶│ Session │───▶│ Dashboard │ │
|
||||
│ │ (HTML) │ │ (Memory) │ │ (HTML) │ │
|
||||
│ └──────────────┘ └──────────────┘ └──────────────┘ │
|
||||
│ │ │ │
|
||||
│ ▼ ▼ │
|
||||
│ ┌──────────────────────────────────────────────────────┐ │
|
||||
│ │ Express Server │ │
|
||||
│ │ PORT 3080 │ │
|
||||
│ └────────────────┬─────────────────────┬───────────────┘ │
|
||||
│ │ │ │
|
||||
└────────────────────┼─────────────────────┼───────────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌──────────────┐ ┌──────────────┐
|
||||
│ SQLite │ │ AWS RDS │
|
||||
│ (agentes) │ │ (cambio_db) │
|
||||
│ AUTH │ │ READ-ONLY │
|
||||
└──────────────┘ └──────────────┘
|
||||
```
|
||||
|
||||
- **SQLite local** — controle de agentes (email, senha bcrypt, agente_id). Sem dependência do RDS para autenticação.
|
||||
- **MySQL RDS** — somente leitura. Consulta `br_transaction_to_usa` (BRL→USD) e `pagamento_br` (USD→BRL) filtradas pelo `agente_id` da sessão.
|
||||
### Bancos de Dados
|
||||
|
||||
## Estrutura
|
||||
| Banco | Tipo | Proposito |
|
||||
|-------|------|-----------|
|
||||
| SQLite (`agentes.db`) | Local | Autenticacao - email, senha hash, agente_id |
|
||||
| MySQL (`cambio_db`) | AWS RDS | Transacoes - somente leitura |
|
||||
|
||||
### Fluxos de Transacao
|
||||
|
||||
| Fluxo | Tabela RDS | Descricao |
|
||||
|-------|------------|-----------|
|
||||
| BRL → USD | `br_transaction_to_usa` | Envio de reais para dolares |
|
||||
| USD → BRL | `pagamento_br` | Recebimento de dolares em reais |
|
||||
|
||||
## Estrutura do Projeto
|
||||
|
||||
```
|
||||
bi-agentes/
|
||||
├── server.js ← Entry point (Express + sessions)
|
||||
├── server.js # Entry point Express
|
||||
├── package.json # Dependencias
|
||||
├── .env.example # Template de configuracao
|
||||
├── .gitignore
|
||||
│
|
||||
├── src/
|
||||
│ ├── auth.js ← Login/logout, bcrypt, middleware requireAuth
|
||||
│ ├── db-local.js ← SQLite (better-sqlite3) — tabela agentes
|
||||
│ ├── db-rds.js ← MySQL pool read-only (mysql2)
|
||||
│ ├── queries.js ← SQL parametrizado por agente_id
|
||||
│ └── dashboard.js ← Gerador HTML (KPIs, Chart.js, tabela)
|
||||
│ ├── auth.js # Login, logout, bcrypt, middleware
|
||||
│ ├── db-local.js # SQLite - tabela agentes
|
||||
│ ├── db-rds.js # MySQL pool read-only
|
||||
│ ├── queries.js # SQL parametrizado + serializacao
|
||||
│ └── dashboard.js # Geracao HTML (KPIs, graficos, tabela)
|
||||
│
|
||||
├── public/
|
||||
│ └── login.html ← Tela de login
|
||||
│ └── login.html # Tela de login
|
||||
│
|
||||
├── scripts/
|
||||
│ └── seed-agente.js ← CLI para cadastrar/listar agentes
|
||||
│ └── seed-agente.js # CLI gerenciamento de agentes
|
||||
│
|
||||
├── data/
|
||||
│ └── agentes.db ← SQLite database (gerado automaticamente)
|
||||
├── .env ← Credenciais RDS + SESSION_SECRET
|
||||
└── .gitignore
|
||||
│ └── agentes.db # SQLite (criado automaticamente)
|
||||
│
|
||||
└── docs/ # Documentacao adicional
|
||||
├── ARCHITECTURE.md # Detalhes tecnicos
|
||||
├── API.md # Endpoints
|
||||
└── GUIA-USUARIO.md # Manual do agente
|
||||
```
|
||||
|
||||
## Setup
|
||||
## Instalacao
|
||||
|
||||
### Pre-requisitos
|
||||
|
||||
- Node.js 18+
|
||||
- Acesso ao AWS RDS (credenciais read-only)
|
||||
|
||||
### Setup
|
||||
|
||||
```bash
|
||||
# Clonar e instalar
|
||||
git clone <repo>
|
||||
cd bi-agentes
|
||||
npm install
|
||||
cp .env.example .env # editar com credenciais do RDS
|
||||
|
||||
# Configurar ambiente
|
||||
cp .env.example .env
|
||||
# Editar .env com credenciais
|
||||
```
|
||||
|
||||
## Configuração (.env)
|
||||
### Configuracao (.env)
|
||||
|
||||
```
|
||||
MYSQL_URL=<host-rds>
|
||||
USER_MYSQL=<usuario-readonly>
|
||||
PW_MYSQL=<senha>
|
||||
SESSION_SECRET=<chave-secreta-sessao>
|
||||
```env
|
||||
# Conexao AWS RDS
|
||||
MYSQL_URL=cambio-db.xxx.us-east-1.rds.amazonaws.com
|
||||
USER_MYSQL=bi_readonly
|
||||
PW_MYSQL=senha_segura
|
||||
|
||||
# Aplicacao
|
||||
SESSION_SECRET=chave_secreta_aleatoria_32_chars
|
||||
PORT=3080
|
||||
```
|
||||
|
||||
## Gerenciar Agentes
|
||||
## Uso
|
||||
|
||||
Cadastrar:
|
||||
### Iniciar o Servidor
|
||||
|
||||
```bash
|
||||
node scripts/seed-agente.js --email agente@email.com --senha 123456 --agente 76 --nome "ValorFx"
|
||||
# Desenvolvimento
|
||||
node server.js
|
||||
|
||||
# Producao (PM2)
|
||||
pm2 start server.js --name bi-agentes
|
||||
pm2 save
|
||||
```
|
||||
|
||||
Listar:
|
||||
Acesse: `http://localhost:3080`
|
||||
|
||||
### Gerenciar Agentes
|
||||
|
||||
**Cadastrar novo agente:**
|
||||
```bash
|
||||
node scripts/seed-agente.js \
|
||||
--email agente@cambioreal.com \
|
||||
--senha senha123 \
|
||||
--agente 76 \
|
||||
--nome "ValorFx"
|
||||
```
|
||||
|
||||
**Listar agentes:**
|
||||
```bash
|
||||
node scripts/seed-agente.js --list
|
||||
```
|
||||
|
||||
## Rodar
|
||||
|
||||
```bash
|
||||
node server.js
|
||||
# BI Agentes rodando: http://localhost:3080
|
||||
**Saida:**
|
||||
```
|
||||
ID | Agente | Nome | Email | Ativo | Criado
|
||||
1 | 76 | ValorFx | agente@cambioreal.com | Sim | 2024-01-15
|
||||
```
|
||||
|
||||
Para produção (intranet):
|
||||
## Funcionalidades
|
||||
|
||||
```bash
|
||||
pm2 start server.js --name bi-agentes
|
||||
### Dashboard
|
||||
|
||||
#### KPIs Exibidos
|
||||
| KPI | Descricao |
|
||||
|-----|-----------|
|
||||
| Transacoes | Quantidade total no periodo |
|
||||
| Volume BRL | Soma em reais |
|
||||
| Volume USD | Soma em dolares |
|
||||
| Taxa Media | Media ponderada da taxa cobrada |
|
||||
| Spread Medio | % medio sobre PTAX |
|
||||
| IOF Total | Soma do IOF cobrado |
|
||||
| Ticket Medio | USD medio por transacao |
|
||||
| Clientes Ativos | Quantidade de clientes unicos |
|
||||
|
||||
#### Graficos (Chart.js)
|
||||
- **Volume por Periodo** - Barras duplas BRL/USD com eixo dual
|
||||
- **Top 10 Clientes** - Barras horizontais por volume
|
||||
- **Taxa vs PTAX** - Linha comparando taxa cobrada com PTAX
|
||||
|
||||
#### Filtros Disponiveis
|
||||
- Data inicio / Data fim
|
||||
- Granulacao: Dia, Mes, Ano
|
||||
- Fluxo: BRL→USD, USD→BRL, Ambos
|
||||
- Cliente especifico
|
||||
|
||||
### Tabela de Transacoes
|
||||
|
||||
Colunas: Data, Cliente, Valor BRL, Valor USD, IOF %, IOF R$, PTAX, Taxa Cobrada, Spread, Spread %, Status
|
||||
|
||||
## Seguranca
|
||||
|
||||
- **Senhas**: Hash bcrypt com 10 salt rounds
|
||||
- **SQL**: Queries parametrizadas (previne SQL injection)
|
||||
- **Sessoes**: 8 horas de timeout, armazenamento em memoria
|
||||
- **RDS**: Acesso somente leitura (sem permissao de escrita)
|
||||
- **Isolamento**: Cada agente ve apenas seus dados (`WHERE agente_id = ?`)
|
||||
|
||||
## Tech Stack
|
||||
|
||||
| Camada | Tecnologia |
|
||||
|--------|------------|
|
||||
| Runtime | Node.js 18+ |
|
||||
| Framework | Express.js |
|
||||
| Auth DB | better-sqlite3 (WAL mode) |
|
||||
| Data DB | mysql2/promise (pool) |
|
||||
| Seguranca | bcrypt, express-session |
|
||||
| Frontend | HTML5, CSS3, Vanilla JS |
|
||||
| Graficos | Chart.js 4.x (CDN) |
|
||||
| Tipografia | Google Fonts Inter |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Erro de conexao RDS
|
||||
```
|
||||
Error: connect ETIMEDOUT
|
||||
```
|
||||
- Verificar se IP esta liberado no Security Group
|
||||
- Confirmar credenciais no .env
|
||||
|
||||
## Funcionalidades do Dashboard
|
||||
### Agente nao consegue logar
|
||||
```
|
||||
Credenciais invalidas
|
||||
```
|
||||
- Verificar se agente esta ativo: `node scripts/seed-agente.js --list`
|
||||
- Recriar senha se necessario
|
||||
|
||||
- **Autenticação** — cada agente loga com email/senha e vê apenas suas transações
|
||||
- **KPIs** — transações, volume BRL/USD, taxa média, spread, IOF, ticket médio, clientes ativos
|
||||
- **Gráficos** — volume por período (BRL+USD dual axis), volume por cliente (top 10), taxa cobrada vs PTAX
|
||||
- **Tabela** — todas as transações com data/hora, cliente, valores, IOF, taxas, spread, status
|
||||
- **Filtros** — período (de/até), granulação (dia/mês/ano), fluxo (BRL→USD / USD→BRL), cliente
|
||||
- **Dados ao vivo** — cada refresh consulta o RDS em tempo real
|
||||
- **Dois fluxos** — BRL→USD (`br_transaction_to_usa`) e USD→BRL (`pagamento_br`)
|
||||
### Graficos nao aparecem
|
||||
- Verificar conexao com internet (Chart.js via CDN)
|
||||
- Abrir DevTools e verificar erros no console
|
||||
|
||||
## Stack
|
||||
## Roadmap
|
||||
|
||||
- Node.js + Express
|
||||
- express-session (sessões em memória)
|
||||
- better-sqlite3 (auth local)
|
||||
- mysql2 (RDS read-only)
|
||||
- bcrypt (hash de senhas)
|
||||
- Chart.js 4.x (gráficos client-side via CDN)
|
||||
- Google Fonts Inter (tipografia via CDN)
|
||||
- [x] Autenticacao local
|
||||
- [x] Dashboard com KPIs
|
||||
- [x] Graficos interativos
|
||||
- [x] Filtros de periodo
|
||||
- [ ] Exportacao Excel/CSV
|
||||
- [ ] Comparativo periodo anterior
|
||||
- [ ] Dashboard administrativo
|
||||
- [ ] Alertas de spread
|
||||
|
||||
## Licenca
|
||||
|
||||
Uso interno CambioReal.
|
||||
|
||||
32
docker-entrypoint.sh
Normal file
32
docker-entrypoint.sh
Normal file
@@ -0,0 +1,32 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "=== BI Agentes Startup ==="
|
||||
|
||||
# Check for Netbird setup key
|
||||
if [ -n "$NETBIRD_SETUP_KEY" ]; then
|
||||
echo "Connecting to Netbird network..."
|
||||
echo "Management URL: ${NETBIRD_MANAGEMENT_URL:-https://netbird.cambioreal.com}"
|
||||
|
||||
# Start netbird daemon directly (not via systemd - doesn't work in containers)
|
||||
MGMT_URL="${NETBIRD_MANAGEMENT_URL:-https://netbird.cambioreal.com}"
|
||||
|
||||
# Run netbird daemon in background
|
||||
netbird service run &
|
||||
sleep 3
|
||||
|
||||
# Connect using setup key and management URL
|
||||
netbird up --setup-key "$NETBIRD_SETUP_KEY" --management-url "$MGMT_URL" &
|
||||
|
||||
# Wait for connection
|
||||
echo "Waiting for Netbird connection..."
|
||||
sleep 10
|
||||
|
||||
# Check connection status
|
||||
netbird status || echo "Netbird connection pending..."
|
||||
else
|
||||
echo "WARNING: NETBIRD_SETUP_KEY not set. Database connection may fail."
|
||||
fi
|
||||
|
||||
echo "Starting BI Agentes server..."
|
||||
exec node server.js
|
||||
350
docs/API.md
Normal file
350
docs/API.md
Normal file
@@ -0,0 +1,350 @@
|
||||
# API Reference - BI Agentes
|
||||
|
||||
Documentacao dos endpoints HTTP do sistema BI Agentes.
|
||||
|
||||
## Base URL
|
||||
|
||||
```
|
||||
http://localhost:3080
|
||||
```
|
||||
|
||||
## Autenticacao
|
||||
|
||||
O sistema usa autenticacao baseada em sessao. Apos login bem-sucedido, um cookie de sessao e enviado automaticamente pelo browser.
|
||||
|
||||
**Cookie:** `connect.sid`
|
||||
**Duracao:** 8 horas
|
||||
|
||||
---
|
||||
|
||||
## Endpoints
|
||||
|
||||
### GET /login
|
||||
|
||||
Exibe a pagina de login.
|
||||
|
||||
**Autenticacao:** Nao requerida
|
||||
|
||||
**Comportamento:**
|
||||
- Se usuario ja autenticado: redireciona para `/dashboard`
|
||||
- Se nao autenticado: retorna `login.html`
|
||||
|
||||
**Response:**
|
||||
|
||||
| Status | Descricao |
|
||||
|--------|-----------|
|
||||
| 200 | Pagina HTML de login |
|
||||
| 302 | Redirect para /dashboard (se ja logado) |
|
||||
|
||||
**Query Parameters:**
|
||||
|
||||
| Param | Tipo | Descricao |
|
||||
|-------|------|-----------|
|
||||
| error | number | Se `1`, exibe mensagem de erro |
|
||||
|
||||
**Exemplo:**
|
||||
```http
|
||||
GET /login HTTP/1.1
|
||||
Host: localhost:3080
|
||||
```
|
||||
|
||||
```http
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: text/html
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### POST /login
|
||||
|
||||
Processa tentativa de login.
|
||||
|
||||
**Autenticacao:** Nao requerida
|
||||
|
||||
**Content-Type:** `application/x-www-form-urlencoded`
|
||||
|
||||
**Request Body:**
|
||||
|
||||
| Campo | Tipo | Obrigatorio | Descricao |
|
||||
|-------|------|-------------|-----------|
|
||||
| email | string | Sim | Email do agente |
|
||||
| senha | string | Sim | Senha em texto plano |
|
||||
|
||||
**Response:**
|
||||
|
||||
| Status | Descricao | Redirect |
|
||||
|--------|-----------|----------|
|
||||
| 302 | Login bem-sucedido | /dashboard |
|
||||
| 302 | Login falhou | /login?error=1 |
|
||||
|
||||
**Comportamento:**
|
||||
1. Busca agente por email no SQLite
|
||||
2. Compara senha com hash (bcrypt)
|
||||
3. Se valido, cria sessao com dados do agente
|
||||
4. Redireciona para dashboard
|
||||
|
||||
**Exemplo:**
|
||||
```http
|
||||
POST /login HTTP/1.1
|
||||
Host: localhost:3080
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
email=agente@cambioreal.com&senha=minhasenha123
|
||||
```
|
||||
|
||||
```http
|
||||
HTTP/1.1 302 Found
|
||||
Location: /dashboard
|
||||
Set-Cookie: connect.sid=s%3A...; Path=/; HttpOnly
|
||||
```
|
||||
|
||||
**Dados da Sessao (interno):**
|
||||
```javascript
|
||||
req.session.agente = {
|
||||
id: 1, // ID interno SQLite
|
||||
email: "agente@email.com",
|
||||
agente_id: 76, // ID do agente no RDS
|
||||
nome: "ValorFx"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### GET /logout
|
||||
|
||||
Encerra a sessao do usuario.
|
||||
|
||||
**Autenticacao:** Nao requerida (mas so faz sentido se logado)
|
||||
|
||||
**Response:**
|
||||
|
||||
| Status | Descricao | Redirect |
|
||||
|--------|-----------|----------|
|
||||
| 302 | Sessao destruida | /login |
|
||||
|
||||
**Comportamento:**
|
||||
1. Destroi sessao server-side
|
||||
2. Invalida cookie
|
||||
3. Redireciona para login
|
||||
|
||||
**Exemplo:**
|
||||
```http
|
||||
GET /logout HTTP/1.1
|
||||
Host: localhost:3080
|
||||
Cookie: connect.sid=s%3A...
|
||||
```
|
||||
|
||||
```http
|
||||
HTTP/1.1 302 Found
|
||||
Location: /login
|
||||
Set-Cookie: connect.sid=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### GET /dashboard
|
||||
|
||||
Exibe o dashboard de transacoes do agente.
|
||||
|
||||
**Autenticacao:** Requerida (cookie de sessao)
|
||||
|
||||
**Response:**
|
||||
|
||||
| Status | Descricao |
|
||||
|--------|-----------|
|
||||
| 200 | Pagina HTML do dashboard |
|
||||
| 302 | Redirect para /login (nao autenticado) |
|
||||
| 500 | Erro ao carregar dados |
|
||||
|
||||
**Comportamento:**
|
||||
1. Middleware `requireAuth` verifica sessao
|
||||
2. Busca `agente_id` da sessao
|
||||
3. Executa queries no RDS (BRL→USD e USD→BRL)
|
||||
4. Serializa e ordena dados
|
||||
5. Gera HTML com KPIs, graficos e tabela
|
||||
6. Retorna HTML completo
|
||||
|
||||
**Dados Retornados no HTML:**
|
||||
|
||||
O dashboard inclui dados embedded como JavaScript:
|
||||
|
||||
```javascript
|
||||
window.TRANSACOES = [
|
||||
{
|
||||
fluxo: "BRL → USD",
|
||||
cliente: "Cliente A",
|
||||
data_operacao: "2024-01-15 14:30",
|
||||
valor_reais: 10000.00,
|
||||
valor_dolar: 2000.00,
|
||||
iof_pct: 1.10,
|
||||
iof_valor_rs: 110.00,
|
||||
taxa_ptax: 4.95,
|
||||
taxa_cobrada: 5.00,
|
||||
spread_bruto: 0.05,
|
||||
spread_pct: 1.00,
|
||||
status: "completed"
|
||||
},
|
||||
// ... mais transacoes
|
||||
];
|
||||
```
|
||||
|
||||
**Exemplo:**
|
||||
```http
|
||||
GET /dashboard HTTP/1.1
|
||||
Host: localhost:3080
|
||||
Cookie: connect.sid=s%3A...
|
||||
```
|
||||
|
||||
```http
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: text/html
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### GET /
|
||||
|
||||
Redireciona para o dashboard.
|
||||
|
||||
**Autenticacao:** Nao requerida (redirect acontece primeiro)
|
||||
|
||||
**Response:**
|
||||
|
||||
| Status | Descricao | Redirect |
|
||||
|--------|-----------|----------|
|
||||
| 302 | Redirect | /dashboard |
|
||||
|
||||
**Exemplo:**
|
||||
```http
|
||||
GET / HTTP/1.1
|
||||
Host: localhost:3080
|
||||
```
|
||||
|
||||
```http
|
||||
HTTP/1.1 302 Found
|
||||
Location: /dashboard
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Arquivos Estaticos
|
||||
|
||||
### GET /public/*
|
||||
|
||||
Serve arquivos estaticos da pasta `public/`.
|
||||
|
||||
**Autenticacao:** Nao requerida
|
||||
|
||||
**Arquivos Disponiveis:**
|
||||
|
||||
| Path | Arquivo |
|
||||
|------|---------|
|
||||
| /public/login.html | Pagina de login |
|
||||
|
||||
**Exemplo:**
|
||||
```http
|
||||
GET /public/login.html HTTP/1.1
|
||||
Host: localhost:3080
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Codigos de Erro
|
||||
|
||||
| Codigo | Significado | Causa Comum |
|
||||
|--------|-------------|-------------|
|
||||
| 302 | Redirect | Login/logout, acesso nao autorizado |
|
||||
| 500 | Erro interno | Falha conexao RDS, erro de query |
|
||||
|
||||
---
|
||||
|
||||
## Middleware
|
||||
|
||||
### requireAuth
|
||||
|
||||
Middleware que protege rotas que exigem autenticacao.
|
||||
|
||||
**Comportamento:**
|
||||
```javascript
|
||||
function requireAuth(req, res, next) {
|
||||
if (req.session && req.session.agente) {
|
||||
return next(); // Continua para rota
|
||||
}
|
||||
res.redirect('/login'); // Redireciona
|
||||
}
|
||||
```
|
||||
|
||||
**Rotas Protegidas:**
|
||||
- GET /dashboard
|
||||
|
||||
---
|
||||
|
||||
## Modelo de Dados (Sessao)
|
||||
|
||||
### Agente (sessao)
|
||||
|
||||
```typescript
|
||||
interface AgenteSession {
|
||||
id: number; // ID interno (SQLite)
|
||||
email: string; // Email do agente
|
||||
agente_id: number; // ID no sistema RDS
|
||||
nome: string; // Nome de exibicao
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Exemplos de Uso
|
||||
|
||||
### Fluxo Completo de Login
|
||||
|
||||
```bash
|
||||
# 1. Tentar acessar dashboard (sera redirecionado)
|
||||
curl -v http://localhost:3080/dashboard
|
||||
# < HTTP/1.1 302 Found
|
||||
# < Location: /login
|
||||
|
||||
# 2. Fazer login
|
||||
curl -v -X POST http://localhost:3080/login \
|
||||
-d "email=agente@cambioreal.com" \
|
||||
-d "senha=minhasenha" \
|
||||
-c cookies.txt
|
||||
# < HTTP/1.1 302 Found
|
||||
# < Location: /dashboard
|
||||
# < Set-Cookie: connect.sid=...
|
||||
|
||||
# 3. Acessar dashboard com cookie
|
||||
curl -v http://localhost:3080/dashboard \
|
||||
-b cookies.txt
|
||||
# < HTTP/1.1 200 OK
|
||||
# < Content-Type: text/html
|
||||
|
||||
# 4. Fazer logout
|
||||
curl -v http://localhost:3080/logout \
|
||||
-b cookies.txt
|
||||
# < HTTP/1.1 302 Found
|
||||
# < Location: /login
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Futuras Extensoes
|
||||
|
||||
Endpoints planejados para proximas versoes:
|
||||
|
||||
| Endpoint | Metodo | Descricao |
|
||||
|----------|--------|-----------|
|
||||
| /api/export | GET | Exportar transacoes (CSV/Excel) |
|
||||
| /api/kpis | GET | KPIs em formato JSON |
|
||||
| /api/transacoes | GET | Transacoes paginadas (JSON) |
|
||||
## Futuras Extensoes
|
||||
|
||||
Endpoints planejados para proximas versoes:
|
||||
|
||||
| Endpoint | Metodo | Descricao |
|
||||
|----------|--------|-----------|
|
||||
| /api/export | GET | Exportar transacoes (CSV/Excel) |
|
||||
| /api/kpis | GET | KPIs em formato JSON |
|
||||
| /api/transacoes | GET | Transacoes paginadas (JSON) |
|
||||
349
docs/ARCHITECTURE.md
Normal file
349
docs/ARCHITECTURE.md
Normal file
@@ -0,0 +1,349 @@
|
||||
# Arquitetura - BI Agentes
|
||||
|
||||
Documentacao tecnica da arquitetura do sistema BI Agentes.
|
||||
|
||||
## Visao Geral
|
||||
|
||||
O BI Agentes e uma aplicacao web monolitica construida com Node.js/Express que serve dashboards de BI para agentes de cambio. A arquitetura prioriza simplicidade, seguranca e isolamento de dados.
|
||||
|
||||
## Diagrama de Arquitetura
|
||||
|
||||
```
|
||||
INTERNET
|
||||
│
|
||||
▼
|
||||
┌───────────────────────────────────────────────────────────────────────────┐
|
||||
│ SERVIDOR EXPRESS │
|
||||
│ (PORT 3080) │
|
||||
│ │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌────────────┐ │
|
||||
│ │ Static │ │ Session │ │ Auth │ │ Dashboard │ │
|
||||
│ │ Files │ │ Middleware │ │ Middleware │ │ Generator │ │
|
||||
│ │ /public │ │ (in-memory) │ │ requireAuth │ │ (HTML) │ │
|
||||
│ └─────────────┘ └──────┬──────┘ └──────┬──────┘ └─────┬──────┘ │
|
||||
│ │ │ │ │
|
||||
│ ▼ ▼ ▼ │
|
||||
│ ┌────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ REQUEST ROUTER │ │
|
||||
│ │ │ │
|
||||
│ │ GET /login ──────▶ login.html │ │
|
||||
│ │ POST /login ─────▶ authenticate() ──▶ session.create() │ │
|
||||
│ │ GET /logout ─────▶ session.destroy() │ │
|
||||
│ │ GET /dashboard ──▶ requireAuth ──▶ fetchTransacoes() ──▶ HTML │ │
|
||||
│ │ GET / ───────────▶ redirect /dashboard │ │
|
||||
│ └────────────────────────────────────────────────────────────────────┘ │
|
||||
│ │ │ │
|
||||
└────────────────────────────┼───────────────────────────┼──────────────────┘
|
||||
│ │
|
||||
┌──────────────┘ └──────────────┐
|
||||
▼ ▼
|
||||
┌───────────────────┐ ┌───────────────────┐
|
||||
│ SQLite │ │ AWS RDS │
|
||||
│ (agentes.db) │ │ (cambio_db) │
|
||||
│ │ │ │
|
||||
│ ┌─────────────┐ │ │ ┌─────────────┐ │
|
||||
│ │ agentes │ │ │ │br_transac...│ │
|
||||
│ │ - id │ │ │ │- id │ │
|
||||
│ │ - email │ │ │ │- id_conta │ │
|
||||
│ │ - senha_hash│ │ │ │- amount_brl │ │
|
||||
│ │ - agente_id │ │ │ │- amount_usd │ │
|
||||
│ │ - nome │ │ │ │- exchange...| │
|
||||
│ │ - ativo │ │ │ │- status │ │
|
||||
│ └─────────────┘ │ │ └─────────────┘ │
|
||||
│ │ │ │
|
||||
│ LOCAL (WAL) │ │ ┌─────────────┐ │
|
||||
│ WRITE + READ │ │ │pagamento_br │ │
|
||||
└───────────────────┘ │ │- id │ │
|
||||
│ │- id_conta │ │
|
||||
│ │- valor │ │
|
||||
│ │- valor_sol │ │
|
||||
│ │- cotacao │ │
|
||||
│ └─────────────┘ │
|
||||
│ │
|
||||
│ REMOTE (MySQL) │
|
||||
│ READ-ONLY │
|
||||
└───────────────────┘
|
||||
```
|
||||
|
||||
## Componentes
|
||||
|
||||
### 1. Camada de Apresentacao
|
||||
|
||||
**Tecnologias:** HTML5, CSS3, Vanilla JavaScript, Chart.js
|
||||
|
||||
| Arquivo | Responsabilidade |
|
||||
|---------|------------------|
|
||||
| `public/login.html` | Formulario de login estilizado |
|
||||
| `src/dashboard.js` | Geracao dinamica do HTML do dashboard |
|
||||
|
||||
**Caracteristicas:**
|
||||
- Server-Side Rendering (SSR) - HTML gerado no backend
|
||||
- Chart.js carregado via CDN (sem build step)
|
||||
- CSS inline no dashboard para simplicidade
|
||||
- Google Fonts Inter para tipografia
|
||||
|
||||
### 2. Camada de Aplicacao
|
||||
|
||||
**Tecnologias:** Node.js, Express.js
|
||||
|
||||
| Modulo | Responsabilidade |
|
||||
|--------|------------------|
|
||||
| `server.js` | Entry point, rotas, middleware |
|
||||
| `src/auth.js` | Autenticacao, bcrypt, sessoes |
|
||||
| `src/queries.js` | Queries SQL, serializacao de dados |
|
||||
|
||||
**Middleware Stack:**
|
||||
```
|
||||
Request
|
||||
│
|
||||
▼
|
||||
express.urlencoded() ← Parse form data
|
||||
│
|
||||
▼
|
||||
express.json() ← Parse JSON
|
||||
│
|
||||
▼
|
||||
express-session() ← Gerenciar sessao
|
||||
│
|
||||
▼
|
||||
requireAuth() ← Verificar autenticacao (rotas protegidas)
|
||||
│
|
||||
▼
|
||||
Route Handler
|
||||
```
|
||||
|
||||
### 3. Camada de Dados
|
||||
|
||||
#### SQLite (Local)
|
||||
|
||||
**Biblioteca:** better-sqlite3 (sincrono, WAL mode)
|
||||
|
||||
```sql
|
||||
CREATE TABLE agentes (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
email TEXT UNIQUE NOT NULL,
|
||||
senha_hash TEXT NOT NULL,
|
||||
agente_id INTEGER NOT NULL,
|
||||
nome TEXT NOT NULL,
|
||||
ativo INTEGER DEFAULT 1,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
```
|
||||
|
||||
**Caracteristicas:**
|
||||
- WAL mode para melhor concorrencia
|
||||
- Arquivo em `data/agentes.db`
|
||||
- Criado automaticamente no primeiro start
|
||||
|
||||
#### MySQL (RDS)
|
||||
|
||||
**Biblioteca:** mysql2/promise (pool de conexoes)
|
||||
|
||||
**Configuracao do Pool:**
|
||||
```javascript
|
||||
{
|
||||
host: process.env.MYSQL_URL,
|
||||
user: process.env.USER_MYSQL,
|
||||
password: process.env.PW_MYSQL,
|
||||
database: 'cambio_db',
|
||||
waitForConnections: true,
|
||||
connectionLimit: 10,
|
||||
queueLimit: 0
|
||||
}
|
||||
```
|
||||
|
||||
**Tabelas Consultadas:**
|
||||
|
||||
| Tabela | Fluxo | Campos Principais |
|
||||
|--------|-------|-------------------|
|
||||
| `br_transaction_to_usa` | BRL→USD | amount_brl, amount_usd, exchange_rate, iof, ptax |
|
||||
| `pagamento_br` | USD→BRL | valor, valor_sol, cotacao, ptax |
|
||||
| `ag_contas` | Join | agente_id, conta_id |
|
||||
| `conta` | Join | id_conta, nome |
|
||||
|
||||
## Fluxo de Dados
|
||||
|
||||
### Autenticacao
|
||||
|
||||
```
|
||||
┌──────────┐ POST /login ┌──────────┐
|
||||
│ Browser │ ─────────────────▶ │ Express │
|
||||
│ │ email, senha │ │
|
||||
└──────────┘ └────┬─────┘
|
||||
│
|
||||
┌────────────────┘
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ SQLite │
|
||||
│ SELECT WHERE │
|
||||
│ email = ? │
|
||||
└──────┬───────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ bcrypt │
|
||||
│ compare │
|
||||
└──────┬───────┘
|
||||
│
|
||||
┌───────┴───────┐
|
||||
▼ ▼
|
||||
[MATCH] [NO MATCH]
|
||||
│ │
|
||||
▼ ▼
|
||||
session.agente redirect
|
||||
= {...} /login?error=1
|
||||
│
|
||||
▼
|
||||
redirect
|
||||
/dashboard
|
||||
```
|
||||
|
||||
### Carregamento do Dashboard
|
||||
|
||||
```
|
||||
┌──────────┐ GET /dashboard ┌──────────┐
|
||||
│ Browser │ ─────────────────▶ │ Express │
|
||||
│ (cookie) │ │ │
|
||||
└──────────┘ └────┬─────┘
|
||||
│
|
||||
requireAuth()────┘
|
||||
│
|
||||
┌─────────────┴─────────────┐
|
||||
▼ ▼
|
||||
[NO SESSION] [HAS SESSION]
|
||||
│ │
|
||||
▼ ▼
|
||||
redirect ┌─────────────────┐
|
||||
/login │ fetchTransacoes │
|
||||
│ (agente_id) │
|
||||
└────────┬────────┘
|
||||
│
|
||||
┌─────────────────┘
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ AWS RDS │
|
||||
│ 2 queries: │
|
||||
│ BRL→USD │
|
||||
│ USD→BRL │
|
||||
└──────┬───────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ serialize() │
|
||||
│ merge + │
|
||||
│ sort │
|
||||
└──────┬───────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ buildHTML() │
|
||||
│ KPIs + │
|
||||
│ Charts + │
|
||||
│ Table │
|
||||
└──────┬───────┘
|
||||
│
|
||||
▼
|
||||
res.send(html)
|
||||
```
|
||||
|
||||
## Seguranca
|
||||
|
||||
### Autenticacao
|
||||
|
||||
| Aspecto | Implementacao |
|
||||
|---------|---------------|
|
||||
| Hash de senha | bcrypt, 10 salt rounds |
|
||||
| Sessao | express-session, in-memory |
|
||||
| Timeout | 8 horas (cookie maxAge) |
|
||||
| Middleware | requireAuth em rotas protegidas |
|
||||
|
||||
### Protecao de Dados
|
||||
|
||||
| Aspecto | Implementacao |
|
||||
|---------|---------------|
|
||||
| SQL Injection | Queries parametrizadas (?) |
|
||||
| Isolamento | WHERE agente_id = ? em todas queries |
|
||||
| RDS Access | Usuario read-only (sem INSERT/UPDATE/DELETE) |
|
||||
|
||||
### Recomendacoes para Producao
|
||||
|
||||
- [ ] Usar Redis para sessoes (em vez de in-memory)
|
||||
- [ ] HTTPS obrigatorio (TLS)
|
||||
- [ ] Helmet.js para headers de seguranca
|
||||
- [ ] Rate limiting no login
|
||||
- [ ] Logs de auditoria
|
||||
|
||||
## Performance
|
||||
|
||||
### Estrategias Atuais
|
||||
|
||||
- **Connection Pool**: 10 conexoes MySQL reutilizaveis
|
||||
- **WAL Mode**: SQLite com Write-Ahead Logging
|
||||
- **CDN**: Chart.js e fonts via CDN (cache do browser)
|
||||
- **SSR**: HTML pre-renderizado (sem SPA overhead)
|
||||
|
||||
### Gargalos Potenciais
|
||||
|
||||
| Componente | Risco | Mitigacao |
|
||||
|------------|-------|-----------|
|
||||
| Sessoes in-memory | Perda em restart | Migrar para Redis |
|
||||
| Queries RDS | Tabelas grandes | Adicionar indices, paginacao |
|
||||
| HTML generation | Muitas transacoes | Paginacao server-side |
|
||||
|
||||
## Estrutura de Modulos
|
||||
|
||||
```
|
||||
src/
|
||||
├── auth.js # Autenticacao
|
||||
│ ├── authenticate(email, senha)
|
||||
│ └── requireAuth(req, res, next)
|
||||
│
|
||||
├── db-local.js # SQLite setup
|
||||
│ └── initDB()
|
||||
│
|
||||
├── db-rds.js # MySQL pool
|
||||
│ └── pool (export)
|
||||
│
|
||||
├── queries.js # Data access
|
||||
│ ├── fetchTransacoes(agenteId)
|
||||
│ └── serialize(rowsBrlUsd, rowsUsdBrl)
|
||||
│
|
||||
└── dashboard.js # View generation
|
||||
└── buildHTML(data, agente)
|
||||
```
|
||||
|
||||
## Dependencias
|
||||
|
||||
| Pacote | Versao | Proposito |
|
||||
|--------|--------|-----------|
|
||||
| express | ^4.x | Framework web |
|
||||
| express-session | ^1.x | Gerenciamento de sessao |
|
||||
| better-sqlite3 | ^9.x | SQLite driver (sync) |
|
||||
| mysql2 | ^3.x | MySQL driver (async) |
|
||||
| bcrypt | ^5.x | Hash de senhas |
|
||||
| dotenv | ^16.x | Variaveis de ambiente |
|
||||
|
||||
## Extensibilidade
|
||||
|
||||
### Adicionar Nova Feature
|
||||
|
||||
1. **Novo endpoint**: Adicionar rota em `server.js`
|
||||
2. **Nova query**: Adicionar funcao em `src/queries.js`
|
||||
3. **Nova UI**: Modificar `src/dashboard.js` ou criar novo modulo
|
||||
|
||||
### Adicionar Novo Fluxo de Transacao
|
||||
|
||||
1. Criar query em `src/queries.js`
|
||||
2. Atualizar `serialize()` para incluir novo fluxo
|
||||
3. Atualizar `buildHTML()` para exibir dados
|
||||
|
||||
## Decisoes de Arquitetura
|
||||
|
||||
| Decisao | Justificativa |
|
||||
|---------|---------------|
|
||||
| Monolito | Simplicidade para equipe pequena |
|
||||
| SSR | Sem necessidade de SPA, SEO nao relevante |
|
||||
| SQLite para auth | Independencia do RDS, portabilidade |
|
||||
| Vanilla JS | Sem build step, menor complexidade |
|
||||
| better-sqlite3 | Sync API mais simples para auth |
|
||||
| mysql2 | Pool de conexoes async para RDS |
|
||||
BIN
docs/CambioReal_original.png
Normal file
BIN
docs/CambioReal_original.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
272
docs/GUIA-USUARIO.md
Normal file
272
docs/GUIA-USUARIO.md
Normal file
@@ -0,0 +1,272 @@
|
||||
# Guia do Usuario - BI Agentes
|
||||
|
||||
Manual de uso do dashboard de transacoes para agentes CambioReal.
|
||||
|
||||
---
|
||||
|
||||
## Indice
|
||||
|
||||
1. [Acessando o Sistema](#acessando-o-sistema)
|
||||
2. [Visao Geral do Dashboard](#visao-geral-do-dashboard)
|
||||
3. [Usando os Filtros](#usando-os-filtros)
|
||||
4. [Entendendo os KPIs](#entendendo-os-kpis)
|
||||
5. [Interpretando os Graficos](#interpretando-os-graficos)
|
||||
6. [Tabela de Transacoes](#tabela-de-transacoes)
|
||||
7. [Perguntas Frequentes](#perguntas-frequentes)
|
||||
|
||||
---
|
||||
|
||||
## Acessando o Sistema
|
||||
|
||||
### Login
|
||||
|
||||
1. Abra o navegador e acesse o endereco fornecido pelo administrador
|
||||
2. Na tela de login, digite:
|
||||
- **E-mail**: Seu email cadastrado
|
||||
- **Senha**: Sua senha pessoal
|
||||
3. Clique em **Entrar**
|
||||
|
||||
```
|
||||
┌─────────────────────────────────┐
|
||||
│ BI Agentes │
|
||||
│ CambioReal - Dashboard │
|
||||
│ │
|
||||
│ E-mail: [_________________] │
|
||||
│ │
|
||||
│ Senha: [_________________] │
|
||||
│ │
|
||||
│ [ Entrar ] │
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Dica:** Se aparecer a mensagem "E-mail ou senha incorretos", verifique se digitou corretamente. Caso persista, contate o administrador.
|
||||
|
||||
### Logout
|
||||
|
||||
Para sair do sistema, clique no botao **Sair** no canto superior direito do dashboard.
|
||||
|
||||
---
|
||||
|
||||
## Visao Geral do Dashboard
|
||||
|
||||
Apos o login, voce vera o dashboard com suas transacoes:
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────────────────────┐
|
||||
│ [Nome do Agente] - Agente [ID] [Ao vivo] [Sair] │
|
||||
├────────────────────────────────────────────────────────────────────┤
|
||||
│ De: [____] Ate: [____] Granulacao: [___] Fluxo: [___] [Aplicar] │
|
||||
├────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │
|
||||
│ │ KPI 1 │ │ KPI 2 │ │ KPI 3 │ │ KPI 4 │ <- Indicadores │
|
||||
│ └────────┘ └────────┘ └────────┘ └────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────┐ ┌─────────────────┐ │
|
||||
│ │ Grafico 1 │ │ Grafico 2 │ <- Graficos │
|
||||
│ └─────────────────┘ └─────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────┐ │
|
||||
│ │ Tabela de Transacoes │ <- Detalhes │
|
||||
│ └─────────────────────────────────────────┘ │
|
||||
│ │
|
||||
└────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Importante:** Voce ve apenas suas proprias transacoes. Outros agentes nao tem acesso aos seus dados.
|
||||
|
||||
---
|
||||
|
||||
## Usando os Filtros
|
||||
|
||||
Os filtros ficam na barra abaixo do cabecalho. Use-os para refinar a visualizacao:
|
||||
|
||||
### Periodo (De / Ate)
|
||||
|
||||
Selecione a data inicial e final para ver transacoes em um periodo especifico.
|
||||
|
||||
**Exemplo:** Para ver transacoes de janeiro:
|
||||
- De: `2024-01-01`
|
||||
- Ate: `2024-01-31`
|
||||
|
||||
### Granulacao
|
||||
|
||||
Escolha como agrupar os dados nos graficos:
|
||||
|
||||
| Opcao | Uso |
|
||||
|-------|-----|
|
||||
| **Dia** | Ver detalhes diarios (periodos curtos) |
|
||||
| **Mes** | Visao mensal (padrao, recomendado) |
|
||||
| **Ano** | Comparar anos inteiros |
|
||||
|
||||
### Fluxo
|
||||
|
||||
Filtre por tipo de operacao:
|
||||
|
||||
| Opcao | Significado |
|
||||
|-------|-------------|
|
||||
| **Todos** | BRL→USD e USD→BRL juntos |
|
||||
| **BRL → USD** | Apenas envios (reais para dolares) |
|
||||
| **USD → BRL** | Apenas recebimentos (dolares para reais) |
|
||||
|
||||
### Cliente
|
||||
|
||||
Selecione um cliente especifico ou deixe em "Todos" para ver todas as transacoes.
|
||||
|
||||
### Aplicar
|
||||
|
||||
**Importante:** Apos alterar os filtros, clique em **Aplicar** para atualizar a tela.
|
||||
|
||||
---
|
||||
|
||||
## Entendendo os KPIs
|
||||
|
||||
Os KPIs (Indicadores-Chave) mostram um resumo do periodo selecionado:
|
||||
|
||||
### Transacoes
|
||||
Quantidade total de operacoes realizadas.
|
||||
|
||||
### Volume BRL
|
||||
Soma de todos os valores em reais movimentados.
|
||||
|
||||
**Exemplo:** Se teve 3 transacoes de R$ 10.000, R$ 20.000 e R$ 15.000, o volume e R$ 45.000.
|
||||
|
||||
### Volume USD
|
||||
Soma de todos os valores em dolares movimentados.
|
||||
|
||||
### Taxa Media
|
||||
Media ponderada da taxa de cambio cobrada (BRL/USD).
|
||||
|
||||
**Ponderada significa:** Transacoes maiores tem mais peso no calculo.
|
||||
|
||||
### Spread Medio
|
||||
Porcentagem media de lucro sobre a taxa PTAX.
|
||||
|
||||
**O que e PTAX?** Taxa de cambio oficial do Banco Central.
|
||||
|
||||
**Exemplo:** Se a PTAX e 5,00 e voce cobra 5,05, o spread e 1% (0,05 / 5,00).
|
||||
|
||||
### IOF Total
|
||||
Soma do IOF (Imposto sobre Operacoes Financeiras) recolhido.
|
||||
|
||||
### Ticket Medio
|
||||
Valor medio em USD por operacao.
|
||||
|
||||
**Calculo:** Volume USD / Numero de Transacoes
|
||||
|
||||
### Clientes Ativos
|
||||
Quantos clientes diferentes fizeram transacoes no periodo.
|
||||
|
||||
---
|
||||
|
||||
## Interpretando os Graficos
|
||||
|
||||
### Volume BRL / USD por Periodo
|
||||
|
||||
Grafico de barras mostrando a evolucao do volume ao longo do tempo.
|
||||
|
||||
- **Barras verdes** = Volume em BRL (eixo esquerdo)
|
||||
- **Barras azuis** = Volume em USD (eixo direito)
|
||||
|
||||
**Como ler:**
|
||||
- Barras mais altas = mais volume naquele periodo
|
||||
- Compare meses para identificar sazonalidade
|
||||
|
||||
### Volume por Cliente (Top 10)
|
||||
|
||||
Grafico horizontal mostrando os 10 maiores clientes por volume USD.
|
||||
|
||||
**Como usar:**
|
||||
- Identifique seus clientes mais importantes
|
||||
- Foque atencao nos maiores volumes
|
||||
|
||||
### Taxa Cobrada vs PTAX
|
||||
|
||||
Grafico de linha comparando sua taxa com a taxa oficial.
|
||||
|
||||
- **Linha roxa** = Sua taxa cobrada (media ponderada)
|
||||
- **Linha azul** = PTAX oficial
|
||||
|
||||
**O que observar:**
|
||||
- A distancia entre as linhas e seu spread
|
||||
- Quanto maior a distancia, maior sua margem
|
||||
|
||||
---
|
||||
|
||||
## Tabela de Transacoes
|
||||
|
||||
A tabela mostra todas as transacoes individuais do periodo.
|
||||
|
||||
### Colunas
|
||||
|
||||
| Coluna | Descricao |
|
||||
|--------|-----------|
|
||||
| **Data/Hora** | Quando a transacao foi realizada |
|
||||
| **Cliente** | Nome do cliente |
|
||||
| **Valor BRL** | Valor em reais |
|
||||
| **Valor USD** | Valor em dolares |
|
||||
| **IOF %** | Aliquota de IOF aplicada |
|
||||
| **IOF R$** | Valor do IOF em reais |
|
||||
| **PTAX** | Taxa oficial do BC no momento |
|
||||
| **Taxa Cobrada** | Sua taxa de cambio |
|
||||
| **Spread** | Diferenca entre taxa cobrada e PTAX |
|
||||
| **Spread %** | Spread em porcentagem |
|
||||
| **Status** | Situacao da operacao |
|
||||
|
||||
### Organizacao
|
||||
|
||||
As transacoes sao separadas por fluxo:
|
||||
- **BRL → USD** (fundo roxo claro) - Envios
|
||||
- **USD → BRL** (fundo azul claro) - Recebimentos
|
||||
|
||||
### Navegando
|
||||
|
||||
- Use a barra de rolagem horizontal para ver todas as colunas
|
||||
- A tabela ordena por data (mais antigas primeiro)
|
||||
|
||||
---
|
||||
|
||||
## Perguntas Frequentes
|
||||
|
||||
### Posso ver transacoes de outros agentes?
|
||||
|
||||
**Nao.** Cada agente ve apenas suas proprias transacoes. O sistema garante isolamento total dos dados.
|
||||
|
||||
### Os dados sao atualizados em tempo real?
|
||||
|
||||
**Sim.** A cada vez que voce carrega o dashboard ou aplica filtros, os dados sao buscados diretamente do sistema.
|
||||
|
||||
### O que fazer se os graficos nao aparecerem?
|
||||
|
||||
1. Verifique sua conexao com a internet (os graficos usam biblioteca externa)
|
||||
2. Tente recarregar a pagina (F5)
|
||||
3. Se persistir, contate o suporte tecnico
|
||||
|
||||
### Como exportar os dados?
|
||||
|
||||
*Em breve!* Estamos desenvolvendo funcionalidade de exportacao para Excel/CSV.
|
||||
|
||||
### A sessao expira?
|
||||
|
||||
**Sim.** Apos 8 horas de inatividade, voce precisara fazer login novamente.
|
||||
|
||||
### Posso acessar de celular?
|
||||
|
||||
**Sim.** O dashboard e responsivo e funciona em dispositivos moveis, porem a experiencia e melhor em telas maiores.
|
||||
|
||||
### O que significa PTAX?
|
||||
|
||||
PTAX e a taxa de cambio oficial divulgada pelo Banco Central do Brasil. E usada como referencia para calcular o spread das operacoes.
|
||||
|
||||
---
|
||||
|
||||
## Suporte
|
||||
|
||||
Em caso de problemas:
|
||||
1. Verifique usuario e senha
|
||||
2. Tente recarregar a pagina
|
||||
3. Contate o administrador do sistema
|
||||
|
||||
---
|
||||
|
||||
*CambioReal - BI Agentes v1.0*
|
||||
BIN
docs/logo-small.png
Normal file
BIN
docs/logo-small.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.8 KiB |
70
scripts/seed-admin.js
Normal file
70
scripts/seed-admin.js
Normal file
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* CLI para cadastrar administradores no SQLite
|
||||
*
|
||||
* Uso:
|
||||
* node scripts/seed-admin.js --email admin@cambioreal.com --senha 123456 --nome "Admin"
|
||||
*
|
||||
* Listar admins:
|
||||
* node scripts/seed-admin.js --list
|
||||
*/
|
||||
require('dotenv').config({ path: require('path').join(__dirname, '..', '.env') });
|
||||
|
||||
const db = require('../src/db-local');
|
||||
const { createAdmin } = require('../src/admin-auth');
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
function getArg(name) {
|
||||
const idx = args.indexOf('--' + name);
|
||||
return idx !== -1 && args[idx + 1] ? args[idx + 1] : null;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
// List mode
|
||||
if (args.includes('--list')) {
|
||||
const rows = db.prepare('SELECT id, email, nome, created_at FROM admins').all();
|
||||
if (!rows.length) {
|
||||
console.log('Nenhum administrador cadastrado.');
|
||||
} else {
|
||||
console.log('\n ID | Nome | Email | Criado em');
|
||||
console.log(' ' + '-'.repeat(75));
|
||||
rows.forEach(r => {
|
||||
console.log(` ${String(r.id).padEnd(3)}| ${r.nome.padEnd(19)}| ${r.email.padEnd(29)}| ${r.created_at}`);
|
||||
});
|
||||
}
|
||||
console.log();
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Create mode
|
||||
const email = getArg('email');
|
||||
const senha = getArg('senha');
|
||||
const nome = getArg('nome');
|
||||
|
||||
if (!email || !senha || !nome) {
|
||||
console.log(`
|
||||
Uso: node scripts/seed-admin.js --email <email> --senha <senha> --nome "<nome>"
|
||||
|
||||
Exemplos:
|
||||
node scripts/seed-admin.js --email admin@cambioreal.com --senha 123456 --nome "Admin"
|
||||
node scripts/seed-admin.js --list
|
||||
`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
await createAdmin(email, senha, nome);
|
||||
console.log(`\n Administrador cadastrado com sucesso!`);
|
||||
console.log(` Nome: ${nome}`);
|
||||
console.log(` Email: ${email}\n`);
|
||||
} catch (err) {
|
||||
if (err.message.includes('UNIQUE')) {
|
||||
console.error(`\n Erro: email "${email}" ja esta cadastrado.\n`);
|
||||
} else {
|
||||
console.error('Erro:', err.message);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
131
src/queries.js
131
src/queries.js
@@ -94,4 +94,133 @@ function serialize(rowsBrlUsd, rowsUsdBrl) {
|
||||
return [...dataBrlUsd, ...dataUsdBrl].sort((a, b) => a.data_sort.localeCompare(b.data_sort));
|
||||
}
|
||||
|
||||
module.exports = { fetchTransacoes, serialize };
|
||||
// Fetch ALL transactions (for admin) - with date filter for performance
|
||||
async function fetchAllTransacoes(diasAtras = 90) {
|
||||
const conn = await pool.getConnection();
|
||||
try {
|
||||
const [rowsBrlUsd] = await conn.execute(`
|
||||
SELECT
|
||||
c.nome AS cliente,
|
||||
t.created_at AS data_operacao,
|
||||
t.amount_brl AS valor_reais,
|
||||
t.amount_usd AS valor_dolar,
|
||||
t.iof AS iof_pct,
|
||||
ROUND(t.iof / 100 * t.amount_usd * t.exchange_rate, 2) AS iof_valor_rs,
|
||||
ROUND(t.ptax, 4) AS taxa_ptax,
|
||||
ROUND(t.exchange_rate, 4) AS taxa_cobrada,
|
||||
ROUND(t.exchange_rate - t.ptax, 4) AS spread_bruto,
|
||||
ROUND((t.exchange_rate - t.ptax) / t.exchange_rate * 100, 2) AS spread_pct,
|
||||
t.status
|
||||
FROM br_transaction_to_usa t
|
||||
INNER JOIN conta c ON c.id_conta = t.id_conta
|
||||
WHERE t.created_at >= DATE_SUB(CURDATE(), INTERVAL ? DAY)
|
||||
ORDER BY t.created_at DESC
|
||||
`, [diasAtras]);
|
||||
|
||||
const [rowsUsdBrl] = await conn.execute(`
|
||||
SELECT
|
||||
c.nome AS cliente,
|
||||
p.created_at AS data_operacao,
|
||||
p.valor_sol AS valor_reais,
|
||||
p.valor AS valor_dolar,
|
||||
0 AS iof_pct,
|
||||
0 AS iof_valor_rs,
|
||||
ROUND(p.ptax, 4) AS taxa_ptax,
|
||||
ROUND(p.cotacao, 4) AS taxa_cobrada,
|
||||
ROUND(p.ptax - p.cotacao, 4) AS spread_bruto,
|
||||
CASE WHEN p.cotacao > 0 THEN ROUND((p.ptax - p.cotacao) / p.ptax * 100, 2) ELSE 0 END AS spread_pct,
|
||||
p.pgto AS status
|
||||
FROM pagamento_br p
|
||||
INNER JOIN conta c ON c.id_conta = p.id_conta
|
||||
WHERE p.created_at >= DATE_SUB(CURDATE(), INTERVAL ? DAY)
|
||||
ORDER BY p.created_at DESC
|
||||
`, [diasAtras]);
|
||||
|
||||
return { rowsBrlUsd, rowsUsdBrl };
|
||||
} finally {
|
||||
conn.release();
|
||||
}
|
||||
}
|
||||
|
||||
// Fast daily stats for admin home (today and yesterday only)
|
||||
// Separates USD->BRL (with cotacao) from USD->USD (balance/no cotacao)
|
||||
async function fetchDailyStats() {
|
||||
const conn = await pool.getConnection();
|
||||
try {
|
||||
// BRL -> USD counts and volumes for today and yesterday
|
||||
const [brlUsdStats] = await conn.execute(`
|
||||
SELECT
|
||||
DATE(created_at) as dia,
|
||||
COUNT(*) as qtd,
|
||||
ROUND(SUM(amount_brl), 2) as total_brl,
|
||||
ROUND(SUM(amount_usd), 2) as total_usd
|
||||
FROM br_transaction_to_usa
|
||||
WHERE created_at >= DATE_SUB(CURDATE(), INTERVAL 1 DAY)
|
||||
GROUP BY DATE(created_at)
|
||||
ORDER BY dia DESC
|
||||
`);
|
||||
|
||||
// USD -> BRL (with cotacao - real currency exchange)
|
||||
const [usdBrlStats] = await conn.execute(`
|
||||
SELECT
|
||||
DATE(created_at) as dia,
|
||||
COUNT(*) as qtd,
|
||||
ROUND(SUM(valor_sol), 2) as total_brl,
|
||||
ROUND(SUM(valor), 2) as total_usd
|
||||
FROM pagamento_br
|
||||
WHERE created_at >= DATE_SUB(CURDATE(), INTERVAL 1 DAY)
|
||||
AND cotacao IS NOT NULL AND cotacao > 0
|
||||
AND (pgto IS NULL OR pgto != 'balance')
|
||||
GROUP BY DATE(created_at)
|
||||
ORDER BY dia DESC
|
||||
`);
|
||||
|
||||
// USD -> USD (balance or no cotacao - dollar to dollar)
|
||||
const [usdUsdStats] = await conn.execute(`
|
||||
SELECT
|
||||
DATE(created_at) as dia,
|
||||
COUNT(*) as qtd,
|
||||
ROUND(SUM(valor), 2) as total_usd
|
||||
FROM pagamento_br
|
||||
WHERE created_at >= DATE_SUB(CURDATE(), INTERVAL 1 DAY)
|
||||
AND (cotacao IS NULL OR cotacao = 0 OR pgto = 'balance')
|
||||
GROUP BY DATE(created_at)
|
||||
ORDER BY dia DESC
|
||||
`);
|
||||
|
||||
// Format results
|
||||
const today = new Date().toISOString().slice(0, 10);
|
||||
const yesterday = new Date(Date.now() - 86400000).toISOString().slice(0, 10);
|
||||
|
||||
const formatDay = (stats, targetDate) => {
|
||||
const row = stats.find(r => {
|
||||
const d = r.dia instanceof Date ? r.dia.toISOString().slice(0, 10) : String(r.dia).slice(0, 10);
|
||||
return d === targetDate;
|
||||
});
|
||||
return row ? {
|
||||
qtd: Number(row.qtd),
|
||||
total_brl: Number(row.total_brl) || 0,
|
||||
total_usd: Number(row.total_usd) || 0
|
||||
} : { qtd: 0, total_brl: 0, total_usd: 0 };
|
||||
};
|
||||
|
||||
return {
|
||||
brlUsd: {
|
||||
hoje: formatDay(brlUsdStats, today),
|
||||
ontem: formatDay(brlUsdStats, yesterday)
|
||||
},
|
||||
usdBrl: {
|
||||
hoje: formatDay(usdBrlStats, today),
|
||||
ontem: formatDay(usdBrlStats, yesterday)
|
||||
},
|
||||
usdUsd: {
|
||||
hoje: formatDay(usdUsdStats, today),
|
||||
ontem: formatDay(usdUsdStats, yesterday)
|
||||
}
|
||||
};
|
||||
} finally {
|
||||
conn.release();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { fetchTransacoes, fetchAllTransacoes, serialize, fetchDailyStats };
|
||||
|
||||
Reference in New Issue
Block a user