---
id: MOD-SUPPORT-SUPPORTCONFIGS
tipo: modulo
onda: 3
status: concluido
unidades: [MOD-SUPPORT-SUPPORTCONFIGS]
fontes:
  - V2/Modules/Support/SupportConfigs/
depende_de: [CORE-BOOT, CORE-AUTH, CORE-DATA, CORE-CMS, SVC-STORAGE, SVC-ACTIVITYLOGGER]
consumidores: [MOD-SUPPORT-STRUCTURALLANDINGEDITOR, UI-CMS, UI-PORTAL, CORE-RUNTIME, SVC-MAILSERVICE, SVC-SEO, SVC-FEEDRSS]
criticidade: alta
dono: PageSupportConfigs
---

# Support/SupportConfigs

> Painel técnico de uma pessoa (o e-mail de suporte) que controla feature flags, modo de
> manutenção, depuração, logs, reset de senha de qualquer usuário — e, por tabela, o tema
> do CMS e as configurações do portal que outros módulos consomem. Não é um CRUD de
> configurações: é um console de operações com efeito global e guarda por action.

## 0. Ficha de identidade

| Campo | Valor |
|---|---|
| Efeito primário | Um único usuário (e-mail de `config/v2.php`) liga/desliga módulos, ativa manutenção, expõe stack traces, trunca logs do Laravel, redefine a senha de qualquer conta e restaura o tema do CMS, com efeito imediato no portal e no CMS. |
| Entrada | `GET/POST /cms/infra/support-configs/*` (10 rotas) e `POST /cms/infra/user-configs/cms-theme/reset`; parâmetros `module_key`, `active`, `module_*`, `maintenance_mode`, `app_debug`, `debugbar_enabled`, `q`, `user_id`, `new_password`, `lines`, `file`. |
| Saída | HTML de 4 abas; JSON para toggle, busca, detalhe e tail de log; `302` com flash `success`/`error`; `403` `Acesso restrito ao suporte técnico autorizado.`; linhas em `v2_module_flags`, `v2_system_controls`, `v2_cms_theme_configs`; linhas em `activity_log`; backup `.bak` e truncamento em `storage/logs`; nova senha Sentinel. |
| Efeitos colaterais | `activity_log` (subject fake), log de aplicação em `storage/logs/laravel*.log`, limpeza de cache de aplicação/config/views, escrita de senha sem confirmação, alteração global de CSS do CMS e de variáveis do portal. |
| Particularidade central | A guarda é **por action** (`authorizeSupportAccess()` no início de todas), não por middleware: o `active_pattern` do provider é uma string com `|` (`ModuleProvider.php:87`) que `Request::is()` nunca casa, então RBAC e bloqueio do grupo `Support` não se aplicam (`V2PermissionMiddleware.php:40-65,74-77`). |
| Como se integra | Rota nomeada `v2.supportconfigs.*`; view namespace `v2-supportconfigs`; classes em `V2\Modules\Support\SupportConfigs\Services`; tabelas `v2_*` lidas por `ModuleFlagService`, `CmsThemeComposer`, `PortalConfigsService`, `MailService`. |
| Se removido, quebra | `v2-portal::*` perde `$portalTheme`/`$portalConfigs`; `v2-cms::*` perde `$cmsThemeCss`/`$cmsThemeConfigs`; sidebar perde filtro de flags (mas `ModuleFlagService` continua existindo); `MailService` mantém a tabela, mas o formulário de e-mail fica órfão. |
| Superfície pública | `SupportConfigsService` (fachada), `PortalConfigsService`, `SystemDiagnosticsService`, `SystemMaintenanceService`, `UserManagementService`, `CmsThemeComposer`, `SupportConfigsCmsController`. |

## 1. Efeito

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

- **Suporte** (`config('v2.support.email')`, default `suporte@maxima.inf.br` — `config/v2.php:5`): abrir o painel e ver diagnóstico (PHP, Laravel, ambiente, debug, drivers, último cron, banco) (`SystemDiagnosticsService.php:38-54`).
- **Suporte**: limpar cache da aplicação (`cache:clear`, `config:clear`, `view:clear`) e ver a mensagem `Cache do sistema limpo com sucesso.` / `Falha ao limpar o cache do sistema.` (`SupportConfigsCmsController.php:81-92`).
- **Suporte**: ligar/desligar um módulo por AJAX, com modal de confirmação no navegador; o módulo some do menu e suas rotas CMS passam a responder `404` (`modulos.blade.php:187-224`; `ModuleAccessMiddleware.php:38-62`).
- **Suporte**: alternar `maintenance_mode`, `app_debug` e `debugbar_enabled`; manutenção bloqueia o portal público com `503` e página `v2-cms::staticPages.maintenance` (`MaintenanceMiddleware.php:41-52`).
- **Suporte**: buscar usuário por e-mail/nome (mínimo 2 caracteres, limite 20), ver papéis, permissões e as 30 últimas atividades, e **forçar nova senha** (`UserManagementService.php:38-68,74-120,122-145`).
- **Suporte**: listar `storage/logs/laravel*.log`, ler as últimas N linhas (50–2000) e truncar o arquivo com backup automático (`SystemDiagnosticsService.php:63-84,91-129,135-153`).
- **Qualquer usuário CMS autenticado com ao menos um papel**: `POST /cms/infra/user-configs/cms-theme/reset` restaura o tema do CMS, porque essa rota não tem guarda de controller (SEC-G1-04).

### 1.2 O que muda no sistema

| Efeito | Onde é produzido | Fonte |
|---|---|---|
| `v2_module_flags.active` atualizado ou linha criada com `module_group='OUTROS'` | `toggleModule` | `SystemMaintenanceService.php:127-161` |
| `Cache::forget('v2_module_flags')` (chave que ninguém grava) | `toggleModule` | `SystemMaintenanceService.php:157` |
| `v2_system_controls.control_value` vira `'true'`/`'false'` | `updateSystemControl` | `SystemMaintenanceService.php:357-405` |
| `config(['app.debug'…])`, `ini_set('display_errors'…)`, `debugbar->enable/disable` **apenas na requisição corrente** | `applySystemControl` | `SystemMaintenanceService.php:568-597` |
| `v2_cms_theme_configs` atualizado/criado por chave; logo/favicon podem virar caminho no storage | `saveCmsThemeConfigs` / `saveSingleCmsThemeConfig` | `SystemMaintenanceService.php:244-289,439-487` |
| 6 chaves de tema CMS voltam aos defaults (marca, logo e favicon inclusive) | `resetCmsThemeToDefault` | `SystemMaintenanceService.php:292-317` |
| Senha Sentinel do usuário-alvo é sobrescrita | `Sentinel::update($user, ['password' => $newPassword])` | `UserManagementService.php:130` |
| Arquivo de log copiado para `.YYYYmmdd_His.bak` e truncado para `''` | `clearLogFile` | `SystemDiagnosticsService.php:135-153` |
| `activity_log` recebe `feature_flag_updated`, `system_control_updated`, `log_clear_updated` etc. com subject fake | `logSimpleAudit` | `SystemMaintenanceService.php:611-625`; `SystemDiagnosticsService.php:263-274` |
| Cache de aplicação, configuração compilada e views compiladas são apagados | `clearSystemCache` | `SystemMaintenanceService.php:47-63` |

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

