---
id: MOD-SUPPORT-SUPPORTCONFIGS-ROTAS
tipo: modulo
onda: 3
status: concluido
unidades: [MOD-SUPPORT-SUPPORTCONFIGS]
fontes:
  - V2/Modules/Support/SupportConfigs/
depende_de: [CORE-BOOT, CORE-AUTH, CORE-CMS]
consumidores: [MOD-SUPPORT-STRUCTURALLANDINGEDITOR, UI-CMS]
criticidade: alta
dono: PageSupportConfigs
---

# Support/SupportConfigs — rotas

> Anexo do módulo `spec.md`. Onze endpoints: dez no painel (`cms/infra/support-configs`)
> e um de reset de tema em `cms/infra/user-configs`, cujo controller vive em
> `MOD-SUPPORT-STRUCTURALLANDINGEDITOR`. Nenhum tem permissão RBAC efetiva; dez têm
> guarda de controller e um não tem.

## 0. Ficha de identidade

| Campo | Valor |
|---|---|
| Efeito primário | Expor operações globais (flags, manutenção, debug, logs, senha, reset de tema) sob o prefixo `cms/infra/*`, autenticadas por sessão Sentinel e autorizadas por e-mail. |
| Entrada | Verbo + URI + parâmetros de §5. Nenhum FormRequest; validação manual no controller. |
| Saída | HTML, JSON (`toggle`, `search`, `detail`, `logs/tail`) e `302` com flash; `403`/`404`/`422`. |
| Efeitos colaterais | Escrita em três tabelas de configuração, `users` (senha), `activity_log`, `storage/logs`; limpeza de cache; reset de tema CMS. |
| Particularidade central | `active_pattern` em string com `|` não casa → o middleware de permissão não aplica RBAC nem o grupo `Support`; a única barreira é `authorizeSupportAccess()` em 10 das 11 rotas. |
| Como se integra | `Route::group(['prefix'=>'cms/infra/support-configs','middleware'=>[CmsAuthenticate::class]])`; nomes `v2.supportconfigs.*`; controller `SupportConfigsCmsController`. |
| Se removido, quebra | Botões/formulários do painel, links da sidebar (`v2.supportconfigs.index`), o reset de tema referenciado pela SLE e o contrato de AJAX da aba de logs. |
| Superfície pública | 11 rotas nomeadas; nenhuma API pública/JSON versionada. |

## 1. Efeito

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

- Abrir o painel e ver diagnóstico + listas (R01).
- Limpar cache da aplicação (R02).
- Alternar uma flag com confirmação modal e atualização visual imediata (R03).
- Aplicar várias flags de uma vez via formulário (R04; sem chamador na UI atual).
- Salvar manutenção/debug (R05).
- Buscar usuários, abrir detalhe e forçar senha (R06–R08).
- Escolher arquivo de log, ler o tail e truncar com backup (R09–R10).
- Resetar o tema CMS a partir de qualquer sessão CMS, sem guarda (R11).

### 1.2 O que muda no sistema

| Rota | Efeito em dados | Efeito externo |
|---|---|---|
| R01 | nenhum | 1 query em `v2_module_flags`, 1 em `v2_system_controls`, 1 conexão para diagnóstico |
| R02 | nenhum | apaga cache de app/config/views; auditoria `cache_cleared` |
| R03 | upsert `v2_module_flags` | auditoria; menu/rotas do módulo mudam na próxima request |
| R04 | até N upserts | idem, em transação (sem rollback efetivo) |
| R05 | upsert `v2_system_controls` (3 chaves) | `config()`/`ini_set()`/debugbar na request corrente; manutenção vale para as próximas |
| R06 | nenhum | `SELECT` em `users` + `roles` |
| R07 | nenhum | `SELECT` em `users` + `activity_log` |
| R08 | `users.password` via Sentinel | auditoria `password_reset_updated`; sessão do alvo não é invalidada |
| R09 | nenhum | leitura de `storage/logs` |
| R10 | arquivo truncado | cria `.bak`; auditoria `log_clear_updated` |
| R11 | reset de 6 chaves em `v2_cms_theme_configs` | CSS/marca/favicon do CMS mudam em todas as telas |

### 1.3 Contrato observável

| Rota | Entrada válida | Entrada inválida |
|---|---|---|
| R01 | sessão de suporte | `403` `Acesso restrito ao suporte técnico autorizado.` |
| R02 | idem | idem; falha de Artisan → flash `Falha ao limpar o cache do sistema.` |
| R03 | `module_key` + `active` | sem `module_key`: JSON `422` `module_key é obrigatório.` / HTML flash |
| R04 | ≥1 campo `module_*` | flash `Nenhum módulo selecionado para alteração.` |
| R05 | ≥1 de `maintenance_mode`,`app_debug`,`debugbar_enabled` | flash `Nenhum controle selecionado para alteração.` |
| R06 | `q` com ≥2 chars | `{"users":[]}` (JSON) / flash `Digite ao menos 2 caracteres para buscar.` |
| R07 | `id` existente | JSON `404 {"error":"Usuário não encontrado."}` / flash |
| R08 | `user_id` + `new_password` ≥6 | `user_id e new_password são obrigatórios.` / `A nova senha deve ter no mínimo 6 caracteres.` |
| R09 | `lines` (clamp 50–2000), `file` opcional | nunca erro por parâmetro; cai no default |
| R10 | `file` opcional | arquivo inexistente → flash de sucesso |
| R11 | sessão CMS com ≥1 papel | **não há validação de identidade**; resposta é `200` vazio (ação sem `return`) |

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

