WhatsApp ↔ GLPI
Integração entre o atendimento por WhatsApp e o GLPI, unificando os canais de suporte em um fluxo só.
Olá, eu sou
Desenvolvedor Fullstack · Frontend · IA
Construo sistemas e soluções que resolvem problemas reais, do backend à interface, com cuidado no design e uma camada de inteligência quando ela faz diferença.
01 · sobre
Sou desenvolvedor em Campo Grande/MS, graduado em Sistemas da Informação. Minha base é o frontend: é onde tenho mais experiência, unindo desenvolvimento a design, UI/UX e identidade visual. Em cima disso construí um perfil fullstack, que hoje vai do backend à infra.
Atuo na UFMS, onde passei do suporte técnico para a equipe de inovação da DIDIA (Diretoria de Inovação e Inteligência Artificial) e, de lá, me tornei gerente substituto da GERIA, a gerência de inteligência artificial dentro da DIDIA. No caminho, construí sistemas inteiros (alguns sozinho) que estão em produção sendo usados todos os dias.
Em paralelo, desenvolvo projetos próprios e presto serviços para empresas: sites, sistemas sob medida e soluções com IA. Já coloquei no ar sites para clientes reais, e gosto de entregar coisas que funcionam de verdade, com testes, segurança e um cuidado honesto com quem vai usar.
sistemas completos já entregues
anos construindo software profissionalmente
foco atual em automações, integrações e sistemas inteligentes
02 · experiência
De manutenção de hardware à gerência de uma equipe de IA. Cada fase me ajudou a construir o que sei hoje.
※ Os sistemas listados são apenas uma amostra do que desenvolvi; escolhi os que considero mais relevantes.
jul 2022 a fev 2023
Terceirizada da Positivo Tecnologia
Onde tudo começou: manutenção de equipamentos. Aprendi na prática hardware e suporte, consertando computadores, notebooks e celulares, fazendo atendimentos presenciais em órgãos públicos e empresas privadas que utilizavam tecnologia da Positivo.
fev 2023 a meados de 2024
UFMS · Universidade Federal de Mato Grosso do Sul
Um ano e meio no suporte, mas além de só atender chamados, comecei a automatizar o que travava o nosso dia a dia e a desenvolver o que poderia nos ajudar. Por mais que a Agetic (agência de tecnologia da UFMS, onde eu trabalhava) tinha uma equipe própria de desenvolvimento, o foco em estudantes não permitia que desenvolvessem sistemas para outras áreas. Aqui comecei a ter a oportunidade de construir sistemas e fluxos funcionais para a universidade.
Integração entre o atendimento por WhatsApp e o GLPI, unificando os canais de suporte em um fluxo só.
Sistema implantado para a rádio da universidade, com melhoria de uso pela equipe responsável e a liberdade de transmitir um evento de qualquer lugar por meio de canais seguros.
Formulários inteligentes que resolviam chamados de suporte no GLPI automaticamente, sem precisar de um humano no fluxo.
meados de 2024 a 2025
DIDIA · Diretoria de Inovação e Inteligência Artificial (UFMS)
A partir da disposição de construir sistemas que pudessem resolver problemas reais (e aqui agradeço muito quem me ajudou a ter essa oportunidade), participei da formação de uma nova equipe: a equipe de inovação, onde o objetivo era criar soluções inovadoras para a universidade e para a sociedade. Aqui desenvolvi sistemas inteiros, às vezes sozinho, do design ao deploy. Nessa equipe, a experiência aumentou muito: desenvolvemos scripts, softwares, plugins, IA, usando ferramentas modernas e às vezes até criando novas, permitindo criar sistemas completos que não existiam em outros lugares (realmente inovação). Só nesse período foram 12 sistemas em 1 ano! Junto vieram logos, identidades visuais e frontends para diversos outros projetos. Abaixo, alguns dos que considero mais relevantes.
Plugin com camadas de IA para o GLPI: classificação automática de chamados, balanceamento de carga entre técnicos, sugestões inteligentes de resposta, chatbot com base de conhecimento e análise de sentimento. Desenvolvido por mim, com ajuda de sugestões de colegas mais experientes em IA.
Gerou artigos para a comunidade de TI e virou tema de palestra (inclusive apresentada por mim) em um evento de tecnologia entre instituições.
Central de segurança inteligente, com recursos de guardas virtuais, alertas inteligentes, mosaicos de câmeras e visão computacional.
Gestão de fechaduras eletrônicas em toda a universidade: permissões de acesso, logs, chaves NFC e mais.
Controle de dispositivos IoT e da infraestrutura elétrica do campus com foco em sustentabilidade: medidores de energia, quadros elétricos, postes e luminárias, além de outros dispositivos, todos georreferenciados no mapa.
Portal inteligente do campus da UFMS, com mapa dinâmico e hub de acesso a todos os sistemas desenvolvidos pela equipe. Backend em padrão BFF com login institucional.
Sistema em formato de jogo para a recepção de calouros, com premiações, rankings e fluxo seguido por mapa.
Jogos de formatos variados para eventos da universidade, incluindo desenvolvimento em Unity.
2025 · atual
GERIA · Gerência de Inteligência Artificial (dentro da DIDIA · UFMS)
Hoje atuo como gerente substituto da GERIA, a gerência de inteligência artificial dentro da DIDIA, onde criamos soluções e sistemas inovadores para a universidade. Uno o que aprendi construindo com a responsabilidade de ajudar a guiar um time, sem largar o código: sigo desenvolvendo sistemas inteiros. Abaixo, alguns deles.
Sistema de vendas da livraria UFMS em dois canais: e-commerce para a comunidade acadêmica e o público em geral, e PDV para as vendas presenciais no caixa da loja, com estoque, pedidos e relatórios unificados. Pagamentos via Mercado Pago, cálculo de frete e login pelo Passaporte UFMS, Google e conta local.
Ingresso digital de novos servidores da UFMS: o nomeado preenche os dados, gera a ficha cadastral e a assina digitalmente pelo gov.br. O sistema valida a assinatura, abre o processo automaticamente com os documentos anexados e avisa por e-mail a cada etapa concluída.
Mapa interativo do campus, incorporável em qualquer site, com painel administrativo de locais e salas, login institucional, papéis de editor e administrador e trilha de auditoria.
Sistema de design unificado para todos os sistemas da DIDIA: tokens de cor, tipografia, espaçamento e sombra (com temas claro e escuro), biblioteca de componentes React e site de documentação interativa.
03 · projetos
Projetos pessoais e de clientes, do zero ao deploy, cada um com um problema real por trás.
Um time de agentes de IA: você traz a ideia, o time entrega o software
Plugin open source para o Claude Code que funciona como uma software house completa. Você entra apenas como cliente: traz a ideia e toma as decisões de negócio. O time cuida de todo o resto, do levantamento de requisitos ao código executável, e ainda complementa o seu projeto com ideias inovadoras pesquisadas em produtos parecidos, cada uma com impacto e esforço medidos para você escolher o que entra. São agentes especializados: analista de ideias, desenvolvedor de inovação, escritor de plano, designer, devs de backend/banco/frontend/IA, infra e segurança. O time dimensiona o porte do projeto, chama só quem é necessário, pensa em segurança desde o início e retoma o trabalho de onde parou. Instale, descreva a sua ideia e acompanhe o seu time trabalhar.
Um plugin do Claude Code que conduz um software da ideia ao código executável,
através de um time de papéis seniores — sem cerimônia a mais nem segurança a menos.
analista de ideias · desenvolvedor de ideias · escritor de plano ·
designer · dev backend · dev banco · dev frontend ·
dev IA · analista de infra · analista de segurança
Você descreve uma ideia. O plugin conversa para entendê-la de verdade, escreve um plano de implementação dimensionado ao porte do projeto e então constrói o sistema — com testes, segurança em todas as camadas e um caminho claro de “como rodar”. Tudo em português, registrado em arquivos no seu repositório, e retomável em outra sessão.
Você é o cliente; o time faz o resto. Assim como numa software house, você só traz a ideia e toma as decisões de negócio. O time cuida de todo o resto: levanta requisitos, pesquisa o que produtos parecidos já fazem, propõe ideias inovadoras que você nem tinha pensado (cada uma com impacto e esforço medidos, para você escolher o que entra), desenha a interface, planeja as sprints, escreve o código e os testes, sobe a infraestrutura e audita a segurança.
Pensado tanto para uso real quanto como peça de portfólio: demonstra orquestração de múltiplos agentes, engenharia de prompt e práticas modernas de engenharia de software.
1. Instale (no Claude Code, dentro de qualquer projeto):
/plugin marketplace add enzoterra/claude-project-builder
/plugin install project-builder@claude-project-builder
2. Construa — no projeto onde quer o sistema, descreva a ideia:
/construir-projeto um app para a fila de espera do meu consultório
Pronto. O orquestrador assume a partir daí e conduz as fases, parando para sua aprovação ao fim de cada uma. Você não precisa saber de stack, infra ou arquitetura — ele pergunta o que precisar e recomenda quando você não souber.
Só quer planejar? Use um papel isolado:
/analista-de-ideias(brainstorming) ou/escritor-de-plano(documentação técnica). Retomar mais tarde? É só rodar/construir-projetode novo no mesmo projeto — ele lê o progresso e continua de onde parou.
docker compose up + seed de
demonstração (com usuários de exemplo) + README de como rodar localmente.O plugin funciona como uma empresa de software atendendo você: cada papel é um profissional sênior, e você é o cliente que aprova as decisões que importam.
Os pontos de aprovação são proporcionais: num protótipo enxuto o time confirma só o essencial; num produto de produção há mais checkpoints. Você é incomodado onde sua opinião muda o resultado — não mais.
O orquestrador construir-projeto conduz quatro fases, delegando a cada especialista:
💡 Descoberta → 📝 Planejamento → 🏗️ Implementação (por sprints) → ✅ Validação
(entender) (documentar) (infra → banco → backend → IA → design → frontend → segurança) (UAT: validar com o cliente)
A trilha de implementação se adapta à forma do projeto: papéis sem camada correspondente (design e frontend num projeto sem UI, banco num projeto sem persistência, IA quando não há inteligência no produto) simplesmente não entram.
O estado não vive na conversa — vive em arquivos docs/projeto/ do seu repositório. É isso que
permite aos subagentes (que começam sem contexto) e a sessões futuras retomarem o trabalho:
| Arquivo | O que guarda |
|---|---|
00-descoberta.md | A ideia entendida: necessidade real, perfil, usuários, restrições |
01-plano.md … 09-operacao-e-deploy.md | Plano completo (conforme o porte e a forma): requisitos, arquitetura, banco, sprints, user stories, testes, segurança, operação |
10-design.md (+ design/mockup.html) | Sistema de design, quando há interface: tipografia, cores/tokens, ícones, componentes, estados |
PADROES.md | Os padrões transversais adaptados ao projeto |
PROGRESSO.md | Checkpoint vivo: status de cada sprint e passos concluídos |
| Papel | Tipo | O que faz |
|---|---|---|
analista-de-ideias | skill | Brainstorming interativo: necessidade real, porte, tipo/forma do projeto, usuários, dados, segurança, infra, design, inovações. Confirma o entendimento e recomenda quando você não sabe. |
desenvolvedor-de-ideias | subagente | Pesquisa o que projetos parecidos fazem e propõe ideias inovadoras — para o produto e para cada fluxo — com impacto e complexidade medidos. Trabalha junto da analista; você decide o que entra. |
escritor-de-plano | skill | Plano dimensionado ao porte e à forma: objetivos, requisitos (F/NF), arquitetura e stack (versões pesquisadas), banco, sprints, user stories, testes, segurança e operação. Consulta os especialistas para avaliar viabilidade e alternativas antes de fixar. |
designer | subagente | Sistema de design (só se houver interface): tipografia, cores/tokens, ícones, botões, cards, border-radius, estados — e um mockup de referência. O design desenha; o frontend desenvolve. |
dev-backend | subagente | API, regras de negócio, authn/authz e segurança rota a rota, testes. |
dev-banco-de-dados | subagente | Schema, migrações, índices e seed de demonstração. |
dev-frontend | subagente | UI moderna, limpa e responsiva, acessível, fiel ao sistema de design, com validação de entradas, integração com a API e testes. |
dev-ia | subagente | Camada de inteligência quando há IA central: seleção de modelo, prompts versionados, RAG/agentes, evals, guardrails, custo/latência e segurança de IA (prompt injection, vazamento, OWASP LLM). Também lidera pipeline de ML quando o projeto desenvolve modelo. |
analista-de-infra | subagente | Esqueleto, Docker/compose, config por env, CI e o caminho “como rodar”. |
analista-de-seguranca | subagente | Auditoria adversária de backend e frontend contra todas as vulnerabilidades da stack (pesquisando ameaças atuais), conduzindo a correção até zerar falhas altas/críticas. |
construir-projeto | skill (orquestrador) | Conduz todas as fases, dimensiona ao porte e delega aos especialistas. |
Todos atuam como seniores na linguagem e framework que você pedir.
O porte é decidido com você na descoberta e respeitado por todos os papéis:
| Perfil | Para quê | O que entrega |
|---|---|---|
| Enxuto | Script, protótipo, ferramenta pessoal, trabalho de faculdade, MVP de validação | Roda local direto, plano leve (1 doc), sem cerimônia. Foco em funcionar rápido. |
| Padrão | Produto real com usuários, vai a produção | Docker + seed demo, CI, testes, segurança sólida, plano dividido. |
| Robusto | Sistema empresarial ou de missão crítica | Tudo: observabilidade, SLO, DR, DevSecOps, compliance, multi-ambiente. |
.claude-plugin/
plugin.json # manifesto do plugin
marketplace.json # catálogo (permite adicionar este repo como marketplace)
skills/
construir-projeto/ # orquestrador + referencia/padroes.md (padrões transversais)
analista-de-ideias/ # descoberta interativa
escritor-de-plano/ # planejamento + templates/
agents/
desenvolvedor-de-ideias.md designer.md
dev-backend.md dev-banco-de-dados.md dev-frontend.md dev-ia.md
analista-de-infra.md analista-de-seguranca.md
Via marketplace (recomendado) — funciona em qualquer projeto após adicionado:
/plugin marketplace add enzoterra/claude-project-builder
/plugin install project-builder@claude-project-builder
(ou aponte para a URL: /plugin marketplace add https://github.com/enzoterra/claude-project-builder)
Importante: Se os comandos desse plugin não aparecerem no Claude Code Desktop App após a instalação, pode ser necessário encerrar o Claude (inclusive pelo ícone da bandeja do sistema, no Windows) e iniciar novamente.
Clonando localmente — útil para inspecionar ou ajustar antes:
git clone https://github.com/enzoterra/claude-project-builder
/plugin marketplace add ./caminho/para/claude-project-builder
/plugin install project-builder@claude-project-builder
git init se não houver).Enzo Andrade Terra · enzoterra18@gmail.com
Suas estatísticas do Spotify, de qualquer período, sem o histórico sair do aparelho
Retrospectiva do Spotify quando você quiser: tops de artistas, músicas e álbuns, mapa de calor dos horários em que você ouve, curiosidades sobre você mesmo e cartazes prontos para os Stories. Privacidade por arquitetura: o histórico completo é processado num Web Worker, no próprio navegador, sem nenhuma requisição com o conteúdo.
Suas estatísticas do Spotify quando você quiser: tops, horários e curiosidades de qualquer período, sem que o seu histórico saia do aparelho.
encore-stats.vercel.app · ver a demo