| Entrada | Resultado |
|---|---|
| `GET /cms/infra/support-configs` como suporte | `200`, view `v2-supportconfigs::cms.supportConfigs.index` com `diagnostics`, `modules`, `systemControls`, `history` (`SupportConfigsCmsController.php:59-71`). |
| Qualquer action como não-suporte | `403` `Acesso restrito ao suporte técnico autorizado.` (`SupportConfigsCmsController.php:47-53`), **exceto** `resetCmsTheme`. |
| Sem autenticação | `302` para `v2.cms.login` (ou `401 Unauthorized.` em AJAX) via `CmsAuthenticate.php:21-28`. |
| Autenticado sem papel | `302` para login com `Acesso negado. Seu usuário não possui permissões administrativas.` (`CmsAuthenticate.php:30-44`). |
| `POST /modules/toggle` sem `module_key` | JSON `422 {"success":false,"message":"module_key é obrigatório."}`; HTML: redirect com `error` (`SupportConfigsCmsController.php:108-113`). |
| `POST /modules/toggle` válido | JSON `200 {"success":true,"message":"Módulo 'x' ativado com sucesso."}` ou HTML com flash equivalente (`:117-128`). |
| `POST /modules/batch` sem campos `module_*` | flash `error` `Nenhum módulo selecionado para alteração.` (`:146-148`). |
| `POST /system-controls/save` | flash `success` `Controles de sistema atualizados com sucesso.` ou `error` `Falha ao atualizar controles de sistema.` (`:171-180`). |
| `GET /users/search?q=a` | `{"users":[]}` (JSON) / flash `error` `Digite ao menos 2 caracteres para buscar.` (`:195-200`). |
| `GET /users/detail/{id}` inexistente | JSON `404 {"error":"Usuário não encontrado."}` / redirect com `error` (`:222-227`). |
| `POST /users/reset-password` sem campos | flash `error` `user_id e new_password são obrigatórios.` (`:246-248`). |
| `POST /users/reset-password` senha < 6 | flash `error` `A nova senha deve ter no mínimo 6 caracteres.` (`:250-252`). |
| `GET /logs/tail?lines=9999&file=../../.env` | `lines` vira `2000`; `file` inválido cai no log do dia/`laravel.log` (`SupportConfigsCmsController.php:271-278`; `SystemDiagnosticsService.php:171-187`). |
| `POST /logs/clear` | arquivo truncado + backup; flash `Arquivo de log limpo com sucesso (backup gerado automaticamente).` (`:297-303`). |
| `POST /cms/infra/user-configs/cms-theme/reset` como qualquer papel | `200` com corpo vazio (a action não retorna redirect nem flash); tema CMS sobrescrito (`Routes/cms.php:61-64`). |

### 1.4 O que este módulo NÃO faz

- Não tem middleware de permissão próprio: não há `v2_can`, `abort_unless` de role, nem slug RBAC em nenhuma rota; a única barreira é e-mail + `CmsAuthenticate` (`Routes/cms.php:18,61`).
- Não cobre criação/edição de configurações de portal, e-mail, SEO ou tema: essas telas e o CRUD estão em `MOD-SUPPORT-STRUCTURALLANDINGEDITOR` (SLE); aqui só ficam os serviços consumidos (`PortalConfigsService`, `getCmsThemeConfigs`, `saveCmsThemeConfigs`, `resetCmsThemeToDefault`).
- Não envia e-mail; apenas lê/escreve a tabela SMTP consumida por `SVC-MAILSERVICE`.
- Não apaga tabelas nem dados de módulos ao desativar uma flag: só `active=false` (`SystemMaintenanceService.php:138-143`).
- Não apaga arquivos de log na limpeza: gera `.bak` e trunca (`SystemDiagnosticsService.php:146-148`).
- Não invalida cache de página/HTTP após alterar flags; `Cache::forget('v2_module_flags')` não atinge o cache estático real (`ModuleFlagService.php:24`).
- Não protege o próprio reset de tema CMS, que é a rota mais destrutiva do conjunto (SEC-G1-04).

## 2. Abordagem

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

O módulo foi desenhado como **fachada + sub-serviços + chamadas diretas a `DB::table`**, sem models. `SupportConfigsService` só delega para três serviços (`SupportConfigsService.php:42-50`); as escritas são upsert manual (`where(...)->first()` → `update`/`insert`) e a auditoria usa um `Model` anônimo como subject descartável. O roteamento do CMS descobre o provider, registra a view namespace `v2-supportconfigs` e o grupo de rotas com `web` + `module.active:SupportConfigs` (`V2ServiceProvider.php:100-127,303-306`). A restrição real é feita no controller, action por action.

```mermaid
flowchart LR
  U[suporte autenticado] --> CA[CmsAuthenticate]
  CA --> PM[V2PermissionMiddleware]
  PM -->|active_pattern nao casa| PASS[segue sem RBAC]
  PASS --> C[SupportConfigsCmsController]
  C --> G{authorizeSupportAccess}
  G -->|email != v2.support.email| X[403 / log warning]
  G -->|ok| S[SupportConfigsService]
  S --> D[SystemDiagnosticsService]
  S --> M[SystemMaintenanceService]
  S --> UM[UserManagementService]
  D --> F[(storage/logs)]
  M --> T1[(v2_module_flags)]
  M --> T2[(v2_system_controls)]
  M --> T3[(v2_cms_theme_configs)]
  UM --> U1[(users / Sentinel)]
  C --> V[v2-supportconfigs::cms.supportConfigs.*]
```

### 2.2 Mecânica passo a passo

1. **Boot**: `V2ServiceProvider` varre `V2/Modules/Support/SupportConfigs`, instancia `ModuleProvider`, injeta `group='Support'` no config e registra o namespace `v2-supportconfigs` (`V2ServiceProvider.php:100-127`). O construtor do provider registra dois composers: `v2-portal::*` → closure com `$portalTheme`/`$portalConfigs` (`ModuleProvider.php:19-62`) e `v2-cms::*` → `CmsThemeComposer` (`ModuleProvider.php:69-72`).
2. **Rota**: grupo `cms/infra/support-configs` com `CmsAuthenticate` (`Routes/cms.php:18`). O mesmo `cms.php` registra um segundo grupo, `cms/infra/user-configs`, com uma única rota que aponta para um controller da SLE (`Routes/cms.php:61-64`).
3. **Autenticação**: `CmsAuthenticate` exige Sentinel + ≥1 papel e delega a `V2PermissionMiddleware` (`CmsAuthenticate.php:21-44`).
4. **Autorização de módulo**: `V2PermissionMiddleware` percorre `v2.modules.configs`, testa `$request->is($pattern)` e, se casar, aplica o bloqueio do grupo `Support` e o RBAC `none/read/write` (`V2PermissionMiddleware.php:40-65,74-110`). Com o padrão atual, não casa (ver 3.1).
5. **Guarda de action**: cada método começa com `authorizeSupportAccess()`, que compara estritamente `Sentinel::getUser()->email` com `config('v2.support.email')` e aborta `403` logando warning (`SupportConfigsCmsController.php:47-53`).
6. **Leitura do painel**: `index` chama `getDiagnostics`, `getModulesStatus`, `getSystemControls` e devolve `history` vazio (`:59-71`).
7. **Escrita**: cada action valida entrada no controller (não há FormRequest), chama o serviço e traduz `bool` em flash; AJAX recebe `json` em `toggle`, `searchUsers`, `getUserDetail` e `getLogTail`.
8. **Efeito de configuração**: o portal lê `v2_portal_configs` no composer; o CMS lê `v2_cms_theme_configs` no composer; a manutenção é lida a cada request pelo helper; as flags são lidas por `ModuleFlagService` (cache estático) — ver `dados.md`.

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

- **Guarda no controller em vez de middleware** `[C]`: permite que o mesmo prefixo tenha rotas com e sem guarda (foi exatamente o que produziu SEC-G1-04) e evita depender do `active_pattern`. Repetida em 10 actions (`SupportConfigsCmsController.php:47-53`).
- **`restricted` no `getConfig()`** `[C]`: o sidebar usa `restricted` para esconder o item de quem não é o e-mail de suporte (`SidebarComposer.php:50-58`); não é um controle de acesso a rota.
- **Configurações em tabelas chave-valor separadas por domínio** `[C]`: cinco tabelas (`mail`, `module_flags`, `system_controls`, `cms_theme`, `portal_configs`) em vez de uma `configurations` genérica; cada serviço lê só as suas chaves e faz fallback em defaults quando a tabela não existe (`SystemMaintenanceService.php:325-350`; `PortalConfigsService.php:37-56`).
- **Upsert sem model e sem transação por chave** `[C]`: `saveSingleCmsThemeConfig` e `PortalConfigsService::save` fazem `first()` + `update`/`insert`; a transação fica no chamador (`SystemMaintenanceService.php:273-287`; `PortalConfigsService.php:79-92`).
- **Auditoria com subject fake** `[C]`: como não há model de configuração, usa-se `new class extends Model {}` e `id = contexto_alvo`; o `subject_type` gravado é a classe anônima, o que quebra a consulta de histórico (`PortalConfigsService.php:141-160,163-195`).
- **Runtime apply para debug** `[I]`: `applySystemControl` só altera `config()`/`ini_set()`/debugbar na requisição que salvou; nada no boot relê `v2_system_controls` para `app_debug`, então o toggle é um efeito de um request só (`SystemMaintenanceService.php:568-597`; `grep app_debug` não encontra outro consumidor).
- **Manutenção via helper por request** `[C]`: `v2_is_maintenance_active()` lê a tabela e exige literal `'true'`; não há cache (`helpers.php:236-248`).

