---
id: MOD-INSTITUCIONAL-FALECONOSCO-ROTAS
tipo: modulo
onda: 4
status: concluido
unidades: [MOD-INSTITUCIONAL-FALECONOSCO]
fontes:
  - V2/Modules/Institucional/FaleConosco/
depende_de: [MOD-INSTITUCIONAL-FALECONOSCO, CORE-BOOT, CORE-AUTH, CORE-CMS, CORE-RUNTIME]
consumidores: [UI-CMS, UI-PORTAL]
criticidade: alta
dono: InstitucionalA
---

# FaleConosco — rotas e contratos HTTP

> Anexo de [`spec.md`](spec.md). Seis declarações efetivas: quatro endpoints CMS
> autenticados e dois públicos. A pilha de middleware é a do provider global; o arquivo CMS
> não declara `web` (diferente de História) e o arquivo público declara `web` de novo. As
> rotas públicas são excluídas dos dois middlewares de cache.

## 0. Ficha de identidade

| Campo | Valor |
|---|---|
| Efeito primário | Expõe o formulário público e as quatro ações de triagem/resposta da inbox, com CSRF e RBAC por módulo. |
| Entrada | Método + URI; `{id}` UUID; query `search`, `unread`, `page`; corpo do formulário e `reply_body`. |
| Saída | HTML, `302` com flash, `404`, `419`, `500`; JSON não é usado. |
| Efeitos colaterais | INSERT/UPDATE em `v2_mensagens_contato`; e-mails; `v2_mail_logs`; `activity_log`. |
| Particularidade central | O grupo CMS não declara `web`; o grupo público declara; ambos são agrupados pelo provider, gerando pilhas ligeiramente diferentes das de História. |
| Como se integra | Nomes `v2.fale-conosco.*`; namespace de controller derivado do diretório; exclusão de cache por path. |
| Se removido, quebra | Formulário e inbox deixam de existir; o `BasicPage` semeado passa a responder em `/fale-conosco` (form inerte). |
| Superfície pública | Contrato HTTP abaixo; sem API. |

## 1. Efeito

### 1.1 O que o usuário consegue fazer

- Público: abrir e enviar o formulário.
- CMS: listar, abrir, responder e arquivar.

### 1.2 O que muda no sistema

| Rota | Efeito |
|---|---|
| `portal.index` | nenhuma escrita (exceto rastreamento de SEO) |
| `portal.store` | INSERT + até 2 e-mails + logs |
| `cms.inbox` | leitura (+ 500 se `unread`) |
| `cms.show` | UPDATE `is_read` + activity log |
| `cms.reply` | UPDATE `reply_body`/`replied_at` + e-mail + logs + activity |
| `cms.archive` | UPDATE `is_archived` + activity |

### 1.3 Contrato observável (entrada → saída)

| Rota | Sucesso | Falha |
|---|---|---|
| `GET /fale-conosco` | `200` HTML | `503` manutenção |
| `POST /fale-conosco` | `302` + flash | `302` back com erros; `419`; `302` back captcha |
| `GET /cms/fale-conosco` | `200` HTML | `302` login/dashboard; `500` com `unread` |
| `GET /cms/fale-conosco/{id}` | `200` HTML | `404`; `302` login |
| `POST .../{id}/reply` | `302` back | `404`; `302` back validação; `403/302` sem `write`; `419` |
| `POST .../{id}/archive` | `302` inbox | `404`; `403/302`; `419` |

### 1.4 O que estas rotas NÃO fazem

- Não têm throttle nem rate limit.
- Não têm API JSON, versão, token ou assinatura.
- Não têm rota de desarquivar, excluir ou marcar como não lida.
- Não têm `FormRequest`; validação inline.
- Não têm `where()` de UUID no parâmetro.
- Não respondem JSON em nenhum caso.

## 2. Abordagem

### 2.1 Estratégia em uma página

```mermaid
flowchart LR
  P[V2ServiceProvider] -->|web + module.active:FaleConosco + namespace| C[cms/fale-conosco group]
  C -->|CmsAuthenticate| E4[4 endpoints]
  P -->|web + Maintenance + page.cache + public.cache| W[web.php group]
  W -->|web| E2[GET/POST /fale-conosco]
  PC[page.cache] -.->|exceto fale-conosco*| W
  PB[public.cache] -.->|exceto /fale-conosco| W
```

### 2.2 Mecânica passo a passo

1. `mapModuleRoutes` monta o namespace `V2\Modules\Institucional\FaleConosco\Controllers`
   (`V2ServiceProvider.php:293-295`).