Todas as imagens abaixo são da demo, com artistas e músicas inventados.
Escolha um mês, um ano, desde sempre ou um intervalo. O Encore mostra os artistas, as músicas e os álbuns que você mais ouviu, quantos minutos de música foram e quantos artistas diferentes apareceram.

Quando você ouve: os dias e os horários em que a música mais toca, com o seu pico e o horário mais quieto. |
Você por você: comparações com o seu próprio histórico, e não com outras pessoas. |
De quem você tem mais músicas curtidas? Junto do histórico, você pode enviar também o arquivo “Dados da conta” do Spotify. Ele é opcional e, dele, o Encore lê só as suas curtidas: o resto do arquivo nem é aberto. Dá para mandar os dois de uma vez ou mandar as curtidas depois, com o painel aberto. O quadro vale para qualquer época, porque o Spotify não informa quando você curtiu cada música.

Entrando com a conta do Spotify, você vê os tops das últimas 4 semanas, dos últimos 6 meses e do último ano, quem subiu e quem caiu, e de quem você tem mais músicas curtidas (é só tocar em “Descobrir”).
|
|
Pela conexão, o Spotify só mostra esses três períodos. Os atalhos “Desde o começo” e “Selecionar período” explicam isso e levam você ao envio do histórico completo, que libera qualquer período.

