baseUrl = 'https://'.$host->wg_ip.':8006/api2/json'; $this->token = (string) $host->api_token_ref; return $this; } private function http(): PendingRequest { return Http::withoutVerifying() ->withHeaders(['Authorization' => 'PVEAPIToken='.$this->token]) ->baseUrl($this->baseUrl) ->timeout(15); } public function listNodes(): array { return $this->http()->get('/nodes')->throw()->json('data', []); } public function nodeStatus(string $node): array { return $this->http()->get("/nodes/{$node}/status")->throw()->json('data', []); } public function nodeStorage(string $node): array { return $this->http()->get("/nodes/{$node}/storage")->throw()->json('data', []); } public function createRole(string $roleId, string $privs): void { $response = $this->http()->asForm()->post('/access/roles', [ 'roleid' => $roleId, 'privs' => $privs, ]); // Idempotent: an existing role is not an error. if ($response->failed() && ! str_contains($response->body(), 'already exists')) { $response->throw(); } } public function createUserAndToken(string $user, string $roleId): array { // Ensure the user exists (ignore "already exists"), grant the role, mint a token. $this->http()->asForm()->post('/access/users', [ 'userid' => $user, 'password' => Str::random(40), ]); $this->http()->asForm()->put('/access/acl', [ 'path' => '/', 'users' => $user, 'roles' => $roleId, ])->throw(); $tokenName = config('provisioning.proxmox.token_name', 'clupilot'); $data = $this->http()->asForm() ->post("/access/users/{$user}/token/{$tokenName}", ['privsep' => 0]) ->throw() ->json('data', []); return [ 'token_id' => $data['full-tokenid'] ?? "{$user}!{$tokenName}", 'secret' => $data['value'] ?? '', ]; } }