Safari caches static assets far more aggressively than Chrome/Firefox and largely ignores heuristic revalidation, so a new deploy could keep showing the old CSS/JS indefinitely even on a hard refresh — no Cache-Control headers were set anywhere to guide it otherwise. inc/assets.php adds asset_version($path), which appends a ?v=<mtime> query string derived from the file's last-modified time. Every page that links styles.css or main.js (index.php, menu.php, and all five admin pages) now uses it, so the URL changes whenever the file's contents change and browsers always fetch the new version. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
85 lines
3.6 KiB
PHP
85 lines
3.6 KiB
PHP
<?php
|
|
require_once __DIR__ . '/../inc/auth.php';
|
|
require_once __DIR__ . '/../inc/assets.php';
|
|
|
|
$error = '';
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
$username = $_POST['username'] ?? '';
|
|
$password = $_POST['password'] ?? '';
|
|
|
|
if (authenticate_user($username, $password)) {
|
|
header('Location: index.php');
|
|
exit;
|
|
} else {
|
|
$error = 'Invalid username or password.';
|
|
}
|
|
}
|
|
?>
|
|
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Admin Login | The Old Fitz Public House</title>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Fraunces:wght@600&family=Karla:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="../styles.css<?= asset_version('styles.css') ?>">
|
|
</head>
|
|
<body class="admin-login-body">
|
|
<div class="login-card">
|
|
<div class="login-header">
|
|
<img src="../assets/knot.png" alt="" class="knot">
|
|
<h1>The Old Fitz</h1>
|
|
<p>Management Portal</p>
|
|
</div>
|
|
|
|
<?php if (!empty($error)): ?>
|
|
<div class="error-msg" role="alert"><?= htmlspecialchars($error) ?></div>
|
|
<?php endif; ?>
|
|
|
|
<form method="POST" action="login.php">
|
|
<div class="form-group">
|
|
<label for="username">Username</label>
|
|
<input type="text" id="username" name="username" class="form-input" required autofocus autocomplete="username">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="password">Password</label>
|
|
<div class="password-input-wrap">
|
|
<input type="password" id="password" name="password" class="form-input" required autocomplete="current-password">
|
|
<button type="button" class="password-toggle-btn" aria-label="Show password" id="btn-toggle-login-password">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="icon-eye">
|
|
<path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z" />
|
|
<circle cx="12" cy="12" r="3" />
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<button type="submit" class="btn btn-primary login-btn">Sign In</button>
|
|
</form>
|
|
|
|
<a href="../" class="back-link">← Return to Pub Website</a>
|
|
</div>
|
|
|
|
<script>
|
|
(function() {
|
|
var btn = document.getElementById('btn-toggle-login-password');
|
|
var input = document.getElementById('password');
|
|
if (!btn || !input) return;
|
|
|
|
var eyeOpen = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="icon-eye"><path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z"/><circle cx="12" cy="12" r="3"/></svg>';
|
|
var eyeOff = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="icon-eye-off"><path d="M9.88 9.88a3 3 0 1 0 4.24 4.24"/><path d="M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68"/><path d="M6.61 6.61A13.526 13.526 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61"/><line x1="2" y1="2" x2="22" y2="22"/></svg>';
|
|
|
|
btn.addEventListener('click', function() {
|
|
var isPassword = input.type === 'password';
|
|
input.type = isPassword ? 'text' : 'password';
|
|
btn.setAttribute('aria-label', isPassword ? 'Hide password' : 'Show password');
|
|
btn.innerHTML = isPassword ? eyeOff : eyeOpen;
|
|
input.focus();
|
|
});
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|