Backend Node.js + Express para gerenciamento de moedas de ouro, com sistema de autenticação admin e API pública.
coins-backend/
├── src/
│ ├── index.js # Entry point da aplicação
│ ├── db.js # Pool de conexões PostgreSQL
│ ├── middlewares/
│ │ ├── authApiKey.js # Validação de x-api-key
│ │ └── authAdminToken.js # Validação de JWT admin
│ └── routes/
│ ├── coins.js # Rotas públicas (POST /coins)
│ ├── adminAuth.js # Rotas de autenticação admin
│ └── adminCoins.js # Rotas admin de gerenciamento
├── package.json
└── Dockerfile
- REST API em Node.js + Express
- Banco de dados PostgreSQL
- Autenticação via API Key e JWT
- Rastreamento de consultas (
user_consults_quantityestatement_consults_quantity) - CRUD completo de usuários e moedas
- Histórico de transações (spend_history)
CREATE TABLE coins (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
coins INTEGER NOT NULL DEFAULT 0,
user_consults_quantity INTEGER NOT NULL DEFAULT 0,
statement_consults_quantity INTEGER NOT NULL DEFAULT 0,
user_consulted_at TIMESTAMP WITH TIME ZONE,
admin_consulted_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
spend_history JSONB NOT NULL DEFAULT '[]'::jsonb
);Busca moedas de um usuário pelo e-mail.
Headers:
x-api-key: <API_KEY>
Content-Type: application/json
Body:
{
"email": "usuario@exemplo.com",
"consultType": "user" // ou "statement" para extrato
}Respostas:
200 OK:{ "email": "usuario@exemplo.com", "coins": 123, "spend_history": [...] }404 Not Found:{ "error": "Você ainda não possui moedas de ouro 😢" }400 Bad Request:{ "error": "Email is required" }ou{ "error": "Invalid email format" }
Autentica o administrador e retorna token JWT.
Headers:
x-api-key: <API_KEY>
Content-Type: application/json
Body:
{
"email": "admin@exemplo.com",
"password": "senha_secreta"
}Resposta 200:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"email": "admin@exemplo.com"
}Respostas de erro:
401 Unauthorized:{ "error": "Invalid credentials" }400 Bad Request:{ "error": "Email and password are required" }
Valida a sessão do admin e retorna informações.
Headers:
x-api-key: <API_KEY>
Authorization: Bearer <JWT_TOKEN>
Resposta 200:
{
"email": "admin@exemplo.com",
"role": "admin"
}Respostas de erro:
401 Unauthorized:{ "error": "Invalid or expired token" }ou{ "error": "Missing or invalid Authorization header" }
Importante: Todas as rotas /admin/* (exceto /admin/login e /admin/me) exigem dupla proteção:
- Header
x-api-keyválido - Header
Authorization: Bearer <JWT_TOKEN>válido
Lista registros da tabela coins com filtros opcionais.
Headers:
x-api-key: <API_KEY>
Authorization: Bearer <JWT_TOKEN>
Query Parameters (opcionais):
name- Filtro por nome (busca parcial, case-insensitive)email- Filtro por email (busca parcial, case-insensitive)
Exemplo:
GET /admin/coins?name=João&email=exemplo
Resposta 200:
{
"items": [
{
"id": "uuid",
"name": "João Silva",
"email": "joao@exemplo.com",
"coins": 150,
"user_consults_quantity": 5,
"statement_consults_quantity": 3,
"user_consulted_at": "2024-01-15T10:30:00.000Z",
"admin_consulted_at": "2024-01-20T14:00:00.000Z",
"created_at": "2024-01-01T00:00:00.000Z",
"updated_at": "2024-01-20T14:00:00.000Z",
"spend_history": []
}
],
"count": 1
}Cria um novo registro na tabela coins.
Headers:
x-api-key: <API_KEY>
Authorization: Bearer <JWT_TOKEN>
Content-Type: application/json
Body:
{
"name": "Nome do usuário",
"email": "usuario@exemplo.com",
"coins": 0
}Resposta 201: Retorna o registro criado completo (mesmo formato da listagem).
Respostas de erro:
409 Conflict:{ "error": "Email already exists" }400 Bad Request:{ "error": "Name is required" }ou{ "error": "Email is required" }
Atualiza a quantidade de moedas de um usuário.
Headers:
x-api-key: <API_KEY>
Authorization: Bearer <JWT_TOKEN>
Content-Type: application/json
Body (opção 1 - valor absoluto):
{
"email": "usuario@exemplo.com",
"coins": 200
}Body (opção 2 - delta):
{
"email": "usuario@exemplo.com",
"coinsDelta": 50
}Body (atualizar múltiplos campos):
{
"email": "usuario@exemplo.com",
"coins": 200,
"name": "Novo Nome",
"spend_history": [...]
}Regras:
emailé obrigatório- Pelo menos um entre
coins,coinsDelta,nameouspend_historydeve ser fornecido - Se ambos
coinsecoinsDeltaforem fornecidos,coinstem prioridade ecoinsDeltaé ignorado - O valor final de
coinsnão pode ser negativo
Resposta 200: Retorna o registro atualizado completo (mesmo formato da listagem).
Respostas de erro:
404 Not Found:{ "error": "Email not found" }400 Bad Request:{ "error": "Email is required" }ou{ "error": "Coins cannot be negative" }
Remove um registro da tabela coins pelo email.
Headers:
x-api-key: <API_KEY>
Authorization: Bearer <JWT_TOKEN>
Content-Type: application/json
Body:
{
"email": "usuario@exemplo.com"
}Resposta 200:
{
"message": "User deleted successfully"
}Respostas de erro:
404 Not Found:{ "error": "Email not found" }
Crie um arquivo .env na pasta coins-backend/:
Obrigatórias:
API_KEY- Chave de API para autenticação nas rotas públicas e adminPGHOST- Host do PostgreSQL (ou useDATABASE_URL)PGPORT- Porta do PostgreSQL (padrão: 5432)PGDATABASE- Nome do banco de dadosPGUSER- Usuário do PostgreSQLPGPASSWORD- Senha do PostgreSQLADMIN_EMAIL- Email do administradorADMIN_PASSWORD- Senha do administradorJWT_SECRET- Chave secreta para assinar tokens JWT
Opcionais:
DATABASE_URL- URL completa de conexão (substitui PGHOST, PGPORT, etc.)JWT_EXPIRES_IN- Tempo de expiração do token JWT (padrão: '8h')PORT- Porta do servidor (padrão: 3000)NODE_ENV- Ambiente de execução (development/production)
cd coins-backend
npm installnpm startPara desenvolvimento com auto-reload:
npm run devcd coins-backend
docker build -t coins-backend .docker run -p 3000:3000 --env-file .env coins-backendExecute a migração para adicionar a coluna statement_consults_quantity:
ALTER TABLE coins
ADD COLUMN IF NOT EXISTS statement_consults_quantity INTEGER NOT NULL DEFAULT 0;Ou execute o arquivo coins-backend/migration_add_statement_consults.sql.
O sistema rastreia dois tipos de consultas:
user_consults_quantity: Incrementado quando o usuário carrega a página com suas moedas (consultType: "user")statement_consults_quantity: Incrementado quando o usuário visualiza o extrato (consultType: "statement")
- Todas as rotas (exceto
/health) exigemx-api-keyválido - Rotas admin de gerenciamento exigem JWT válido além do
x-api-key - Tokens JWT expiram após o tempo configurado em
JWT_EXPIRES_IN(padrão: 8 horas) - Use HTTPS em produção
- Mantenha
JWT_SECRETeADMIN_PASSWORDseguros
ISC
Pedro Nalis