$msg]); exit; } function require_auth(): array { $h = $_SERVER['HTTP_AUTHORIZATION'] ?? ''; if (!str_starts_with($h, 'Bearer ')) json_err('Unauthorized', 401); $payload = JWT::decode(substr($h, 7)); if ($payload === null) json_err('Unauthorized', 401); return $payload; } function require_admin(): array { $p = require_auth(); if (($p['role'] ?? '') !== 'admin') json_err('Forbidden', 403); return $p; } function require_manager_or_admin(): array { $p = require_auth(); $r = $p['role'] ?? ''; if ($r !== 'admin' && $r !== 'manager') json_err('Forbidden', 403); return $p; } function uuid(): string { $b = random_bytes(16); $b[6] = chr(ord($b[6]) & 0x0f | 0x40); $b[8] = chr(ord($b[8]) & 0x3f | 0x80); return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($b), 4)); } function body(): array { return json_decode(file_get_contents('php://input'), true) ?? []; } function resolve_role(string $email, string $dbRole): string { return in_array(strtolower(trim($email)), ADMIN_EMAILS) ? 'admin' : $dbRole; }