Configurações do kit em /admin
O que a instalação perguntou — e mais um punhado de coisas que antes só se mudava editando arquivo — vive em /admin/configuracoes-do-kit, em seis abas. Nada de .env, nada de deploy.
| Aba | O que você troca |
|---|---|
| Identidade | nome da aplicação, cor primária (a paleta do Filament ou um hexadecimal livre), logo da marca, favicon e a arte das telas de autenticação |
transporte (log, array, smtp), servidor, porta, criptografia, usuário, senha e remetente | |
| Tabelas | linhas por página, linhas listradas, persistência do recorte do usuário e colunas arrastáveis — os defaults de toda tabela dos três painéis |
| Registro | cadastro sem convite no /app, aprovação manual e validação de e-mail (detalhes) |
| Login | os quatro provedores de login social, cada um com interruptor, Client ID e Client Secret (cifrado), e o rodapé da tela de login (detalhes) |
| Kit | hub de navegação em cartões, e como o seu negócio chama cada organização (singular e plural) |
Tudo é gravado pelo spatie/laravel-settings na tabela settings, com a tela vindo do filament/spatie-laravel-settings-plugin — os dois já estavam instalados no kit e sem uso até esta versão.
Quem manda: o banco ou o .env?
Esta é a pergunta que decide se a tela é útil ou decorativa, e a resposta é uma só:
O banco vence em tempo de execução. O
.envsemeia a primeira gravação e é o plano B.
Como isso funciona sem que nenhum consumidor saiba que o settings existe:
- A migration
database/settings/*_create_kit_settings.phpsemeia cada propriedade com o valor deconfig(...), que vem do.env. Numa instalação nova, a cor e o nome que você escolheu nokit:installchegam ao banco sozinhos — omigrateroda depois de o instalador ter escrito o arquivo. App\Providers\KitServiceProvider::configureSettingsDoKit()sobrepõe a configuração do processo com o que está no banco, uma vez por request e por comando artisan.App\Support\CorPrimaria, os trêsPanelProvider, a configuração global de tabela e o próprioMailManagerdo Laravel continuam lendoconfig(). Nenhum deles foi alterado.
O que acontece em cada situação:
| Situação | Quem vence |
|---|---|
| a propriedade tem linha no banco | o banco |
| a propriedade não tem linha (você acrescentou uma e não migrou) | o .env, com um warning no log |
a tabela settings não existe (antes do primeiro migrate) | o .env, em silêncio |
| o banco está inacessível | o .env, com um warning |
kit:install numa instalação nova | o .env → a migration leva os valores para o banco |
kit:install --force | apaga o banco, reescreve o .env e re-migra → o banco nasce igual ao .env novo |
kit:install --custom num projeto já instalado | reescreve o .env e grava no settings — as duas fontes ficam iguais |
Não existe interruptor para “usar ou não o settings”, e isso é decisão, não esquecimento: uma flag seria uma terceira fonte da verdade, que é justamente o problema que a regra acima resolve. Para desligar, php artisan migrate:rollback na migration de settings — sem linha na tabela, o alinhamento é no-op e o .env volta a ser a única fonte.
Cor: lista fechada e cor livre
São dois campos, e a precedência é declarada:
hexadecimal válido → nome da paleta → padrão do Filament.
O hexadecimal vence porque é o campo mais específico: quem digita #7c3aed escolheu aquela cor, enquanto o seletor da lista tem valor padrão e pode nunca ter sido tocado. Valor fora do formato (#abcd, azul, #gggggg) é ignorado e a resolução cai para o nome — a mesma tolerância que o kit já tinha para nome de cor inválido, e pelo mesmo motivo: isto roda no boot de todo painel, e uma exceção ali derrubaria toda página do projeto, não uma tela.
Dentro de /app/{organização}, a cor da organização continua vencendo as duas.
Permissão
Uma só: View:ConfiguracoesDoKit, gerada pelo ShieldPermissionsSeeder e entregue ao papel admin pelo PapeisSeeder — sem nenhuma lista para editar, porque a matriz do papel é a do painel inteiro. master_global entra pelo Gate::before; infra e panel_user não recebem.
É uma permissão para abrir e para salvar, de propósito. O canEdit() do plugin desabilita o formulário mas não esconde valor — o próprio README do pacote diz isso por escrito —, e esta tela guarda a senha do SMTP. Um papel “só leitura” aqui seria um papel que lê credencial.
Teto de upload: 10 MB, e onde mudar
Todo upload do kit — a logo, o favicon e a arte do login desta tela, a logo da organização em /admin/organizacoes e os anexos de Projeto — aceita arquivo de até 10 MB, e recusa SVG.
O número é uma chave, no .env:
# Em MEGABYTES. Vazio, 0 ou ausente = 10.
KIT_UPLOAD_MAXIMO_MB=10
Ela alimenta config('kit.uploads.maximo_em_kb') — a config guarda kilobytes, porque é a unidade que o ->maxSize() do Filament e a regra de upload temporário do Livewire recebem. A multiplicação por 1024 vive num lugar só, no config/kit.php, e quem lê a chave é App\Support\TetoDeUpload. Não há campo na tela para isto de propósito: é decisão de instalação, não de operação diária.
Um upload atravessa quatro limites, e o menor manda. Eles não recusam igual, e é isso que torna o desalinhamento caro:
| Camada | Onde | Valor no kit | Como aparece o erro |
|---|---|---|---|
| nginx | docker/nginx/nginx.conf | client_max_body_size 60M | falha de rede no console |
| PHP | docker/php/uploads.ini | upload_max_filesize=52M, post_max_size=60M | idem |
| Livewire (upload temporário) | alinhado à chave do kit por KitServiceProvider, com 1 MB de folga | 11 MB | 422 no XHR, erro genérico |
Filament (->maxSize()) | a chave do kit | 10 MB | mensagem em português, no campo |
Só a última recusa com mensagem clara — por isso o kit alinha o Livewire à chave em vez de deixar o default dele (12 MB) mais frouxo que a tela.
Para subir muito o teto, mude junto:
KIT_UPLOAD_MAXIMO_MB— cobre a tela e o Livewire de uma vez;- acima de 52 MB,
docker/php/uploads.ini(upload_max_filesizeepost_max_size); - acima de 60 MB,
docker/nginx/nginx.conf(client_max_body_size).
⚠️ Fora do Docker do kit, o PHP costuma vir com upload_max_filesize=2M de fábrica. Ali o teto real é 2 MB, não o da chave — e o erro aparece como falha de rede, sem mencionar tamanho. Confira com php -i | grep upload_max_filesize antes de culpar o kit.
Por que SVG é recusado
SVG é XML, e XML aceita <script>. A logo, o favicon e a arte do login são servidos pelo mesmo origin da aplicação, com visibilidade pública: abrir a URL de um SVG enviado executaria o script com acesso ao cookie de sessão — XSS armazenado. Quem envia é o admin, que já tem acesso total, então é escalada de insider e não porta anônima; num starter kit vale fechar.
A barreira é a regra mimes do Laravel (não o ->image() do Filament, que é outra coisa e aceita image/*, SVG incluído), com a lista de formatos em ConfiguracoesDoKit::FORMATOS_DE_IMAGEM: jpg, jpeg, png, gif, bmp, webp, avif, heic, heif, ico, tif e tiff. SVG é o único formato de imagem fora, e é o único que carrega script.
E ela não olha a extensão: o MIME vem do conteúdo do arquivo no disco temporário, então renomear logo.svg para logo.png não passa. Nos anexos de Projeto, onde uma allow-list fecharia o campo para PDF e planilha, a regra recusa apenas image/svg+xml.
Trilha de alterações
Toda alteração aparece em /infra/audits, com quem mudou, quando, o nome da propriedade e os valores antigo e novo. Uma linha por propriedade alterada; salvar sem mudar nada não gera registro.
A senha de e-mail é cifrada na tabela settings e entra na trilha mascarada (••••••): o registro diz que o segredo mudou, nunca qual é.
Dois detalhes que valem para quem for mexer nisso:
- A trilha não vem da trait
App\Traits\AuditsFillables. Um settings do spatie não é um model Eloquent, e apontar o repositório dele para um model com a trait auditaria só a criação — a alteração de propriedade existente passa porupsert(), que não dispara evento de Eloquent. A trilha sai de um listener deSavingSettings, que é o único ponto do pacote com valor antigo e novo juntos. - O evento gravado é
settings-updated, e nãoupdated, para o botão “restaurar” da trilha não aparecer: ele fariafill(['nome_da_aplicacao' => …])numa linha cujas colunas sãogroup/name/payload.
Isto não é o settings de uma organização
A identidade visual de um tenant (cor e logo por organização) continua sendo CRUD comum em /admin/organizacoes, nas colunas cor_primaria (hexadecimal livre), cor_primaria_nome (a mesma paleta do Filament deste settings — o hexadecimal vence quando preenchido) e logo do model Tenant, e ela vence a do kit dentro de /app/{slug}. Nada foi movido para cá.
O que ficou fora, e por quê
| Item | Por quê |
|---|---|
| driver, host e nome do banco | trocar depois do migrate não é reescrita de configuração, é outra instalação |
| ligar/desligar a multi-organização | as tabelas de permissão só nascem com a coluna de contexto se permission.teams estiver ativo antes do migrate; o caminho é php artisan kit:tenancy |
| e-mail e senha do administrador | o UsuarioAdminSeeder não sincroniza, de propósito (ele roda em todo db:seed, e atualizar senha ali reverteria em silêncio a troca feita no perfil). Um campo que não troca a credencial é pior que campo nenhum — o caminho é a tela de perfil |
| slug do CRUD de organizações | é lido no registro de rota, não no render, e a URL é identificador permanente |
| idiomas do painel | a internacionalização do kit não está feita: ligar um segundo idioma hoje troca metade da tela. Ver o bloco idiomas de config/kit.php |
| retenção das trilhas | não é pergunta da instalação; fica no .env, onde o zero tem semântica documentada |
Desempenho
O alinhamento custa uma query por boot (o grupo inteiro vem de uma só leitura). Se isso incomodar, SETTINGS_CACHE_ENABLED=true no .env — lembrando que, com o cache ligado, gravar pela tela exige php artisan settings:clear-cache.
Acrescentando uma propriedade
Três lugares, sempre, e o teste tests/Kit/ConfiguracoesDoKitTest.php reprova se você esquecer um:
- a propriedade tipada em
app/Settings/ConfiguracoesDoKit.php; - a linha em
ConfiguracoesDoKit::mapaDeConfiguracao()(propriedade → chave deconfig()); - o par
add()/deleteIfExists()numa migration nova emdatabase/settings/.
E o campo na aba certa de app/Filament/Admin/Pages/ConfiguracoesDoKit.php.