### 2.4 Trechos de código pivô

```php
// V2/Modules/Support/SupportConfigs/ModuleProvider.php:80-94 — o contrato de menu e o padrão quebrado.
public function getConfig(): array
{
return [
    'title' => 'Suporte Técnico',
    'description' => 'Configurações técnicas, manutenção, feature flags e logs do CMS.',
    'icon' => 'bi bi-wrench',
    'position' => 90,
    'active_pattern' => 'cms/infra/support-configs*|cms/infra/user-configs*',
    'restricted' => config('v2.support.email'),
    'subModulesLevel1' => [
        [
            'title' => 'Painel de Suporte',
            'icon' => 'bi bi-wrench',
            'route' => 'v2.supportconfigs.index',
            'position' => 1,
            'subModulesLevel2' => []
        ],
    ]
];
```

Mostra a fonte do defeito: `active_pattern` é uma **string** e o problema é o `|` dentro dela. String única é formato suportado — vários providers usam uma e funcionam (`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`). `Request::is()` recebe um único padrão; o `|` é escapado por `Str::is()` e passa a ser literal, então nenhuma URI do módulo casa. Efeito em cadeia: `$matched` fica `false` em `V2PermissionMiddleware.php:40`, o bloco do grupo `Support` (`V2PermissionMiddleware.php:51-77`, incluindo o `if (!$matched) return $next` de `:75-77`) não roda e o RBAC do módulo (`:78-110`) também não. A sidebar continua escondendo o item por `group`/`restricted`, mas a rota não é protegida pelo middleware — só pela guarda do controller.

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

