shared/platform-tools was a git submodule. On this branch it is a plain
directory of tracked files, frozen at ea3616abae30106df283e73a3e2f9e5bb0e6ae90
— the same commit fotografia.andmore.pt pins. Contents are byte-identical to
that tree, verified blob by blob.
The submodule caused every deployment failure this site has had:
- .gitmodules named no `branch`, so the update fell back to
refs/remotes/origin/HEAD — a ref a plain server-side clone never writes.
"Atualizar submódulo" failed with "Unable to find refs/remotes/origin/HEAD
revision in submodule path".
- Its URL was http://, which the origin answers with 523.
- This install is deployed by uploading files, and a submodule is invisible
to that. The tree moved forward while shared/platform-tools stayed on the
old pin, config/functions_secrets.php died on a missing compat_secrets.php,
and the whole site — public pages included — went down.
Each of those was separately fixable. Together they mean a second repository
nobody can see from the deployment path, gating whether the first one boots. A
plain directory deploys like every other file and cannot fall out of step.
The cost is that platform-tools updates no longer arrive by bumping a pin; they
are a copy-over-and-commit. shared/platform-tools/VENDORED.md records the
upstream SHA, the procedure, and which four files differ from upstream in line
endings only (social/*.js, .css, functions_publications.php — CRLF upstream,
normalised to LF here; content otherwise identical).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
129 lines
6.1 KiB
PHP
129 lines
6.1 KiB
PHP
<?php
|
|
require_once __DIR__ . '/functions_settings.php'; // platform_admin_url()
|
|
// ─── Shared sysadmin hub ─────────────────────────────────────────────────────
|
|
//
|
|
// Landing page for the sysadmin area. The host navbar points only here; every
|
|
// other sysadmin tool is reached via the cards on this page.
|
|
//
|
|
// Bootstrap contract — stub at <host>/views/sysadmin/index.php must:
|
|
// 1. Load $pdo (config/db.php + functions.php)
|
|
// 2. Run require_sysadmin()
|
|
// 3. Set $repo_root
|
|
// Optional overrides (same as platform_update.php / backups.php):
|
|
// $platform_layout_header / $platform_layout_navbar / $platform_layout_footer
|
|
// $sysadmin_tools — array of card definitions; merged with the defaults below.
|
|
// Each entry: ['title'=>str, 'description'=>str, 'icon'=>str (bi-*),
|
|
// 'url'=>str, 'badge'=>?str, 'color'=>str (primary/info/…)]
|
|
|
|
if (!isset($repo_root) || !is_string($repo_root)) {
|
|
$repo_root = realpath(dirname(__DIR__, 2)) ?: dirname(__DIR__, 2);
|
|
}
|
|
$repo_root = rtrim($repo_root, '/');
|
|
|
|
$base = function (string $path): string {
|
|
return function_exists('base_url') ? base_url($path) : '/' . ltrim($path, '/');
|
|
};
|
|
|
|
$default_tools = [
|
|
[
|
|
'title' => 'Atualizar plataforma',
|
|
'description' => 'Git pull/push, OPcache, migrations e log de erros.',
|
|
'icon' => 'bi-arrow-repeat',
|
|
'url' => platform_admin_url('platform_update.php'),
|
|
'color' => 'primary',
|
|
],
|
|
[
|
|
'title' => 'Backups',
|
|
'description' => 'Criar e transferir cópias de segurança dos uploads e da base de dados.',
|
|
'icon' => 'bi-archive',
|
|
'url' => platform_admin_url('backups.php'),
|
|
'color' => 'secondary',
|
|
],
|
|
[
|
|
'title' => 'Estado SMTP',
|
|
'description' => 'Configuração do envio de email, teste de ligação e mensagem de teste.',
|
|
'icon' => 'bi-envelope-check',
|
|
'url' => platform_admin_url('smtp_status.php'),
|
|
'color' => 'info',
|
|
],
|
|
// Por omissão e não site a site: a partir do momento em que o cliente do hub
|
|
// é partilhado, uma plataforma que o tenha instalado sem mostrar a página
|
|
// fica ligada ao hub sem ninguém saber por onde a desligar.
|
|
[
|
|
'title' => 'Integração com o hub',
|
|
'description' => 'Anunciar esta plataforma à Frota e gerir as chaves de leitura e de operações.',
|
|
'icon' => 'bi-hdd-network',
|
|
'url' => platform_admin_url('hub_integration.php'),
|
|
'color' => 'secondary',
|
|
// Um site que ainda não tenha o stub não deve mostrar um cartão para 404.
|
|
'available' => is_file($repo_root . '/public/views/sysadmin/hub_integration.php')
|
|
|| is_file($repo_root . '/public/views/admin/hub_integration.php'),
|
|
],
|
|
];
|
|
|
|
// Um site pode esconder um cartão por omissão declarando
|
|
// $platform_hide_tools = ['smtp_status.php'] no seu stub. Serve para quando a
|
|
// plataforma tem uma página própria que já faz o mesmo -- o dance tem as
|
|
// Comunicações, que editam a configuração e correm os mesmos testes, e ter os
|
|
// dois cartões lado a lado só faz hesitar em qual clicar.
|
|
$hide = array_map('strval', $platform_hide_tools ?? []);
|
|
if ($hide) {
|
|
$default_tools = array_filter(
|
|
$default_tools,
|
|
fn($t) => !array_filter($hide, fn($h) => str_contains((string) $t['url'], $h))
|
|
);
|
|
}
|
|
|
|
// Cards podem declarar 'available' => bool para serem escondidos quando o
|
|
// servidor não suporta a ferramenta (ex.: exige proc_open). Ausente = visível.
|
|
$tools = array_filter(
|
|
array_merge($default_tools, $sysadmin_tools ?? []),
|
|
fn($t) => !isset($t['available']) || $t['available']
|
|
);
|
|
|
|
$layout_header = $platform_layout_header ?? ($repo_root . '/includes/header.php');
|
|
$layout_navbar = $platform_layout_navbar ?? ($repo_root . '/includes/navbar.php');
|
|
$layout_footer = $platform_layout_footer ?? ($repo_root . '/includes/footer.php');
|
|
|
|
$pageTitle = 'Sistema';
|
|
if (is_file($layout_header)) include $layout_header;
|
|
if (is_file($layout_navbar)) include $layout_navbar;
|
|
?>
|
|
<div class="container py-4" style="max-width:1000px;">
|
|
<div class="mb-4">
|
|
<h2 class="mb-1"><i class="bi bi-shield-lock me-2"></i>Sistema</h2>
|
|
<p class="text-muted small mb-0">Ferramentas de administração da plataforma.</p>
|
|
</div>
|
|
|
|
<div class="row g-3">
|
|
<?php foreach ($tools as $t): ?>
|
|
<div class="col-12 col-md-6">
|
|
<a href="<?= htmlspecialchars($t['url']) ?>"
|
|
class="card shadow-sm h-100 text-decoration-none text-reset border-0">
|
|
<div class="card-body d-flex gap-3">
|
|
<div class="flex-shrink-0">
|
|
<span class="d-inline-flex align-items-center justify-content-center rounded-3 text-bg-<?= htmlspecialchars($t['color'] ?? 'primary') ?>"
|
|
style="width:48px;height:48px;font-size:1.5rem;">
|
|
<i class="bi <?= htmlspecialchars($t['icon'] ?? 'bi-tools') ?>"></i>
|
|
</span>
|
|
</div>
|
|
<div class="flex-grow-1">
|
|
<div class="d-flex align-items-center gap-2">
|
|
<h5 class="mb-0"><?= htmlspecialchars($t['title']) ?></h5>
|
|
<?php if (!empty($t['badge'])): ?>
|
|
<span class="badge text-bg-warning"><?= htmlspecialchars($t['badge']) ?></span>
|
|
<?php endif; ?>
|
|
</div>
|
|
<p class="text-muted small mb-0 mt-1"><?= htmlspecialchars($t['description']) ?></p>
|
|
</div>
|
|
<div class="flex-shrink-0 align-self-center">
|
|
<i class="bi bi-chevron-right text-muted"></i>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</div>
|
|
<?php if (is_file($layout_footer)) include $layout_footer; ?>
|