2. O grupo CMS é embrulhado com `web` + `module.active:FaleConosco`
   (`V2ServiceProvider.php:298-306`); o arquivo adiciona `CmsAuthenticate` e prefixo
   `cms/fale-conosco` (`Routes/cms.php:3-6`).
3. `CmsAuthenticate` exige Sentinel + papel; delega ao RBAC por `active_pattern`
   `cms/fale-conosco*` (`CmsAuthenticate.php:19-45`; `V2PermissionMiddleware.php:40-101`).
4. O grupo público é embrulhado com `web` + manutenção + `page.cache` + `public.cache`;
   o arquivo adiciona `web` novamente e declara GET/POST (`Routes/web.php:3-16`).
5. `page.cache` verifica `$request->is('fale-conosco*')` e não cacheia; `public.cache`
   compara o prefixo `/fale-conosco` e não adiciona headers.
6. As ações executam e redirecionam; não há resposta JSON.

### 2.3 Decisões de design e por quê

- **Prefixos literais e verbos explícitos**: sem `Route::resource`, pois não há create/edit/
  destroy de recurso `[C]` (`Routes/cms.php:8-30`).
- **`{id}` depois da raiz**: a inbox é `/`, o detalhe é `/{id}`; não há rota literal
  conflitante `[C]`.
- **Sem `web` no arquivo CMS**: o provider já aplica; funciona, mas é assimétrico em relação
  a História `[C]`.
- **Exclusão de cache por path**: a página pública não pode ser cacheada por causa do CSRF
  e dos dados do SLE `[C]`.

### 2.4 Trechos de código pivô

```php
// V2/Modules/Institucional/FaleConosco/Routes/cms.php:3-30 — grupo e endpoints
Route::group([
    'middleware' => [\V2\Core\Middleware\CmsAuthenticate::class],
    'prefix'     => 'cms/fale-conosco',
], function () {
    Route::get('/', ['as' => 'v2.fale-conosco.cms.inbox', 'uses' => 'FaleConoscoCmsController@inbox']);
    // Configurações movidas para o Editor Estrutural > Dados Estruturais > Contato
    Route::get('/{id}', ['as' => 'v2.fale-conosco.cms.show', 'uses' => 'FaleConoscoCmsController@show']);
    Route::post('/{id}/reply', ['as' => 'v2.fale-conosco.cms.reply', 'uses' => 'FaleConoscoCmsController@reply']);
    Route::post('/{id}/archive', ['as' => 'v2.fale-conosco.cms.archive', 'uses' => 'FaleConoscoCmsController@archive']);
});
```

O comentário documenta a mudança das configurações para o SLE; o controller continua lendo
`contato_*` de `v2_portal_configs`.

```php
// V2/Modules/Institucional/FaleConosco/Routes/web.php:3-16 — público
Route::group([
    'middleware' => ['web'],
], function () {
    Route::get('/fale-conosco', [
        'as'   => 'v2.fale-conosco.portal.index',
        'uses' => 'FaleConoscoPortalController@index',
    ]);
    Route::post('/fale-conosco', [
        'as'   => 'v2.fale-conosco.portal.store',
        'uses' => 'FaleConoscoPortalController@store',
    ]);
});
```

O provider aplica `web` + manutenção + caches; o arquivo adiciona `web` (duplicado).

```php
// V2/Core/Middleware/PageCacheMiddleware.php:16-23 — exclusão do cache de página
protected $except = [
    'cms*',
    'api*',
    'trabalhe-conosco*',
    'associe-se*',
    'fale-conosco*',
    '_debugbar*',
];
```

Como o padrão é aplicado sobre `$request->is(...)`, tanto `/fale-conosco` quanto
`/fale-conosco/...` ficam fora.

```php
// V2/Core/Middleware/PublicCacheMiddleware.php:31-36 — exclusão de headers públicos
protected $excludedPrefixes = [
    '/cms',
    '/trabalhe-conosco',
    '/associe-se',
    '/fale-conosco',
];
```

Comparação por `strpos($path, $prefix) === 0`; `/fale-conosco` e subpaths ficam sem
`Cache-Control: public`.

```php
// V2/Modules/Institucional/FaleConosco/Controllers/FaleConoscoCmsController.php:58 — o filtro que quebra a rota
if ($request->has('unread') && $request->boolean('unread')) {
    $query->unread();
}
```

`Request::boolean()` chega a `Macroable::__call` no Laravel 5.5 e lança
`BadMethodCallException`; a rota responde `500` antes de renderizar.

### 2.5 Algoritmos, consultas e custos