// só executa quando $matched === true
if ($matched && strtoupper($currentModuleConfig['group']) === 'SUPPORT') {
    if ($user->email !== config('v2.support.email')) {
        Log::warning('[Permission] Acesso negado ao grupo Support: ' . $user->email);
        // ... 403 (AJAX) ou redirect com "Acesso negado. Esta área é restrita..."
    }
}
```

O bloco `Support` **só executa se `$matched` for verdadeiro**. Como o `group` é injetado pelo diretório (`V2ServiceProvider.php:109-111` → `'Support'`), o módulo *seria* restrito se o padrão casasse. Hoje o middleware segue para `if (!$matched) return $next($request)` (`:74-77`) e entrega a decisão ao controller. Para `user-configs` não existe guarda de controller.

```php
// V2/Modules/Support/SupportConfigs/Controllers/SupportConfigsCmsController.php:47-53
private function authorizeSupportAccess()
{
    if (!Sentinel::check() || Sentinel::getUser()->email !== config('v2.support.email')) {
        Log::warning('[SupportConfigsCmsController] Tentativa de acesso negado ao Suporte Técnico por: ' . (Sentinel::check() ? Sentinel::getUser()->email : 'usuário não autenticado'));
        abort(403, 'Acesso restrito ao suporte técnico autorizado.');
    }
}
```

A guarda é chamada nas 10 actions (`:61,83,103,135,167,192,218,241,271,294`). É estrita (`!==`, case-sensitive), não usa `inRole('super-admin')` e **não** é aplicada em `UserConfigsCmsController@resetCmsTheme`.

```php
// V2/Modules/Support/SupportConfigs/Services/SystemMaintenanceService.php:127-159 — upsert de flag + cache errado.
$record = DB::table('v2_module_flags')->where('module_key', $moduleKey)->first();
if ($record) {
    DB::table('v2_module_flags')->where('module_key', $moduleKey)
        ->update(['active' => $active, 'updated_at' => now()]);
} else {
    DB::table('v2_module_flags')->insert([
        'id' => (string) v2_uuid(),
        'module_key' => $moduleKey,
        'module_title' => $this->getDefaultModuleTitle($moduleKey),
        'active' => $active,
        'module_group' => 'OUTROS', // ... created_at/updated_at
    ]);
}
$this->logSimpleAudit('feature_flag', $moduleKey, $active);
Cache::forget('v2_module_flags'); // cache real é ModuleFlagService::$flags
return true;
// catch (\Exception $e) { Log::error(...); return false; }
```

Aceita qualquer `module_key` (não há whitelist), cria linha se não existir (com grupo `OUTROS`, invisível na UI) e engole a exceção retornando `false` — o que impede o rollback do batch (3.3). O `Cache::forget` é inócuo: o cache real é `ModuleFlagService::$flags`, estático (`ModuleFlagService.php:24`).

```php
// V2/Modules/Support/SupportConfigs/Services/SystemDiagnosticsService.php:135-153 — truncamento com backup.
public function clearLogFile($filename = null)
{
    $dir = storage_path('logs');
    $logFile = $this->resolveLogPath($dir, $filename);
    if (!file_exists($logFile)) {
        return true;
    }
    try {
        $backupFile = $dir . '/' . basename($logFile) . '.' . date('Ymd_His') . '.bak';
        copy($logFile, $backupFile);
        file_put_contents($logFile, '');
        $this->logSimpleAudit('log_clear', basename($logFile), 'arquivo limpo, backup em ' . basename($backupFile));
        Log::info('[SupportConfigsService] Log limpo: ' . basename($logFile) . '. Backup: ' . $backupFile);
        return true;
    } catch (\Exception $e) {
        Log::error('[SupportConfigsService] Erro ao limpar log: ' . $e->getMessage());
        return false;
    }
}
```

Arquivo inexistente retorna `true` (sucesso silencioso). O caminho é resolvido por `resolveLogPath` (`:171-187`), que só aceita basename casando `^laravel[\-0-9a-zA-Z._]+\.log$`; qualquer outro valor cai no log do dia ou em `laravel.log`. A cópia `.bak` fica no mesmo diretório e **não** é apagada.

```php
// V2/Modules/Support/SupportConfigs/ViewComposers/CmsThemeComposer.php:32-50 — tema CMS aplicado de fato.
try {
    $configs = DB::table('v2_cms_theme_configs')
        ->whereNull('deleted_at')
        ->pluck('config_value', 'config_key')
        ->toArray();
} catch (\Exception $e) {
    $configs = [];
}
$sidebarBg    = $configs['cms_sidebar_bg'] ?? $defaults['cms_sidebar_bg'];
$brandPrimary = $configs['cms_brand_primary'] ?? $defaults['cms_brand_primary'];
$bodyBg       = $configs['cms_body_bg'] ?? $defaults['cms_body_bg'];
$cmsThemeConfigs = [
    'cms_sidebar_bg'    => $sidebarBg,
    'cms_brand_primary' => $brandPrimary,
    'cms_body_bg'       => $bodyBg,
    'cms_brand_name'    => $configs['cms_brand_name'] ?? 'Maxima',
    'cms_brand_logo'    => $configs['cms_brand_logo'] ?? '',
    'cms_favicon'       => $configs['cms_favicon'] ?? '',
];
```

Prova que o tema CMS **não é inerte**: `$cmsThemeCss` é injetado em `V2/layouts/cms/layouts/app.blade.php:44-46`, `cms_brand_name` vira `<title>` (`:7`), `cms_favicon` vira `<link rel="icon">` (`:10-14`) e `cms_brand_logo` vira imagem da sidebar (`V2/layouts/cms/layouts/sidebar.blade.php:4-9`). Cada view `v2-cms::*` (inclusive parciais) dispara uma query, pois não há cache.

### 2.5 Algoritmos, consultas e custos

- `getModulesStatus()`: uma query `pluck('active','module_key')` + merge com o container; ordenação em PHP por `group` e `title`; sem paginação (`SystemMaintenanceService.php:77-120`).
- `toggleModule`: 1 `SELECT` + 1 `UPDATE` (ou `INSERT`); auditoria faz 1 `INSERT` em `activity_log`. Batch de N módulos = 2N+ consultas dentro de uma transação que **não** reverte em falha capturada (`:173-186`).
- `getSystemControls`: 1 `pluck` + `filter_var` por linha; defaults mesclados (`:325-350`).
- `saveCmsThemeConfigs`: até 6 upserts + 6 inserts de auditoria; logo pode gerar upload (`UploadService::processAndUploadImage`), favicon gera `deleteSafe` + `put` no storage (`:244-289,491-536`).
- `clearSystemCache`: 3 chamadas Artisan síncronas (podem custar segundos), mais auditoria. `cache:clear` apaga também `last_cron_run`, então o diagnóstico volta a `Nunca` (`SystemDiagnosticsService.php:48`).
- `searchUsers`: `ILIKE` em três colunas, `with('roles')`, `limit(20)`; sem índice dedicado garantido, full scan proporcional a `users` (`UserManagementService.php:38-68`).
- `getLogTail`: `shell_exec('which tail')` + `tail -n N` quando disponível; fallback PHP lê em blocos de 4096 bytes de trás para frente (`SystemDiagnosticsService.php:196-238`). Custo O(N) no pior caso, memória O(linhas).
- `getLogFiles`: `glob` + `filesize`/`filemtime` por arquivo, ordenado por nome desc (`:63-84`). `getLogTail` sempre devolve a lista completa — duas varreduras por chamada.

## 3. Particularidades

### 3.1 O que só existe aqui

- **Guarda por action repetida** em vez de middleware: é a única unidade que protege 10 endpoints com um `private function` copiado em cada um, e a única que tem uma rota irmã (reset de tema) **sem** essa guarda.
- **Console de operações**: nenhum outro módulo apaga cache, trunca logs, força senha de terceiros e expõe stack trace. O poder é concentrado em um único e-mail.
- **Tabelas de configuração sem model**: cinco tabelas globais lidas por serviços de outros módulos; não há `Models/` no diretório.
- **Composers registrados no construtor do provider**: o provider tem efeito colateral de view (composer de portal e de CMS) além do menu.
- **`history` sempre vazio**: `index` passa `collect()` literal (`SupportConfigsCmsController.php:67-68`) mesmo existindo `activity_log`.
- **Segunda rota para um controller de outro módulo**: `Routes/cms.php:61-64` registra `UserConfigsCmsController@resetCmsTheme`, fisicamente em `V2/Modules/Support/StructuralLandingEditor/`.

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

| ID | Regra | Efeito | Fonte |
|---|---|---|---|
| `RN-SUPPORTCONFIGS-01` | Só o e-mail exato de `config('v2.support.email')` passa em `authorizeSupportAccess`; `super-admin` não tem bypass. | `403` para qualquer outro, inclusive admin. | `SupportConfigsCmsController.php:47-53` |
| `RN-SUPPORTCONFIGS-02` | O `active_pattern` do módulo nunca casa porque contém `|` em string única. | RBAC do módulo e bloqueio do grupo `Support` não são aplicados pelo middleware. | `ModuleProvider.php:87`; `V2PermissionMiddleware.php:40-42,74-77` |
| `RN-SUPPORTCONFIGS-03` | `resetCmsTheme` não tem guarda de controller nem permissão de módulo efetiva. | Qualquer usuário CMS com ≥1 papel sobrescreve o tema do CMS. | `Routes/cms.php:61-64`; `UserConfigsCmsController.php:38-46` |
| `RN-SUPPORTCONFIGS-04` | Flag ausente em `v2_module_flags` significa **ativo**. | Módulo novo aparece e funciona sem seed. | `SystemMaintenanceService.php:102`; `ModuleFlagService.php:93-94` |
| `RN-SUPPORTCONFIGS-05` | `maintenance_mode` só é verdadeiro quando `control_value === 'true'`. | `'1'`/`'TRUE'` não ativam manutenção, embora o dashboard os trate como ativos. | `helpers.php:236-248`; `DashboardStatsService.php:394-398` |
| `RN-SUPPORTCONFIGS-06` | `app_debug`/`debugbar_enabled` só são aplicados na requisição do save. | Persistem no banco, mas não no próximo request. | `SystemMaintenanceService.php:568-597` |
| `RN-SUPPORTCONFIGS-07` | Clamp do tail: 50 ≤ `lines` ≤ 2000, default 500. | Valores fora da faixa são corrigidos, não rejeitados. | `SupportConfigsCmsController.php:271-274` |
| `RN-SUPPORTCONFIGS-08` | Nome de log aceito: `laravel[-0-9a-zA-Z._]+\.log` (basename). | Path traversal é descartado; `file` inválido vira log do dia. | `SystemDiagnosticsService.php:171-187` |
| `RN-SUPPORTCONFIGS-09` | Troca de senha exige ≥6 caracteres apenas no controller; `Sentinel::update` não valida política. | Senha fraca é aceita; não há e-mail de aviso. | `SupportConfigsCmsController.php:250-252`; `UserManagementService.php:130` |
| `RN-SUPPORTCONFIGS-10` | `resetCmsThemeToDefault` grava `''` em logo/favicon e `Maxima` em marca. | Não apaga os arquivos do storage, só as referências. | `SystemMaintenanceService.php:292-317` |
| `RN-SUPPORTCONFIGS-11` | `v2_portal_configs` não tem linha seedada para `theme_secondary_color`. | Valor efetivo vem do default `#0a4c8c` do composer/serviço. | `2026_06_30_000005_create_v2_portal_configs_table.php:55-65`; `ModuleProvider.php:33` |

### 3.3 Casos-limite e comportamentos surpreendentes

- **`toggleModule` nunca lança**: retorna `false` em qualquer exceção. Em `updateModulesBatch`, o `try/catch` externo nunca vê o erro, então a transação **commita** as alterações anteriores e devolve `true` mesmo com falhas parciais (`SystemMaintenanceService.php:173-186`). O mesmo vale para `updateSystemControlsBatch` (`:412-430`).
- **`updateModulesBatch` só vê checkboxes enviados**: campos `module_*` ausentes não são desligados; um form que não envia nada retorna `Nenhum módulo selecionado...`. A UI atual não usa esse endpoint (usa toggle AJAX), então ele é código de contrato sem chamador na view.
- **Auto-bloqueio**: o endpoint de toggle aceita `module_key=supportconfigs`; desligar a flag faz `module.active:SupportConfigs` responder `404 Modulo desativado.` no próprio painel (`ModuleAccessMiddleware.php:38-62`). A recuperação exige `UPDATE` manual no banco. A UI esconde o grupo `Support`, mas não impede o POST forjado.
- **`getLogTail` sem JSON**: se chamado sem `Accept: application/json`, retorna a view `index` passando **apenas** `logData`, sem `$diagnostics`, `$modules`, `$systemControls` → erro de variável indefinida na renderização (`SupportConfigsCmsController.php:282-284`).
- **Log vazio/inexistente**: `getLogTail` devolve `content = ['[INFO] Arquivo de log não encontrado ou ainda vazio.']` e `size='0 B'`; `clearLogFile` devolve `true` sem criar backup (`SystemDiagnosticsService.php:96-104,141-143`).
- **Busca com curinga**: `%` e `_` no termo não são escapados; `q=%` lista os 20 primeiros usuários (`UserManagementService.php:43-48`). `ILIKE` é específico de PostgreSQL — SQLite/MySQL quebram e o `catch` devolve coleção vazia.
- **`getUserDetail` engole exceção** e devolve `null`; o controller responde `404` mesmo para falha de banco (`UserManagementService.php:74-120`).
- **Tema CMS**: `cms_brand_logo` aceita ID da galeria (`v2_gallery_media`) convertido em `file_path`; se o ID não existir, grava o próprio ID como caminho e a sidebar renderiza URL inválida (`SystemMaintenanceService.php:246-252`). `cms_favicon` como string é **ignorado** (`unset`), só upload vale (`:256-261`).
- **Favicon**: só `.ico`, MIME `image/x-icon|image/vnd.microsoft.icon|image/ico|application/octet-stream`, ≤100 KB; caminho fixo `structural-landing-editor/favicon/cms.ico`; erro lança `InvalidArgumentException` (`:491-536`). No único caminho HTTP (`theme.save` da SLE), `UserConfigsCmsController@update` re-lança (`:30-35`) e `SleCmsController::saveTheme` captura (`:155-159`) → **`302` + flash `Erro ao salvar tema: ...`**; `500` só ocorreria em chamador que não capture a exceção.
- **`v2_cms_theme_configs.config_value` é `string(255)`**: uploads de logo/favicon guardam caminho, não conteúdo; qualquer valor maior quebra no banco.
- **Cache limpo apaga `last_cron_run`**: depois de “Limpar Cache”, o diagnóstico mostra `Nunca` até o próximo cron gravar a chave (`SystemDiagnosticsService.php:48`).
- **Composer do portal**: o `static $portalTheme` é por processo; sob PHP-FPM cada request refaz a query, mas em worker persistente (Octane) o tema fica congelado até o processo reiniciar (`ModuleProvider.php:20-22`).
- **Dois composers em `v2-portal::*`**: `V2ServiceProvider.php:55-72` define `portalConfigs=[]` e `siteTitle`; o do módulo (`ModuleProvider.php:19-62`) sobrescreve `portalConfigs` com o mapa completo depois, pois é registrado depois. Ordem é contrato implícito.

