Files
theoldfitz/inc/db.php
T
David Dyer 242c5165e1 Refactor admin panel: enhance user management and security features
- Added role-based access control for user management links in admin navigation.
- Improved user account display with a dedicated account link.
- Implemented form submission confirmation for delete actions in the admin interface.
- Introduced a password visibility toggle on the login page.
- Added login throttling to prevent brute-force attacks, recording failed attempts.
- Created a CLI script for creating an admin user, replacing the previous hardcoded admin credentials.
- Updated database schema to support login attempt tracking.
- Enhanced user editing functionality, allowing role changes with restrictions.
- Improved modal handling for user actions, ensuring better focus management.
- Added styles for new account management page and improved modal transitions.
2026-10-25 12:58:19 -04:00

167 lines
6.0 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
/**
* Database connection & schema management for SQLite
*/
function get_db(): PDO {
static $pdo = null;
if ($pdo !== null) {
return $pdo;
}
$dbDir = __DIR__ . '/../db';
if (!is_dir($dbDir)) {
mkdir($dbDir, 0755, true);
}
$dbPath = $dbDir . '/fitz.sqlite';
$isNew = !file_exists($dbPath);
$pdo = new PDO('sqlite:' . $dbPath, null, null, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_TIMEOUT => 5
]);
// Enable WAL mode for concurrency and performance
$pdo->exec('PRAGMA journal_mode = WAL;');
$pdo->exec('PRAGMA foreign_keys = ON;');
// Initialize tables if needed
init_db_schema($pdo);
return $pdo;
}
function init_db_schema(PDO $pdo): void {
$pdo->exec("
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
role TEXT NOT NULL DEFAULT 'admin',
is_enabled INTEGER NOT NULL DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS announcements (
id INTEGER PRIMARY KEY AUTOINCREMENT,
is_active INTEGER NOT NULL DEFAULT 1,
type TEXT NOT NULL DEFAULT 'holiday',
headline TEXT NOT NULL,
message TEXT NOT NULL,
start_date TEXT NULL,
end_date TEXT NULL,
show_banner INTEGER NOT NULL DEFAULT 1,
show_in_hours INTEGER NOT NULL DEFAULT 1,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS menu_categories (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
description TEXT NULL,
sort_order INTEGER NOT NULL DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS menu_items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
category_id INTEGER NOT NULL REFERENCES menu_categories(id) ON DELETE CASCADE,
name TEXT NOT NULL,
description TEXT NULL,
price TEXT NOT NULL,
badge TEXT NULL,
is_available INTEGER NOT NULL DEFAULT 1,
sort_order INTEGER NOT NULL DEFAULT 0,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS pub_hours (
day_of_week INTEGER PRIMARY KEY,
is_open INTEGER NOT NULL DEFAULT 0,
open_time TEXT NULL,
close_time TEXT NULL,
kitchen_close_time TEXT NULL,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- Single-row table (id is pinned to 1) for site-wide toggles that
-- don't belong to any one record — starting with whether the
-- public menu page shows its items at all.
CREATE TABLE IF NOT EXISTS site_settings (
id INTEGER PRIMARY KEY CHECK (id = 1),
menu_enabled INTEGER NOT NULL DEFAULT 1,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- Failed admin logins, used by inc/login-throttle.php. Rows older
-- than the throttle window are pruned on every new failure.
CREATE TABLE IF NOT EXISTS login_attempts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
ip TEXT NOT NULL,
username TEXT NOT NULL,
attempted_at INTEGER NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_login_attempts_ip ON login_attempts (ip, attempted_at);
CREATE INDEX IF NOT EXISTS idx_login_attempts_username ON login_attempts (username, attempted_at);
");
// users.is_enabled shipped after the initial release — CREATE TABLE IF
// NOT EXISTS above is a no-op against an existing table, so an install
// from before this needs the column added explicitly, once.
$hasIsEnabled = false;
foreach ($pdo->query("PRAGMA table_info(users)") as $col) {
if ($col['name'] === 'is_enabled') {
$hasIsEnabled = true;
break;
}
}
if (!$hasIsEnabled) {
$pdo->exec("ALTER TABLE users ADD COLUMN is_enabled INTEGER NOT NULL DEFAULT 1");
}
// No default admin is seeded — a fresh database has no users until one
// is created from the CLI (run as www-data so the sqlite file stays
// writable by Apache):
// docker exec -it -u www-data the-old-fitz php /var/www/html/bin/create-user.php
// Seed default weekly hours if the table is empty: closed Sun–Wed,
// 4–10pm Thu–Sat with the kitchen closing at 9 — matches the hours the
// site originally shipped with as hardcoded copy.
$stmt = $pdo->query("SELECT COUNT(*) as count FROM pub_hours");
$hoursCount = $stmt->fetchColumn();
if ($hoursCount == 0) {
$insert = $pdo->prepare("
INSERT INTO pub_hours (day_of_week, is_open, open_time, close_time, kitchen_close_time)
VALUES (?, ?, ?, ?, ?)
");
// 0=Sunday ... 6=Saturday
$defaults = [
0 => null, // Sunday — closed
1 => null, // Monday — closed
2 => null, // Tuesday — closed
3 => null, // Wednesday — closed
4 => ['16:00', '22:00', '21:00'], // Thursday
5 => ['16:00', '22:00', '21:00'], // Friday
6 => ['16:00', '22:00', '21:00'], // Saturday
];
foreach ($defaults as $day => $times) {
if ($times === null) {
$insert->execute([$day, 0, null, null, null]);
} else {
$insert->execute([$day, 1, $times[0], $times[1], $times[2]]);
}
}
}
// Seed the singleton settings row (menu on by default) if it doesn't exist yet.
$stmt = $pdo->query("SELECT COUNT(*) as count FROM site_settings");
$settingsCount = $stmt->fetchColumn();
if ($settingsCount == 0) {
$pdo->exec("INSERT INTO site_settings (id, menu_enabled) VALUES (1, 1)");
}
}