feat(dev): environment ribbon and one-click dev login on the public site and auth pages
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5f64ae57db
commit
b73c23bf55
8 files changed
+75
-4
No files matched your search
+2
-1
@@ -13,7 +13,8 @@ if (!is_file($_dev_shared)) { http_response_code(404); exit; } // submódulo s
|
|||||||
require_once $_dev_shared;
|
require_once $_dev_shared;
|
||||||
|
|
||||||
platform_dev_login_handle([
|
platform_dev_login_handle([
|
||||||
'roles' => ['admin' => 'admin@mail.pt', 'sysadmin' => 'sysadmin@mail.pt'],
|
'roles' => ['client' => 'client@mail.pt', 'staff' => 'staff@mail.pt',
|
||||||
|
'admin' => 'admin@mail.pt', 'sysadmin' => 'sysadmin@mail.pt'],
|
||||||
'bootstrap' => function () { global $pdo; require __DIR__ . '/config/db.php'; require_once __DIR__ . '/config/functions.php'; },
|
'bootstrap' => function () { global $pdo; require __DIR__ . '/config/db.php'; require_once __DIR__ . '/config/functions.php'; },
|
||||||
'csrf_ok' => fn(): bool => is_string($_POST['_csrf'] ?? null) && is_string($_SESSION['csrf_token'] ?? null)
|
'csrf_ok' => fn(): bool => is_string($_POST['_csrf'] ?? null) && is_string($_SESSION['csrf_token'] ?? null)
|
||||||
&& $_SESSION['csrf_token'] !== '' && hash_equals($_SESSION['csrf_token'], $_POST['_csrf']),
|
&& $_SESSION['csrf_token'] !== '' && hash_equals($_SESSION['csrf_token'], $_POST['_csrf']),
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
</main>
|
</main>
|
||||||
<script src="<?= base_url('assets/vendor/bootstrap5/js/bootstrap.bundle.min.js') ?>"></script>
|
<script src="<?= base_url('assets/vendor/bootstrap5/js/bootstrap.bundle.min.js') ?>"></script>
|
||||||
<?= script_tag('assets/js/password-toggle.js') ?>
|
<?= script_tag('assets/js/password-toggle.js') ?>
|
||||||
|
<?php include __DIR__ . '/dev_toolbar.php'; // DEV ONLY: sem saída fora de dev ?>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -65,6 +65,7 @@ $app_name = defined('APP_NAME') ? APP_NAME : 'FitMe 3070';
|
|||||||
/* 16px inputs stop iOS from auto-zooming on focus */
|
/* 16px inputs stop iOS from auto-zooming on focus */
|
||||||
@media (max-width: 575.98px) { .form-control { font-size: 16px; } .auth-card { padding: 1.5rem; } }
|
@media (max-width: 575.98px) { .form-control { font-size: 16px; } .auth-card { padding: 1.5rem; } }
|
||||||
</style>
|
</style>
|
||||||
|
<?php include __DIR__ . '/env_ribbon.php'; // moldura de aviso fora de produção ?>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<main class="auth-card">
|
<main class="auth-card">
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
// DEV ONLY -- barra de login de um clique (platform-tools). Não imprime nada
|
// DEV ONLY -- barra de login de um clique (platform-tools). Não imprime nada
|
||||||
// fora de dev. Incluída por includes/footer.php.
|
// fora de dev. Incluída por includes/footer.php, auth_footer.php e site/includes/footer.php.
|
||||||
require_once __DIR__ . '/../config/dev_guard.php';
|
require_once __DIR__ . '/../config/dev_guard.php';
|
||||||
if (!function_exists('platform_dev_toolbar')) return;
|
if (!function_exists('platform_dev_toolbar')) return;
|
||||||
platform_dev_toolbar([
|
platform_dev_toolbar([
|
||||||
'login_url' => base_url('dev_login'),
|
'login_url' => base_url('dev_login'),
|
||||||
'logout_url' => base_url('views/auth/logout.php'),
|
'logout_url' => base_url('views/auth/logout.php'),
|
||||||
'roles' => ['admin' => 'Admin', 'sysadmin' => 'Sysadmin'],
|
'roles' => ['client' => 'Cliente', 'staff' => 'Staff', 'admin' => 'Admin', 'sysadmin' => 'Sysadmin'],
|
||||||
'csrf_field' => 'csrf_field',
|
'csrf_field' => 'csrf_field',
|
||||||
]);
|
]);
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Non-production environment marker.
|
||||||
|
*
|
||||||
|
* Dev and prod are the same codebase on near-identical URLs, so it is genuinely
|
||||||
|
* easy to edit a real client's session while believing you are on dev. This
|
||||||
|
* paints an unmissable hazard frame around every page whenever APP_ENV is not
|
||||||
|
* 'prod', plus a corner chip naming the environment.
|
||||||
|
*
|
||||||
|
* Included from <head> in both includes/header.php (backoffice) and
|
||||||
|
* site/includes/head.php (public site). It hangs off html::before / html::after
|
||||||
|
* rather than a wrapper element, so no page markup has to change and nothing
|
||||||
|
* can shift the layout: the frame is fixed, on top, and click-through.
|
||||||
|
*
|
||||||
|
* Reads env() directly rather than the APP_ENV constant, because the public
|
||||||
|
* site does not always load config/functions.php.
|
||||||
|
*/
|
||||||
|
|
||||||
|
$_env = strtolower((string) (function_exists('env') ? env('APP_ENV', 'prod') : 'prod'));
|
||||||
|
if ($_env === 'prod' || $_env === 'production') return;
|
||||||
|
?>
|
||||||
|
<style>
|
||||||
|
/* Faixa de aviso de ambiente — só fora de produção. */
|
||||||
|
html::after {
|
||||||
|
content: '';
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 2147483646;
|
||||||
|
pointer-events: none; /* nunca intercepta cliques */
|
||||||
|
border: 6px solid transparent;
|
||||||
|
border-image: repeating-linear-gradient(
|
||||||
|
45deg,
|
||||||
|
#111 0 14px,
|
||||||
|
#f5c400 14px 28px
|
||||||
|
) 6;
|
||||||
|
}
|
||||||
|
html::before {
|
||||||
|
content: '<?= htmlspecialchars(strtoupper($_env), ENT_QUOTES) ?>';
|
||||||
|
position: fixed;
|
||||||
|
top: 6px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
z-index: 2147483647;
|
||||||
|
pointer-events: none;
|
||||||
|
padding: .1rem .8rem .15rem;
|
||||||
|
background: #f5c400;
|
||||||
|
color: #111;
|
||||||
|
font: 700 .7rem/1.5 system-ui, -apple-system, "Segoe UI", sans-serif;
|
||||||
|
letter-spacing: .18em;
|
||||||
|
border-radius: 0 0 .35rem .35rem;
|
||||||
|
box-shadow: 0 1px 4px rgba(0, 0, 0, .35);
|
||||||
|
}
|
||||||
|
/* Em ecrãs pequenos a moldura rouba demasiado espaço horizontal. */
|
||||||
|
@media (max-width: 575.98px) {
|
||||||
|
html::after { border-width: 4px; border-image-slice: 4; }
|
||||||
|
html::before { font-size: .6rem; top: 4px; }
|
||||||
|
}
|
||||||
|
/* Ao imprimir não faz sentido nenhum. */
|
||||||
|
@media print {
|
||||||
|
html::after, html::before { display: none; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -9,4 +9,8 @@ declare(strict_types=1);
|
|||||||
$_dev_shared = dirname(__DIR__) . '/shared/platform-tools/functions_dev.php';
|
$_dev_shared = dirname(__DIR__) . '/shared/platform-tools/functions_dev.php';
|
||||||
if (!is_file($_dev_shared)) { fwrite(STDERR, "REFUSED: platform-tools sem functions_dev.php\n"); exit(1); }
|
if (!is_file($_dev_shared)) { fwrite(STDERR, "REFUSED: platform-tools sem functions_dev.php\n"); exit(1); }
|
||||||
require_once $_dev_shared;
|
require_once $_dev_shared;
|
||||||
exit(platform_dev_accounts_run(['flags' => ['is_active' => 1]]));
|
exit(platform_dev_accounts_run([
|
||||||
|
'flags' => ['is_active' => 1],
|
||||||
|
'accounts' => ['sysadmin@mail.pt' => 'sysadmin', 'admin@mail.pt' => 'admin',
|
||||||
|
'client@mail.pt' => 'client', 'staff@mail.pt' => 'staff'],
|
||||||
|
]));
|
||||||
@@ -39,5 +39,6 @@
|
|||||||
<?= script_tag('assets/vendor/jquery/jquery-3.7.1.min.js') ?>
|
<?= script_tag('assets/vendor/jquery/jquery-3.7.1.min.js') ?>
|
||||||
<?= script_tag('assets/vendor/bootstrap5/js/bootstrap.bundle.min.js') ?>
|
<?= script_tag('assets/vendor/bootstrap5/js/bootstrap.bundle.min.js') ?>
|
||||||
<?= script_tag('site/assets/js/main.js') ?>
|
<?= script_tag('site/assets/js/main.js') ?>
|
||||||
|
<?php include dirname(__DIR__, 2) . '/includes/dev_toolbar.php'; // DEV ONLY: sem saída fora de dev ?>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -85,6 +85,7 @@ $canonical = $GLOBALS['_canonical_override'] ?? match ($page) {
|
|||||||
|
|
||||||
<!-- Structured data -->
|
<!-- Structured data -->
|
||||||
<?= view('seo') ?>
|
<?= view('seo') ?>
|
||||||
|
<?php include dirname(__DIR__, 2) . '/includes/env_ribbon.php'; // moldura de aviso fora de produção ?>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<a href="#conteudo" class="skip-link">Saltar para o conteúdo</a>
|
<a href="#conteudo" class="skip-link">Saltar para o conteúdo</a>
|
||||||
Reference in new issue
Block a user