### 3.4 Armadilhas de reimplementação

- **Não trocar a guarda por `active_pattern` sem antes corrigir o formato**: em Laravel 5.5, `Request::is('a*|b*')` não separa alternativas; use array `['a*','b*']`. Só isso já restaura o bloqueio do grupo `Support` (`group` é injetado como `'Support'`).
- **Não confiar em `Cache::forget('v2_module_flags')`**: o cache efetivo é estático em `ModuleFlagService`; um toggle não invalida a sidebar no mesmo processo. Em FPM o próximo request relê; em workers longos, não.
- **Não tratar `maintenance_mode` como boolean do banco**: o helper compara string exata `'true'`; gravar `1`, `true` (boolean) ou `'TRUE'` não ativa a manutenção. A UI envia `1`/`0`, mas o serviço normaliza para `'true'`/`'false'` (`SystemMaintenanceService.php:366,374`).
- **Não aplicar `app_debug` só no save**: para paridade é preciso um boot reader (middleware/provider) que leia `v2_system_controls` em toda request; hoje o comportamento é “liga até o fim da requisição”.
- **Não usar `getHistory` como fonte**: o filtro `subject_type = Illuminate\Database\Eloquent\Model::class` não casa com a classe anônima usada na escrita; a consulta devolve vazio (`PortalConfigsService.php:165-180`).
- **Não reaproveitar `clearLogFile` para apagar**: a operação trunca e deixa `.bak`; o disco cresce com backups sem expurgo.
- **Não assumir transação atômica no batch**: falhas internas são convertidas em `false`, não em exceção.
- **Não assumir que a UI cobre todas as flags**: `modulos.blade.php` descarta grupos `SUPPORT` e `OUTROS`; flags seedadas de módulos não registrados (ex.: `diario`, se não houver diretório) nunca aparecem.

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

| Divergência | Documentação diz | Código faz | Fonte |
|---|---|---|---|
| Local do `UserConfigsCmsController` | `README.md` lista o controller neste módulo | Arquivo está em `StructuralLandingEditor/`; aqui só a rota | `README.md:16`; `Routes/cms.php:62` |
| Migrations | README lista só a de e-mail | Existem 7 migrations (5 tabelas + FKs + encrypt) | `README.md:106-107` |
| Serviços | README cita `UserConfigsService` aqui | Serviço está na SLE; aqui há 5 serviços diferentes | `Services/README.md:3-5,12-16` |
| Namespace | READMEs usam `V2\Modules\SupportConfigs\…` | Código usa `V2\Modules\Support\SupportConfigs\…` | `Controllers/README.md:5`; `SupportConfigsCmsController.php:3` |
| Abas | Docblock de `index` diz “5 abas” | São 4 abas (Módulos, Sistema, Suporte, Logs) | `SupportConfigsCmsController.php:57`; `index.blade.php:41-57` |
| Auditoria | README descreve subject `Configuration` (V1) | Subject é `Model` anônimo; `history` da view é `collect()` fixo | `README.md:90-97`; `SupportConfigsCmsController.php:67-68` |
| Rotação de logs | UI afirma “rotação diária, retenção 30 dias” | `config/app.php:127` usa `APP_LOG=single` por padrão; `log_max_files=30` só vale no driver `daily` | `logs.blade.php:89-91`; `config/app.php:127-131` |
| Busca por CPF | Docblock diz “e-mail, nome ou CPF” | Só `email`, `first_name`, `last_name` | `UserManagementService.php:34-45` |
| `refresh()` de flags | `ModuleFlagService` oferece `refresh()` | Ninguém chama após toggle | `ModuleFlagService.php:31-35` |
| Dependências injetadas | — | `ActivityLoggerService` no controller e `UploadService` em `SystemDiagnosticsService`/`UserManagementService` não são usados | `SupportConfigsCmsController.php:36-40`; `SystemDiagnosticsService.php:23-27` |
| FKs | Migration “add_v2_foreign_keys” está no módulo | Não toca nenhuma das 5 tabelas de configuração; mexe em `users`, `v2_digital_certificates`, `v2_signature_logs` | `2026_07_02_000001_add_v2_foreign_keys.php:14-53` |

## 4. Integrabilidade

### 4.1 Superfície pública

| Símbolo | Tipo | Assinatura | Precondição | Consumidores |
|---|---|---|---|---|
| `SupportConfigsService` | fachada | `getDiagnostics()`, `getLogTail($lines,$filename)`, `clearLogFile($filename)`, `clearSystemCache()`, `getModulesStatus()`, `toggleModule($key,$active)`, `updateModulesBatch(array)`, `getCmsThemeConfigs()`, `saveCmsThemeConfigs(array)`, `resetCmsThemeToDefault()`, `getSystemControls()`, `updateSystemControl($k,$v)`, `updateSystemControlsBatch(array)`, `searchUsers($term)`, `getUserDetail($id)`, `forcePasswordReset($id,$pwd)`, `getUserActivityLog($id)` | Tabelas podem não existir (fallback); nenhum método valida autorização | `UserConfigsCmsController`; `UserConfigsService` (SLE) |
| `PortalConfigsService` | serviço | `getAll(): array`, `getByGroup($group)`, `updateBatch(array)`, `save($key,$value)`, `getHistory(array,$limit=10)` | `save` não valida chave; `updateBatch` abre transação | `V2ServiceProvider`; `SEOServiceProvider`; `FeedRssService`; controllers de portal; views de homepage |
| `SystemDiagnosticsService` | serviço | `getDiagnostics()`, `getLogFiles()`, `getLogTail()`, `clearLogFile()` | `storage/logs` gravável para limpar | Só via fachada/controller |
| `SystemMaintenanceService` | serviço | flags, tema CMS, controles, cache, favicon | idem | `SupportConfigsService`; `UserConfigsService` |
| `UserManagementService` | serviço | `searchUsers`, `getUserDetail`, `forcePasswordReset`, `getUserActivityLog` | Sentinel; PostgreSQL para `ILIKE` | `SupportConfigsService` |
| `CmsThemeComposer` | ViewComposer | `compose(View $view)` injeta `$cmsThemeCss`, `$cmsThemeConfigs` | Registrado em `v2-cms::*` | `V2/layouts/cms/layouts/app.blade.php`; `sidebar.blade.php` |
| `SupportConfigsCmsController` | HTTP | 10 actions | Sessão Sentinel + papel + e-mail de suporte | Rotas `v2.supportconfigs.*` |