- CMS: 1–2 queries por ação; SMTP síncrono no reply.
- Público: 1 INSERT + até 2 SMTP + até 2 logs; cURL opcional.
- Sem cache em nenhuma das rotas.
- Sem throttle: custo ilimitado por cliente.

## 3. Particularidades

### 3.1 O que só existe aqui

- **Assimetria de middleware** entre os arquivos CMS e web.
- **Configuração “movida” por comentário**, sem alteração de código.
- **Rota pública excluída de cache por path** (diferente de `/historia`).
- **500 acionável por um checkbox de UI**.

### 3.2 Invariantes e regras não óbvias

| ID | Regra | Efeito | Fonte |
|---|---|---|---|
| `RN-FALECONOSCO-ROTAS-01` | Nomes `v2.fale-conosco.{cms,portal}.*`; 6 endpoints. | Views/dashboard dependem dos nomes. | `Routes/cms.php:8-29`; `Routes/web.php:7-15` |
| `RN-FALECONOSCO-ROTAS-02` | CMS exige `CmsAuthenticate` + RBAC `FaleConosco`; portal é público. | Dois regimes de acesso no mesmo módulo. | `Routes/cms.php:3-6`; `V2PermissionMiddleware.php:40-101` |
| `RN-FALECONOSCO-ROTAS-03` | `module.active:FaleConosco` só no CMS. | Portal responde mesmo com a flag desligada. | `V2ServiceProvider.php:215-219,299-306` |
| `RN-FALECONOSCO-ROTAS-04` | Nenhuma rota tem throttle. | Spam e abuso de SMTP possíveis. | `Routes/*.php` (sem `throttle`) |
| `RN-FALECONOSCO-ROTAS-05` | `{id}` sem validação de formato; `findOrFail` → 404. | Strings arbitrárias geram consulta. | `FaleConoscoCmsController.php:76,97,140` |
| `RN-FALECONOSCO-ROTAS-06` | `GET /cms/fale-conosco?unread=…` → 500. | Filtro de não lidas inutilizável. | `:58` |
| `RN-FALECONOSCO-ROTAS-07` | GET na raiz do CMS não tem `{id}` opcional; `/{id}` captura qualquer segmento. | `/cms/fale-conosco/qualquer` tenta abrir mensagem. | `Routes/cms.php:8-20` |
| `RN-FALECONOSCO-ROTAS-08` | Reply/archive usam POST; não há DELETE/PUT. | Semântica de arquivamento é POST idempotente (set true). | `:22-30` |
| `RN-FALECONOSCO-ROTAS-09` | As rotas públicas são excluídas de `page.cache`/`public.cache`. | Sem `X-Cache-Status` e sem headers públicos. | `PageCacheMiddleware.php:16-23`; `PublicCacheMiddleware.php:31-36` |

### 3.3 Casos-limite e comportamentos surpreendentes

- **`/cms/fale-conosco/`** (com barra) casa `/` e abre a inbox.
- **`/cms/fale-conosco/reply`** (GET) casaria `/{id}` e devolveria 404 (não há mensagem com
  id `reply`).
- **`POST /cms/fale-conosco/`** (raiz) não existe: 405.
- **CSRF em POST público**: um formulário externo sem token recebe 419; o form da página
  sempre envia `_token`.
- **Módulo desativado**: CMS → `ModuleAccessMiddleware` decide redirect pelo prefixo; como o
  prefixo é `cms/fale-conosco`, cai em `abort(404, 'Modulo desativado.')`
  (`ModuleAccessMiddleware.php:52-61`).
- **Permissão `read` em POST**: `403`/redirect com mensagem de somente leitura.
- **AJAX**: as ações CMS não devolvem JSON; um `wantsJson` recebe redirect/HTML (exceto os
  erros 401/403 do middleware, que são texto puro).
- **Manutenção**: só afeta GET/POST públicos.
- **Cache**: uma extensão/proxy/CDN externo ainda pode cachear; a aplicação não envia
  diretivas públicas.

### 3.4 Armadilhas de reimplementação

- **Adicionar `web` ao grupo CMS “por simetria”**: hoje o provider já o aplica; duplicaria a
  pilha (como em História).
- **Confiar no redirect de módulo desativado**: na prática é 404.
- **Trocar `/{id}` por `/{id}` com regex**: mudaria o 404 de id arbitrário para 404 de rota
  (efeito observável parecido, mas mensagem diferente).
- **Cachear `/fale-conosco`**: o token CSRF ficaria preso e o formulário quebraria.
- **Criar `DELETE` para arquivar**: o contrato atual é POST e idempotente.

### 3.5 Divergências documentação × código e dívidas

- **Comentário da rota** diz que a config foi movida, mas não há código de config no SLE
  para `contato_*` (só exibição).
