Files
nutriscan/README.md
T

8.8 KiB
Raw Blame History

Ícone do NutriScan

NutriScan

Busca de produtos alimentícios sobre o Open Food Facts — por código de barras ou nome, com filtros de Nutri-Score e NOVA, cache em camadas e listas de compras por ID.

Next.js TypeScript Licença

Demo · Recursos · Como funciona · Execução local · Licença

Note

Estado do projeto: redesign completo concluído em 2026-07-16 (feat: complete NutriScan redesign), seguido de correções de deploy com Prisma/SQLite e de integração do Rybbit Analytics. A instância pública do autor roda em nutriscan.evandro.dev.br. Não há suíte de testes automatizados no repositório; o único check disponível é pnpm lint.

Important

Licença de Uso Pessoal: o código só pode ser usado para fins pessoais e não comerciais, apenas em localhost. É proibido fazer deploy público, redistribuir, usar comercialmente ou criar obras derivadas para distribuição (texto completo em LICENSE). A instância pública acima é a do próprio autor.

Recursos

Recurso O que faz
Busca por código de barras ou nome O tipo é detectado sozinho: entrada com 8 a 13 dígitos é tratada como código de barras, o resto como nome/marca
Filtros Nutri-Score A–E, grupos NOVA 1–4, categorias, marcas, alérgenos e aditivos (incluir/excluir, ou "sem aditivos"), faixas por 100 g de energia, gordura, carboidratos, proteínas, açúcares, fibras e sódio, e países
12 critérios de ordenação Relevância, Nutri-Score, nome, marca, energia, gordura, açúcares, completude, com imagem, com dados nutricionais, Eco-Score e grupo NOVA, em ordem crescente ou decrescente
Cache em camadas localStorage no navegador (TTL padrão de 1 hora) e um cache JSON no servidor (data/products-cache.json, 232 produtos no repositório) que é consultado antes do Open Food Facts
Modo cache-only Em produção, o usuário pode ligar um switch para o servidor responder só com o cache JSON, sem consultar o Open Food Facts
País por IP A rota /api/location descobre o país do visitante e a busca prioriza produtos dele; o resultado fica 7 dias no localStorage, com Brasil como fallback
Listas de compras Várias listas acessíveis por ID em /lista/[id]; cada item tem nome, quantidade, unidade e status de comprado, persistidos com Prisma + SQLite
Interface Tema claro/escuro, componentes shadcn/ui sobre Radix UI, animações com Framer Motion, skeletons, paginação e manifest de PWA

Páginas: / (landing), /resultados, /produto/[barcode], /lista/[id] e /sobre.

Como funciona

flowchart LR
    U["Navegador<br/>SearchBar · FilterSidebar"] --> L{"localStorage<br/>TTL 1 h"}
    L -->|hit| R["Resultados"]
    L -->|miss| A["Rotas Next.js<br/>/api/search · /api/products/[barcode]"]
    A --> J{"data/products-cache.json<br/>cache JSON no servidor"}
    J -->|hit| R
    J -->|"miss (fora do cache-only)"| O["Open Food Facts<br/>world.openfoodfacts.org"]
    O -->|salva no cache JSON| R
    P["/lista/[id]"] --> S["Server Actions<br/>app/actions.ts"] --> D["Prisma + SQLite"]
Detalhes técnicos
Peça Onde O que faz
Wrapper da API lib/openFoodFactsApi.ts Timeout de 8 s por requisição, intervalo de 200 ms entre páginas, páginas de até 1000 produtos, ordenação com score de completude
Cache do navegador lib/cacheManager.ts, hooks/useSearchCache.ts Buscas com filtros em localStorage, índice de chaves e estatísticas
Cache do servidor lib/jsonCacheManager.ts Singleton que lê e grava data/products-cache.json com merge dos produtos
Sincronização POST /api/cache/sync Recebe produtos do cliente e os grava no cache JSON
Estatísticas GET /api/storage-stats Estatísticas de armazenamento do cache
Localização GET /api/location IP via x-forwarded-for/x-real-ip → país via ip-api.com
Listas app/actions.ts verificarLista, criarItens, listarProdutos, deletarProduto, toggleComprado
Imagens next.config.mjs Libera static.openfoodfacts.org e images.openfoodfacts.org, formatos WebP/AVIF

Modelos Prisma (prisma/schema.prisma, SQLite): Lista (id, timestamps) e Produtos (nome, qtd, tipoUN, comprado, listaId).

Estrutura:

app/                 App Router: landing, /resultados, /produto, /lista, /sobre
app/api/             search, products/[barcode], location, cache/sync, storage-stats
app/actions.ts       Server Actions das listas de compras
lib/                 wrapper da API, caches (navegador e JSON), geolocalização, Prisma
hooks/               busca, filtros, cache-only, geolocalização, monitor de storage
components/ui/       componentes shadcn/ui
data/                products-cache.json
prisma/              schema.prisma
docs/                notas da API Open Food Facts e material do redesign

Limitações conhecidas

  • O switch de cache-only só existe em produção (NODE_ENV=production); em desenvolvimento ele fica desligado.
  • Toda busca nova depende do Open Food Facts: o wrapper aplica timeout de 8 s e intervalo de 200 ms entre páginas, mas não há fila ou fallback além do cache.
  • A localização é por IP (o servidor consulta o ip-api.com), não pela API de geolocalização do navegador.
  • O código de barras é digitado ou colado; não há leitura por câmera.
  • O sitemap.xml lista só a página inicial.

Execução local

Warning

Não existe .env.example e pnpm db:migrate não funciona num clone novo: ele roda prisma migrate deploy, mas /prisma/migrations está no .gitignore. Crie o banco com pnpm db:push. O DATABASE_URL é resolvido a partir da pasta prisma/, então file:./db/database.sqlite vira prisma/db/database.sqlite (pasta também ignorada pelo git).

Requisitos: Node.js 18+ (o deploy usa nodejs_18) e pnpm (o deploy fixa [email protected]).

git clone https://git.evandro.dev.br/evandro/nutriscan.git
cd nutriscan
pnpm install                      # o postinstall já roda `prisma generate`

# .env na raiz (o Prisma lê .env, não .env.local)
echo 'DATABASE_URL="file:./db/database.sqlite"' > .env

pnpm db:push                      # cria o banco SQLite a partir do schema
pnpm dev                          # http://localhost:3000

Variáveis opcionais lidas pelo código:

Variável Efeito
NEXT_PUBLIC_CACHE_ONLY_MODE=true A rota de produto responde só com o cache JSON
NEXT_PUBLIC_RYBBIT_HOST Destino do proxy /analytics/* (padrão https://analytics.evandro.dev.br/api)
Script Comando
pnpm dev next dev
pnpm build prisma generate && next build
pnpm start next start
pnpm lint next lint
pnpm db:push prisma db push
pnpm db:generate prisma generate
pnpm db:migrate prisma migrate deploy (exige migrações locais, veja o aviso acima)

Deploy

O repositório traz duas configurações, sem workflow de CI:

  • dokploy.yaml: instala o pnpm, roda pnpm install --no-frozen-lockfile, pnpx prisma generate e pnpm run build.
  • nixpacks.toml: nodejs_18 + openssl, [email protected], DATABASE_URL=file:./db/database.sqlite, prisma db push --accept-data-loss, build e pnpm start.

Licença

Licença de Uso Pessoal, em LICENSE (Copyright (c) 2024 Evandro): uso pessoal, não comercial e local; sem deploy público, redistribuição ou obras derivadas. Dúvidas sobre a licença: [email protected].

Bugs e sugestões: issues do repositório. Autor: evandro.dev.br.

Avisos