# Outras Configurações — Gaps e Dívidas

> Módulo `outras-configuracoes`. Registro honesto do que está quebrado,
> incompleto ou enganoso na v6, e do que precisa ser decidido antes da v7.

---

## 1. Sobreposição com `configuracoes-da-instituicao`

O módulo **não tem nenhuma implementação própria**. Ele reaproveita tudo:

| Aspecto | `outras-configuracoes` | `configuracoes-da-instituicao` |
|---|---|---|
| Controller | `v6/app/Http/Controllers/Prefecture/ConfigurationController.php` | o **mesmo** arquivo |
| Model | `App\Prefecture\Configuration` | o **mesmo** model |
| Tabela | `configurations` | a **mesma** tabela |
| Form Request | `v6/app/Http/Requests/ConfigurationRequest.php` | o **mesmo** |
| Endpoint de gravação | `POST /cms/update/configuration` (`update.configurations.by.category`) | o **mesmo** |
| Categoria | `outros` (id 4) | `site` (id 1) |
| Recurso (`resources.slug`) | `outros` | `configuracoes` |
| Tela própria | Não | `prefecture.configurations.configuracoes.edit` |
| Formulário por categoria | Não (`case 'outros'` → `index()`) | Sim (abas Site/Dashboard/Selos) |

Ou seja, a distinção entre os dois "módulos" é **apenas o rótulo do menu e o
slug do recurso**. Detalhe completo em `../configuracoes-da-instituicao/`.

Consequência prática: qualquer correção em `ConfigurationController`,
`ConfigurationRequest`, `form.blade.php` ou na view SQL `configuracoes`
afeta **os dois módulos simultaneamente** — não é possível corrigir um sem
arrastar o outro.

## 2. Achados verificados

### 2.1 Categoria `outros` vazia

| Item | Valor verificado |
|---|---|
| Categoria | `category_configurations.id = 4`, `slug = 'outros'`, `name = 'Outros'` |
| Configurações ativas | **0** (0 no total, incluindo soft-deleted) |
| Origem | `v6/database/seeds/CategoryConfigurationsTableSeeder.php` cria a categoria; `v6/database/seeds/ConfigurationsTableSeeder.php` **não** a popula (popula `site`, `temas`, `covid`, `barra-privacidade`) |
| Consequência | O módulo não tem conteúdo próprio; a tela "Outras Configurações" é, na prática, a listagem global de configurações |

### 2.2 Divergência entre o que o controller carrega e o que a tela mostra

| Camada | O que acontece |
|---|---|
| Controller | `index()` executa `$configurations = $this->getConfiguration('outros')` → coleção **vazia** |
| View | `v6/resources/views/prefecture/configurations/index.blade.php` **não usa** `$configurations`; o `<tbody>` é preenchido só por AJAX |
| AJAX | `GET /cms/datatable/configuracoes` → `DatatableController@get_configuracoes()` → `DB::table("configuracoes")->get()` sobre a **view SQL `configuracoes`**, que agrega **todas** as categorias |
| Resultado na tela | As 87 configurações ativas do sistema (site 57, temas 9, covid 11, barra-privacidade 4, dashboard 6), com a coluna "Categoria" indicando a origem |

O operador vê, portanto, um conjunto **maior** do que o módulo deveria
administrar; e a coluna "ID" mostra o `row_number()` da view, não o `id` real.

### 2.3 Ausência de formulário por categoria para `outros`

`editByCategory('outros')` cai no `case 'outros':` e executa
`return $this->index();`. Não existe view
`v6/resources/views/prefecture/configurations/outros/`. O menu "Outras
Configurações" leva, portanto, à mesma tela de listagem — não há um formulário
dedicado a "outras configurações".

### 2.4 O nome do módulo não corresponde ao conteúdo

As configurações que o rótulo sugere (servidor, e-mail, API keys, Gemini) **não
estão** na categoria `outros`:

