handle('add', $attr, fn(array $log): bool => $this->response->success(['api_key_added', self::label($this->keys->create($attr))], $log)); } /** POST edit/api-key: one message per id */ public function editKeys(?array $items, array $attr): bool { $data = array_merge(['id' => $items], $attr); return $this->handle('edit', $data, function (array $log) use ($data, $attr): bool { $this->keys->requireAdmin(); return $this->batch((array)($data['id'] ?? []), $log, fn(int $id): array => ['api_key_modified', self::label($this->keys->update($id, $attr))]); }); } /** POST delete/api-key: one message per id */ public function deleteKeys(array $items): bool { return $this->handle('delete', ['id' => $items], function (array $log) use ($items): bool { $this->keys->requireAdmin(); return $this->batch($items, $log, fn(int $id): array => ['api_key_deleted', self::label($this->keys->delete($id))]); }); } /** GET api-key/all */ public function getKeys(): array { return array_map(fn(ApiKey $key): array => $key->toArray(), $this->keys->all()); } /** GET api-key/, also edit.php */ public function getKey(mixed $id): array { $id = $this->toId($id); $key = ($id === null) ? null : $this->keys->get($id); return ($key === null) ? [] : $key->toArray(); } /** Name of a key in messages: its description, else its id */ private static function label(ApiKey $key): string { return htmlspecialchars($key->description !== '' ? $key->description : (string)$key->id); } /** * Runs one write call: $work gets the log entry and returns the result for process_*_return(). * A UserError becomes a danger message, a database error stops the call with one `mysql_error` message. */ private function handle(string $action, mixed $data, callable $work): bool { $log = ['api', $action, 'key', $data]; try { $result = $work($log); } catch (UserError $e) { $result = $this->response->error($e, $log); } catch (PDOException $e) { $result = $this->response->danger(['mysql_error', $e->getMessage()], $log); } $this->response->flush(); return $result; } /** One message per id: the success message returned by $work(id), or the UserError */ private function batch(array $ids, array $log, callable $work): bool { foreach ($ids as $id) { try { $this->response->success($work($this->requireId($id)), $log); } catch (UserError $e) { $this->response->error($e, $log); } } return true; } /** Integer id from request input ("12", 12); null for anything else */ private function toId(mixed $id): ?int { return (is_numeric($id) && (float)$id == (int)(float)$id) ? (int)(float)$id : null; } /** @throws AccessDenied for ids that cannot exist */ private function requireId(mixed $id): int { return $this->toId($id) ?? throw new AccessDenied(); } }