📚 Documentação inicial do ALETHEIA
- 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
This commit is contained in:
679
docs/MANUAL-TECNICO.md
Normal file
679
docs/MANUAL-TECNICO.md
Normal file
@@ -0,0 +1,679 @@
|
||||
# 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*
|
||||
Reference in New Issue
Block a user