- Não expõem API pública, versão, token ou throttle.
- Não aplicam permissão por módulo: nenhuma linha tem `v2_can`/slug RBAC; o middleware `V2PermissionMiddleware` não casa o prefixo (3.1).
- Não validam CSRF além do grupo `web` (`app/Http/Kernel.php:38-48`).
- Não confirmam no servidor: as confirmações (`confirm()`, modal de flag) são só client-side; um POST direto executa.
- R04 não tem chamador na UI atual; R11 não pertence ao controller do painel.

## 2. Abordagem

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

Duas camadas de middleware independentes se somam: `mapModuleRoutes` aplica `web` + `module.active:<ModuleName>` a **todo** `Routes/cms.php` (`V2ServiceProvider.php:303-306`); o arquivo adiciona `CmsAuthenticate` no grupo. `CmsAuthenticate` delega a `V2PermissionMiddleware`, que tenta casar o `active_pattern` do provider e, ao falhar, libera. A autorização real é feita no controller. O segundo grupo (`user-configs`) usa o mesmo pipeline, mas aponta para um controller sem guarda.

```mermaid
flowchart TD
  REQ[POST /cms/infra/support-configs/...] --> WEB[web: sessão, CSRF, CmsAiToken]
  WEB --> FLAG["module.active:SupportConfigs"]
  FLAG -->|flag false| E404[404 Modulo desativado.]
  FLAG --> AUTH[CmsAuthenticate]
  AUTH -->|sem sessão| L[302 login / 401 AJAX]
  AUTH -->|sem papel| L2[302 login + erro de papel]
  AUTH --> PM[V2PermissionMiddleware]
  PM -->|pattern não casa| CTRL[Controller]
  CTRL --> GUARD{authorizeSupportAccess}
  GUARD -->|não-suporte| E403[403]
  GUARD -->|suporte| ACT[action + serviço]
  REQ2[POST /cms/infra/user-configs/cms-theme/reset] --> WEB
  WEB --> FLAG
  FLAG --> AUTH
  AUTH --> PM
  PM -->|pattern não casa| CTRL2[UserConfigsCmsController@resetCmsTheme]
  CTRL2 --> RESET[reset sem guarda]
```

### 2.2 Mecânica passo a passo

1. **Descoberta**: o provider do módulo é instanciado com `group='Support'` e registrado em `v2.modules.configs` (`V2ServiceProvider.php:100-133`).
2. **Registro das rotas**: `mapModuleRoutes` carrega `Routes/cms.php` com `web` + `module.active:SupportConfigs` e namespace `V2\Modules\Support\SupportConfigs\Controllers` (`:290-306`).
3. **Flag**: `ModuleAccessMiddleware` resolve `SupportConfigs` → `supportconfigs` (`ModuleFlagService::isActive`, `:74-95`). Se `active=false`, `abort(404,'Modulo desativado.')` (o redirect para dashboard só ocorre quando `route()->getPrefix() === 'cms'`, o que **não** acontece com prefixo `cms/infra/...`) (`ModuleAccessMiddleware.php:44-61`).
4. **Autenticação**: `CmsAuthenticate` exige `Sentinel::check()` e `roles()->count() > 0`; sem papel, faz logout/flush e redireciona (`CmsAuthenticate.php:21-44`).
5. **Permissão**: `V2PermissionMiddleware` itera `v2.modules.configs` e testa `$request->is($config['active_pattern'])` (`:40-48`). Como o padrão é `'cms/infra/support-configs*|cms/infra/user-configs*'`, o teste falha; `$matched=false`; `:74-77` retorna `$next`.
6. **Guarda**: o método chama `authorizeSupportAccess()` antes de qualquer efeito (`SupportConfigsCmsController.php:47-53`).
7. **Validação e resposta**: cada action valida manualmente, chama `SupportConfigsService` e escolhe JSON (`expectsJson`) ou redirect.
8. **R11**: o pipeline é o mesmo (R11 está no mesmo `cms.php`, portanto também recebe `module.active:SupportConfigs`), mas o destino não chama guarda (`UserConfigsCmsController.php:38-46`).

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

- **Um grupo por prefixo + `CmsAuthenticate`** `[C]`: rotas declarativas e nomes estáveis; a autorização fina fica no controller para permitir a rota de reset “herdada” da SLE.
- **`expectsJson()` como discriminador** `[C]`: a mesma action serve AJAX e formulário; não há negociação por `Accept` explícita nem sufixo `.json`.
- **`abort(403)` em vez de redirect** `[C]`: negação de suporte gera página 403, diferente do padrão do middleware (redirect com flash). Consequência: não há mensagem amigável no layout, só a página de erro.
- **Nomes `v2.supportconfigs.*` inclusive para a rota da SLE** `[C]`: o reset é semanticamente do painel, mas implementado no controller do editor estrutural — acoplamento entre unidades.
- **Sem `throttle`/`cache`** `[C]`: todas as rotas são POST/GET autenticados e não cacheados; a de logs pode ser chamada em loop pelo `onchange` do select.
- **Prefixos `cms/infra`** `[C]`: agrupam ferramentas de infraestrutura; a sidebar usa `position=90` (`ModuleProvider.php:83-90`).

### 2.4 Trechos de código pivô

```php
// V2/Modules/Support/SupportConfigs/Routes/cms.php:18-30 — grupo principal e nomes.
Route::group(['prefix' => 'cms/infra/support-configs', 'middleware' => [\V2\Core\Middleware\CmsAuthenticate::class]], function () {

    // Painel de Suporte Técnico (verificação adicional no controller)
    Route::get('/', 'SupportConfigsCmsController@index')->name('v2.supportconfigs.index');
    Route::post('/clear-cache', 'SupportConfigsCmsController@clearCache')->name('v2.supportconfigs.clear_cache');

    // ========================================================================
    //  ABA 1 — MÓDULOS (FEATURE FLAGS)
    // ========================================================================
    Route::post('/modules/toggle', 'SupportConfigsCmsController@toggleModule')
        ->name('v2.supportconfigs.modules.toggle');
    Route::post('/modules/batch', 'SupportConfigsCmsController@updateModulesBatch')
        ->name('v2.supportconfigs.modules.batch');
```