Quatro modelos, Line-up, Músicas, Mix e Básico, em Stories (9:16) ou quadrado (1:1). A imagem é feita no seu aparelho, com o período que está na tela, e vai direto para os Stories, o feed ou o WhatsApp.


![]() |
![]() |
![]() |
O Encore não é afiliado ao Spotify.
Stack: Next.js 16 (App Router, Turbopack) · React 19 · TypeScript 6 (strict) · Tailwind CSS 4 ·
next-intl (PT-BR e EN) · Zod 4 · Vitest 5 · Playwright + axe. Deploy na Vercel. Sem banco.
O plano completo está em docs/projeto/.
Estado: MVP completo (Sprints 0–7: upload, demo, Conectar, cards e auditoria de segurança) e no ar em modo Upload + Demo. A Iteração 8b trouxe os cartazes Músicas e Mix, a logo nova e os textos em linguagem do dia a dia; a Sprint 8 segue com a validação e o Conectar em produção (veja
docs/projeto/PROGRESSO.md). Contrato do BFF:docs/api.md.
Por baixo, os três modos funcionam assim:
.zip do “Histórico de streaming estendido” é processado num Web Worker, no
navegador, sem nenhuma requisição com o conteúdo.HttpOnly.| Ferramenta | Versão | Observação |
|---|---|---|
| Node.js | 24.x (.nvmrc, engines) | O CI e a Vercel usam Node 24; com outra major o pnpm avisa (Unsupported engine). |
| pnpm | 10.34.5 (packageManager) | Via Corepack (abaixo). A Vercel ainda não suporta pnpm 11+. |
O jeito mais simples de ter a versão certa do pnpm é o Corepack, que vem com o Node 24 e lê o
campo packageManager do package.json:
corepack enable # uma vez por máquina; depois, `pnpm` já roda a 10.34.5 neste projeto
pnpm --version # 10.34.5
Sem Corepack: npm install -g pnpm@10.34.5.
git clone <url-do-repositório> encore && cd encore
pnpm install
cp .env.example .env.local # pode ficar vazio: Upload e Demo funcionam sem credenciais
pnpm dev # http://127.0.0.1:3000 (redireciona para /pt-BR ou /en)
Use http://127.0.0.1:3000, não localhost: o Spotify só aceita redirect URI de loopback
com IP literal, e o cookie de sessão depende da mesma origem.
O “seed” local é o modo Demo (gerador determinístico, sem banco e sem rede). Não há credenciais de demonstração: o Demo não exige login.
| Rota | O quê |
|---|---|
/{locale} | Landing: os 3 modos (Conectar desabilitado sem credenciais), selo de privacidade |
/{locale}/onboarding | Como pedir o histórico estendido ao Spotify + lembrete .ics gerado no navegador |
/{locale}/upload | Envio do .zip/.json (Web Worker, nada sai do aparelho) e dashboard do histórico |
/{locale}/demo | Dashboard com histórico fictício, com as abas “Visão Upload” e “Visão Conectar” |
/{locale}/privacy | Política de privacidade (LGPD): o que é tratado, onde, por quanto tempo, como revogar |
/{locale}/connect | Modo Conectar: entrada/login, erros do OAuth e dashboard ao vivo (tops, tendências…) |
O botão Compartilhar (Upload, Demo e Conectar) abre o modal de cards: Line-up, Músicas, Mix ou Básico, Stories 9:16 ou Quadrado 1:1, com o período/janela selecionado no dashboard (Músicas e Mix ficam desabilitados quando o período não tem músicas). A imagem (PNG) é gerada no navegador (Web Worker com satori + resvg-wasm, carregados só nesse toque) e compartilhado pela Web Share API quando o navegador aceita arquivos; senão, é baixado.
{locale} é pt-BR ou en; o idioma troca pelo menu do cabeçalho sem perder o histórico
carregado (o Dataset fica só na memória da aba; recarregar a página exige novo envio).
Para testar o upload sem o seu histórico real, use as fixtures sintéticas de tests/fixtures/
(valid-two-files.zip, ou os maliciosos zip-bomb.zip e path-traversal.zip).
Para rodar os testes E2E pela primeira vez, instale os navegadores do Playwright (Chromium e WebKit, este último para aproximar o Safari/iOS):
pnpm exec playwright install chromium webkit
| Comando | O que faz |
|---|---|
pnpm dev | Servidor de desenvolvimento em 127.0.0.1:3000 |
pnpm build / pnpm start | Build de produção / servidor de produção em 127.0.0.1:3000 |
pnpm lint | ESLint (flat config: Next + typescript-eslint), sem warnings |
pnpm format / pnpm format:check | Prettier (com ordenação de classes do Tailwind) |
pnpm typecheck | Gera os tipos de rota do Next e roda tsc --noEmit |
pnpm test | Vitest (unit/integração em Node e componentes em jsdom) |
pnpm test:coverage | Vitest com cobertura (meta ≥ 80% em src/domain) |
pnpm test:e2e | Playwright (Chromium + WebKit) + axe; faz build + start se preciso |
pnpm fixtures | Regenera tests/fixtures/ (zips sintéticos válidos e maliciosos) |
UPDATE_CARD_SNAPSHOTS=1 pnpm test | Regrava as referências visuais dos cards em tests/snapshots/cards/ |
pnpm bench | Benchmark do upload: ~50 MB sintéticos (meta < 10 s) e troca de período |
Catálogo completo e comentado em .env.example; validação com Zod em
src/server/env.ts (por ambiente na Vercel: veja “Deploy na Vercel”). Resumo:
| Variável | Obrigatória | Para quê |
|---|---|---|
SPOTIFY_CLIENT_ID, SPOTIFY_CLIENT_SECRET, SPOTIFY_REDIRECT_URI | Não (as três juntas) | Modo Conectar. Sem elas, o botão fica desabilitado |
SESSION_SECRET | Se houver Conectar | 32 bytes em base64url para o cookie JWE |
SESSION_SECRET_PREVIOUS | Não | Rotação do segredo de sessão |
NEXT_PUBLIC_SITE_URL | Em produção | URL canônica (local: http://127.0.0.1:3000) |
NEXT_PUBLIC_REPO_URL | Não | Link “veja o código” do selo de privacidade (só https://) |
NEXT_PUBLIC_PRIVACY_CONTROLLER, NEXT_PUBLIC_PRIVACY_CONTACT | Em produção | Controlador e e-mail de contato exibidos em /privacy (LGPD) |
SPOTIFY_API_BASE, SPOTIFY_ACCOUNTS_BASE | Não | Só testes: mock do Spotify em loopback (proibidas na Vercel) |
Configuração parcial do Spotify (ex.: só o Client ID) falha no next build e na
inicialização com a lista das variáveis que faltam (o schema fica em src/server/env-schema.ts,
que o next.config.ts também chama). Nunca commite .env.local (já está no .gitignore).
Gerar um SESSION_SECRET:
node -e "console.log(require('crypto').randomBytes(32).toString('base64url'))"
http://127.0.0.1:3000/api/auth/callbackhttps://<domínio>/api/auth/callback (veja “Deploy na Vercel”).env.local (SPOTIFY_CLIENT_ID,
SPOTIFY_CLIENT_SECRET), defina SPOTIFY_REDIRECT_URI com a URI local acima e gere o
SESSION_SECRET.pnpm dev. A página inicial passa a mostrar o Conectar como disponível.Escopos pedidos (mínimos): user-top-read, user-read-recently-played, user-library-read.
http://127.0.0.1:3000Faça os passos acima (app no dashboard, redirect URI http://127.0.0.1:3000/api/auth/callback,
sua conta em User Management) e preencha o .env.local:
SPOTIFY_CLIENT_ID=<client id>
SPOTIFY_CLIENT_SECRET=<client secret>
SPOTIFY_REDIRECT_URI=http://127.0.0.1:3000/api/auth/callback
SESSION_SECRET=<saída do comando node acima>
pnpm dev e abra http://127.0.0.1:3000/pt-BR/connect (use 127.0.0.1, não
localhost: os cookies vivem na origem da redirect URI; se abrir localhost, o login te leva
para 127.0.0.1).
Clique em Entrar com o Spotify, autorize e volte para a mesma página, que passa a mostrar o dashboard do Conectar (“Oi, …”): janelas de 4 semanas / 6 meses / 1 ano, top artistas e músicas, tendências, tocadas recentemente, gêneros e a varredura de curtidas (“Descobrir”).
Na mesma aba, abra as rotas do BFF para ver os dados reduzidos (JSON):
/api/spotify/me, /api/spotify/top?type=artists&range=short_term,
/api/spotify/top?type=tracks&range=long_term, /api/spotify/recent,
/api/spotify/saved?offset=0.
Nas DevTools (Application → Cookies) o cookie encore_session é HttpOnly e SameSite=Lax,
com validade de 30 dias. Localmente ele sai sem Secure e sem o prefixo __Host-, porque
o Safari descarta cookies Secure em HTTP e o Chrome rejeita __Host- fora de HTTPS; em
produção (HTTPS) o nome é __Host-encore_session, com Secure (detalhes em docs/api.md).
No avatar do cabeçalho, Sair (com confirmação): o cookie some, o cache da aba
(TanStack Query + sessionStorage) é apagado e /api/spotify/me passa a responder
401 {"error":{"code":"UNAUTHENTICATED"}}.
Casos para conferir: cancelar no Spotify volta com “Autorização cancelada”; uma conta fora do User
Management volta com a tela “Este app ainda está em modo de teste” (?error=not_allowlisted),
que explica o limite de 5 contas e oferece Upload e Demo.
scripts/spotify-mock-server.ts simula o Accounts e a Web API com dados fictícios (confere o PKCE
e rotaciona o refresh token). Em dois terminais:
node scripts/spotify-mock-server.ts # http://127.0.0.1:4010
No .env.local (credenciais quaisquer, porque o mock não as confere contra nada real):
SPOTIFY_CLIENT_ID=mock
SPOTIFY_CLIENT_SECRET=mock
SPOTIFY_REDIRECT_URI=http://127.0.0.1:3000/api/auth/callback
SESSION_SECRET=<32 bytes em base64url>
SPOTIFY_ACCOUNTS_BASE=http://127.0.0.1:4010
SPOTIFY_API_BASE=http://127.0.0.1:4010/v1
Depois pnpm dev (ou pnpm build && pnpm start) e siga os passos 2–6 acima. Apague as duas
linhas SPOTIFY_*_BASE para voltar ao Spotify real.
Variáveis do mock: MOCK_SPOTIFY_DENY=1 (simula “cancelar”), MOCK_SPOTIFY_FORBIDDEN=1 (conta
fora da allowlist), MOCK_SPOTIFY_EXPIRES_IN=30 (força refresh a cada chamada),
MOCK_SPOTIFY_SAVED_TOTAL=1200 (biblioteca maior) e MOCK_SPOTIFY_SAVED_DELAY_MS=300 (atraso por
página de curtidas, para ver o progresso da varredura). As capas do mock (i.scdn.co/image/mock…)
não existem de verdade; o dashboard mostra a inicial no lugar.
O pnpm test:e2e sobe três servidores, em ordem (playwright.config.ts): o app em :3000 sem
credenciais (faz o build), o mock do Spotify em :4010 e o mesmo build em :3100 com o Conectar
ligado contra o mock (SESSION_SECRET descartável, só de teste). e2e/connect-live.spec.ts faz
login → dashboard → trocar janela → varredura → logout (cache limpo), mais axe, 360 px, QUOTA e 401. Com o pnpm 10 via Corepack, o
pnpm test:e2e sobe os três sozinho também no Windows. Para depurar, dá para subi-los à mão (os
servidores já de pé são reaproveitados):
pnpm build
pnpm start # :3000
node scripts/spotify-mock-server.ts # :4010
SPOTIFY_CLIENT_ID=mock-client SPOTIFY_CLIENT_SECRET=mock-secret SPOTIFY_REDIRECT_URI=http://127.0.0.1:3100/api/auth/callback SESSION_SECRET=e2e-only-not-a-secret-000000000000000000000 SPOTIFY_ACCOUNTS_BASE=http://127.0.0.1:4010 SPOTIFY_API_BASE=http://127.0.0.1:4010/v1 NEXT_PUBLIC_SITE_URL=http://127.0.0.1:3100 node node_modules/next/dist/bin/next start --hostname 127.0.0.1 --port 3100
Os logos em public/brand/ são os arquivos oficiais, sem alteração, das Spotify Design
Guidelines (developer.spotify.com/documentation/design): Full_Logo_White_RGB.svg do pacote
2024-spotify-full-logo.zip (→ spotify-full-logo-white.svg) e Primary_Logo_White_RGB.svg do
pacote 2024-spotify-logo-icon.zip (→ spotify-icon-white.svg), baixados em 2026-09-24. Só
aparecem junto de dados vindos da API do Spotify (modo Conectar), nunca no Upload nem no Demo.
O deploy é sem configuração e sem vercel.json: a Vercel detecta o Next.js, instala com o
pnpm 10 (pelo pnpm-lock.yaml + packageManager), usa o Node 24 (pelo engines.node: 24.x) e
roda pnpm build. Os cabeçalhos de segurança já vêm do proxy.ts (CSP com nonce) e do
next.config.ts; não há rewrite, redirect, cron nem região que precise de vercel.json.
O next build valida as variáveis de ambiente (src/server/env-schema.ts, chamado pelo
next.config.ts). Configuração inválida falha o build com a lista dos nomes que faltam ou
estão errados (nunca os valores), e o deployment anterior continua no ar.
pnpm@10.x) e a do Node (24.x).
Opcional: se o pnpm do log for anterior à 10.34.5, crie a variável
ENABLE_EXPERIMENTAL_COREPACK=1 (todos os ambientes). Assim a Vercel usa, via Corepack,
exatamente a versão do packageManager.| Ambiente | Branch | URL | Conectar | Proteção |
|---|---|---|---|---|
| Produção | main | https://<projeto>.vercel.app (ou domínio próprio) | Sim | Pública |
| Staging | staging | URL fixa do branch: https://<projeto>-git-staging-<escopo>.vercel.app | Sim (redirect URI cadastrada no Spotify) | Deployment Protection |
| Previews (PRs etc.) | qualquer outro | dinâmica, uma por deployment | Não (sem variáveis do Spotify) | Deployment Protection |
git switch -c staging && git push -u origin staging. A URL fixa aparece em
Deployments → deployment do staging → Domains (a que tem -git-staging-). Para uma
URL mais curta, em Settings → Domains adicione um domínio e ligue-o ao branch staging.staging, e deixa o domínio de produção público).
Quem abre o staging precisa entrar com uma conta Vercel com acesso ao projeto (no Hobby, só
1 usuário externo) ou usar um Shareable Link do deployment.main. Proteja o main no GitHub
exigindo os jobs do CI (veja docs/projeto/relatorio-seguranca.md, seção 9).Em Settings → Environment Variables. Para o staging, escolha Preview e, em “Select a
custom Preview branch”, o branch staging: variáveis de um branch sobrescrevem as de Preview
geral. Não crie as variáveis do Spotify em Preview geral: é a ausência delas que desliga o
Conectar nos previews dinâmicos (a redirect URI deles não está cadastrada no Spotify).
| Variável | Produção | Preview do staging | Previews dinâmicos | Observação |
|---|---|---|---|---|
NEXT_PUBLIC_SITE_URL | Obrigatória: https://<produção> | Recomendada: https://<staging> | Deixe vazia | Só HTTPS em deploys |
NEXT_PUBLIC_PRIVACY_CONTROLLER | Obrigatória: Enzo Terra | Recomendada: Enzo Terra | Opcional | Nome do controlador, exibido em /privacy |
NEXT_PUBLIC_PRIVACY_CONTACT | Obrigatória: enzoterra18@gmail.com | Recomendada: enzoterra18@gmail.com | Opcional | E-mail de contato do titular, exibido em /privacy |
SPOTIFY_CLIENT_ID | Sim | Sim (o mesmo app) | Não | As três do Spotify vão juntas, ou nenhuma |
SPOTIFY_CLIENT_SECRET | Sim, Sensitive | Sim, Sensitive | Não | Só no servidor |
SPOTIFY_REDIRECT_URI | https://<produção>/api/auth/callback | https://<staging>/api/auth/callback | Não | Idêntica à cadastrada no Spotify |
SESSION_SECRET | Sim, Sensitive, só de produção | Sim, Sensitive, outro valor | Não | Obrigatória quando o Conectar está configurado |
SESSION_SECRET_PREVIOUS | Só durante uma rotação | Só durante uma rotação | Não | O segredo anterior, para não derrubar sessões |
NEXT_PUBLIC_REPO_URL | Opcional | Opcional | Opcional | Só https:// |
SPOTIFY_API_BASE, SPOTIFY_ACCOUNTS_BASE | Nunca | Nunca | Nunca | Só mocks locais; o build de deploy falha com elas |
VERCEL_ENV é definida pela própria Vercel (production ou preview) e liga as regras de deploy
do env.ts: HTTPS obrigatório, mocks proibidos, segredo dos e2e recusado e, em produção, as três
primeiras linhas da tabela obrigatórias.
Os valores de privacidade acima foram informados pelo cliente em 2026-09-25 e existem só no
painel da Vercel: o código e o .env.example não têm valor padrão para eles.
Gere um SESSION_SECRET por ambiente (rode o comando duas vezes, cole direto no painel e não
guarde a saída em outro lugar):
node -e "console.log(require('crypto').randomBytes(32).toString('base64url'))"
Rotação: copie o valor atual para SESSION_SECRET_PREVIOUS, gere um novo SESSION_SECRET e faça
Redeploy. Apague o SESSION_SECRET_PREVIOUS depois de 30 dias (a validade máxima da sessão).
Mudou alguma variável? Ela só vale para deployments novos: use Redeploy no último deployment.
As NEXT_PUBLIC_* entram no JavaScript no build, então também só mudam com um novo build.
https://<produção>/api/auth/callbackhttps://<staging>/api/auth/callback (a URL fixa do branch, nunca a de um deployment)http://127.0.0.1:3000/api/auth/callback (desenvolvimento local)curl -s https://<produção>/api/health # {"status":"ok"}
curl -sI https://<produção>/pt-BR # CSP com nonce, HSTS, nosniff, COOP...
curl -sI https://<produção>/api/spotify/me # 401, default-src 'none', CORP same-origin
curl -s -o /dev/null -w "%{http_code}\n" https://<produção>/pt-BR/nao-existe # 404
Depois, no navegador: /pt-BR/privacy mostra o controlador e o contato, e o Conectar faz login e
logout com uma conta da allowlist (em produção e no staging).
Deploys da Vercel são imutáveis e não há banco nem migração, então voltar é instantâneo:
main sozinha. Corrigido o
problema, use Undo Rollback (ou promova um deployment novo) para reativar a publicação
automática.Antes de cada go-live, anote qual é o deployment de produção atual (o alvo do rollback).
app/[locale]/ páginas (RSC) em /pt-BR e /en
app/api/ BFF (auth e proxy do Spotify) + /api/health
proxy.ts nonce de CSP, cabeçalhos de segurança e negociação de idioma
src/domain/ lógica pura (histórico, Dataset, stats, demo), testável em Node
src/server/ env validado, cabeçalhos de segurança, sessão JWE, cliente Spotify, logger
src/components/ layout (cabeçalho, rodapé, selo), UI base e a logo (`brand/`)
src/features/ UI por área
src/workers/ Web Workers (processamento do upload e geração dos cards), sem rede
src/config/ dados públicos e fixos (contatos do autor, `author.ts`)
src/i18n/ rotas e mensagens do next-intl
e2e/ testes Playwright + axe
scripts/ fixtures (`pnpm fixtures`) e mock local do Spotify
tests/ setup, stubs, fixtures, mocks do Spotify e benchmark (`tests/perf`)
docs/api.md contrato do BFF (rotas, parâmetros, erros, TTLs, OAuth)
docs/readme/ logo e screenshots deste README (demo, dados fictícios)
Alias de import: @/… aponta para src/….
nosniff, Referrer-Policy: no-referrer,
Permissions-Policy, COOP) e, nas páginas, CSP com nonce por requisição (strict-dynamic,
frame-ancestors 'none').
Confira com curl -I http://127.0.0.1:3000/pt-BR.'wasm-unsafe-eval' só na CSP dos scripts de Web Worker (/_next/static, via next.config.ts),
com connect-src data:: o WASM dos cards roda no worker e nenhum worker faz requisição. As
páginas não liberam WASM; o connect-src delas é 'self' + https://i.scdn.co (capa do card
do Conectar, ADR 9)..github/workflows/ci.yml), com permissions: contents: read e actions
fixadas por SHA: lint + formatação, tipos, testes com cobertura, build, e2e + axe (Chromium e
WebKit; Conectar contra o mock local), pnpm audit --audit-level=high e dependency-review nos
PRs.allowBuilds) e minimumReleaseAge de 1 dia em
pnpm-workspace.yaml.Feito por Enzo Terra. Fale comigo:
Os mesmos contatos ficam no rodapé de todas as páginas do site:

