feat(audit): platform-tools atualizado (12f26fd) e registo de auditoria ligado

- shared/platform-tools: ea3616a → 12f26fd (27 commits). Traz o módulo de
  audit (f761d99, o pin da frota), «manter sessão iniciada», o registo de
  segurança da frota (o gancho que o login já tinha passa a funcionar) e a
  correção do IP do cliente encontrada nesta auditoria (último X-Forwarded-For
  / X-Real-IP), promovida ao partilhado. client_ip() da app delega nele.
- config/functions_audit.php: audit() sobre platform_audit(); tabela
  audit_log criada no primeiro uso, segredos ocultados, nunca parte a ação.
- Registado: login, falha de login, bloqueio por excesso de tentativas,
  logout, pedidos de recuperação, mudança/definição de palavra-passe,
  registo e criação de utilizadores, convites, perfil (antes/depois), todas
  as definições (db_update_setting, com valor anterior), cada passo da
  lixeira (apagar, restaurar, eliminar de vez) e ações nas mensagens.
- Sistema → Auditoria (views/sysadmin/audit.php), só sysadmin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
andreandClaude Opus 5.5 committed 2026-09-25 07:28:13 +01:00
1 parent 5e300fc341
commit 0e8b53f620
65 files changed
+5852 -455

No files matched your search

+14 -27
View File
@@ -13,9 +13,9 @@ $host_root = dirname(__DIR__, 3);
// Three host patterns coexist:
// - fotografia-style : config/env.php exposes env_load(), needs an explicit call
// - dsa/moremio-style: config/env.php auto-loads .env via an IIFE; require is enough
// - surf-style : no config/ directory at all -- src/config.php plus
// vlucas/phpdotenv, which has already populated $_ENV and
// getenv() by the time a web request reaches here.
// - surf-style : no config/env.php -- config/db.php loads .env with
// vlucas/phpdotenv, and the workers run without it, so
// they read .env themselves (the fallback below).
// Requiring config/env.php unconditionally fataled on the third layout.
if (is_file($host_root . '/config/env.php')) {
require_once $host_root . '/config/env.php';
@@ -51,7 +51,10 @@ if (!function_exists('env')) {
}
define('BACKUP_TOKEN', env('BACKUP_TOKEN', 'replace_me'));
define('BACKUP_KEEP_DAYS', (int) env('BACKUP_KEEP_DAYS', 7));
// Retenção avô-pai-filho — ver backup_gfs_keep() em functions_backup.php.
define('BACKUP_KEEP_DAYS', (int) env('BACKUP_KEEP_DAYS', 7));
define('BACKUP_KEEP_WEEKS', (int) env('BACKUP_KEEP_WEEKS', 4));
define('BACKUP_KEEP_MONTHS', (int) env('BACKUP_KEEP_MONTHS', 1));
// Pasta (relativa à raiz do host) que o backup de ficheiros arquiva. Só os
// uploads têm informação externa — todo o resto é reconstituível a partir do
@@ -68,28 +71,12 @@ define('DB_USER', env('DB_USER', ''));
define('DB_PASS', env('DB_PASS', ''));
define('DB_CHARSET', 'utf8mb4');
/**
* Apaga backups mais antigos que BACKUP_KEEP_DAYS na pasta indicada.
* $pattern — glob pattern para identificar os ficheiros (ex: 'backup_*.zip')
*/
function cleanOldBackups(string $dir, string $pattern): void
/** Poda avô-pai-filho no fim de um worker (ver backup_prune()), com o relatório no output. */
function backup_prune_report(): void
{
$cutoff = time() - (BACKUP_KEEP_DAYS * 86400);
$files = glob($dir . '/' . $pattern) ?: [];
$deleted = 0;
foreach ($files as $file) {
if (filemtime($file) < $cutoff) {
if (@unlink($file)) {
$deleted++;
echo "Apagado (antigo): " . basename($file) . "\n";
}
}
}
if ($deleted === 0) {
echo "Rotação: nenhum backup antigo para apagar.\n";
} else {
echo "Rotação: $deleted ficheiro(s) apagado(s).\n";
}
$r = backup_prune();
foreach ($r['deleted'] as $f) echo "Apagado (retenção): $f\n";
foreach ($r['errors'] as $f) echo "ERRO a apagar: $f\n";
echo 'Rotação: ' . count($r['deleted']) . ' apagado(s), ' . count($r['kept']) . ' mantido(s) — '
. backup_retention_summary($r['policy']) . ".\n";
}