O comentário “verificação adicional no controller” é o contrato: o grupo não tem middleware de suporte, só `CmsAuthenticate` (somado ao `module.active` externo). Qualquer action nova que se esqueça da guarda fica aberta a todo usuário com papel.

```php
// V2/Modules/Support/SupportConfigs/Routes/cms.php:57-64 — a rota sem guarda.
Route::group(['prefix' => 'cms/infra/user-configs', 'middleware' => [\V2\Core\Middleware\CmsAuthenticate::class]], function () {
    Route::post('/cms-theme/reset', '\V2\Modules\Support\StructuralLandingEditor\Controllers\UserConfigsCmsController@resetCmsTheme')
        ->name('v2.supportconfigs.cms_theme.reset');
});
```

A rota está no arquivo deste módulo, mas o controller é da SLE. O comentário acima dela diz “mantida para compatibilidade com o formulário no Editor Estrutural”. É a única rota do conjunto cujo destino não executa `authorizeSupportAccess()` — achado SEC-G1-04 (`_qa/security-g1.md:79-94`).

```php
// V2/Core/Middleware/V2PermissionMiddleware.php:40-48 — por que o prefixo nunca casa.
foreach ($moduleConfigs as $moduleKey => $config) {
    $pattern = $config['active_pattern'] ?? null;
    if ($pattern && $request->is($pattern)) {
        $currentModuleKey = $moduleKey;
        $currentModuleConfig = $config;
        $matched = true;
        break;
    }
}
```

`Request::is()` recebe a string inteira; `Str::is()` escapa `|` como literal. O padrão esperado seria `['cms/infra/support-configs*', 'cms/infra/user-configs*']`. Enquanto for string, `$matched` é sempre `false` para este módulo e o bloco `Support` (`:51-65`) e o RBAC (`:78-110`) não rodam.

```php
// V2/Modules/Support/SupportConfigs/Controllers/SupportConfigsCmsController.php:101-129 — toggle JSON/HTML.
$moduleKey = $request->input('module_key');
$active    = (bool) $request->input('active', false);
if (empty($moduleKey)) {
    if ($request->expectsJson()) {
        return response()->json(['success' => false, 'message' => 'module_key é obrigatório.'], 422);
    }
    return redirect()->back()->with('error', 'module_key é obrigatório.');
}
$result = $this->supportConfigsService->toggleModule($moduleKey, $active);
if ($request->expectsJson()) {
    return response()->json([
        'success' => $result,
        'message' => $result ? "Módulo '{$moduleKey}' " . ($active ? 'ativado' : 'desativado') . " com sucesso." : 'Falha ao alterar módulo.',
    ]);
}
return redirect()->back()->with(
    $result ? 'success' : 'error',
    $result ? "Módulo '{$moduleKey}' " . ($active ? 'ativado' : 'desativado') . "." : 'Falha ao alterar módulo.'
);
```

`(bool)$request->input('active')` faz `"0"` → `false` e `"false"` → `true` (string não vazia). A UI envia JSON com `0`/`1`, então funciona; um cliente enviando `"false"` ligaria a flag. Não há validação de `module_key` contra a lista de módulos.

```php
// V2/Modules/Support/SupportConfigs/Controllers/SupportConfigsCmsController.php:269-284 — tail com clamp.
$lines = (int) $request->input('lines', 500);
$lines = min(max($lines, 50), 2000);
$filename = $request->input('file');
$logData = $this->supportConfigsService->getLogTail($lines, $filename);
if ($request->expectsJson()) {
    return response()->json($logData);
}
return view('v2-supportconfigs::cms.supportConfigs.index', [
    'logData' => $logData,
]);
```

O clamp é a única defesa de volume. O ramo não-JSON renderiza a view sem as variáveis exigidas (`$diagnostics`, `$modules`, `$systemControls`), produzindo erro — na prática a rota é usada só como AJAX (`logs.blade.php:133-135` envia `Accept: application/json`).

```php
// V2/Modules/Support/SupportConfigs/Controllers/SupportConfigsCmsController.php:239-261 — senha forçada.
$userId = $request->input('user_id');
$newPassword = $request->input('new_password');
if (empty($userId) || empty($newPassword)) {
    return redirect()->back()->with('error', 'user_id e new_password são obrigatórios.');
}
if (strlen($newPassword) < 6) {
    return redirect()->back()->with('error', 'A nova senha deve ter no mínimo 6 caracteres.');
}
$result = $this->supportConfigsService->forcePasswordReset($userId, $newPassword);
return redirect()->back()->with(
    $result ? 'success' : 'error',
    $result ? 'Senha do usuário alterada com sucesso.' : 'Falha ao alterar senha do usuário.'
);
```

Sem confirmação server-side, sem exigir senha atual do alvo, sem invalidar sessões existentes e sem limite por usuário. `Sentinel::update` grava hash; a regra de 6 caracteres não é reaplicada no serviço, então qualquer chamador interno pode ignorá-la.

### 2.5 Custos

- R01: 3 leituras + conexão de banco para `getDriverName()`/`getDatabaseName()`; sem cache.
- R02: 3 comandos Artisan síncronos; em diretório de views grande pode levar segundos.
- R03/R04: 2N+1 queries + N auditorias.
- R06: `ILIKE` em 3 colunas; `limit(20)`; eager load de roles.
- R07: 2 queries + até 30 registros de activity com `subject` eager loaded.
- R09: `glob` + `filesize`/`filemtime` de todos os logs + `tail` externo (ou leitura em blocos).
- R10: cópia integral do arquivo (I/O do tamanho do log).
- R11: 6 upserts + 6 auditorias em transação.