| Grupo | Chaves | Categoria real |
|---|---|---|
| E-mail / servidor | `mail_driver`, `mail_host`, `mail_port`, `mail_username`, `mail_password`, `mail_encryption`, `mail_from_address`, `mail_from_name` | `site` (id 1) |
| Ambiente | `app_env`, `app_debug` | `site` (id 1) |
| Recaptcha | `recaptcha_public_key`, `recaptcha_private_key` | `site` (id 1) |
| Gemini | `gemini_api_key` | `site` (id 1) |
| Transparência externa | `portal_transparencia_ativo`, `portal_transparencia_codigo_ente`, `portal_transparencia_url_portal` | `site` (id 1) |
| Analytics / dashboard | `google_analytics_property_id`, `google_analytics_measurement_id`, `google_analytics_sa_json`, `dashboard_enhanced_enabled`, `dashboard_widget_refresh_minutes`, `dashboard_widgets_enabled` | `dashboard` (id 8) |

Não existe, no legado, nenhuma separação real entre "configurações do site" e
"outras configurações" no que diz respeito a credenciais técnicas. **Não se
deve** documentar (nem implementar na v7) uma separação que não existe no
código.

### 2.5 Permissão assimétrica entre leitura e gravação

| Operação | Permissão exigida |
|---|---|
| Ver o menu "Outras Configurações" | `outros.index` **e** `$modulos['outras-configuracoes']` |
| Abrir a tela (`edit.configuracoes.by.category`) | `outros.edit` |
| Gravar por categoria (`update.configurations.by.category`) | `configuracoes.edit` (a **mesma** para as 4 categorias) |
| Botão editar/excluir na DataTable | `configuracoes.edit` / `configuracoes.delete` (não `outros.*`) |

### 2.6 Menu "Outras Configurações" não aparece fora do dashboard

`v6/app/Providers/ModulosServiceProvider.php` registra composers **apenas**
para `portal.layouts.modelo1..6.*`, `prefecture.contents.noticias.form`,
`admin.layouts.dashboard` e `admin.layouts.dashboard-legacy`. `admin.layouts.app`
e `adminlte::page` não recebem `$modulos`. Verificado empiricamente no
container: os eventos `composing: admin.layouts.app` e `composing:
adminlte::page` chegam sem `modulos`; ao registrar manualmente um composer em
`admin.layouts.app`, `adminlte::page` passa a receber `modulos`.

Efeito: em todas as telas do CMS **exceto** o dashboard, os itens "Configurações",
"Temas & Cores", "LGPD" e "Outras Configurações" **não são exibidos**. As telas
existem e funcionam por URL direta.

### 2.7 `echo` dentro do controller

`updateByCategory()` executa `echo "JSON file created successfully..."` quando a
categoria é `site`. Com `output_buffering => 0` no container, o texto é emitido
antes do redirecionamento (cabeçalhos já enviados) — risco de "headers already
sent".

### 2.8 `config.json` obrigatório para o boot

`v6/config/app.php` (linha 6), `v6/config/mail.php` (linha 3) e
`v6/config/recaptcha.php` (linha 3) fazem
`file_get_contents(base_path()."/config.json")` e acessam `$data->config[0..2]`.
Sem o arquivo (ou incompleto), a aplicação não sobe. Quem o reescreve é a tela
de configurações da instituição.

### 2.9 `configurations.key` sem índice único

`updateByCategory()` usa `Configuration::where('key',$key)->first()`; chaves
duplicadas gravam apenas a primeira ocorrência, e o `index` da tabela não é
único (apenas `configurations_pkey`).

### 2.10 N+1 no Form Request

`ConfigurationRequest::rules()` e `::messages()` executam **uma query por campo
enviado**. Em formulários grandes (categoria `site`), isso multiplica as
consultas.

### 2.11 DataTable sem filtro de categoria

`DatatableController@get_configuracoes()` lê a view SQL `configuracoes` e não
aplica filtro — listagem global usada também por outras telas.

### 2.12 Sem FK entre `configurations` e `modulos`

A ligação módulo ↔ chave é indireta: `configurations → category_configurations`
e `resources.slug → resources.modulo_id → modulos.id`.