- **Sem throttle** e **sem captcha efetivo**.
- **Sem rota de desarquivar** apesar de `scopeArchived`.
- **Sem teste de integração** das rotas.

## 4. Integrabilidade

### 4.1 Superfície pública

| Símbolo | Tipo | Assinatura | Precondição | Consumidores |
|---|---|---|---|---|
| `v2.fale-conosco.portal.index` | rota | GET `/fale-conosco` | — | links do site |
| `v2.fale-conosco.portal.store` | rota | POST `/fale-conosco` | CSRF | form |
| `v2.fale-conosco.cms.inbox` | rota | GET `/cms/fale-conosco` | auth + `read` | menu CMS |
| `v2.fale-conosco.cms.show` | rota | GET `/{id}` | auth + `read` | inbox |
| `v2.fale-conosco.cms.reply` | rota | POST `/{id}/reply` | auth + `write` | detalhe |
| `v2.fale-conosco.cms.archive` | rota | POST `/{id}/archive` | auth + `write` | inbox/detalhe |

### 4.2 Contrato de UI/JS

- Todas as URLs vêm de `route()` nas views; não há JS montando URL.
- Forms usam `csrf_field()`; não há AJAX.
- O botão de arquivar usa `confirm()` inline; o de reply é submit nativo.
- Parâmetro `{id}` é sempre UUID gerado no INSERT.

### 4.3 Ordem e ciclo de vida

1. Provider registra rotas CMS e web; fallback por último.
2. `module.active` antes de `CmsAuthenticate`? A ordem efetiva é a do array do grupo do
   provider seguida do grupo do arquivo: `web`, `module.active`, `CmsAuthenticate`.
3. `CmsAuthenticate` chama `V2PermissionMiddleware` explicitamente.
4. Caches globais pulam a rota pública.

### 4.4 Receitas de uso

```php
// Gerar os links corretos
route('v2.fale-conosco.portal.index');
route('v2.fale-conosco.cms.show', ['id' => $mensagem->id]);
```

```blade
{{-- Form mínimo compatível com a validação --}}
<form action="{{ route('v2.fale-conosco.portal.store') }}" method="POST">
  {{ csrf_field() }}
  <input name="name" required maxlength="255">
  <input name="email" type="email" required maxlength="255">
  <input name="phone" maxlength="16" placeholder="(61) 99999-9999">
  <input name="subject" required maxlength="255">
  <textarea name="message" required></textarea>
  <button>Enviar</button>
</form>
```

```http
POST /cms/fale-conosco/{id}/archive HTTP/1.1

_token=...

HTTP/1.1 302 Found
Location: /cms/fale-conosco
```

### 4.5 Anti-uso

- **Chamar `POST /cms/fale-conosco/{id}/archive` esperando 200/JSON**: o contrato é 302.
- **Usar `GET /cms/fale-conosco?unread=1`**: 500.
- **Assumir que a rota pública está sob RBAC**: é aberta; o RBAC é só do CMS.

### 4.6 Custos e limites da integração

- POST público pode disparar 2 e-mails; sem throttle.
- `reply` também dispara e-mail síncrono.
- Sem paginação além de 20 na inbox (fixa).
- Sem timeout explícito para cURL/SMTP.

## 5. Rotas e contratos HTTP

| Tipo | Método | URI | Nome | Middleware | Controller@ação | Permissão | Efeito | Fonte |
|---|---|---|---|---|---|---|---|---|
| grupo | — | `cms/fale-conosco` | — | `web`; `module.active:FaleConosco`; `CmsAuthenticate`; `V2PermissionMiddleware` | — | módulo `FaleConosco` | — | `Routes/cms.php:3-6`; `V2ServiceProvider.php:299-306` |
| endpoint | GET | `/cms/fale-conosco` | `v2.fale-conosco.cms.inbox` | idem | `inbox` | `read` | HTML | `Routes/cms.php:8-11` |
| endpoint | GET | `/cms/fale-conosco/{id}` | `v2.fale-conosco.cms.show` | idem | `show` | `read` | HTML + marca lida | `:17-20` |
| endpoint | POST | `/cms/fale-conosco/{id}/reply` | `v2.fale-conosco.cms.reply` | idem | `reply` | `write` | 302 back | `:22-25` |
| endpoint | POST | `/cms/fale-conosco/{id}/archive` | `v2.fale-conosco.cms.archive` | idem | `archive` | `write` | 302 inbox | `:27-30` |
| grupo | — | — | — | `web`; `MaintenanceMiddleware`; `page.cache`; `public.cache`; `web` | — | pública | — | `Routes/web.php:3-5`; `V2ServiceProvider.php:215-219` |
| endpoint | GET | `/fale-conosco` | `v2.fale-conosco.portal.index` | idem (sem cache efetivo) | `index` | pública | HTML | `Routes/web.php:7-10` |
| endpoint | POST | `/fale-conosco` | `v2.fale-conosco.portal.store` | idem | `store` | pública | 302 | `:12-15` |