## 3. Particularidades

### 3.1 O padrão que nunca casa

`ModuleProvider.php:87` declara:

```php
'active_pattern' => 'cms/infra/support-configs*|cms/infra/user-configs*',
```

String única é formato suportado e usado com sucesso por vários providers (`Infraestrutura/CertificadoDigital/ModuleProvider.php:25`, `Infraestrutura/MailAudit/ModuleProvider.php:20`, `Layouts/BasicPage/ModuleProvider.php:28`, `Layouts/Homepage/ModuleProvider.php:44`, `Support/StructuralLandingEditor/ModuleProvider.php:35`, `_Exemplos/ExemploAPI/ModuleProvider.php:27`); arrays também (`Publicacoes/Noticias/ModuleProvider.php:40`, `Conexoes/Associados/ModuleProvider.php:21`). O defeito é o `|` dentro da string: em Laravel 5.5, `Request::is($pattern)` passa a string para `Str::is`, que faz `preg_quote` antes de trocar `\*` por `.*`; o `|` vira literal. Logo:

1. `$matched` permanece `false` para `/cms/infra/support-configs/...` e `/cms/infra/user-configs/...`.
2. O bloco de grupo `Support` (`V2PermissionMiddleware.php:51-65`) não executa → nenhum `403`/redirect do middleware.
3. O RBAC por `PermissionService` (`:78-110`) não executa → as rotas não exigem permissão `SupportConfigs`.
4. Quem protege é apenas `authorizeSupportAccess()`; onde ela falta (R11), não há proteção. O bloco que deixa de rodar é `V2PermissionMiddleware.php:51-77` (grupo `Support`, bypass de suporte/super-admin e `if (!$matched) return $next`).

Correção de paridade: usar array e manter a guarda. Com array, R11 passaria a ser barrada pelo middleware para não-suporte, mesmo sem guarda de controller.

### 3.2 R11 sem guarda (SEC-G1-04)

`UserConfigsCmsController@resetCmsTheme` (`V2/Modules/Support/StructuralLandingEditor/Controllers/UserConfigsCmsController.php:38-46`) só chama `resetCmsThemeToDefault()` e relança exceção. Não há checagem de e-mail, papel, `v2_can` nem confirmação. Como o middleware não casa, qualquer sessão CMS com ao menos um papel executa o reset. Impacto: sobrescreve `cms_sidebar_bg`, `cms_brand_primary`, `cms_body_bg`, `cms_brand_name`, `cms_brand_logo`, `cms_favicon` (`SystemMaintenanceService.php:292-303`). O `update()` do mesmo controller também não tem guarda, mas **não tem rota** no arquivo atual — logo é código inalcançável por HTTP. Os internals desse controller (formulário de tema, uploads, `UserConfigsService`) pertencem à unidade `MOD-SUPPORT-STRUCTURALLANDINGEDITOR`; o doc de lá (`05_modulos/Support/StructuralLandingEditor/`) deve referenciar esta rota para o contrato de reset. Aqui só se documenta o contrato consumido: `POST` + CSRF + efeito.

### 3.3 Invariantes de rota

| ID | Regra | Efeito | Fonte |
|---|---|---|---|
| `RN-SUPPORTCONFIGS-21` | Todo `cms.php` recebe `web` + `module.active:<ModuleName>` automaticamente. | Desativar a flag derruba **todas** as 11 rotas, inclusive R11. | `V2ServiceProvider.php:303-306` |
| `RN-SUPPORTCONFIGS-22` | `CmsAuthenticate` exige ≥1 papel; sem papel, logout + redirect. | Um usuário autenticado sem role não chega ao controller. | `CmsAuthenticate.php:30-44` |
| `RN-SUPPORTCONFIGS-23` | `403` do controller é `abort`, não redirect. | Não há flash; o usuário vê a página de erro padrão. | `SupportConfigsCmsController.php:51` |
| `RN-SUPPORTCONFIGS-24` | CSRF é o do grupo `web`; AJAX envia `X-CSRF-TOKEN`. | POST sem token → `419`. | `app/Http/Kernel.php:44`; `modulos.blade.php:238-245` |
| `RN-SUPPORTCONFIGS-25` | `module.active` compara por `module_key` e tenta PascalCase→lowercase. | `SupportConfigs` resolve para `supportconfigs`. | `ModuleFlagService.php:72-94` |
| `RN-SUPPORTCONFIGS-26` | R09/R10 aceitam `file` só como basename `laravel*.log`. | Path traversal não é possível; arquivo inválido vira default. | `SystemDiagnosticsService.php:171-187` |

### 3.4 Casos-limite

- **Usuário sem papel**: `302` login, não `403`; o `403` do controller só aparece para usuário com papel e e-mail diferente.
- **Flag desativada**: `404 Modulo desativado.` mesmo para o suporte; a condição de redirect do `ModuleAccessMiddleware` compara `getPrefix() === 'cms'` e nunca é verdadeira aqui (`:55-58`).
- **`toggleModule` com `active` string**: `"false"` → `true`; `"0"` → `false`; `null` → `false`.
- **R04 via formulário**: campos `module_*` precisam existir; a UI não envia esse form, então é contrato morto.
- **R07 com `id` não numérico**: `Sentinel::findById('abc')` pode lançar; o serviço captura e devolve `null` → `404`.
- **R08 com o próprio e-mail de suporte**: permitido; o suporte pode trocar a própria senha sem senha atual.
- **R09 com log gigante**: `tail` externo limita a leitura, mas o JSON inclui a lista de arquivos e até 2000 linhas; payload pode ser grande.
- **R10 em arquivo inexistente**: responde sucesso, sem criar `.bak`.
- **R11 concorrente com o save da SLE**: ambos escrevem `v2_cms_theme_configs`; last-write-wins, sem lock.

