Covil do Mineiro — Docs

Arquitetura

Visão geral

O Covil do Mineiro é um monólito modular sobre Next.js 15 (App Router) e Supabase, publicado na Vercel. Não existe um backend separado: o próprio Next.js serve o site, executa a lógica de servidor (Server Actions e o webhook) e conversa com o Supabase, que é o backend gerenciado (Postgres + Auth + Storage), com a segurança ancorada em RLS.

Essa escolha é deliberada para um time pequeno — ver ADR-0001. O que parece "backend e frontend juntos" é, na prática, um sistema em camadas bem separadas.

Diagrama de camadas

flowchart TD
    subgraph cliente["Navegador"]
        RSC["Server Components<br/>(app/**/page.tsx)"]
        CC["Client Components<br/>('use client')"]
    end

    subgraph servidor["Servidor Vercel (Next.js)"]
        SA["Server Actions<br/>(lib/actions/*)"]
        LIB["Regras de negócio<br/>(lib/*)"]
        WH["Webhook Sicoob<br/>(api/sicoob/webhook/pix)"]
        ADMIN["supabase/admin<br/>(service role · server-only)"]
    end

    subgraph db["Supabase"]
        DB[("Postgres + RLS")]
        AUTH["Auth"]
        STG["Storage · bucket site-media"]
    end

    MP["Sicoob — API Pix (BACEN)"]

    CC -->|mutações| SA
    RSC --> LIB
    SA --> LIB
    LIB -->|"client servidor/anon<br/>(respeita RLS)"| DB
    SA -.->|escrita de pedido| ADMIN
    SA -->|"cria cobrança PIX"| MP
    MP -->|"webhook (reconsulta por txid)"| WH
    WH --> ADMIN
    ADMIN -->|"ignora RLS"| DB
    LIB --> AUTH
    LIB --> STG

As camadas

1. Apresentação (src/app, src/components)

  • Server Components (a maioria das page.tsx) leem dados no servidor e renderizam HTML. Buscam via src/lib / content-store.
  • Client Components ('use client') cuidam só de interação: estado de UI, formulários, carrinho. Não acessam o banco diretamente.
  • Componentes de UI reutilizáveis em src/components/ui; telas admin em src/components/admin (padrão AdminPageFrame/AdminModal/AdminField).

2. Casos de uso — Server Actions (src/lib/actions)

Toda mutação passa por aqui: auth-actions, checkout-actions, admin-actions, user-actions, loyalty-actions, coupon-actions, plan-actions, partner-actions, member-photo-actions. Cada action:

  1. valida autenticação/autorização (guarda de papel e seção);
  2. valida e recalcula dados no servidor (nunca confia no cliente);
  3. chama a regra de negócio / o banco;
  4. revalida os caminhos públicos afetados (revalidatePath/revalidateTag).

3. Regras de negócio (src/lib)

Lógica reutilizável, idealmente pura e testável:

  • coupons.tsevaluateCoupon (puro, testado).
  • loyalty.ts (server-only) + loyalty-format.ts (client-safe).
  • membership-card.ts — número, QR e HMAC de autenticidade da carteirinha.
  • content-store.ts — snapshot do conteúdo público com cache e fallback.
  • sicoob/* — criação de cobrança PIX (createPixCharge) e reconsulta autoritativa por txid (getPixCharge).
  • supabase/* — os clients (anon, browser, server, admin, middleware).

4. Dados e segurança — Supabase

Postgres é a fonte da verdade. Toda tabela tem RLS; a lógica sensível (pontos, associação, cupons, carteirinha) vive em funções SECURITY DEFINER. Ver banco-de-dados.md.

Fronteira server/client

Server ComponentClient Component ('use client')
Ler dados✅ direto via lib/content-store❌ recebe por props ou Server Action
Mutar dadosvia Server Actionvia Server Action (nunca banco direto)
Segredos✅ (server-only)❌ nunca

Módulos com segredo têm import 'server-only' (ex.: supabase/admin.ts, loyalty.ts, membership-card.ts) — o next build quebra se forem importados em bundle client. Nos testes, server-only é neutralizado em src/test/setup.ts.

Autenticação e sessão

  • Supabase Auth (e-mail/senha + Google OAuth). O callback OAuth troca o código por sessão em /auth/callback.
  • O middleware.ts protege /admin/* e atualiza a sessão (supabase/middleware.ts). Cada página admin revalida o papel com requireSectionAccess, e cada Server Action revalida de novo — defesa em profundidade, com a RLS como último anteparo.
  • O cadastro grava o perfil via trigger handle_new_user() (evita UPDATE bloqueado por RLS no signup).

Integrações externas

  • Sicoob (PIX) — cobrança via API Pix Recebimentos (padrão BACEN): o servidor cria a cobrança e o QR Code + "copia e cola" aparecem na própria página (/pedido/retorno, com polling), sem redirect. A confirmação chega pelo webhook em POST /api/sicoob/webhook/pix, que reconsulta a cobrança por txid na API. Ver o fluxo em seguranca.md e banco-de-dados.md.
  • Gmail SMTP (Nodemailer) — e-mails transacionais (recuperação de senha).
  • Google Analytics 4 — opcional, ativado por NEXT_PUBLIC_GA_ID (src/lib/analytics.ts; no-op sem a variável).

Cache e revalidação

  • O conteúdo público é lido por content-store.ts com unstable_cache (tag site-snapshot, ~60s) e fallback local (src/data/site-fallback.ts) quando o Supabase não está configurado.
  • As Server Actions admin chamam revalidateTag('site-snapshot') e revalidatePath(...) para refletir alterações imediatamente.

Estrutura de pastas

src/
  app/            rotas: (public), (auth), admin/*, api/*, auth/callback
  components/     ui/ (design system), admin/ (managers), layout/, seções
  data/           fallback local do conteúdo público
  lib/            actions/ (server actions), supabase/, sicoob/, regras
  types/          content.ts (domínio) e supabase.ts (schema gerado)
  test/           setup do Vitest
supabase/
  migrations/     schema evolutivo (SQL, com RLS e funções)
  seed.sql        dados iniciais
docs/             esta documentação

Decisões de arquitetura

Registradas como ADRs em decisoes/. Comece pela ADR-0001 — Monólito Next.js + Supabase.

On this page