Memoriais digitais para homenagear quem partiu
Plataforma onde famílias criam memoriais com biografia, fotos, linha do tempo e outras informações importantes. Amigos homenageiam comprando coroas de flores digitais, e 90% do valor vai para uma instituição de caridade escolhida pela família. Funerárias exibem os memoriais em TVs e tablets, mostrando quem homenageou sem expor valores. Tudo desenhado com delicadeza, transmitindo esperança e calma em um momento difícil.
Homenagens virtuais para momentos que merecem ser lembrados.
O MemoryGarden (Jardim de Memórias) é uma plataforma de memoriais digitais. A família ou a funerária cria uma página para quem partiu, com foto, biografia, linha do tempo e as informações do velório e do sepultamento. Amigos e parentes prestam homenagem comprando uma coroa de flores digital com uma mensagem, e a maior parte do valor vai automaticamente para uma instituição de caridade escolhida pela família.
Durante a cerimônia, as homenagens aparecem em tempo real numa página de slides exibida em TVs e tablets do velório, mostrando quem homenageou e a mensagem, sem nunca expor valores.
Todo o visual foi pensado com delicadeza: um momento difícil pede calma, esperança e nenhuma fricção.
/nome-do-homenageado). Qualquer pessoa acessa, lê a
história e escolhe enviar uma homenagem./nome-do-homenageado/slides), feitos para ficarem
rodando numa TV durante o velório.| Perfil | O que faz |
|---|---|
| Público | Visita memoriais, envia homenagens e recebe o comprovante da doação |
| Usuário (família) | Cria e edita memoriais, acompanha as homenagens recebidas |
| Funerária (gestor) | Gerencia os homenageados da funerária e a sua equipe |
| Funerária (equipe) | Cadastra e atualiza homenageados no dia a dia |
| Administrador | Instituições parceiras, modelos de coroa, funerárias, usuários, relatórios e configurações gerais |
O fluxo de dinheiro é a parte mais sensível do sistema, e foi desenhado para ser seguro e à prova de falhas:
Pessoa compra a coroa → paga o PIX (QR Code gerado pelo gateway)
→ gateway confirma por webhook
→ sistema confere o status direto na API do gateway (double-check)
→ doação marcada como paga
→ repasse via PIX para a chave da instituição
401.transfer_failed e o erro é registrado para intervenção manual.| Camada | Tecnologia |
|---|---|
| Frontend | Next.js 16 + React 19 + Tailwind CSS 4 |
| Backend | NestJS 11 + TypeORM |
| Banco de dados | PostgreSQL (com migrations versionadas) |
| Autenticação | JWT + Passport, com papéis (RBAC) |
| Pagamentos | PIX via gateway Asaas (cobrança + transferências) |
| Infraestrutura | Docker, Docker Compose e Nginx com TLS |
| CI/CD | Pipeline com testes, análise de segurança, build e scan das imagens |
/
├── frontend/ # Next.js (App Router)
│ └── src/app/
│ ├── (public)/ # landing, memorial, envio de homenagem, slides, login
│ ├── (protected)/ # painel da família e da funerária
│ └── (admin)/ # relatórios e configurações
├── backend/ # API NestJS
│ └── src/
│ ├── honorees/ # homenageados + linha do tempo + mídias
│ ├── wreaths/ # modelos de coroa
│ ├── donations/ # homenagens/doações
│ ├── payments/ # PIX, webhook e repasse
│ ├── institutions/ # instituições de caridade
│ ├── managers/ # funerárias
│ ├── reports/ # relatórios
│ └── settings/ # configurações do sistema
└── nginx.conf # proxy reverso e TLS
class-validator em todos os DTOsPré-requisitos: Node.js 20+, Docker e PostgreSQL (local ou via Docker).
# variáveis de ambiente
cp backend/.env.example backend/.env
cp frontend/.env.example frontend/.env
# dependências (raiz, backend e frontend)
npm run install:all
# banco: migrations e seed
cd backend
npm run migration:run
npm run seed
cd ..
# backend (3001) e frontend (3000) juntos
npm run dev
Acesse http://localhost:3000. Para testar o webhook de pagamento localmente, exponha o backend
com um túnel (ex.: ngrok http 3001) e cadastre a URL /payments/webhook no painel do gateway, em
modo sandbox.
Projeto privado — todos os direitos reservados.
Rede comunitária para reunir pets perdidos com seus donos
Reúne num só lugar o que hoje se perde em dezenas de grupos de WhatsApp. Qualquer pessoa vê, sem cadastro, os animais perdidos e encontrados perto de si. Ao publicar um pet perdido, o sistema procura sozinho os encontrados parecidos na região e avisa o dono. A conversa acontece dentro da plataforma, sem expor telefone, o que protege contra golpes, e cada pet ganha um cartaz com QR Code para imprimir e compartilhar. O endereço de ninguém aparece e a localização dos pets é sempre aproximada.

