Aplicação Next.js 16 (App Router) em TypeScript, organizada em Ports & Adapters: o domínio (entidades, regras e interfaces de repositório) não importa Next, Prisma nem Firebase; os serviços recebem o repositório por injeção e um composition root escolhe o adapter pela variável de ambiente (Postgres via Prisma em produção, em memória para testes, CI e desenvolvimento sem banco). Os dados ficam no Postgres do Supabase, acessado só pelo servidor; o Firebase cuida apenas da identidade dos administradores. As páginas públicas são Server Components com ISR de 1 hora e revalidação sob demanda a cada publicação. Há ainda um canal de publicação por agente de IA, via um servidor MCP local que só conversa com a API HTTP do site.
- Front-end
- Next.js 16.4 (App Router, Server Components)React 19.3TypeScript 6Tailwind CSS 4shadcn/ui sobre Radix UIEmbla Carousel
- Back-end
- Route Handlers do NextPrisma 7.10 com driver adapter @prisma/adapter-pgFirebase Auth com verificação de ID token via jose (JWKS)
- Banco
- PostgreSQL no Supabase (pooler em modo transação)migrations versionadas com RLS travando as APIs públicas do Supabase
- IA
- servidor MCP próprio (@modelcontextprotocol/sdk) rodando em Dockerque lê feeds RSS/Atom e publica pela API autenticada do site
- Infra
- Vercel (ISR, revalidatePath, Vercel Analytics atrás de consentimento)PWA com manifest e service workerNode 24 e pnpm
- Qualidade
- ESLint 9Vitest 4 + Testing LibraryPlaywright (E2E)GitHub Actions com ações fixadas por SHADependabot e varredura de segredos com gitleaks
Arquitetura
Domínio desacoplado do banco
Cada feature tem entidade, port (interface do repositório) e dois adapters: Prisma e in-memory. Um único composition root decide qual usar por DATA_DRIVER, então os testes de serviço rodam sem I/O e o CI faz build e E2E sem nenhum segredo.
Segurança
Supabase fechado por RLS, acesso só via Prisma
Uma migration habilita RLS sem policies e revoga os grants padrão de anon e authenticated, inclusive para tabelas futuras. Assim o PostgREST exposto pelo Supabase não lê nem grava nada, enquanto o app continua acessando como dono das tabelas.
Segurança
Login sem chave privada no servidor
O ID token do Firebase é validado com jose contra o JWKS público do Google (com cache e respeito à rotação de chaves), sem firebase-admin nem service account. O acesso ao painel exige também um perfil em admin_profiles com nível 1 a 3; o middleware só faz um redirecionamento barato e a checagem real acontece no layout e em cada rota de API.
IA
Publicação por agente de IA com regras de domínio
Um servidor MCP local expõe ferramentas como ler página, listar fontes e publicar notícia, e só fala com /api/agent/* por Bearer token comparado em tempo constante. O serviço do agente exige fonte com URL http(s), recusa URL de origem repetida, limita o número de categorias e monta o HTML a partir de texto puro escapado, nunca de HTML enviado pela IA.
Performance
ISR com invalidação na publicação
Home, categorias, notícias, feed e sitemaps revalidam de hora em hora; ao publicar ou editar, revalidatePath invalida o layout raiz e os route handlers de feed, sitemap e llms.txt. A conexão com o Postgres usa o pooler em modo transação com pool pequeno por instância serverless, evitando esgotar conexões.
SEO
SEO para portal de notícias
JSON-LD NewsArticle e NewsMediaOrganization, sitemap no formato Google News (janela de 48 h), RSS, llms.txt e robots.txt gerado que bloqueia previews e libera crawlers de IA. Cada matéria tem imagem Open Graph própria gerada com next/og numa URL estável, e fotos de terceiros só aparecem dentro da matéria, com crédito.