### 4.2 Contrato de UI/JS

Detalhado em `cms.md`. Resumo: quatro abas Bootstrap (`data-bs-toggle="pill"`), um modal de confirmação de flag (`#toggleConfirmModal`), um modal de reset de senha (`#passwordResetModal`), `fetch` para `modules/toggle`, `users/detail/{id}` e `logs/tail`, e formulários POST clássicos para o resto. Os stacks usados são `scripts`, `js`, `modals`, `css` (`V2/layouts/cms/layouts/app.blade.php:140-142`).

### 4.3 Ordem e ciclo de vida

1. `V2ServiceProvider::boot()` registra composer global do portal (`:55-72`), depois varre módulos (`:86-128`) — o provider de SupportConfigs é instanciado nessa varredura e registra seus composers **depois** do global.
2. Namespace de view `v2-supportconfigs` é registrado por `loadViewsFrom` (`V2ServiceProvider.php:121-124`).
3. Rotas CMS são carregadas com `web` + `module.active:SupportConfigs` (`:303-306`); o grupo interno adiciona `CmsAuthenticate` (`Routes/cms.php:18`).
4. `PermissionService`/`v2.modules.configs` já estão no container quando o middleware roda (`:132-133`).
5. `CmsThemeComposer` roda em toda view `v2-cms::*`; `v2-cms::components.box` e parciais também contam.
6. `PortalConfigsService` é resolvido por `app()` em vários pontos; se a tabela não existir, cai em defaults silenciosamente (`PortalConfigsService.php:37-56`).

### 4.4 Receitas de uso

**1) Ligar/desligar módulo por AJAX (contrato da UI):**

```http
POST /cms/infra/support-configs/modules/toggle
Content-Type: application/json
X-CSRF-TOKEN: <token>
X-Requested-With: XMLHttpRequest

{"module_key":"noticias","active":0}
```

Resposta `200`: `{"success":true,"message":"Módulo 'noticias' desativado com sucesso."}`. Efeito: `v2_module_flags.active=false`; o item some da sidebar na próxima request e as rotas CMS do módulo passam a `404` (`ModuleAccessMiddleware.php:43-62`).

**2) Ler o tema do CMS em uma view do painel:**

```php
// qualquer view v2-cms::* recebe as variáveis do composer
<title>{{ $cmsThemeConfigs['cms_brand_name'] ?? 'Maxima' }}</title>
<style>{!! $cmsThemeCss !!}</style>
```

Não chame `CmsThemeComposer` manualmente: o composer já roda; duplicar gera duas queries e CSS repetido.

**3) Consumir configurações do portal fora de `v2-portal::*`:**

```php
$configs = app(\V2\Modules\Support\SupportConfigs\Services\PortalConfigsService::class)->getAll();
$titulo = $configs['titulo_site'];       // default '' no serviço
$primary = $configs['theme_primary_color']; // default '#1075ad'
```

Use o serviço (não `DB::table`) para herdar defaults e o fallback quando a tabela não existe.

**4) Gravar tema CMS a partir de outra tela (padrão SLE):**

```php
app(\V2\Modules\Support\SupportConfigs\Services\SupportConfigsService::class)
    ->saveCmsThemeConfigs(['cms_sidebar_bg' => '#101820']);
```

Só chaves com prefixo `cms_` são aceitas; `UploadedFile` em `cms_favicon` dispara validação `.ico` (≤100 KB) e caminho fixo.

**5) Verificar manutenção:**

```php
if (v2_is_maintenance_active()) {
    // portal público já é interceptado por MaintenanceMiddleware; aqui é leitura direta
}
```

Para simular na reimplementação, grave literal `'true'` em `v2_system_controls.control_value`.

### 4.5 Anti-uso

**1) Proteger uma rota nova só com `active_pattern` deste módulo.** O padrão não casa; a rota ficaria aberta a qualquer usuário com papel. Ou corrija o formato (`['cms/infra/support-configs*','cms/infra/user-configs*']`) e mantenha a guarda, ou replique `authorizeSupportAccess()`.

**2) Chamar `resetCmsThemeToDefault()` de uma tela comum sem checar identidade.** É o caminho do SEC-G1-04: a rota existe, não tem guarda e destrói marca/logo/favicon. Qualquer novo chamador deve exigir `config('v2.support.email')` explicitamente.

**3) Alterar `v2_module_flags` por `DB::table()->update()` direto esperando desligar o módulo na hora.** `ModuleFlagService` mantém cache estático (`ModuleFlagService.php:24-63`); em worker persistente o menu continua igual até o processo reiniciar. Use `toggleModule` (que ainda assim não invalida o estático) e, na reimplementação, um cache invalidável.

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

- **Composer CMS sem cache**: uma query por view `v2-cms::*`, incluindo parciais; telas grandes fazem dezenas de queries idênticas.
- **Composer portal com cache por processo**: 1 query por request em FPM; congelado em Octane.
- **`getLogTail` devolve sempre a lista de arquivos** (glob + stat) e o conteúdo; com muitos logs e 2000 linhas a resposta JSON pode ter centenas de KB.
- **Limpeza de log** grava `.bak` do tamanho original: um log de 1 GB consome 2 GB no pico.
- **`clearSystemCache`** executa 3 comandos Artisan de forma síncrona; em produção com cache grande pode estourar timeout do request.
- **Senha**: não há rate limit por usuário-alvo nas rotas de suporte além do limite global do CMS; um suporte comprometido troca senhas em série.
- **Throttle/cache**: nenhuma rota declara `throttle` ou `cache` (`Routes/cms.php:18-64`).

## 5. Rotas e contratos HTTP

Inventário completo, middleware por rota, permissão e efeito em `rotas.md`. Resumo:

| Tipo | Método | URI | Nome | Controller@ação | Efeito |
|---|---|---|---|---|---|
| endpoint | GET | `/cms/infra/support-configs` | `v2.supportconfigs.index` | `SupportConfigsCmsController@index` | Renderiza 4 abas + diagnóstico |
| endpoint | POST | `/cms/infra/support-configs/clear-cache` | `v2.supportconfigs.clear_cache` | `@clearCache` | `cache:clear`, `config:clear`, `view:clear` |
| endpoint | POST | `/cms/infra/support-configs/modules/toggle` | `v2.supportconfigs.modules.toggle` | `@toggleModule` | Upsert de flag; JSON/redirect |
| endpoint | POST | `/cms/infra/support-configs/modules/batch` | `v2.supportconfigs.modules.batch` | `@updateModulesBatch` | Upsert de N flags |
| endpoint | POST | `/cms/infra/support-configs/system-controls/save` | `v2.supportconfigs.system_controls.save` | `@saveSystemControls` | Grava 3 controles |
| endpoint | GET | `/cms/infra/support-configs/users/search` | `v2.supportconfigs.users.search` | `@searchUsers` | JSON/redirect com 20 usuários |
| endpoint | GET | `/cms/infra/support-configs/users/detail/{id}` | `v2.supportconfigs.users.detail` | `@getUserDetail` | JSON com papéis/permissões/30 logs |
| endpoint | POST | `/cms/infra/support-configs/users/reset-password` | `v2.supportconfigs.users.reset_password` | `@forcePasswordReset` | Troca senha Sentinel |
| endpoint | GET | `/cms/infra/support-configs/logs/tail` | `v2.supportconfigs.logs.tail` | `@getLogTail` | JSON com N linhas + lista de arquivos |
| endpoint | POST | `/cms/infra/support-configs/logs/clear` | `v2.supportconfigs.logs.clear` | `@clearLogFile` | Backup + truncamento |
| endpoint | POST | `/cms/infra/user-configs/cms-theme/reset` | `v2.supportconfigs.cms_theme.reset` | `UserConfigsCmsController@resetCmsTheme` (SLE) | Reset do tema CMS **sem guarda** |

Corpo/erros: ver 1.3. Não há FormRequest, throttle, cache nem API pública. CSRF é o do grupo `web` (`app/Http/Kernel.php:38-48`).