Plataforma web comunitária para reunir animais perdidos com seus donos, usando a própria vizinhança como rede de busca — pensada para o Mato Grosso do Sul e desenhada mobile-first, para quem cadastra na rua, com uma mão e pressa.
Projeto full-stack completo (backend geoespacial, SPA, infra, CI e testes), construído do zero em sprints. A visão, o modelo de dados e o detalhamento de cada sprint estão em
PLANO_ACHEI_MEU_PET.md.
Quando um animal some, o tempo é tudo — e a informação está fragmentada em dezenas de grupos de WhatsApp. O Achei Meu Pet centraliza isso num feed regional e tenta fechar o ciclo perdido → encontrado com o mínimo de fricção.
A métrica-norte (North Star) do produto é o tempo médio até o primeiro contato qualificado: quanto tempo, depois de um pet ser publicado, até alguém abrir uma conversa sobre ele. O sistema instrumenta essa métrica de ponta a ponta.
O que torna o projeto interessante além da lista de telas:
ST_DWithin sobre coluna geography indexada (GIST) —
comparação em metros, sem trazer a tabela inteira para a aplicação.httpOnly + SameSite + CSRF) em vez de JWT no
localStorage — imune a roubo de token por XSS.axe).| Camada | Tecnologia |
|---|---|
| Backend | Django 5 + DRF + GeoDjango/PostGIS (Python 3.12) |
| Banco | PostgreSQL 16 + PostGIS |
| Frontend | React 19 + Vite + TypeScript, Tailwind v4, shadcn/ui |
| Assíncrono | Celery + Redis (matching, e-mails, expiração) |
| Storage | Cloudflare R2 (S3-compatível) |
| Edge | Caddy (TLS automático, reverse proxy, headers de segurança/CSP) |
| Infra | Docker Compose (single-host) |
| CI / Erros | GitHub Actions · Sentry |
Internet
│
┌──┴──┐
│Caddy│ TLS automático, reverse proxy, CSP
└──┬──┘
┌───────────┼───────────────┐
app (SPA estático) api (Django/gunicorn)
│
┌────────────────┼─────────────────┐
PostgreSQL+PostGIS Redis Celery worker + beat
│
Cloudflare R2 (imagens)
User (com localizacao/endereco privados) · Pet + PetFoto (UUID, coordenada
real indexada vs. rótulo exibível) · Match (auditável) · Conversa + Mensagem
(contato intermediado) · Notificacao · PrimeiroContato (North Star). Detalhes
no plano.
backend/ Django + DRF (API, settings base/dev/prod, Celery, ~95 testes)
frontend/ React 19 + Vite SPA (Tailwind v4, tokens de design, Playwright e2e)
infra/ Caddy (edge), backup/restore do Postgres, testes de carga k6
.github/ GitHub Actions (CI + E2E manual)
Docker + Docker Compose, ou Python 3.12 + Node 24 para rodar fora de container.
Sobe a stack completa em modo de desenvolvimento (HTTP puro, hot-reload, sem
TLS) já populada com dados de exemplo — nenhum .env necessário:
docker compose -f docker-compose.local.yml up --build
Na subida, o comando manage.py seed_demo cria usuários, posts de perdidos e
encontrados (com fotos geradas), matches, notificações e uma conversa
intermediada. Login sugerido para explorar tudo:
| senha | |
|---|---|
ana@demo.acheimeu.pet | demoAcheiPet1 |
O seed é idempotente: rode
docker compose -f docker-compose.local.yml exec api python manage.py seed_demoquando quiser recriar os dados. E-mails (ex.: verificação, match) saem no console do containerapi.
cp backend/.env.example backend/.env # ajuste SECRET_KEY etc.
cp .env.example .env
docker compose --env-file backend/.env up --build
cd backend
python -m venv .venv && . .venv/Scripts/activate # Windows
pip install -r requirements-dev.txt
cp .env.example .env
python manage.py migrate
python manage.py runserver # http://localhost:8000/api/health
GeoDjango exige as libs GDAL/GEOS/PROJ instaladas no sistema.
cd frontend
npm install
npm run dev # http://localhost:5173 (proxy /api -> :8000)
# Backend
cd backend && pytest && ruff check . && black --check .
# Frontend (unit + componentes + acessibilidade)
cd frontend && npm test && npm run lint && npm run build
frontend/e2e/README.md.infra/load/README.md..github/workflows/ci.yml roda em cada push/PR (lint, testes, build). Os testes
E2E rodam sob demanda em .github/workflows/e2e.yml. Nenhum merge sem CI verde.
GET /api/health (banco + cache/Redis).GET /api/metrics/north-star
(staff) + log estruturado. Logs em JSON via LOG_FORMAT=json.GET /api/auth/me/exportar. Exclusão: DELETE /api/auth/me./privacidade, linkada do cadastro.Camada de IA, sempre como assistente e nunca como autoridade em casos sensíveis: moderação de imagens/texto com revisão humana, similaridade visual por embeddings, coleira QR preventiva, parceiros verificados (clínicas/ONGs) e mapa interativo com pontos ofuscados por jitter. Detalhes na §10 do plano.
Scripts em infra/backup/; o procedimento de restauração —
que deve ser testado, não só configurado — está em
RESTORE_RUNBOOK.md. Nunca commitar .env;
veja *.env.example para a lista de variáveis.
Plataforma cívica para mapear e gerir o conserto de buracos
O cidadão relata um buraco em menos de 15 segundos, só marcando o local no mapa, no estilo Waze. Relatos do mesmo buraco são agrupados automaticamente, a prefeitura prioriza e organiza os consertos em lote, e cada etapa fica registrada até a confirmação do conserto. Um painel público mostra à população o que foi relatado e o que já foi resolvido.