### 3.5 Armadilhas de reimplementação

- **Não remover a guarda do controller ao “consertar” o pattern**: em produção o pattern está quebrado; se você corrigir só o pattern, R11 passa a ser protegida, mas as outras 10 continuam dependendo da guarda (que também cobre `super-admin` ausente).
- **Não replicar o `403` como redirect**: o contrato atual é página 403; um redirect muda o comportamento observado e os testes de fumaça.
- **Não usar `$request->is('cms/infra/*')` como padrão do módulo**: outros módulos usam prefixos dentro de `cms/` (ex.: `cms/infra/structural-editor`); o padrão precisa ser específico.
- **Não assumir que `module.active` redireciona para o dashboard**: para prefixos compostos ele aborta com 404.
- **Não tratar `lines` como ilimitado**: a UI pede 500, o servidor impõe 50–2000.
- **Não expor R11 sem CSRF/guard**: ela é POST e CSRF-protegida, mas qualquer sessão válida consegue forjar o POST.

### 3.6 Divergências

| Divergência | Documentação/comentário | Código | Fonte |
|---|---|---|---|
| “Restrito ao e-mail de suporte” | docblock do arquivo de rotas | o grupo não tem middleware de e-mail; só controller | `Routes/cms.php:7-9,18` |
| R11 “mantida para compatibilidade” | comentário | o form existe, mas em `cms/infra/structural-editor/theme` (prefixo diferente do `user-configs` da rota) | `Routes/cms.php:58-64`; `StructuralLandingEditor/Views/cms/submodules/theme/index.blade.php:96-101` |
| `update()` de `UserConfigsCmsController` | README descreve `index`/`update` | nenhuma rota para `update`/`index` no módulo | `Controllers/README.md:12-18`; `StructuralLandingEditor/Routes/cms.php:19-70` |
| Prefixo `cms/infra/user-configs` | — | não há tela nesse prefixo; a tela atual é `cms/infra/structural-editor/*` | `Routes/cms.php:61` |

## 4. Integrabilidade

### 4.1 Superfície

| Rota | Nome | Método | Precondição | Consumidores |
|---|---|---|---|---|
| R01 | `v2.supportconfigs.index` | GET | Sentinel + papel + e-mail | sidebar, breadcrumb |
| R02 | `v2.supportconfigs.clear_cache` | POST | idem | form da aba diagnóstico |
| R03 | `v2.supportconfigs.modules.toggle` | POST | idem | `fetch` da aba Módulos |
| R04 | `v2.supportconfigs.modules.batch` | POST | idem | nenhum chamador atual |
| R05 | `v2.supportconfigs.system_controls.save` | POST | idem | form da aba Manutenção |
| R06 | `v2.supportconfigs.users.search` | GET | idem | form da aba Suporte |
| R07 | `v2.supportconfigs.users.detail` | GET | idem | `fetch` da aba Suporte |
| R08 | `v2.supportconfigs.users.reset_password` | POST | idem | modal da aba Suporte |
| R09 | `v2.supportconfigs.logs.tail` | GET | idem | `fetch` da aba Logs |
| R10 | `v2.supportconfigs.logs.clear` | POST | idem | form da aba Logs |
| R11 | `v2.supportconfigs.cms_theme.reset` | POST | Sentinel + papel (sem e-mail) | form oculto da tela de tema da SLE (`StructuralLandingEditor/Views/cms/submodules/theme/index.blade.php:96-101`); botão em `tabs/userconfigs/tema-cms.blade.php:157-161` |

### 4.2 Contrato de UI/JS

- R03: `fetch(route, {method:'POST', headers:{'Content-Type':'application/json','X-Requested-With':'XMLHttpRequest','Accept':'application/json','X-CSRF-TOKEN':…}, body: JSON.stringify({module_key, active})})`; espera `{success:boolean,message:string}`; erro HTTP com corpo JSON é relançado e exibido em `alert` (`modulos.blade.php:238-289`).
- R06: form GET `q`; resposta é redirect com `search_results`/`search_term` em sessão (`suporte.blade.php:9-28`).
- R07: `fetch('/cms/infra/support-configs/users/detail/' + id, {headers:{'Accept':'application/json'}})`; URL **hardcoded**, não usa `route()` (`suporte.blade.php:150-153`).
- R09: `fetch('{{ route(...) }}?lines=500&_=' + Date.now())` com `file` opcional; espera `{file,size,lines,content[],files[]}` (`logs.blade.php:114-140`).
- R02/R05/R08/R10/R11: POST clássico com `csrf_field()` e `confirm()` opcional.

### 4.3 Ordem e ciclo de vida

1. `V2ServiceProvider::boot()` registra o provider e o grupo de rotas (namespace `V2\Modules\Support\SupportConfigs\Controllers`).
2. O grupo externo aplica `web` + `module.active:SupportConfigs`; o interno, `CmsAuthenticate`.
3. O controller resolve `SupportConfigsService` + `ActivityLoggerService` por DI no construtor (`:36-41`).
4. `SupportConfigsService` resolve os três sub-serviços; `SystemMaintenanceService` recebe `UploadService`, `ActivityLoggerService`, `StorageService`.
5. A view depende de `$diagnostics`, `$modules`, `$systemControls`, `$history` (R01) — nenhum outro endpoint popula a view inteira.

### 4.4 Receitas de uso

**1) Alternar flag via HTTP (equivalente ao JS):**

```bash
curl -X POST https://v2.dominio.com/cms/infra/support-configs/modules/toggle \
  -H 'Content-Type: application/json' \
  -H 'X-Requested-With: XMLHttpRequest' \
  -H 'X-CSRF-TOKEN: <token>' \
  -b 'session=<cookie>' \
  -d '{"module_key":"noticias","active":0}'
# 200 {"success":true,"message":"Módulo 'noticias' desativado com sucesso."}
```