## 6. Dados e modelo de domínio

Detalhamento de colunas, seeds e consumidores em `dados.md`. Resumo das cinco tabelas:

```mermaid
erDiagram
  v2_system_mail_configs {
    uuid id PK
    varchar profile UK
    varchar driver
    varchar host
    int port
    varchar encryption
    varchar username
    text password
    varchar from_address
    varchar from_name
  }
  v2_module_flags {
    uuid id PK
    varchar module_key UK
    varchar module_title
    boolean active
    varchar module_group
  }
  v2_system_controls {
    uuid id PK
    varchar control_key UK
    text control_value
    varchar control_title
  }
  v2_cms_theme_configs {
    uuid id PK
    varchar config_key UK
    varchar config_value
    varchar config_title
  }
  v2_portal_configs {
    uuid id PK
    varchar config_key UK
    text config_value
    varchar config_title
    varchar config_group
  }
```

Sem models, sem relações, sem eventos Eloquent. Todas usam `uuid` gerado por `v2_uuid()` na aplicação, `timestamps` e `softDeletes`; os serviços filtram `whereNull('deleted_at')`, mas nenhum faz `delete()`.

## 7. Fluxos e estados

```mermaid
stateDiagram-v2
  [*] --> Ativo
  Ativo --> Inativo: toggle active=false
  Inativo --> Ativo: toggle active=true
  Inativo --> Inativo: rota CMS responde 404
  Ativo --> Manutencao: maintenance_mode='true'
  Manutencao --> Ativo: maintenance_mode='false'
  note right of Manutencao
    portal anônimo 503;
    CMS logado continua
  end note
```

```mermaid
sequenceDiagram
  participant S as Suporte
  participant C as Controller
  participant M as SystemMaintenanceService
  participant DB as PostgreSQL
  participant A as activity_log
  S->>C: POST modules/toggle {module_key, active}
  C->>C: authorizeSupportAccess()
  C->>M: toggleModule(key, bool)
  M->>DB: SELECT v2_module_flags WHERE module_key
  alt existe
    M->>DB: UPDATE active, updated_at
  else não existe
    M->>DB: INSERT (id=v2_uuid, module_group='OUTROS')
  end
  M->>A: INSERT feature_flag_updated (subject fake)
  M-->>C: true/false (nunca exceção)
  C-->>S: 200 JSON ou 302 + flash
```

Fluxo de senha: `POST users/reset-password` → valida presença/6 chars → `Sentinel::findById` → `Sentinel::update(['password'=>$pwd])` → auditoria `password_reset_updated` → flash. Não há confirmação de e-mail, expiração de sessão do alvo nem revogação de "remember me".

## 8. UI CMS

Detalhamento completo (abas, campos, mensagens, JS, estados, acessibilidade) em `cms.md`. Estrutura: `index.blade.php` estende `v2-cms::layouts.app`, exibe alertas de flash, sidebar de abas e inclui os quatro parciais. A view repete a checagem de e-mail e mostra um card “Acesso Negado” — branch inalcançável, porque o controller aborta antes (`index.blade.php:10,132-137`).

## 9. UI Portal

Não se aplica: o módulo não renderiza páginas de portal. Seu efeito no portal é indireto e ocorre por composer (`portalTheme`, `portalConfigs`) e por middlewares (`MaintenanceMiddleware`, `ModuleAccessMiddleware`). O contrato dessas variáveis está em `dados.md` §6.5 e no doc `04_ui/03-portal-design-system.md`.

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

| Chave | Default | Efeito | Fonte |
|---|---|---|---|
| `v2.support.email` | `suporte@maxima.inf.br` (`V2_SUPPORT_EMAIL`) | Identidade única autorizada; comparação estrita; também usada pela sidebar e pelo middleware | `config/v2.php:5`; `SupportConfigsCmsController.php:49` |
| `app.debug` | env `APP_DEBUG` | Valor inicial exibido no diagnóstico e semente de `v2_system_controls.app_debug` | `SystemDiagnosticsService.php:44`; `2026_06_30_000003_create_v2_system_controls_table.php:62` |
| `cache.default`, `session.driver`, `queue.default` | do framework | Apenas exibidos no diagnóstico; não alteráveis por aqui | `SystemDiagnosticsService.php:45-47` |
| `app.log`, `app.log_max_files` | `single`, `30` | Determinam se existirá `laravel-AAAA-MM-DD.log` e por quantos dias | `config/app.php:127-131` |
| `debugbar.enabled` | — | Alterado em runtime no save de `debugbar_enabled` | `SystemMaintenanceService.php:585` |

**Permissões.** Não há slug RBAC em rota. A autorização efetiva é: (a) `CmsAuthenticate` (Sentinel + ≥1 papel), (b) `V2PermissionMiddleware` — que **não** casa este módulo por causa do `|`, (c) `authorizeSupportAccess()` em 10 actions, (d) nada na rota de reset de tema. O índice `permissoes.csv` registra as três camadas e o buraco.

**Flag de módulo.** `module.active:SupportConfigs` é aplicado pelo `mapModuleRoutes` (`V2ServiceProvider.php:303-306`). A flag está seedada como `active=true` em `v2_module_flags` (`2026_06_30_000002_create_v2_module_flags_table.php:61-69`) e é invisível na UI (grupo `Support` é filtrado). Desativada, o painel responde `404 Modulo desativado.` — inclusive para o suporte.

## 11. Observabilidade, erros e limites

- **Log de aplicação** (`storage/logs/laravel*.log`, nível default `debug`): `[SupportConfigsCmsController] Tentativa de acesso negado…` (warning), `[SupportConfigsService] Module toggle: x -> ON/OFF` (info), `[SupportConfigsService] System control: …` (info), `[SupportConfigsService] APP_DEBUG runtime: …`, `[SupportConfigsService] Debugbar runtime: …`, `[SupportConfigsService] Log limpo: …`, erros de tabela ausente em `debug` (`ModuleProvider.php:46`; `SystemMaintenanceService.php:74,343`).
- **Auditoria** (`activity_log`, Spatie): `feature_flag_updated`, `system_control_updated`, `cache_cleared` (description `cache_cleared`), `log_clear_updated`, `password_reset_updated`, `updated` para tema CMS (com `context=cms_theme_config`, `old`, `new`). Subject é model anônimo com `id` sintético (`cms_theme_<key>`, `system_<key>`), o que impede `forSubject`.
- **IP/User-Agent**: `ActivityLoggerService::log` anexa `ip_address` e `user_agent` em toda propriedade (`ActivityLoggerService.php:27-33`).
- **Códigos**: `200` HTML/JSON; `302` redirects com flash; `401 Unauthorized.` (AJAX sem sessão); `403 Forbidden. Support area is restricted.` (middleware, só se o pattern casasse) e `403 Acesso restrito ao suporte técnico autorizado.` (controller); `404 Modulo desativado.`; `422` só no toggle JSON sem `module_key`; `500` apenas em exceções sem `catch` — o favicon inválido via `theme.save` da SLE resulta em `302` + flash `Erro ao salvar tema: ...` (`SleCmsController.php:155-159`).
- **Fail-open**: se `v2_module_flags`/`v2_system_controls`/`v2_cms_theme_configs`/`v2_portal_configs` não existem, o código loga `debug` e segue com defaults (módulos ativos, sem manutenção, cores padrão). Exceção: `v2_system_mail_configs` na migração de encrypt falha e relança.
- **Limites**: logs 50–2000 linhas; busca de usuário 20 resultados; senha ≥6; favicon 100 KB; logo sem limite explícito (delegado ao `UploadService`); sem throttle por rota.

## 12. Seeds e dados iniciais

Sem seeders. Todos os dados iniciais vêm das migrations do módulo: 1 profile SMTP, 7 flags, 3 controles, 3 chaves de tema CMS e 18 chaves de portal. Detalhe linha a linha em `dados.md` §5.

## 13. Lacunas