### 2.13 Segredos gravados no log de atividade

`store()` e `update()` registram em `activity()` o `Valor:` da configuração.
Para `type = password` (ex.: `mail_password`) o segredo é escrito no log de
atividade.

### 2.14 Código morto relacionado

`v6/app/Http/Views/Composers/ConfigurationsComposer.php` não é registrado em
nenhum provider — código morto (a busca por `ConfigurationsComposer` em
`v6/app/Providers/` não retorna registros).

## 3. Funcionalidades incompletas ou mortas

| Item | Situação |
|---|---|
| Categoria `outros` | Existe, mas vazia — não há nenhuma configuração cadastrada |
| Formulário por categoria `outros` | Inexistente (cai em `index()`) |
| `configuracoes.show` | Rota existe (`Route::resource`), mas não é usada pelo fluxo do módulo |
| Restauração de configuração | Coluna `deleted_at` existe e `struct_views()` calcula `active`/`inactivate`, mas não há rota nem botão de restauração |
| `removeArquivo()` | Zera o `value` de um arquivo sem apagar o arquivo físico do storage |
| Filtro por categoria na listagem | Inexistente |

## 4. Itens da v5 que foram removidos na v6 (relevantes ao módulo)

| Item | v5 | v6 |
|---|---|---|
| `v5/database/seeds/PrivacyBarSeeder.php` | Existia | **Removido**; as 4 chaves da barra de privacidade foram incorporadas ao `ConfigurationsTableSeeder` da v6 |
| Validação "É obrigatório selecionar uma posição para todos os selos" | Presente no `updateByCategory` | Removida na v6 |
| Validação de `quantidade_noticias_ultimas_noticias` (número par entre 2 e 20) | Presente | Removida na v6 |
| Caminho de selos | `public/img/selos/` | Disco `public` em `selos/` + `migrarCaminhosSelosAntigos()` |

Nada dessas remoções afeta o módulo `outras-configuracoes` diretamente: elas
afetam `configuracoes-da-instituicao` (`../configuracoes-da-instituicao/`).

## 5. Mapa v5 → v6

| Elemento | v5 | v6 | Impacto no módulo |
|---|---|---|---|
| Controller | `v5/app/Http/Controllers/Prefecture/ConfigurationController.php` (536 linhas) | `v6/app/Http/Controllers/Prefecture/ConfigurationController.php` (856 linhas) | `index()`, `create()`, `store()`, `edit()`, `update()`, `destroy()` e `case 'outros'` **inalterados**; a v6 adiciona o bloco Dashboard/GA, a geração de `config.json`, a validação de template `modelo1..modelo6` e os endpoints de selos |
| Model `Configuration` | `v5/app/Http/Model/Prefecture/Configuration.php` | `v6/app/Http/Model/Prefecture/Configuration.php` | **Idêntico** |
| Model `CategoryConfiguration` | `v5/app/Http/Model/Prefecture/CategoryConfiguration.php` | `v6/app/Http/Model/Prefecture/CategoryConfiguration.php` | **Idêntico** |
| Form Request | `v5/app/Http/Requests/ConfigurationRequest.php` | `v6/app/Http/Requests/ConfigurationRequest.php` | v6 adiciona regras dinâmicas por categoria (com N+1) e a exceção das chaves de GA/Dashboard |
| Views (`index`, `form`, `create`, `edit`) | `v5/resources/views/prefecture/configurations/*` | `v6/resources/views/prefecture/configurations/*` | **Idênticas** |
| JS da DataTable | `v5/public/assets/js/custom/config/configuracoes/datatable.js` | `v6/public/assets/js/custom/config/configuracoes/datatable.js` | **Idêntico** |
| Seed de categorias | `v5/database/seeds/CategoryConfigurationsTableSeeder.php` (delete + insert) | `v6/database/seeds/CategoryConfigurationsTableSeeder.php` (`updateOrCreate` por `slug`) | v6 acrescenta as categorias `barra-privacidade` (7) e `dashboard` (8); `outros` (4) continua vazia nas duas versões |
| Seed de configurações | `v5/database/seeds/ConfigurationsTableSeeder.php` | `v6/database/seeds/ConfigurationsTableSeeder.php` | Nenhuma linha para `outros` nas duas versões |
| Tabela `modulos` | Não existe | `v6/database/migrations/2024_11_01_150206_create_modulos_table.php` | **Novo na v6**: habilita/desabilita o menu do módulo |
| `resources.modulo_id` | Não existe | `v6/database/migrations/2024_11_05_104026_add_modulo_id_to_resources_table.php` | **Novo na v6**: liga o recurso `outros` ao módulo 4 |
| Tela `/cms/modulos` | Não existe | `v6/app/Http/Controllers/Admin/ModulosController.php` | **Nova na v6** |
| `config.json` | Não gerado pelo CMS | Gerado por `updateByCategory` (com `echo`) | **Novo na v6**, com impacto no boot da aplicação |
| View SQL `configuracoes` | Presente | Presente | Necessária à listagem |

