' . '
Algo correu mal. Tente novamente em instantes.
'; } exit(1); } // Required env vars — fail loud if any is missing. Hard-coded fallback creds // (especially `root` / `admin`) were a footgun: a missing .env would let the // app boot with the wrong identity instead of refusing to start. foreach (['DB_NAME', 'DB_USER'] as $req) { if (getenv($req) === false || getenv($req) === '') { _bootstrap_fail("required env var {$req} is not set"); } } if (getenv('DB_PASS') === false) { _bootstrap_fail('required env var DB_PASS is not set (empty string is allowed)'); } $host = env('DB_HOST', '127.0.0.1'); $port = (int) env('DB_PORT', 3306); $db = env('DB_NAME'); // Not $user: that name leaks into the router's flat include scope (router.php // requires this file at top level, no function wrapper) and would shadow the // real logged-in user array that includes/header.php expects. $dbUser = env('DB_USER'); $pass = env('DB_PASS'); $dsn = "mysql:host=$host;port=$port;dbname=$db;charset=utf8mb4"; try { $pdo = new PDO($dsn, $dbUser, $pass, [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // Real server-side prepares (not driver emulation): a tighter defence // against injection than emulated string-interpolation. Safe here — the // codebase uses positional ? params (no reused :named, no bound LIMIT). PDO::ATTR_EMULATE_PREPARES => false, // Keep values coming back as strings, as the emulated driver did, so the // type change from native prepares doesn't ripple through existing code. PDO::ATTR_STRINGIFY_FETCHES => true, ]); } catch (PDOException $e) { _bootstrap_fail('PDO connect failed: ' . $e->getMessage()); } // Distinct session cookie per app so installs that share a host (e.g. // development.andmore.pt/id.andmore.pt/ and /fitme3070.pt/) don't overwrite // each other's PHPSESSID. Must run before session_start(). // // Cookie e modo estrito definidos aqui, no código, para valerem em qualquer // servidor: antes o cookie saía só com path=/ (sem Secure, HttpOnly nem // SameSite) e o PHP aceitava IDs de sessão inventados pelo cliente. if (session_status() === PHP_SESSION_NONE) { ini_set('session.use_strict_mode', '1'); ini_set('session.use_only_cookies', '1'); ini_set('session.cookie_httponly', '1'); ini_set('session.cookie_samesite', 'Lax'); if (request_is_https()) ini_set('session.cookie_secure', '1'); session_name('FIT_SESS'); session_start(); } // ─── Ligação ao platform-tools ─────────────────────────────────────────────── // // A app mantém as suas próprias passwords e sessão (`config/auth.php`, // `views/auth/login.php`) — não passa a usar o functions_auth.php partilhado // (`'auth' => 'own'`), porque a `auth_attempts` desta app não tem a coluna // `identifier` que o throttle partilhado exige (tem `ip`+`scope`, do seu // próprio throttle em functions_throttle.php). Ainda assim liga-se ao // bloqueio de IP da frota, ao router das páginas de sistema (Backups, // Actualizações, Auditoria, IP bans, Geo, Origem das visitas, Hub) e ao // resto do platform-tools, tudo por cima da autenticação própria. require_once dirname(__DIR__) . '/shared/platform-tools/functions_connector.php'; platform_connect($pdo, [ 'auth' => 'own', 'guard' => 'require_sysadmin', 'router' => true, 'conformance' => 'report', // nunca recusa — só reporta no health endpoint // A Comunicações desta app já cobre SMTP (config + testes); o Estado SMTP // partilhado seria um segundo editor da mesma configuração. 'replaced_tools' => ['smtp_status' => 'views/sysadmin/comms.php'], 'hide_tools' => ['smtp_status'], 'sysadmin_tools' => [[ 'title' => 'Comunicações', 'description' => 'Configuração SMTP, fila de mensagens e testes de envio.', 'icon' => 'bi-broadcast', 'url' => 'views/sysadmin/comms.php', 'color' => 'info', ], [ 'title' => 'Fila de mensagens', 'description' => 'O que saiu, o que falhou e porquê.', 'icon' => 'bi-envelope-paper', 'url' => 'views/sysadmin/messages.php', 'color' => 'secondary', ]], ]);