- `LG-SUPPORTCONFIGS-01`: não foi possível determinar **quem** consome `v2.support.password` (`config/v2.php:6`); nenhuma referência no código do módulo. Procurar em seeds de fundador (`CORE-AUTH`). Impacto: baixo para este módulo.
- `LG-SUPPORTCONFIGS-02`: não há teste de feature cobrindo as rotas deste módulo; o comportamento de `resetCmsTheme` sem guarda é derivado de código, não de teste. Procurar em `tests/Unit/V2/Core/Middleware/V2PermissionMiddlewareTest.php`. Impacto: médio (paridade de segurança).
- `LG-SUPPORTCONFIGS-03`: não foi possível confirmar em runtime o efeito de `APP_LOG=daily` em produção (o `.env` não está no repositório); a UI promete 30 dias. Procurar no deploy. Impacto: baixo.
- `LG-SUPPORTCONFIGS-04`: `history` da view é fixo `collect()`; não se sabe se existiu um model de histórico removido. Procurar no histórico git. Impacto: baixo.
- `LG-SUPPORTCONFIGS-05`: não há validação de MIME real para `cms_brand_logo` neste módulo (depende de `UploadService`); impacto de upload malicioso deve ser tratado em `SVC-STORAGE`.

## 14. Checklist de paridade

- [ ] `PC-SUPPORTCONFIGS-01` — Acessar `/cms/infra/support-configs` com usuário que não é `v2.support.email` → `403` com `Acesso restrito ao suporte técnico autorizado.` e linha de warning no log; com o e-mail configurado → `200` com 4 abas e diagnóstico (`SupportConfigsCmsController.php:47-53,59-71`).
- [ ] `PC-SUPPORTCONFIGS-02` — `POST modules/toggle {"module_key":"noticias","active":0}` → `200 success=true`, `v2_module_flags.active=false`, `activity_log` com `feature_flag_updated` (`SystemMaintenanceService.php:127-159`).
- [ ] `PC-SUPPORTCONFIGS-03` — Desativar uma flag e abrir qualquer rota CMS do módulo → `404 Modulo desativado.`; reativar → rota volta (`ModuleAccessMiddleware.php:38-62`).
- [ ] `PC-SUPPORTCONFIGS-04` — Módulo sem linha em `v2_module_flags` aparece como ativo na listagem e funciona (`SystemMaintenanceService.php:102`; `ModuleFlagService.php:93-94`).
- [ ] `PC-SUPPORTCONFIGS-05` — `maintenance_mode='true'` → visitante anônimo recebe `503` com `v2-cms::staticPages.maintenance`; usuário Sentinel continua navegando (`MaintenanceMiddleware.php:41-52`).
- [ ] `PC-SUPPORTCONFIGS-06` — `maintenance_mode='1'` → portal **não** entra em manutenção (literal `'true'`), embora o dashboard mostre “Ativo” (`helpers.php:236-248`; `DashboardStatsService.php:394-398`).
- [ ] `PC-SUPPORTCONFIGS-07` — `POST system-controls/save` com os três campos e um checkbox desmarcado → grava `'false'` para ele e flash de sucesso (`SupportConfigsCmsController.php:165-180`; `SystemMaintenanceService.php:412-430`).
- [ ] `PC-SUPPORTCONFIGS-08` — `GET users/search?q=ana` como suporte → JSON com até 20 usuários, `roles` em slug e `last_login` `d/m/Y H:i:s` (`UserManagementService.php:38-68`).
- [ ] `PC-SUPPORTCONFIGS-09` — `POST users/reset-password` com senha de 6 chars → login do alvo funciona com a nova senha imediatamente; `activity_log` registra `password_reset_updated` (`UserManagementService.php:122-145`).
- [ ] `PC-SUPPORTCONFIGS-10` — `GET logs/tail?lines=10&file=../../.env` → devolve o log do dia (clamp 50, path rejeitado), nunca o `.env` (`SystemDiagnosticsService.php:171-187`).
- [ ] `PC-SUPPORTCONFIGS-11` — `POST logs/clear` → arquivo truncado (0 bytes) e `.AAAAmmdd_His.bak` criado no mesmo diretório (`SystemDiagnosticsService.php:135-153`).
- [ ] `PC-SUPPORTCONFIGS-12` — `POST /cms/infra/user-configs/cms-theme/reset` como usuário CMS comum (não-suporte) → **hoje** `200` vazio (sem redirect) e tema resetado; comportamento de paridade desejado: `403` (SEC-G1-04).
- [ ] `PC-SUPPORTCONFIGS-13` — `active_pattern` corrigido para array → usuário não-suporte em `/cms/infra/user-configs/*` é redirecionado com `Acesso negado. Esta área é restrita ao suporte técnico autorizado.` (`V2PermissionMiddleware.php:51-65`).
- [ ] `PC-SUPPORTCONFIGS-14` — `GET /cms/infra/support-configs` como suporte → HTML contém as 4 pills, os 9 rótulos de diagnóstico e o botão “Limpar Cache” (`index.blade.php:38-104`).
- [ ] `PC-SUPPORTCONFIGS-15` — Alterar `cms_sidebar_bg` → próxima página CMS usa o novo valor em `--cms-sidebar-bg`; `cms_brand_name` altera `<title>` e sidebar (`CmsThemeComposer.php:32-50,53-128`; `app.blade.php:7,44-46`).
- [ ] `PC-SUPPORTCONFIGS-16` — `resetCmsThemeToDefault` → 6 chaves voltam aos defaults, logo/favicon ficam `''`, arquivos permanecem no storage (`SystemMaintenanceService.php:292-317`).

## 15. Evidências consultadas

- `V2/Modules/Support/SupportConfigs/ModuleProvider.php:17-100`
- `V2/Modules/Support/SupportConfigs/Controllers/SupportConfigsCmsController.php:25-305`
- `V2/Modules/Support/SupportConfigs/Routes/cms.php:1-64`
- `V2/Modules/Support/SupportConfigs/Services/SupportConfigsService.php:26-248`
- `V2/Modules/Support/SupportConfigs/Services/SystemMaintenanceService.php:1-625`
- `V2/Modules/Support/SupportConfigs/Services/SystemDiagnosticsService.php:24-277`
- `V2/Modules/Support/SupportConfigs/Services/UserManagementService.php:28-206`
- `V2/Modules/Support/SupportConfigs/Services/PortalConfigsService.php:38-248`
- `V2/Modules/Support/SupportConfigs/ViewComposers/CmsThemeComposer.php:20-135`
- `V2/Modules/Support/SupportConfigs/Migrations/2026_06_30_000001…:32-91` até `2026_07_06_000001…:1-73`
- `V2/Modules/Support/SupportConfigs/Views/cms/supportConfigs/index.blade.php:1-142`
- `V2/Modules/Support/SupportConfigs/Views/cms/supportConfigs/tabs/{logs,manutencao,modulos,suporte}.blade.php`
- `V2/Core/Middleware/V2PermissionMiddleware.php:36-113`
- `V2/Core/Middleware/CmsAuthenticate.php:1-44`
- `V2/Core/Middleware/ModuleAccessMiddleware.php:1-66`
- `V2/Core/Middleware/MaintenanceMiddleware.php:1-52`
- `V2/Core/Services/ModuleFlagService.php:1-127`
- `V2/Core/ViewComposers/SidebarComposer.php:18-105`
- `V2/Core/Providers/V2ServiceProvider.php:45-135,290-306`
- `V2/Core/Helpers/helpers.php:230-254`
- `config/v2.php:4-12`; `config/app.php:127-131`
- `_qa/security-g1.md:79-94` (SEC-G1-04)
- `V2/Modules/Support/StructuralLandingEditor/Controllers/UserConfigsCmsController.php:12-46`
- `V2/Modules/Support/StructuralLandingEditor/Services/UserConfigsService.php:28-145`
- `V2/Services/MailService/MailService.php:200-265`
- `V2/layouts/cms/layouts/app.blade.php:1-46`; `V2/layouts/cms/layouts/sidebar.blade.php:1-12`
- `V2/Modules/Support/SupportConfigs/{README.md,Controllers/README.md,Services/README.md}`
