bootstrap.debug.php
8.25 KB
<?php
/**
* WorkersPanel Bootstrap v4.2.14-alpha
* Matches schema: shared runtime
*/
define('BASE_PATH', __DIR__);
require_once __DIR__ . '/config/version.php';
require_once __DIR__ . '/config/app_paths.php';
if (!defined('DEVELOPMENT')) define('DEVELOPMENT', false);
if (session_status() === PHP_SESSION_NONE) session_start();
// Install check
if (!file_exists(BASE_PATH . '/install.lock')) {
$uri = $_SERVER['REQUEST_URI'] ?? '';
if (strpos($uri, '/install') === false) {
app_redirect('install');
}
} else {
$db_config = BASE_PATH . '/config/database.php';
if (!file_exists($db_config)) {
http_response_code(500);
die('Database config missing. Delete install.lock to reinstall.');
}
try {
if (!extension_loaded('pdo_mysql')) {
throw new RuntimeException('PHP extension pdo_mysql is not enabled on this server.');
}
require_once $db_config;
if (!isset($pdo) || !($pdo instanceof PDO)) {
throw new RuntimeException('Database bootstrap completed but $pdo was not created.');
}
// Set session variables for DB audit triggers
try {
$uid = isset($_SESSION['user_id']) ? (int)$_SESSION['user_id'] : null;
$ip = $_SERVER['REMOTE_ADDR'] ?? null;
$pdo->exec("SET @app_user_id = " . ($uid ?? "NULL"));
$pdo->exec("SET @app_ip = " . ($ip ? $pdo->quote($ip) : "NULL"));
} catch (Throwable $e) {}
} catch (Throwable $e) {
http_response_code(500);
$safeMessage = htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8');
echo '<!doctype html><html><head><meta charset="utf-8"><title>WorkersPanel DB Error</title>';
echo '<meta name="viewport" content="width=device-width, initial-scale=1">';
echo '<style>body{font-family:Arial,sans-serif;background:#111827;color:#f3f4f6;padding:24px;line-height:1.5}';
echo '.card{max-width:900px;margin:40px auto;background:#1f2937;border:1px solid #374151;border-radius:12px;padding:24px}';
echo 'code{background:#111827;padding:2px 6px;border-radius:6px} .muted{color:#9ca3af}</style></head><body>';
echo '<div class="card"><h1>WorkersPanel could not connect to the database</h1>';
echo '<p><strong>Error:</strong> ' . $safeMessage . '</p>';
echo '<p class="muted">This is why the site is returning HTTP 500 at startup.</p>';
echo '<p>Check <code>config/database.php</code>, confirm the database server allows remote connections from this web server, and confirm PHP has <code>pdo_mysql</code> enabled.</p>';
echo '</div></body></html>';
exit;
}
}
// ── DB helpers ─────────────────────────────────────────────
/**
* Returns true if a column exists on a table in the current database.
* (Used to keep updates compatible while schema changes roll out.)
*/
function wp_db_column_exists(PDO $pdo, string $table, string $column): bool {
try {
$stmt = $pdo->prepare(
"SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ? LIMIT 1"
);
$stmt->execute([$table, $column]);
return (bool)$stmt->fetchColumn();
} catch (Throwable $e) {
return false;
}
}
/**
* Returns the first existing column from a preferred list, or null.
*/
function wp_db_pick_column(PDO $pdo, string $table, array $candidates): ?string {
foreach ($candidates as $col) {
if (wp_db_column_exists($pdo, $table, $col)) return $col;
}
return null;
}
// ── Auth helpers ────────────────────────────────────────────
function isLoggedIn(): bool {
return isset($_SESSION['user_id'], $_SESSION['user_role']);
}
function isAdminRole(): bool {
return isLoggedIn() && in_array($_SESSION['user_role'], ['administrator', 'director']);
}
function hasPermission(string $permission): bool {
if (!isLoggedIn()) return false;
$role = strtolower(trim((string)($_SESSION['user_role'] ?? '')));
if (in_array($role, ['driver', 'porter'], true) && str_starts_with($permission, 'calendar.')) {
return false;
}
if (isAdminRole()) return true;
return in_array($permission, $_SESSION['permissions'] ?? [], true);
}
function requireAuth(): void {
if (!isLoggedIn()) { app_redirect('login'); }
}
function requireAdmin(): void {
requireAuth();
if (!isAdminRole()) { http_response_code(403); die('Access denied. Admin required.'); }
}
function requirePermission(string $permission): void {
requireAuth();
if (!hasPermission($permission)) { http_response_code(403); die('Access denied.'); }
}
function getCurrentUser(): ?array {
if (!isLoggedIn()) return null;
global $pdo;
try {
$stmt = $pdo->prepare("SELECT id, username, email, display_name, role, secondary_group FROM users WHERE id = ?");
$stmt->execute([$_SESSION['user_id']]);
return $stmt->fetch() ?: null;
} catch (Throwable $e) { return null; }
}
// ── Activity logging ─────────────────────────────────────────
// Supports both old signature: logActivity($action, $description)
// And new signature: logActivity($action, $entity_type, $entity_id, $description)
function logActivity(string $action, $secondArg = null, $thirdArg = null, $fourthArg = null): void {
if (!isLoggedIn()) return;
global $pdo;
// Detect call signature
if ($fourthArg !== null) {
// New: logActivity($action, $entity_type, $entity_id, $description)
$entity_type = $secondArg;
$entity_id = $thirdArg;
$description = $fourthArg;
} elseif ($thirdArg !== null && is_int($thirdArg)) {
// New: logActivity($action, $entity_type, $entity_id)
$entity_type = $secondArg;
$entity_id = $thirdArg;
$description = null;
} else {
// Old: logActivity($action, $description)
$entity_type = null;
$entity_id = null;
$description = is_string($secondArg) ? $secondArg : null;
}
try {
$pdo->prepare("
INSERT INTO activity_logs (user_id, action, entity_type, entity_id, description, ip_address, user_agent, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, NOW())
")->execute([
$_SESSION['user_id'],
$action,
$entity_type,
$entity_id,
$description,
$_SERVER['REMOTE_ADDR'] ?? null,
$_SERVER['HTTP_USER_AGENT'] ?? null,
]);
} catch (Throwable $e) {}
}
// ── DB change log (audit trail) ──────────────────────────────
function logDbChange(string $table, string $action, int $rowId, ?array $oldData = null, ?array $newData = null): void {
if (!isLoggedIn()) return;
global $pdo;
try {
$pdo->prepare("
INSERT INTO db_change_logs (user_id, table_name, action, row_id, ip_address, old_data, new_data)
VALUES (?, ?, ?, ?, ?, ?, ?)
")->execute([
$_SESSION['user_id'],
$table, $action, $rowId,
$_SERVER['REMOTE_ADDR'] ?? null,
$oldData ? json_encode($oldData) : null,
$newData ? json_encode($newData) : null,
]);
} catch (Throwable $e) {}
}
// ── Utility ─────────────────────────────────────────────────
function e(?string $s): string {
return htmlspecialchars($s ?? '', ENT_QUOTES, 'UTF-8');
}
function getSystemInfo(string $key, string $default = ''): string {
global $pdo;
try {
$stmt = $pdo->prepare("SELECT value FROM system_info WHERE `key` = ?");
$stmt->execute([$key]);
$r = $stmt->fetchColumn();
return $r !== false ? (string)$r : $default;
} catch (Throwable $e) { return $default; }
}
app_boot_output_buffer();
date_default_timezone_set('UTC');
if (defined('DEVELOPMENT') && DEVELOPMENT === true) {
error_reporting(E_ALL);
ini_set('display_errors', 1);
} else {
error_reporting(0);
ini_set('display_errors', 0);
}