**2) Ler o tail dos logs como JSON:**

```http
GET /cms/infra/support-configs/logs/tail?lines=200&file=laravel-2026-10-21.log
Accept: application/json

200 {"file":"laravel-2026-10-21.log","size":"12.4 MB","lines":200,"content":["..."],"files":[{"name":"...","size":"...","size_bytes":0,"modified":"..."}]}
```

**3) Salvar controles (todos os três campos são obrigatórios na prática):**

```http
POST /cms/infra/support-configs/system-controls/save
Content-Type: application/x-www-form-urlencoded
X-CSRF-TOKEN: <token>

maintenance_mode=0&app_debug=0&debugbar_enabled=1
```

**4) Resetar o tema CMS (com guarda no chamador):**

```php
if (Sentinel::getUser()->email === config('v2.support.email')) {
    // POST /cms/infra/user-configs/cms-theme/reset
}
```

**5) Recuperar acesso após auto-bloqueio da flag:**

```sql
UPDATE v2_module_flags SET active = true, updated_at = now()
 WHERE module_key = 'supportconfigs';
```

### 4.5 Anti-uso

**1) Chamar R11 de um link “Restaurar padrão” em qualquer tela.** Sem guarda de controller e sem pattern efetivo, qualquer operador reseta marca/logo/favicon. Se precisar reutilizar, envolva em `authorizeSupportAccess()`.

**2) Enviar `active: "false"` para R03.** `(bool)"false"` é `true`; use `0`/`1`/booleano JSON.

**3) Usar R04 esperando desligar módulos ausentes do form.** Só campos `module_*` enviados são processados; os demais mantêm o estado. E falhas internas não revertem (o serviço retorna `false` em vez de lançar).

### 4.6 Custos e limites

- Sem throttle: um script autenticado pode alternar flags em loop (2 queries + auditoria por chamada) ou pedir 2000 linhas de log repetidamente.
- R02 executa Artisan por request; chamadas concorrentes podem competir por locks de cache.
- R10 copia o arquivo inteiro antes de truncar; logs grandes dobram o uso de disco momentaneamente.
- R07 retorna até 30 atividades com `subject`; usuários muito ativos geram payload maior.
- R06 limita a 20 usuários; não há paginação nem busca por CPF.

## 5. Rotas e contratos HTTP

| Tipo | Método | URI | Nome | Middleware | Controller@ação | Permissão | Efeito | Fonte |
|---|---|---|---|---|---|---|---|---|
| endpoint | GET | `/cms/infra/support-configs` | `v2.supportconfigs.index` | `web`; `module.active:SupportConfigs`; `CmsAuthenticate` → `V2PermissionMiddleware` | `SupportConfigsCmsController@index` | e-mail de suporte (controller) | Renderiza 4 abas + diagnóstico; sem escrita | `Routes/cms.php:21`; `SupportConfigsCmsController.php:59-71` |
| endpoint | POST | `/cms/infra/support-configs/clear-cache` | `v2.supportconfigs.clear_cache` | idem | `@clearCache` | e-mail de suporte | `cache:clear`+`config:clear`+`view:clear`; flash | `Routes/cms.php:22`; `:81-92` |
| endpoint | POST | `/cms/infra/support-configs/modules/toggle` | `v2.supportconfigs.modules.toggle` | idem | `@toggleModule` | e-mail de suporte | upsert de flag; JSON/redirect; auditoria | `Routes/cms.php:27`; `:101-129` |
| endpoint | POST | `/cms/infra/support-configs/modules/batch` | `v2.supportconfigs.modules.batch` | idem | `@updateModulesBatch` | e-mail de suporte | upsert em lote; flash | `Routes/cms.php:29`; `:133-158` |
| endpoint | POST | `/cms/infra/support-configs/system-controls/save` | `v2.supportconfigs.system_controls.save` | idem | `@saveSystemControls` | e-mail de suporte | grava 3 controles + apply runtime | `Routes/cms.php:35`; `:165-182` |
| endpoint | GET | `/cms/infra/support-configs/users/search` | `v2.supportconfigs.users.search` | idem | `@searchUsers` | e-mail de suporte | JSON/redirect com ≤20 usuários | `Routes/cms.php:41`; `:190-207` |
| endpoint | GET | `/cms/infra/support-configs/users/detail/{id}` | `v2.supportconfigs.users.detail` | idem | `@getUserDetail` | e-mail de suporte | JSON com papéis/permissões/30 logs | `Routes/cms.php:43`; `:216-232` |
| endpoint | POST | `/cms/infra/support-configs/users/reset-password` | `v2.supportconfigs.users.reset_password` | idem | `@forcePasswordReset` | e-mail de suporte | grava `users.password`; flash | `Routes/cms.php:45`; `:239-261` |
| endpoint | GET | `/cms/infra/support-configs/logs/tail` | `v2.supportconfigs.logs.tail` | idem | `@getLogTail` | e-mail de suporte | JSON com N linhas + lista | `Routes/cms.php:51`; `:269-286` |
| endpoint | POST | `/cms/infra/support-configs/logs/clear` | `v2.supportconfigs.logs.clear` | idem | `@clearLogFile` | e-mail de suporte | backup + truncamento | `Routes/cms.php:53`; `:292-303` |
| endpoint | POST | `/cms/infra/user-configs/cms-theme/reset` | `v2.supportconfigs.cms_theme.reset` | `web`; `module.active:SupportConfigs`; `CmsAuthenticate` → `V2PermissionMiddleware` | `V2\Modules\Support\StructuralLandingEditor\Controllers\UserConfigsCmsController@resetCmsTheme` | **nenhuma** (SEC-G1-04) | reset de 6 chaves de tema CMS | `Routes/cms.php:62`; `UserConfigsCmsController.php:38-46` |

