. Uma ferramenta nova // no platform-tools aparece em todos os sites no próximo update do submódulo, // sem ficheiro nenhum a criar do lado de lá. // // O que os stubs faziam à mão vem agora do platform_connect() do site: // // guard quem pode entrar (obrigatório — sem ele isto recusa) // guards guarda própria de uma ferramenta, p. ex. ['tracking' => 'require_admin'] // replaced_tools ferramenta que o site substitui por página sua: // ['smtp_status' => 'views/sysadmin/comms.php'] // layout ['header' => …, 'navbar' => …, 'footer' => …] // dashboard_url o «Voltar» das páginas (relativo ao base_url, ou absoluto); // em array, por ferramenta — as outras voltam ao índice // migrations_dir onde vivem as migrações, quando não é database/migrations/ // sysadmin_tools cartões próprios do índice (array, ou callable que o devolve) // hide_tools cartões por omissão a esconder, p. ex. ['smtp_status'] // page_vars variáveis `platform_*` extra de uma página, p. ex. // ['platform_error_log_file' => …] // // Query string (platform.php?tool=backups) e não /platform/backups: um caminho // bonito precisa de regras de reescrita, e a frota tem Apache em Docker, cPanel // e talvez nginx. Isto funciona em todos sem configurar nada. // // A tabela de rotas é uma lista fechada, e cada página continua a fazer as suas // verificações (CSRF, confirmação escrita). O router só despacha e guarda a // porta — nunca inclui um nome vindo do pedido. declare(strict_types=1); require_once __DIR__ . '/hub_bootstrap.php'; // $repo_root, $pdo, config/db.php + functions.php require_once __DIR__ . '/functions_connector.php'; require_once __DIR__ . '/functions_routes.php'; $tool = (string) ($_GET['tool'] ?? 'index'); $route = platform_routes()[$tool] ?? null; if ($route === null) { http_response_code(404); exit('Ferramenta desconhecida.'); } $cfg = platform_config(); // Uma guarda por ferramenta quando o site a declara (a Origem das visitas é de // admin na fotografia, não só de sysadmin); senão a geral. $guard = $cfg['guards'][$tool] ?? ($cfg['guard'] ?? null); if ($cfg === [] || !is_callable($guard)) { // Falha fechada: sem guarda declarada não há forma de saber quem pode entrar, // e uma página de sistema aberta a qualquer um é pior do que nenhuma. http_response_code(500); exit('Este site ainda não declara uma guarda de sysadmin no platform_connect() — ' . 'as páginas de sistema não abrem sem ela.'); } $guard(); // Um site pode ter página própria no lugar de uma partilhada (as Comunicações // da fotografia em vez do Estado SMTP: dois editores da mesma configuração // acabavam por mostrar um o que o outro não gravou). O URL partilhado continua // a chegar lá. if (!empty($cfg['replaced_tools'][$tool])) { header('Location: ' . platform_route_absolute_url((string) $cfg['replaced_tools'][$tool]), true, 302); exit; } if (!$pdo instanceof PDO && !empty($route['needs_db'])) { http_response_code(503); exit('Esta ferramenta precisa de base de dados, e este site não tem.'); } // O que os stubs definiam antes de incluir a página. foreach (['header', 'navbar', 'footer'] as $part) { if (!empty($cfg['layout'][$part])) ${'platform_layout_' . $part} = $cfg['layout'][$part]; } // Em array é por ferramenta, e as que lá não estão voltam ao índice do Sistema // (é de lá que se chega a elas): ['index' => 'views/admin/users.php']. if (is_array($cfg['dashboard_url'] ?? null)) { $platform_dashboard_url = isset($cfg['dashboard_url'][$tool]) ? platform_route_absolute_url((string) $cfg['dashboard_url'][$tool]) : platform_route_url('index'); } elseif (!empty($cfg['dashboard_url'])) { $platform_dashboard_url = platform_route_absolute_url((string) $cfg['dashboard_url']); } if (!empty($cfg['migrations_dir'])) { $GLOBALS['MIGRATIONS_DIR'] = (string) $cfg['migrations_dir']; } foreach ((array) ($cfg['page_vars'] ?? []) as $name => $value) { if (is_string($name) && str_starts_with($name, 'platform_')) ${$name} = $value; } if ($tool === 'index') { $tools = $cfg['sysadmin_tools'] ?? []; $sysadmin_tools = is_callable($tools) ? (array) $tools() : (array) $tools; foreach ($sysadmin_tools as &$card) { if (isset($card['url'])) $card['url'] = platform_route_absolute_url((string) $card['url']); } unset($card); // Sem o .php: o índice compara com str_contains() no URL do cartão, e o do // router é …?tool=smtp_status, não …/smtp_status.php. $platform_hide_tools = array_map(fn($h) => preg_replace('/\.php$/', '', (string) $h), (array) ($cfg['hide_tools'] ?? [])); } if (!empty($route['backup_helpers'])) { hub_load_backup_helpers(); } unset($cfg, $tools, $part, $name, $value); require __DIR__ . '/' . $route['file'];