- Generated SEO titles/descriptions per gym; gym seo fields override each on its own
- Keywords, author, og:site_name, favicon and JSON-LD logo follow the gym
- CrossFit page 404s before output for gyms without a box
- Events: «Mostrar em» gym selector; event page uses its gym's brand
- Hero event button text colour from the event colour (--ev-on)
- Navbar bottom line stops at the other-gym head (--switch-w)
- Diagonal hero blur mask; remove the trial link
img_optimize() decodes .jxl with djxl or Imagick, resizes with GD, re-encodes
with cjxl and writes foto.jxl.webp beside it; skips cleanly without a decoder.
Gallery, hero, navbar logos and JSON-LD serve the JXL with the fallback, and
trash/removal move the fallback with its .jxl.
- Pedido de aula experimental passa a ficar em trial_leads (por ginásio)
ANTES do email: sem SMTP o visitante já não vê «erro» e o pedido não se
perde. Gestão → Aulas experimentais: estados, notas, CSV (auditado),
apagar para a lixeira; aviso «sem email» quando o aviso não saiu.
- Aula experimental, inscrição e eventos usam o anti-spam do platform-tools
(honeypot, carimbo assinado com tempo mínimo, prova de JS, limite por IP,
pontuação de conteúdo) e o bloqueio de IP, em vez de honeypots locais.
Recusas ficam em blocked_submissions; resposta igual à de sucesso.
Falha aberto se o módulo rebentar.
- Checkbox de consentimento da aula experimental passa a ser guardada.
- Audit das escritas de conteúdo: criar/editar equipa, modalidades,
eventos, mensalidades, pedidos de inscrição, galerias, horário (incl.
arrastar no calendário), horas, contactos e ginásios, com antes/depois
campo a campo (audit_row/audit_change). Exportação CSV de inscrições
auditada.
- .table-responsive em todas as tabelas do backoffice.
- Migração: database/migration_20260926_trial_leads.sql (só cria tabelas).
Testado numa cópia da BD de produção: 63/63 verificações HTTP.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Correções da auditoria de 24/09 (audits/fitme3070-auditoria-2026-09-24.html).
Lixeira
- Tudo o que é importante passa a soft delete (deleted_at + deleted_by):
equipa, modalidades, eventos, mensalidades, galerias, imagens, horário,
horas de funcionamento, inscrições em eventos e pedidos de inscrição.
- config/functions_trash.php: um registo por tipo; apagar, restaurar, listar
e eliminar leem todos dele. Eliminar de vez só com 30 dias na lixeira —
a condição está no WHERE do DELETE, um POST forjado não apaga nada.
- Gestão → Lixeira (views/admin/trash/index.php): lista única com quem e
quando, restauro a qualquer momento.
- Imagens de galeria: o site lista a pasta, por isso o ficheiro passa para
.trash/ (negado pelo .htaccess) e volta no restauro.
- As listagens e o site público filtram deleted_at (horário também filtra
modalidades apagadas). Migração: database/migration_20260925_trash.sql
(só acrescenta colunas; tem de ser aplicada antes deste código).
CSRF
- csrf_check() recusa qualquer pedido que não seja POST (405). Antes saía
sem verificar nada em GET, e dez ações de apagar/restaurar eram GET.
- Os dez handlers passaram a POST com token; links viraram formulários.
Autenticação e sessão
- client_ip(): X-Real-IP / último X-Forwarded-For, só vindo de proxy privado
com TRUSTED_PROXY. O throttle contava o IP do proxy (limite global).
- Throttle também por conta (hash do email), além do IP.
- Sem oráculo de tempo: login verifica contra um hash de referência quando
a conta não existe; a recuperação deixa o email na fila.
- Cookie de sessão Secure/HttpOnly/SameSite=Lax e use_strict_mode.
- Definições de sistema só para sysadmin; autocomplete=username.
Servidor e uploads
- .htaccess: nosniff, Referrer-Policy, X-Frame-Options, Permissions-Policy,
CSP (frame-ancestors, base-uri, object-src, form-action); nega parciais
_*.php, PHP soltos do platform-tools e *.md; 404 própria.
- Uploads validados pelo conteúdo (finfo/getimagesize); SVG só sem scripts,
handlers on*=, foreignObject ou hrefs externos.
Testado numa cópia da BD de produção (fitme_audit_test) com um contentor
temporário: 34/34 verificações HTTP ponta-a-ponta.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>