Validação por ação: ver [`spec.md`](spec.md) §5 e [`cms.md`](cms.md)/[`portal.md`](portal.md).

## 6. Dados e modelo de domínio

As rotas escrevem em `v2_mensagens_contato` e `v2_mail_logs`; ver [`spec.md`](spec.md) §6.

## 7. Fluxos e estados

```mermaid
sequenceDiagram
  participant B as Navegador
  participant MW as Middlewares
  participant C as Controller
  B->>MW: GET /cms/fale-conosco
  MW->>MW: web -> module.active -> CmsAuthenticate -> RBAC
  alt autorizado
    MW->>C: inbox(...)
    C-->>B: 200
  else sem permissao
    MW-->>B: 302 /cms com mensagem
  end
```

## 8. UI CMS

Ver [`cms.md`](cms.md).

## 9. UI Portal

Ver [`portal.md`](portal.md).

## 10. Configuração, permissões e flags

- `active_pattern=['cms/fale-conosco*']`; chave `FaleConosco`.
- `module.active:FaleConosco` só no CMS; sem seed de flag.
- Sem throttle/config de rota; `contato_*` influencia e-mails.

## 11. Observabilidade, erros e limites

- `419`, `404`, `500` (`unread`), `401/403` (AJAX), `302`.
- Logs de mail e de permissão; `activity_log` nas ações CMS.
- Sem limite de requisições; sem idempotência formal (archive é set true).
- Sem dados sensíveis na URL; conteúdo no corpo.

## 12. Seeds e dados iniciais

Não se aplica: rotas não são semeadas. O `BasicPage` semeado é sombreado por estas rotas.

## 13. Lacunas

- `LG-FALECONOSCO-ROTAS-01` — Existe macro `Request::boolean` em runtime? Define o 500 do
  filtro. Procurar em providers.
- `LG-FALECONOSCO-ROTAS-02` — Comportamento do `VerifyCsrfToken` em AJAX (JSON vs HTML) não
  exercitado.
- `LG-FALECONOSCO-ROTAS-03` — Se algum proxy/CDN cacheia `/fale-conosco` apesar das
  diretivas; não observável no código.
- `LG-FALECONOSCO-ROTAS-04` — Não há teste de integração das 6 rotas.

## 14. Checklist de paridade

- [ ] `PC-FALECONOSCO-ROTAS-01` — 4 rotas CMS sob `cms/fale-conosco` com nomes exatos.
- [ ] `PC-FALECONOSCO-ROTAS-02` — 2 rotas públicas GET/POST com CSRF e sem cache.
- [ ] `PC-FALECONOSCO-ROTAS-03` — CMS sob `CmsAuthenticate` + RBAC `FaleConosco`;
      `write` exigido em reply/archive.
- [ ] `PC-FALECONOSCO-ROTAS-04` — `module.active:FaleConosco` bloqueia apenas o CMS.
- [ ] `PC-FALECONOSCO-ROTAS-05` — `404` para id inexistente em show/reply/archive.
- [ ] `PC-FALECONOSCO-ROTAS-06` — Sem `throttle` (ou decisão de endurecer documentada).
- [ ] `PC-FALECONOSCO-ROTAS-07` — `/fale-conosco` sem `X-Cache-Status` e sem
      `Cache-Control: public`.
- [ ] `PC-FALECONOSCO-ROTAS-08` — Decisão registrada para `?unread=1` (500 ou correção).

## 15. Evidências consultadas

- `V2/Modules/Institucional/FaleConosco/Routes/cms.php:1-31`; `Routes/web.php:1-16`.
- `V2/Core/Providers/V2ServiceProvider.php:203-224,293-307`.
- `V2/Core/Middleware/CmsAuthenticate.php:1-46`;
  `V2/Core/Middleware/V2PermissionMiddleware.php:1-137`;
  `V2/Core/Middleware/ModuleAccessMiddleware.php:1-67`;
  `V2/Core/Middleware/PageCacheMiddleware.php:1-91`;
  `V2/Core/Middleware/PublicCacheMiddleware.php:1-95`.
- `V2/Modules/Institucional/FaleConosco/Controllers/FaleConoscoCmsController.php:1-154`;
  `Controllers/FaleConoscoPortalController.php:1-165`.