Plataforma colaborativa para mapear e gerir o conserto de buracos em vias públicas — do relato anônimo do cidadão à confirmação do conserto pela prefeitura, com transparência pública. Caso de uso: Campo Grande/MS.
Stack: Django 5 · DRF · GeoDjango/PostGIS · React 18 · Vite · MapLibre · Docker Qualidade: 141 testes automatizados (96 backend / 45 frontend) · revisão de segurança §9/OWASP · CI-ready Status: MVP completo (Sprints 0–7) · pronto para deploy
Identidade visual “asfalto” — grafite escuro + âmbar de marca (Tailwind v4 + shadcn/ui). O app do cidadão é mobile-first: mapa em tela cheia, pins por forma + cor segundo o status, e relato de baixo atrito — posicionar o pino e confirmar.
Cidadão — mapa com clusters por status · arraste o pino até o buraco e confirme.
Painel da prefeitura (desktop), com a visão certa por papel:
Gestão — mapa, filtros e lista priorizada por urgência. |
Conserto com staging — polígono, rua ou clusters → revisão. |
Login da prefeitura — acesso restrito por papel (JWT).
Capturas da stack de demonstração (compose.local.yml) com dados de
exemplo em Campo Grande/MS.
Buracos em vias públicas são reportados de forma dispersa (redes sociais, telefone, ouvidoria), sem geolocalização agregada. A prefeitura não tem um canal único para visualizar, priorizar e acompanhar consertos. O Buraco Zero centraliza esse ciclo de vida em uma plataforma só, com três públicos:
| Público | O que faz |
|---|---|
| 👤 Cidadão (anônimo) | Registra um buraco em < 15s, sem cadastro (UX referência: alerta do Waze). Foto opcional. |
| 📊 Analista (prefeitura) | Prioriza por urgência, filtra, avança status, exporta dados. |
| 🛠️ Funcionário de campo | Marca consertos (polígono/rua/clusters), revisa e confirma em lote. |
| 🌐 População | Painel público de transparência (consertos, tempo médio, por bairro). |
Pré-requisito: Docker + Docker Compose. Na raiz do repositório:
docker compose -f compose.local.yml up --build
Sobe a stack inteira (PostGIS + Django + PWA atrás do nginx), migra o banco e popula dados de demonstração automaticamente. Depois abra:
| Rota | O que ver |
|---|---|
/ | App do cidadão: mapa com clusters coloridos por status, botão “Relatar buraco”. |
/painel | Transparência: métricas e recorte por bairro. |
/admin | Painel da prefeitura (login abaixo). |
Credenciais de demonstração (papéis distintos mudam a experiência):
| Papel | Login | Senha | Vê |
|---|---|---|---|
| Admin | admin@cg.ms.gov.br | demo12345 | gestão + conserto + arquivar |
| Analista | analista@cg.ms.gov.br | demo12345 | mapa, lista, filtros, status, exportar |
| Campo | campo@cg.ms.gov.br | demo12345 | conserto com staging (3 métodos) |
Encerrar: docker compose -f compose.local.yml down -v.
Diferente do
backend/docker-compose.yml(só backend, para desenvolvimento), ocompose.local.ymlentrega o sistema completo em uma URL, como em produção, mas sem TLS e com dados de exemplo.
O que este projeto demonstra além de CRUD:
distance_lte/intersects
sobre campos geography, índice GIST, e testes rodando contra PostGIS real
(não SQLite — queries espaciais não têm equivalente e dariam falso verde).StatusHistory é append-only; MaintenanceZone nasce como rascunho.prefers-reduced-motion.Um bug real encontrado e corrigido durante o desenvolvimento: o
?format=do endpoint de exportação colidia com a negociação de conteúdo do DRF, devolvendo 404 antes da view rodar — resolvido comURL_FORMAT_OVERRIDE=None. Documentado no código.
┌─────────────────────────── nginx ───────────────────────────┐
Cidadão ─────────│ / → PWA (React/Vite, estático) │
Prefeitura ──────│ /api, /admin → proxy → gunicorn + Django/DRF │
│ /static,/media → volumes │
└───────────────────────────────┬──────────────────────────────┘
│
┌─────────────▼─────────────┐
│ Django + GeoDjango │
│ (dedup, staging, auth, │
│ stats, jobs de cron) │
└─────────────┬─────────────┘
│
┌─────────────▼─────────────┐
│ PostgreSQL 16 + PostGIS │
└───────────────────────────┘
Decisões registradas em DOCUMENTACAO_SISTEMA.md (a fonte
de verdade do produto): por que cluster é a entidade central, dedup em duas
camadas, staging no servidor, expiração como flag visual, e mais.
| Camada | Tecnologias |
|---|---|
| Backend | Django 5, DRF, GeoDjango, simplejwt, django-ratelimit, Pillow |
| Banco | PostgreSQL 16 + PostGIS 3.4 |
| Frontend | React 18, Vite, TypeScript, Tailwind v4, shadcn/ui, MapLibre GL, Supercluster, TanStack Query |
| Infra | Docker Compose, nginx, gunicorn, Let’s Encrypt, Sentry (opcional) |
| Testes | pytest + pytest-django (PostGIS real), Vitest + Testing Library, Locust |
tapa-buraco/
├── backend/ Django + DRF + GeoDjango (API, painel, staging, jobs)
├── frontend/ PWA React (cidadão, analista, campo, painel público)
├── deploy/ produção: nginx+TLS, gunicorn, backups, Let's Encrypt
├── loadtest/ teste de carga (Locust) da superfície pública
├── compose.local.yml demo da stack completa (este README)
├── DEPLOY.md guia de deploy e operação
├── SECURITY_REVIEW.md revisão §9 + OWASP Top 10
└── DOCUMENTACAO_SISTEMA.md especificação do produto (fonte de verdade)
# Backend (roda via Docker por causa do GeoDjango/GDAL)
cd backend && cp .env.example .env && docker compose up -d --build
docker compose run --rm web pytest # 96 testes (PostGIS real)
# Frontend
cd frontend && npm install && npm run dev # http://localhost:5173
npm test # 45 testes
Detalhes em backend/README.md e frontend/README.md.
Deploy num VPS único via Docker Compose (nginx + TLS + gunicorn + PostGIS +
backups + Sentry) — passo a passo em DEPLOY.md. Segurança em
SECURITY_REVIEW.md. Teste de carga em loadtest/.
| Sprint | Entrega | Status |
|---|---|---|
| 0–1 | Fundação + API pública de relatos (dedup geoespacial, EXIF strip) | ✅ |
| 2 | Frontend do cidadão (mapa + relato) PWA | ✅ |
| 3 | Auth JWT + API admin (filtros, paginação, transições) | ✅ |
| 4 | Painel do analista (mapa + lista + filtros + detalhe + status) | ✅ |
| 5 | Conserto com staging (polígono/rua/clusters → revisão → commit) | ✅ |
| 6 | Exportação, painel público e expiração (jobs via cron) | ✅ |
| 7 | Endurecimento e deploy (TLS, backups, Sentry, carga, docs) | ✅ |
| 5.5 | Importação CSV de consertos (opcional; depende de OSM + pg_trgm) | ⏳ |
Projeto desenvolvido como estudo de uma plataforma cívica real, priorizando domínio geoespacial, integridade transacional, segurança e testes. Construído por sprints, cada uma entregando algo verificável e testado.
SaaS white-label para agências de marketing digital
Sistema desenvolvido sob contrato: painel administrativo para a agência e portal exclusivo para clientes. Sincronização automática com Google Agenda, relatórios mensais de performance com ROI calculado, cobranças com QR Code PIX, biblioteca de mídia via Google Drive e permissões granulares. Em produção, em uso real por uma agência.
Plataforma SaaS white-label para agências de marketing digital gerenciarem clientes, entregas, finanças e resultados, em um único lugar.
O Portal de Clientes é um sistema web desenvolvido em Laravel 11 para centralizar a gestão de clientes de uma agência de marketing digital. Ele oferece dois ambientes distintos: um painel administrativo para a equipe da agência e um portal exclusivo para os clientes.
A plataforma conecta-se à conta Google da agência para sincronizar automaticamente eventos de reuniões e entregas, permite o envio de relatórios mensais de performance com dados reais de campanhas e redes sociais, e oferece uma área financeira completa com geração de cobranças, QR Code PIX e controle de inadimplência.
super_admin, admin e clientrascunho → publicado → arquivadoInvoice) e cobranças individuais (Payment)pending, under_review, paid, overduevideo, image, document, link
Dashboard do Cliente |
Calendário de Eventos |
Gestão de Pagamentos |
Configurações (Painel Admin) |
O projeto segue uma arquitetura organizada em domains, separando as responsabilidades por contexto de negócio:
app/
├── Console/
│ └── Commands/
│ └── SyncCalendarCommand.php # Comando artisan: calendar:sync
├── Domain/
│ ├── Calendar/ # Integração com Google Agenda
│ ├── Client/ # Gestão de clientes
│ ├── Financial/ # Pagamentos e faturas
│ ├── Media/ # Links de entregáveis
│ ├── Report/ # Relatórios de performance
│ └── Setting/ # Configurações do sistema
├── Http/
│ ├── Controllers/
│ │ ├── Admin/ # Controllers do painel da agência
│ │ └── Client/ # Controllers do portal do cliente
│ └── Middleware/
└── Models/ # User, GoogleOAuthToken, AuditLog
| Camada | Tecnologia |
|---|---|
| Backend | Laravel 11, PHP 8.2 |
| Banco de dados | PostgreSQL |
| Frontend | Blade + Tailwind CSS |
| Permissões | Spatie Laravel Permission |
| Google APIs | google/apiclient (Calendar + Drive) |
| barryvdh/laravel-dompdf | |
| QR Code PIX | chillerlan/php-qrcode |
| Ícones (UI) | Heroicons, Solar Icons, Remix Icons |
| Logs de atividade | Spatie Laravel Activitylog |
| Deploy | Hostinger (shared hosting) |
# Clone o repositório
git clone https://github.com/enzoterra/portal-agencia.git
cd portal-agencia
# Instale as dependências PHP
composer install
# Instale as dependências JS
npm install
# Configure o ambiente
cp .env.example .env
php artisan key:generate
# Rode as migrations
php artisan migrate --seed
# Inicie o servidor de desenvolvimento
composer run dev
Configure as seguintes entradas no .env:
# Banco de dados
DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=portal_agencia
DB_USERNAME=seu_usuario
DB_PASSWORD=sua_senha
# Google OAuth
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
GOOGLE_REDIRECT_URI=
GOOGLE_CALENDAR_ID=
O sistema depende de um Cron Job configurado no servidor para funcionar corretamente. Adicione a seguinte linha ao cron do servidor:
* * * * * /usr/bin/php /caminho/para/artisan schedule:run >> /dev/null 2>&1
O Laravel gerencia internamente os intervalos de cada tarefa:
| Tarefa | Frequência | Descrição |
|---|---|---|
calendar:sync | A cada 5 minutos | Sincroniza eventos do Google Agenda |
| Marcar vencidos | Diário às 00:05 | Atualiza status de pagamentos vencidos |
Este projeto foi desenvolvido para uso interno e fins de portfólio.
Infraestrutura própria para atendentes de IA
A VPS onde rodam meus serviços de atendentes de IA para clientes: provisionamento, Docker, proxy reverso, TLS, monitoramento e deploys. É a base que mantém os projetos e serviços no ar, onde gosto de cuidar do ciclo completo, do código à operação e a facilidade para o cliente.
04 · habilidades
A stack muda conforme o problema; essas são as ferramentas em que costumo trabalhar.
Onde tenho mais domínio: interfaces modernas, responsivas e acessíveis, unindo código e design. Cuido de UI/UX e identidade visual, não só da implementação.
Para onde venho direcionando minha carreira: sistemas com camadas de inteligência que resolvem de verdade.
APIs e arquiteturas escaláveis, com segurança e testes desde o início.
Do código à operação: containers, CI/CD e bancos bem modelados.
Quando o problema pede outra ferramenta.
05 · serviços
Além dos projetos próprios, presto serviços para empresas e pessoas que precisam tirar uma ideia do papel.
Criação de sites do zero e atualização de design de sites existentes: rápidos, bonitos e com SEO de verdade.
Sistemas completos para o seu negócio, resolvendo problemas e otimizando fluxos, aumentando a produtividade da sua equipe.
Atendentes de IA, integrações entre sistemas e automação de processos que economizam horas do seu time.
06 · contato
Estou sempre aberto a boas conversas, seja sobre uma vaga, um projeto ou uma ideia que ainda nem tem nome.
enzoterra18@gmail.com
Assistente do Enzo
pergunte sobre projetos, stack e experiência
Modo demonstração: respondo com uma base de conhecimento local.