**Parâmetros:**

| Rota | Campo | Tipo | Obrigatório | Validação/limite | Default |
|---|---|---|---|---|---|
| R03 | `module_key` | string | sim | não vazio; sem whitelist; ≤100 no banco | — |
| R03 | `active` | bool/int | não | cast booleano PHP | `false` |
| R04 | `module_<key>` | qualquer | ≥1 | `FILTER_VALIDATE_BOOLEAN` | `false` |
| R05 | `maintenance_mode`,`app_debug`,`debugbar_enabled` | 0/1 | ≥1 | whitelist no serviço | ausente = não altera |
| R06 | `q` | string | sim | ≥2 chars | `''` (erro) |
| R07 | `id` | path | sim | existência via Sentinel | — |
| R08 | `user_id`,`new_password` | string | sim | senha ≥6 | — |
| R09 | `lines` | int | não | 50–2000 | 500 |
| R09/R10 | `file` | string | não | basename `laravel*.log` | log do dia/`laravel.log` |

**Auth/CSRF**: sessão Sentinel (`web`), CSRF obrigatório em POST, sem token de API, sem TTL, sem escopo. **Idempotência**: R03/R05/R11 idempotentes no estado final; R04 idempotente por flag; R10 **não** idempotente (gera um `.bak` novo a cada chamada); R02 idempotente no efeito.

**Rotas dinâmicas**: apenas R07 (`{id}` inteiro/string). Não há geração programática de URIs além de `route()` no Blade.

## 6. Dados e modelo de domínio

| Rota | Tabelas lidas | Tabelas escritas |
|---|---|---|
| R01 | `v2_module_flags`, `v2_system_controls` | — |
| R02 | — | `activity_log` |
| R03/R04 | `v2_module_flags` | `v2_module_flags`, `activity_log` |
| R05 | `v2_system_controls` | `v2_system_controls`, `activity_log` |
| R06/R07 | `users`, `roles`, `role_users`, `activity_log` | — |
| R08 | `users` | `users`, `activity_log` |
| R09 | — (filesystem) | — |
| R10 | — (filesystem) | `activity_log` |
| R11 | `v2_cms_theme_configs` | `v2_cms_theme_configs`, `activity_log` |

Colunas e seeds em `dados.md`.

## 7. Fluxos e estados

```mermaid
sequenceDiagram
  participant B as Browser
  participant M as Middlewares
  participant C as Controller
  participant S as Service
  B->>M: POST /modules/toggle (JSON, CSRF)
  M->>M: web -> module.active:SupportConfigs
  M->>M: CmsAuthenticate -> V2PermissionMiddleware (sem match)
  M->>C: handle
  C->>C: authorizeSupportAccess()
  alt não-suporte
    C-->>B: 403 (HTML)
  else suporte
    C->>S: toggleModule(key, bool)
    S-->>C: true/false
    C-->>B: 200 JSON {success,message}
  end
```

```mermaid
stateDiagram-v2
  [*] --> Autenticado
  Autenticado --> Negado: sem papel (302 login)
  Autenticado --> Negado403: papel + e-mail != suporte
  Autenticado --> Autorizado: e-mail == suporte
  Negado403 --> Autorizado: troca de conta
  Autorizado --> Executou: action
  Executou --> Autorizado: redirect/flash
```

## 8. UI CMS

O contrato de DOM/JS de cada chamador está em `cms.md`. Em resumo: R03 é chamada por `fetch` com JSON; R07 e R09 por `fetch` com `Accept: application/json`; R02/R05/R08/R10 por form POST; R06 por form GET; R11 por um form oculto `#reset-cms-form` na tela de tema da SLE, acionado por `requestSubmit()` (`StructuralLandingEditor/Views/cms/submodules/theme/index.blade.php:96-101`; `tabs/userconfigs/tema-cms.blade.php:157-161`).

## 9. UI Portal

Não se aplica: nenhuma rota deste módulo serve o portal. O portal é afetado apenas pelo estado gravado (flag → `ModuleAccessMiddleware`; manutenção → `MaintenanceMiddleware`).

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

- **Config**: `v2.support.email` é a única chave que autoriza (comparação estrita). `app.debug`, `cache.default`, `session.driver`, `queue.default`, `app.log` são lidas/exibidas, não controlam acesso.
- **Permissões**: nenhuma rota declara slug/permissão; `V2PermissionMiddleware` não casa o prefixo. `permissoes.csv` registra a lacuna.
- **Flag**: `module.active:SupportConfigs` cobre as 11 rotas. `supportconfigs` está seedada ativa e invisível na UI; desativar derruba o painel e a rota R11, exigindo SQL para recuperar.
- **Middleware de grupo**: `group='Support'` seria bloqueante se o pattern casasse; hoje não é.

## 11. Observabilidade, erros e limites

- **Logs**: acesso negado (`Log::warning`), toggle (`Log::info`), controle (`Log::info`), debug runtime (`Log::info`), log limpo (`Log::info`), falhas (`Log::error`).
- **Auditoria**: R02 `cache_cleared`; R03/R04 `feature_flag_updated`; R05 `system_control_updated`; R08 `password_reset_updated`; R10 `log_clear_updated`; R11 `updated` por chave (`context=cms_theme_config`).
- **HTTP**: `200` HTML/JSON; `302` redirects; `401 Unauthorized.` (AJAX sem sessão); `403 Acesso restrito ao suporte técnico autorizado.`; `404 Modulo desativado.`; `404 {"error":"Usuário não encontrado."}`; `419` CSRF; `422` toggle sem `module_key`; `500` só sem `catch` — favicon inválido via `theme.save` da SLE resulta em `302` + flash `Erro ao salvar tema: ...` (`SleCmsController.php:155-159`).
- **Fail-open**: `module.active` com tabela ausente considera ativo (`ModuleFlagService.php:93-94`); `getDiagnostics` pode falhar se a conexão de banco estiver indisponível.
- **Limites**: ver 4.6.

