- MANUAL-PRODUTO.md: Manual do usuário final - MANUAL-VENDAS.md: Estratégia comercial e vendas - MANUAL-TECNICO.md: Infraestrutura e deploy - README.md: Visão geral do projeto
680 lines
16 KiB
Markdown
680 lines
16 KiB
Markdown
# ALETHEIA - Manual Técnico
|
|
|
|
## 📐 Arquitetura Geral
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ CLIENTE │
|
|
│ ┌─────────────────────────────────────────────────────────┐ │
|
|
│ │ Next.js (PWA) - Porta 3080 │ │
|
|
│ │ aletheia.aivertice.com (Cloudflare) │ │
|
|
│ └─────────────────────────────────────────────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
│
|
|
│ HTTPS
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ NGINX (Reverse Proxy) │
|
|
│ SSL/TLS Termination │
|
|
│ / → :3080 (Frontend) │
|
|
│ /api → :8090 (Backend) │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
│
|
|
┌───────────────┴───────────────┐
|
|
▼ ▼
|
|
┌─────────────────────────┐ ┌─────────────────────────┐
|
|
│ Next.js Frontend │ │ FastAPI Backend │
|
|
│ Porta 3080 │ │ Porta 8090 │
|
|
│ /opt/aletheia/frontend│ │ /opt/aletheia/backend │
|
|
└─────────────────────────┘ └─────────────────────────┘
|
|
│
|
|
┌─────────────────────────┼─────────────────────────┐
|
|
▼ ▼ ▼
|
|
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
|
│ PostgreSQL │ │ Open Food Facts │ │ OpenAI API │
|
|
│ aletheia DB │ │ API │ │ GPT-4o-mini │
|
|
│ Porta 5432 │ │ (3M+ prods) │ │ │
|
|
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
|
```
|
|
|
|
### Stack Tecnológico
|
|
|
|
| Camada | Tecnologia | Versão |
|
|
|--------|------------|--------|
|
|
| Frontend | Next.js (React) | 14.x |
|
|
| Backend | FastAPI (Python) | 0.100+ |
|
|
| Banco de Dados | PostgreSQL | 15+ |
|
|
| IA | OpenAI GPT-4o-mini | - |
|
|
| Process Manager | PM2 | 5.x |
|
|
| Proxy/SSL | Nginx | 1.24+ |
|
|
| CDN/DNS | Cloudflare | - |
|
|
|
|
---
|
|
|
|
## 🚀 Deploy e Infraestrutura
|
|
|
|
### Estrutura de Diretórios
|
|
|
|
```
|
|
/opt/aletheia/
|
|
├── backend/
|
|
│ ├── main.py
|
|
│ ├── requirements.txt
|
|
│ ├── .env
|
|
│ └── ...
|
|
├── frontend/
|
|
│ ├── package.json
|
|
│ ├── .env.local
|
|
│ ├── public/
|
|
│ │ └── sw.js (Service Worker)
|
|
│ └── ...
|
|
└── docs/
|
|
```
|
|
|
|
### PM2 - Gerenciamento de Processos
|
|
|
|
**Processos ativos:**
|
|
- `aletheia-backend` - FastAPI (porta 8090)
|
|
- `aletheia-frontend` - Next.js (porta 3080)
|
|
|
|
**Comandos úteis:**
|
|
|
|
```bash
|
|
# Status dos processos
|
|
pm2 status
|
|
|
|
# Logs em tempo real
|
|
pm2 logs aletheia-backend
|
|
pm2 logs aletheia-frontend
|
|
|
|
# Reiniciar processos
|
|
pm2 restart aletheia-backend
|
|
pm2 restart aletheia-frontend
|
|
|
|
# Reiniciar tudo
|
|
pm2 restart all
|
|
|
|
# Salvar configuração
|
|
pm2 save
|
|
```
|
|
|
|
### Nginx - Configuração
|
|
|
|
Arquivo: `/etc/nginx/sites-available/aletheia`
|
|
|
|
```nginx
|
|
server {
|
|
listen 443 ssl http2;
|
|
server_name aletheia.aivertice.com;
|
|
|
|
ssl_certificate /etc/letsencrypt/live/aletheia.aivertice.com/fullchain.pem;
|
|
ssl_certificate_key /etc/letsencrypt/live/aletheia.aivertice.com/privkey.pem;
|
|
|
|
# Frontend
|
|
location / {
|
|
proxy_pass http://127.0.0.1:3080;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade $http_upgrade;
|
|
proxy_set_header Connection 'upgrade';
|
|
proxy_set_header Host $host;
|
|
proxy_cache_bypass $http_upgrade;
|
|
}
|
|
|
|
# Backend API
|
|
location /api {
|
|
proxy_pass http://127.0.0.1:8090;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
}
|
|
}
|
|
|
|
server {
|
|
listen 80;
|
|
server_name aletheia.aivertice.com;
|
|
return 301 https://$server_name$request_uri;
|
|
}
|
|
```
|
|
|
|
### SSL/TLS
|
|
|
|
- **Provedor**: Let's Encrypt (Certbot)
|
|
- **Renovação**: Automática via cron
|
|
- **CDN**: Cloudflare (SSL Full Strict)
|
|
|
|
```bash
|
|
# Renovar certificado manualmente
|
|
sudo certbot renew
|
|
|
|
# Verificar certificado
|
|
sudo certbot certificates
|
|
```
|
|
|
|
---
|
|
|
|
## 🗄️ Banco de Dados
|
|
|
|
### Conexão
|
|
|
|
```
|
|
Host: localhost
|
|
Porta: 5432
|
|
Database: aletheia
|
|
User: aletheia
|
|
Password: Aletheia2026!
|
|
```
|
|
|
|
**Connection String:**
|
|
```
|
|
postgresql://aletheia:Aletheia2026!@localhost:5432/aletheia
|
|
```
|
|
|
|
### Tabelas Principais
|
|
|
|
#### users
|
|
```sql
|
|
CREATE TABLE users (
|
|
id SERIAL PRIMARY KEY,
|
|
email VARCHAR(255) UNIQUE NOT NULL,
|
|
password_hash VARCHAR(255) NOT NULL,
|
|
name VARCHAR(255),
|
|
plan VARCHAR(50) DEFAULT 'free', -- 'free' ou 'premium'
|
|
scans_today INTEGER DEFAULT 0,
|
|
last_scan_date DATE,
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
updated_at TIMESTAMP DEFAULT NOW()
|
|
);
|
|
```
|
|
|
|
#### products
|
|
```sql
|
|
CREATE TABLE products (
|
|
id SERIAL PRIMARY KEY,
|
|
barcode VARCHAR(50) UNIQUE NOT NULL,
|
|
name VARCHAR(255),
|
|
brand VARCHAR(255),
|
|
categories TEXT,
|
|
ingredients TEXT,
|
|
nutrition_data JSONB,
|
|
image_url TEXT,
|
|
source VARCHAR(50), -- 'openfoodfacts', 'manual'
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
updated_at TIMESTAMP DEFAULT NOW()
|
|
);
|
|
```
|
|
|
|
#### scans
|
|
```sql
|
|
CREATE TABLE scans (
|
|
id SERIAL PRIMARY KEY,
|
|
user_id INTEGER REFERENCES users(id),
|
|
product_id INTEGER REFERENCES products(id),
|
|
barcode VARCHAR(50),
|
|
score INTEGER, -- 0-100
|
|
analysis JSONB, -- Análise completa da IA
|
|
recipe TEXT, -- Receita sugerida
|
|
scanned_at TIMESTAMP DEFAULT NOW()
|
|
);
|
|
```
|
|
|
|
### Comandos Úteis
|
|
|
|
```bash
|
|
# Conectar ao banco
|
|
psql -U aletheia -d aletheia -h localhost
|
|
|
|
# Backup
|
|
pg_dump -U aletheia -d aletheia > backup_$(date +%Y%m%d).sql
|
|
|
|
# Restore
|
|
psql -U aletheia -d aletheia < backup_20260210.sql
|
|
```
|
|
|
|
---
|
|
|
|
## 🔌 APIs e Endpoints
|
|
|
|
### Autenticação
|
|
|
|
#### POST /api/auth/register
|
|
Registra novo usuário.
|
|
|
|
**Request:**
|
|
```json
|
|
{
|
|
"email": "usuario@email.com",
|
|
"password": "senha123",
|
|
"name": "Nome do Usuário"
|
|
}
|
|
```
|
|
|
|
**Response (201):**
|
|
```json
|
|
{
|
|
"id": 1,
|
|
"email": "usuario@email.com",
|
|
"name": "Nome do Usuário",
|
|
"plan": "free",
|
|
"token": "eyJhbGciOiJIUzI1NiIs..."
|
|
}
|
|
```
|
|
|
|
#### POST /api/auth/login
|
|
Autentica usuário existente.
|
|
|
|
**Request:**
|
|
```json
|
|
{
|
|
"email": "usuario@email.com",
|
|
"password": "senha123"
|
|
}
|
|
```
|
|
|
|
**Response (200):**
|
|
```json
|
|
{
|
|
"token": "eyJhbGciOiJIUzI1NiIs...",
|
|
"user": {
|
|
"id": 1,
|
|
"email": "usuario@email.com",
|
|
"name": "Nome do Usuário",
|
|
"plan": "free",
|
|
"scans_today": 2
|
|
}
|
|
}
|
|
```
|
|
|
|
### Scans
|
|
|
|
#### POST /api/scan
|
|
Analisa um produto pelo código de barras.
|
|
|
|
**Headers:**
|
|
```
|
|
Authorization: Bearer <token>
|
|
```
|
|
|
|
**Request:**
|
|
```json
|
|
{
|
|
"barcode": "7891000100103"
|
|
}
|
|
```
|
|
|
|
**Response (200):**
|
|
```json
|
|
{
|
|
"id": 123,
|
|
"product": {
|
|
"barcode": "7891000100103",
|
|
"name": "Leite Condensado",
|
|
"brand": "Moça",
|
|
"image_url": "https://..."
|
|
},
|
|
"score": 25,
|
|
"classification": "Péssimo",
|
|
"analysis": {
|
|
"summary": "Produto com alto teor de açúcar...",
|
|
"positives": ["Fonte de cálcio"],
|
|
"negatives": ["Alto teor de açúcar", "Calorias elevadas"],
|
|
"additives": [],
|
|
"nutrition": {
|
|
"calories": 321,
|
|
"sugar": 55,
|
|
"sodium": 128
|
|
}
|
|
},
|
|
"recipe": {
|
|
"title": "Leite condensado caseiro saudável",
|
|
"ingredients": ["1 litro de leite desnatado", "..."],
|
|
"instructions": "..."
|
|
}
|
|
}
|
|
```
|
|
|
|
**Erros:**
|
|
- `403`: Limite de scans atingido (plano free)
|
|
- `404`: Produto não encontrado
|
|
- `500`: Erro na análise
|
|
|
|
### Histórico
|
|
|
|
#### GET /api/history
|
|
Lista histórico de scans do usuário.
|
|
|
|
**Headers:**
|
|
```
|
|
Authorization: Bearer <token>
|
|
```
|
|
|
|
**Query Params:**
|
|
- `limit` (opcional): Número de resultados (default: 20)
|
|
- `offset` (opcional): Paginação
|
|
|
|
**Response (200):**
|
|
```json
|
|
{
|
|
"total": 45,
|
|
"scans": [
|
|
{
|
|
"id": 123,
|
|
"barcode": "7891000100103",
|
|
"product_name": "Leite Condensado",
|
|
"score": 25,
|
|
"scanned_at": "2026-02-10T14:30:00Z"
|
|
},
|
|
...
|
|
]
|
|
}
|
|
```
|
|
|
|
#### GET /api/history/{id}
|
|
Retorna detalhes de um scan específico.
|
|
|
|
**Response (200):**
|
|
```json
|
|
{
|
|
"id": 123,
|
|
"product": { ... },
|
|
"score": 25,
|
|
"analysis": { ... },
|
|
"recipe": { ... },
|
|
"scanned_at": "2026-02-10T14:30:00Z"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 🔗 Integrações
|
|
|
|
### Open Food Facts API
|
|
|
|
Base de dados aberta com mais de 3 milhões de produtos.
|
|
|
|
**Endpoint:**
|
|
```
|
|
https://world.openfoodfacts.org/api/v2/product/{barcode}.json
|
|
```
|
|
|
|
**Exemplo:**
|
|
```python
|
|
import requests
|
|
|
|
def get_product(barcode: str):
|
|
url = f"https://world.openfoodfacts.org/api/v2/product/{barcode}.json"
|
|
response = requests.get(url)
|
|
if response.status_code == 200:
|
|
data = response.json()
|
|
if data.get("status") == 1:
|
|
return data["product"]
|
|
return None
|
|
```
|
|
|
|
### OpenAI GPT-4o-mini
|
|
|
|
Usamos o modelo GPT-4o-mini para análise inteligente dos ingredientes.
|
|
|
|
**Uso:**
|
|
```python
|
|
from openai import OpenAI
|
|
|
|
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
|
|
|
|
def analyze_product(product_data: dict) -> dict:
|
|
prompt = f"""
|
|
Analise este produto alimentício e forneça:
|
|
1. Score de saúde (0-100)
|
|
2. Pontos positivos
|
|
3. Pontos negativos
|
|
4. Análise dos aditivos
|
|
5. Sugestão de receita saudável alternativa
|
|
|
|
Produto: {product_data['name']}
|
|
Ingredientes: {product_data['ingredients']}
|
|
Valores nutricionais: {product_data['nutrition']}
|
|
"""
|
|
|
|
response = client.chat.completions.create(
|
|
model="gpt-4o-mini",
|
|
messages=[{"role": "user", "content": prompt}],
|
|
response_format={"type": "json_object"}
|
|
)
|
|
|
|
return json.loads(response.choices[0].message.content)
|
|
```
|
|
|
|
---
|
|
|
|
## 📱 PWA / Service Worker
|
|
|
|
O ALETHEIA é uma Progressive Web App (PWA), permitindo:
|
|
- Instalação na home screen
|
|
- Funcionamento parcial offline
|
|
- Notificações push (futuro)
|
|
|
|
### Service Worker
|
|
|
|
Arquivo: `/opt/aletheia/frontend/public/sw.js`
|
|
|
|
```javascript
|
|
const CACHE_NAME = 'aletheia-v1';
|
|
const urlsToCache = [
|
|
'/',
|
|
'/scan',
|
|
'/history',
|
|
'/offline.html'
|
|
];
|
|
|
|
self.addEventListener('install', (event) => {
|
|
event.waitUntil(
|
|
caches.open(CACHE_NAME)
|
|
.then((cache) => cache.addAll(urlsToCache))
|
|
);
|
|
});
|
|
|
|
self.addEventListener('fetch', (event) => {
|
|
event.respondWith(
|
|
caches.match(event.request)
|
|
.then((response) => response || fetch(event.request))
|
|
);
|
|
});
|
|
```
|
|
|
|
### Manifest
|
|
|
|
Arquivo: `/opt/aletheia/frontend/public/manifest.json`
|
|
|
|
```json
|
|
{
|
|
"name": "ALETHEIA",
|
|
"short_name": "ALETHEIA",
|
|
"description": "Scanner de rótulos com IA",
|
|
"start_url": "/",
|
|
"display": "standalone",
|
|
"background_color": "#ffffff",
|
|
"theme_color": "#22c55e",
|
|
"icons": [
|
|
{
|
|
"src": "/icon-192.png",
|
|
"sizes": "192x192",
|
|
"type": "image/png"
|
|
},
|
|
{
|
|
"src": "/icon-512.png",
|
|
"sizes": "512x512",
|
|
"type": "image/png"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## ⚙️ Variáveis de Ambiente
|
|
|
|
### Backend (.env)
|
|
|
|
```env
|
|
# Database
|
|
DATABASE_URL=postgresql://aletheia:Aletheia2026!@localhost:5432/aletheia
|
|
|
|
# OpenAI
|
|
OPENAI_API_KEY=sk-...
|
|
|
|
# JWT
|
|
JWT_SECRET=sua-chave-secreta-muito-longa
|
|
JWT_ALGORITHM=HS256
|
|
JWT_EXPIRATION_HOURS=24
|
|
|
|
# App
|
|
APP_ENV=production
|
|
DEBUG=false
|
|
CORS_ORIGINS=https://aletheia.aivertice.com
|
|
```
|
|
|
|
### Frontend (.env.local)
|
|
|
|
```env
|
|
NEXT_PUBLIC_API_URL=https://aletheia.aivertice.com/api
|
|
NEXT_PUBLIC_APP_NAME=ALETHEIA
|
|
```
|
|
|
|
---
|
|
|
|
## 📊 Monitoramento
|
|
|
|
### PM2
|
|
|
|
```bash
|
|
# Dashboard em tempo real
|
|
pm2 monit
|
|
|
|
# Status detalhado
|
|
pm2 show aletheia-backend
|
|
|
|
# Métricas
|
|
pm2 info aletheia-backend
|
|
```
|
|
|
|
### Logs
|
|
|
|
```bash
|
|
# Logs do backend
|
|
tail -f ~/.pm2/logs/aletheia-backend-out.log
|
|
tail -f ~/.pm2/logs/aletheia-backend-error.log
|
|
|
|
# Logs do frontend
|
|
tail -f ~/.pm2/logs/aletheia-frontend-out.log
|
|
|
|
# Logs do Nginx
|
|
tail -f /var/log/nginx/access.log
|
|
tail -f /var/log/nginx/error.log
|
|
|
|
# Logs do PostgreSQL
|
|
tail -f /var/log/postgresql/postgresql-15-main.log
|
|
```
|
|
|
|
---
|
|
|
|
## 💾 Backup e Manutenção
|
|
|
|
### Backup Automático (Cron)
|
|
|
|
```bash
|
|
# Editar crontab
|
|
crontab -e
|
|
|
|
# Adicionar backup diário às 3h
|
|
0 3 * * * pg_dump -U aletheia -d aletheia > /opt/aletheia/backups/backup_$(date +\%Y\%m\%d).sql
|
|
|
|
# Limpar backups antigos (manter últimos 30 dias)
|
|
0 4 * * * find /opt/aletheia/backups -name "*.sql" -mtime +30 -delete
|
|
```
|
|
|
|
### Manutenção do Banco
|
|
|
|
```sql
|
|
-- Vacuum e análise (rodar semanalmente)
|
|
VACUUM ANALYZE;
|
|
|
|
-- Verificar tamanho das tabelas
|
|
SELECT
|
|
relname as table,
|
|
pg_size_pretty(pg_total_relation_size(relid)) as size
|
|
FROM pg_catalog.pg_statio_user_tables
|
|
ORDER BY pg_total_relation_size(relid) DESC;
|
|
```
|
|
|
|
### Atualizações
|
|
|
|
```bash
|
|
# Backend
|
|
cd /opt/aletheia/backend
|
|
git pull
|
|
pip install -r requirements.txt
|
|
pm2 restart aletheia-backend
|
|
|
|
# Frontend
|
|
cd /opt/aletheia/frontend
|
|
git pull
|
|
npm install
|
|
npm run build
|
|
pm2 restart aletheia-frontend
|
|
```
|
|
|
|
---
|
|
|
|
## 🔧 Troubleshooting
|
|
|
|
### Problema: Backend não inicia
|
|
```bash
|
|
# Verificar logs
|
|
pm2 logs aletheia-backend --lines 50
|
|
|
|
# Verificar porta em uso
|
|
lsof -i :8090
|
|
|
|
# Testar manualmente
|
|
cd /opt/aletheia/backend
|
|
python -m uvicorn main:app --host 0.0.0.0 --port 8090
|
|
```
|
|
|
|
### Problema: Erro de conexão com banco
|
|
```bash
|
|
# Verificar se PostgreSQL está rodando
|
|
sudo systemctl status postgresql
|
|
|
|
# Testar conexão
|
|
psql -U aletheia -d aletheia -h localhost -c "SELECT 1"
|
|
```
|
|
|
|
### Problema: Certificado SSL expirado
|
|
```bash
|
|
# Renovar
|
|
sudo certbot renew
|
|
|
|
# Reiniciar Nginx
|
|
sudo systemctl restart nginx
|
|
```
|
|
|
|
### Problema: Open Food Facts não responde
|
|
```bash
|
|
# Testar API diretamente
|
|
curl "https://world.openfoodfacts.org/api/v2/product/7891000100103.json"
|
|
|
|
# Verificar rate limiting (máx 100 req/min)
|
|
```
|
|
|
|
---
|
|
|
|
## 📞 Contatos Técnicos
|
|
|
|
- **Infraestrutura**: infra@aivertice.com
|
|
- **Desenvolvimento**: dev@aivertice.com
|
|
|
|
---
|
|
|
|
*Última atualização: Fevereiro 2026*
|