**Resumo do mapa**: o módulo `outras-configuracoes` é **funcionalmente
idêntico** em v5 e v6. A v6 apenas adds o *gate* de ativação do menu
(`modulos`/`resources.modulo_id`). Nenhuma feature do módulo foi criada,
removida ou corrigida entre as versões.

## 6. Riscos de dados

| Risco | Descrição | Mitigação na v7 |
|---|---|---|
| RD1 | Criar índice único em `configurations.key` com dados duplicados | Auditar duplicatas antes; unificar ou renomear chaves |
| RD2 | Migrar `category_configurations_id` (FK) e a view SQL | A view precisa ser recriada ou substituída por query com `JOIN` |
| RD3 | Segredos em texto plano na tabela (`mail_password`, `gemini_api_key`) | Considerar cofre de segredos ou cifragem em nível de aplicação |
| RD4 | Segredos copiados para o log de atividade | Mascarar valores no log |
| RD5 | `config.json` gerado dentro do controller e lido no boot | Gerar por serviço/job e validar o arquivo antes do boot |
| RD6 | Soft delete sem política de restauração → perda de configuração | Expor restauração ou definir política de expurgo |

## 7. Perguntas em aberto para o negócio

1. A categoria `outros` deve ser **populada** com as chaves técnicas hoje em
   `site` (e-mail, recaptcha, Gemini, `app_env`/`app_debug`), ou o nome do
   módulo deve ser alterado para refletir o que ele realmente contém?
2. A listagem deve ser **filtrada** por categoria (comportamento novo) ou
   manter a visão global? A v7 deve preservar a visão global?
3. Quem pode criar/editar/excluir configurações: apenas `super-admin`, como
   hoje, ou também quem tem `configuracoes.edit`?
4. Deve existir um formulário por categoria para `outros`, no formato das
   categorias `site`/`temas`/`barra-privacidade`?
5. As chaves de dashboard/analytics (`categoria dashboard`) devem migrar para
   `outros` ou continuar em `dashboard`?
6. Deve haver restauração de configurações excluídas (soft delete)?
7. Segredos (e-mail, API) devem ficar em `configurations` ou migrar para um
   cofre dedicado?

## 8. Checklist de correção para a v7

- [ ] Tornar a listagem filtrável por categoria (com filtro "Todas" explícito).
- [ ] Remover a `view SQL configuracoes` ou adaptá-la com filtro e coluna `id` real.
- [ ] Corrigir a assimetria de permissões (leitura por recurso, escrita por `super-admin`).
- [ ] Eliminar o N+1 do Form Request.
- [ ] Criar índice único em `key` (após auditoria de duplicatas).
- [ ] Eliminar o `echo` do controller e gerar `config.json` por serviço.
- [ ] Mascarar segredos nas respostas de API e nos logs.
- [ ] Registrar `$modulos` em todas as views do CMS (composer global).
- [ ] Decidir e documentar o destino da categoria `outros`.
