Resolução de Problemas
Este guia cataloga os problemas reais observados no projeto, organizados por categoria. Para cada problema: Sintoma, Causa e Solução objetiva.
1. Banco de Dados
1.1 Servidor MySQL inacessível
Sintoma:
Error: Can't reach database server at `localhost:3306`
PrismaClientInitializationError: P1001
Causa: O servidor MySQL não está em execução, ou DATABASE_URL aponta para um host/porta incorretos.
Solução:
# Subir o MySQL via Docker Compose
npm run db:up
# Verificar se o container está rodando
docker ps | grep mysql
# Verificar a DATABASE_URL no .env
cat .env | grep DATABASE_URL
# Formato esperado: mysql://usuario:senha@localhost:3306/nome_banco
1.2 Permissão negada ao usuário MySQL
Sintoma:
Error: P1010 - User `usuario` was denied access on the database `ljvemasearomas`
Causa: O usuário MySQL não tem as permissões necessárias para acessar o banco de dados.
Solução:
-- Conectar ao MySQL como root
mysql -u root -p
-- Conceder permissões completas
GRANT ALL PRIVILEGES ON ljvemasearomas.* TO 'usuario'@'localhost';
GRANT ALL PRIVILEGES ON ljvemasearomas.* TO 'usuario'@'%';
FLUSH PRIVILEGES;
1.3 Variável DATABASE_URL não encontrada
Sintoma:
Error: Environment variable not found: DATABASE_URL
Causa: O arquivo .env não existe na raiz do projeto.
Solução:
# Criar o .env a partir do template
cp .env.example .env
# Editar com os valores corretos
# DATABASE_URL=mysql://root:root@localhost:3306/ljvemasearomas
Após criar o
.env, reiniciar o servidor de desenvolvimento (npm run dev).
1.4 Prisma Client não gerado
Sintoma:
Error: Module not found: Can't resolve '@/lib/db'
Error: @prisma/client did not initialize yet
Causa: O Prisma Client não foi gerado. Isso acontece após um npm ci limpo, em novos clones ou após alterações no schema.prisma.
Solução:
npx prisma generate
Este comando deve ser executado antes de qualquer
next build,next devou execução de testes. Ele não requer conexão com o banco de dados.
1.5 Falha de constraint única no seed
Sintoma:
Error: Unique constraint failed on the fields: (`email`)
PrismaClientKnownRequestError: P2002
Causa: O banco já foi populado anteriormente (usuário admin ou dados de seed já existem). Rodar db:seed duas vezes causa conflito.
Solução:
# Resetar o banco (apaga tudo e reaplicar migrations + seed)
npm run db:reset
Atenção:
db:resetapaga todos os dados do banco de desenvolvimento. Nunca executar em produção.
2. Ambiente
2.1 Porta 3000 em uso
Sintoma:
Error: listen EADDRINUSE: address already in use :::3000
Causa: Outra instância do servidor (ou outro processo) já está ocupando a porta 3000.
Solução:
No Windows:
netstat -ano | findstr :3000
# Anota o PID na última coluna
taskkill /F /PID <PID>
No Linux/macOS:
lsof -i :3000
# Anota o PID
kill -9 <PID>
Alternativamente, iniciar o servidor em outra porta:
PORT=3001 npm run dev
2.2 Permissão negada na pasta de uploads
Sintoma:
Error: EACCES: permission denied, mkdir './public/uploads'
Causa: O processo Node.js não tem permissão de escrita no diretório public/uploads (comum em servidores Linux após deploy).
Solução:
# No servidor de produção (Linux)
chmod 755 public/uploads
# Se a pasta não existir
mkdir -p public/uploads
chmod 755 public/uploads
# Verificar o usuário do processo PM2
pm2 info ljvemasearomas | grep "exec user"
# Garantir que o usuário tem acesso à pasta
chown -R <usuario-pm2>:<grupo> public/uploads
2.3 Módulo não encontrado com path alias @/
Sintoma:
Module not found: Can't resolve '@/components/ui/button'
Causa: Path aliases TypeScript (@/) não estão configurados no ambiente de execução, ou o Prisma Client não foi gerado.
Solução:
- Verificar se
tsconfig.jsoncontém"paths": { "@/*": ["./src/*"] }ou"./*" - Verificar se
next.config.tsnão está sobrescrevendo os aliases - Rodar
npx prisma generatese o import for de@/lib/db - Parar e reiniciar o servidor de desenvolvimento completamente
3. Testes
3.1 Seed falha com timeout no CI
Sintoma:
Error: P1001 - Can't reach database server at `localhost:3306`
(no seed step in CI)
Causa: O container MySQL ainda não está pronto para aceitar conexões quando o Prisma tenta conectar, ou DATABASE_URL está incorreta no step do workflow.
Solução:
# No workflow .github/workflows/tests.yml
- name: Aguardar MySQL estar pronto
run: |
for i in $(seq 1 30); do
mysqladmin ping -h 127.0.0.1 -u root -proot --silent && break
echo "Aguardando MySQL... tentativa $i"
sleep 2
done
- name: Executar seed
run: npm run db:seed
env:
DATABASE_URL: mysql://root:root@127.0.0.1:3306/ljvemasearomas_test
3.2 Testes E2E sem browsers instalados
Sintoma:
Error: browserType.launch: Executable doesn't exist at
/root/.cache/ms-playwright/chromium-1097/chrome-linux/chrome
Causa: O Playwright foi instalado via npm ci, mas os binários dos browsers não foram baixados. Isso ocorre em toda máquina nova ou container CI limpo.
Solução:
# Instalar apenas o Chromium (usado nos testes deste projeto)
npx playwright install chromium
# Se houver dependências de sistema faltando (Linux)
npx playwright install-deps chromium
No GitHub Actions, adicionar este step antes do step de execução dos testes.
3.3 Login helper falha nos testes E2E
Sintoma:
Error: expect(page).toHaveURL('/admin/dashboard')
Received URL: '/login'
Os testes E2E passam localmente mas falham no CI.
Causa: O banco de dados de testes não foi populado antes dos testes. O helper de login (e2e/helpers/auth.ts) tenta autenticar com credenciais do seed (ex.: admin@ljvemas.com.br), mas o usuário não existe.
Solução:
# Antes de rodar os testes E2E, sempre executar o seed
npm run db:seed
# Ordem correta no CI:
# 1. prisma migrate deploy
# 2. npm run db:seed ← obrigatório
# 3. npm run test:e2e
3.4 Hydration mismatch no carrinho
Sintoma:
Warning: Hydration failed because the initial UI does not match
what was rendered on the server.
Aparece relacionado ao componente de carrinho ou ao estado isOpen.
Causa: O campo isOpen estava sendo persistido no localStorage via Zustand partialize, causando divergência entre o estado do servidor (sempre false) e o estado hidratado no cliente.
Status: Já corrigido — isOpen foi removido do partialize da store do carrinho. Se o erro reaparecer, verificar se algum campo de UI (modais, drawers, estados de abertura) foi adicionado acidentalmente ao partialize.
// store/cart-store.ts — configuração correta
partialize: (state) => ({
items: state.items,
// isOpen NÃO deve ser incluído aqui
}),
4. E-mail
4.1 Gmail bloqueando envio SMTP
Sintoma:
Error: Invalid login: 535-5.7.8 Username and Password not accepted
Causa: O Gmail bloqueia autenticação com senha comum por conta da política de segurança "Acesso a apps menos seguros". O uso de senha da conta Google no SMTP não é mais permitido.
Solução: Criar uma Senha de App no Google:
- Acessar myaccount.google.com
- Segurança → Verificação em duas etapas (deve estar ativada)
- Segurança → Senhas de app
- Selecionar "Outro (nome personalizado)" →
ljvemasearomas - Copiar a senha gerada (16 caracteres)
- No
.env, usar a senha de app:
EMAIL_HOST=smtp.gmail.com
EMAIL_PORT=587
EMAIL_USER=seu@gmail.com
EMAIL_PASS=abcd efgh ijkl mnop # senha de app (sem espaços na prática)
4.2 Testando envio com Mailtrap
Para ambiente de desenvolvimento e staging, usar o Mailtrap em vez de um servidor SMTP real. O Mailtrap captura todos os e-mails sem entregá-los, permitindo inspecionar o conteúdo.
# .env (desenvolvimento)
EMAIL_HOST=sandbox.smtp.mailtrap.io
EMAIL_PORT=2525
EMAIL_USER=<usuario-mailtrap>
EMAIL_PASS=<senha-mailtrap>
Credenciais disponíveis em mailtrap.io → Inbox → SMTP Settings.
4.3 Testando o envio de e-mail manualmente
npm run test:smtp
Se o script não existir, testar via Node.js diretamente:
// scripts/test-email.ts
import nodemailer from "nodemailer";
const transporter = nodemailer.createTransport({
host: process.env.EMAIL_HOST,
port: Number(process.env.EMAIL_PORT),
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASS,
},
});
await transporter.sendMail({
from: process.env.EMAIL_USER,
to: "teste@exemplo.com",
subject: "Teste de envio",
text: "E-mail de teste do sistema ljvemasearomas",
});
console.log("E-mail enviado com sucesso");
npx tsx scripts/test-email.ts
5. Frete
5.1 Provider retornando erro (token expirado)
Sintoma:
FreightError: 401 Unauthorized — token de acesso expirado
FreightError: Melhor Envio: invalid_client
Causa: O token de acesso da API do provider de frete (Melhor Envio, Correios, etc.) expirou ou foi revogado.
Solução:
- Acessar o painel do provider e gerar um novo token de acesso
- Atualizar a variável de ambiente correspondente no servidor:
MELHOR_ENVIO_TOKEN=novo_token_aqui
- Reiniciar o servidor:
pm2 reload ljvemasearomas
5.2 Timeout na consulta de frete
Sintoma:
FreightError: Request timeout after 5000ms
FreightError: ECONNREFUSED — API de frete inacessível
Causa: A API do provider de frete está fora do ar, lenta ou bloqueada por firewall do servidor.
Solução: O sistema possui fallback para tarifas estáticas. Verificar se o fallback está ativado no código:
// lib/freight/index.ts
try {
return await calcularFreteOnline(cep, itens);
} catch (err) {
console.error("Frete online indisponível, usando tarifas estáticas:", err);
return calcularFreteFallback(cep, itens); // tabela de preços estática
}
Se o fallback não estiver implementado, a página de checkout exibirá erro. Adicionar tratamento de erro e exibir mensagem amigável ao usuário enquanto o provider estiver indisponível.
6. Deploy
6.1 PM2 não inicia a aplicação
Sintoma:
[PM2] App [ljvemasearomas] with id [0] and pid [...] exited with code [1] ...
pm2 status mostra errored ou stopped.
Causa: Erro durante a inicialização — pode ser variável de ambiente faltando, build ausente, ou erro de sintaxe no código.
Solução:
# Ver os logs de erro do PM2
pm2 logs ljvemasearomas --lines 50 --nostream
# Verificar se o build existe
ls -la .next/
# Se o build não existir
npm run build && pm2 restart ljvemasearomas
# Verificar variáveis de ambiente
pm2 env 0 # substitua 0 pelo ID do processo
6.2 Nginx retornando 502 Bad Gateway
Sintoma: O site exibe "502 Bad Gateway" no navegador.
Causa: O Nginx não consegue se comunicar com a aplicação Next.js. Geralmente significa que o processo PM2 não está rodando ou está na porta errada.
Solução:
# Verificar se o processo está rodando
pm2 status
# Se não estiver rodando
pm2 start ecosystem.config.js
# Verificar a porta em que o Next.js está escutando
pm2 logs ljvemasearomas | grep "started server"
# Deve mostrar: "started server on 0.0.0.0:3000"
# Verificar configuração do Nginx
cat /etc/nginx/sites-enabled/ljvemasearomas
# A linha proxy_pass deve apontar para http://localhost:3000
6.3 Certificado .pfx da NF-e com erro
Sintoma:
Error: PKCS12 certificate parse error
Error: mac verify failure
Causa: Senha do certificado .pfx incorreta, ou arquivo corrompido durante o upload.
Solução:
# Testar o certificado localmente
openssl pkcs12 -in certificado.pfx -noout
# Será solicitada a senha — deve completar sem erros
# Verificar integridade do arquivo
md5sum certificado.pfx # comparar com o hash do original
Atualizar o certificado no painel admin e garantir que a senha está correta na variável de ambiente correspondente.
6.4 Uploads sem permissão após deploy
Sintoma: Upload de imagens falha com EACCES: permission denied.
Causa: O deploy via git pull ou SSH não preserva as permissões corretas na pasta public/uploads.
Solução:
# No servidor, após o deploy
chmod -R 755 public/uploads
chown -R www-data:www-data public/uploads # ajustar para o usuário correto
# Verificar o usuário do processo PM2
whoami # quando SSH conectado
pm2 info ljvemasearomas | grep "username"
7. Performance
7.1 Queries N+1 com Prisma
Sintoma: Página carrega lentamente; HyperDX mostra dezenas de queries SQL para um único request.
Causa: Loop que executa uma query por iteração em vez de buscar todos os dados em uma única query com include.
Exemplo do problema:
// ❌ N+1: 1 query para pedidos + N queries para cliente de cada pedido
const pedidos = await prisma.order.findMany();
for (const pedido of pedidos) {
const cliente = await prisma.user.findUnique({ where: { id: pedido.userId } });
}
Solução:
// ✅ 1 única query com JOIN via include
const pedidos = await prisma.order.findMany({
include: {
user: { select: { name: true, email: true } },
items: { include: { product: true } },
},
});
7.2 Imagens lentas carregando
Sintoma: Imagens de produto com carregamento lento, LCP (Largest Contentful Paint) alto.
Causa: Imagens servidas em formato JPEG/PNG sem otimização, tamanho original muito grande, ou uso de <img> padrão em vez de next/image.
Solução:
// ❌ Imagem sem otimização
<img src="/uploads/produto.jpg" />
// ✅ Com next/image (converte para WebP automaticamente, lazy loading, tamanhos responsivos)
import Image from "next/image";
<Image
src="/uploads/produto.jpg"
alt="Nome do produto"
width={400}
height={400}
quality={85}
placeholder="blur"
blurDataURL="data:image/jpeg;base64,..."
/>
Configurar domínios externos no next.config.ts se as imagens vierem de CDN:
images: {
remotePatterns: [{ protocol: "https", hostname: "cdn.exemplo.com" }],
},
7.3 Bundle JavaScript grande
Sintoma: Relatório do next build mostra chunks acima de 500KB. Carregamento inicial lento (FCP alto).
Causa: Importações de bibliotecas pesadas sem code splitting, ou importação de bibliotecas inteiras quando apenas uma função é necessária.
Solução:
# Analisar o bundle
ANALYZE=true npm run build
Estratégias de redução:
// ❌ Importa a biblioteca inteira (lodash: ~72KB gzipped)
import _ from "lodash";
_.debounce(fn, 300);
// ✅ Importa apenas a função necessária (tree-shaking)
import debounce from "lodash/debounce";
// ❌ Componente pesado carregado sempre
import { HeavyChart } from "@/components/charts/heavy-chart";
// ✅ Carregado apenas quando necessário (lazy loading)
import dynamic from "next/dynamic";
const HeavyChart = dynamic(() => import("@/components/charts/heavy-chart"), {
loading: () => <p>Carregando gráfico...</p>,
ssr: false,
});