## 12. Seeds e dados iniciais

Não se aplica: as rotas não criam seeds. A flag `supportconfigs` que as habilita é seedada pela migration `2026_06_30_000002_create_v2_module_flags_table.php:61-69` (ver `dados.md` §12.2).

## 13. Lacunas

- `LG-SUPPORTCONFIGS-11`: não há chamador de R04 (`modules/batch`) no código atual; o contrato só existe no controller. Procurar em formulários antigos/templates. Impacto: baixo (rota funcional, mas não usada).
- `LG-SUPPORTCONFIGS-12`: a tela que aciona R11 vive em `cms/infra/structural-editor/theme` (SLE), não em `cms/infra/user-configs`; não se sabe se o prefixo `user-configs` já abrigou uma tela própria. Procurar no histórico do SLE. Impacto: médio (rota funcional, mas o prefixo e o nome não correspondem à tela).
- `LG-SUPPORTCONFIGS-13`: não foi possível confirmar se `super-admin` deveria ter acesso ao painel (a guarda não o permite; a sidebar também não o mostra, pois `group='Support'`). Comparar com `ExemploAPI`, que permite `super-admin` no controller. Impacto: médio (decisão de produto).
- `LG-SUPPORTCONFIGS-14`: não há teste de feature para nenhuma das 11 rotas; o comportamento de `module.active` com prefixo composto foi inferido do código. Procurar em `tests/`. Impacto: médio.

## 14. Checklist de paridade

- [ ] `PC-SUPPORTCONFIGS-29` — As 11 rotas existem com os nomes da tabela §5; `php artisan route:list` mostra `web`, `module.active:SupportConfigs` e `CmsAuthenticate`.
- [ ] `PC-SUPPORTCONFIGS-30` — `POST /modules/toggle` sem `module_key` com `Accept: application/json` → `422 {"success":false,"message":"module_key é obrigatório."}`.
- [ ] `PC-SUPPORTCONFIGS-31` — `POST /modules/toggle` com `active:"false"` liga a flag (cast booleano), reproduzindo o bug.
- [ ] `PC-SUPPORTCONFIGS-32` — Qualquer rota R01–R10 com usuário autenticado e não-suporte → `403` com a mensagem exata e warning no log.
- [ ] `PC-SUPPORTCONFIGS-33` — R11 com usuário autenticado e não-suporte → **hoje** executa o reset; alvo de paridade corrigida: `403`/redirect do middleware.
- [ ] `PC-SUPPORTCONFIGS-34` — Flag `supportconfigs=false` → qualquer rota (inclusive R11) responde `404 Modulo desativado.`.
- [ ] `PC-SUPPORTCONFIGS-35` — `GET /users/detail/{id}` inexistente com `Accept: application/json` → `404 {"error":"Usuário não encontrado."}`.
- [ ] `PC-SUPPORTCONFIGS-36` — `GET /logs/tail?lines=10` → `lines` no JSON = 50 (clamp inferior).
- [ ] `PC-SUPPORTCONFIGS-37` — `GET /logs/tail?file=../../.env` → devolve `laravel.log`/log do dia, nunca `.env`.
- [ ] `PC-SUPPORTCONFIGS-38` — `POST /logs/clear` duas vezes no mesmo segundo → dois `.bak` com o mesmo nome (segunda cópia sobrescreve); comportamento a reproduzir ou corrigir conscientemente.
- [ ] `PC-SUPPORTCONFIGS-39` — POST sem `X-CSRF-TOKEN` → `419`.
- [ ] `PC-SUPPORTCONFIGS-40` — `active_pattern` em array → não-suporte em `/cms/infra/user-configs/cms-theme/reset` recebe `403 Forbidden. Support area is restricted.` (AJAX) ou redirect com `Acesso negado. Esta área é restrita ao suporte técnico autorizado.`.

## 15. Evidências consultadas

- `V2/Modules/Support/SupportConfigs/Routes/cms.php:1-64`
- `V2/Modules/Support/SupportConfigs/Controllers/SupportConfigsCmsController.php:25-305`
- `V2/Modules/Support/SupportConfigs/ModuleProvider.php:17-100`
- `V2/Modules/Support/SupportConfigs/Services/SupportConfigsService.php:26-248`
- `V2/Modules/Support/SupportConfigs/Services/SystemMaintenanceService.php:47-63,127-161,357-430,568-597`
- `V2/Modules/Support/SupportConfigs/Services/SystemDiagnosticsService.php:63-238`
- `V2/Modules/Support/SupportConfigs/Services/UserManagementService.php:38-182`
- `V2/Core/Providers/V2ServiceProvider.php:86-133,290-306`
- `V2/Core/Middleware/CmsAuthenticate.php:18-44`
- `V2/Core/Middleware/V2PermissionMiddleware.php:28-113`
- `V2/Core/Middleware/ModuleAccessMiddleware.php:36-64`
- `V2/Core/Middleware/MaintenanceMiddleware.php:33-52`
- `V2/Core/Services/ModuleFlagService.php:24-127`
- `app/Http/Kernel.php:30-69`
- `V2/Modules/Support/StructuralLandingEditor/Controllers/UserConfigsCmsController.php:12-46`
- `V2/Modules/Support/StructuralLandingEditor/Routes/cms.php:18-63`
- `V2/Modules/Support/SupportConfigs/Views/cms/supportConfigs/tabs/{modulos,suporte,logs,manutencao}.blade.php`
- `_qa/security-g1.md:79-94`
