92 lines
2.6 KiB
PHP
92 lines
2.6 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Proxmox;
|
|
|
|
use App\Models\Host;
|
|
use Illuminate\Http\Client\PendingRequest;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Illuminate\Support\Str;
|
|
|
|
/**
|
|
* Real Proxmox VE REST client. Token auth over the WireGuard tunnel; the PVE
|
|
* certificate is self-signed so TLS verification is disabled (traffic already
|
|
* runs inside the encrypted tunnel). Not unit-tested (live I/O).
|
|
*/
|
|
class HttpProxmoxClient implements ProxmoxClient
|
|
{
|
|
private string $baseUrl = '';
|
|
|
|
private string $token = '';
|
|
|
|
public function forHost(Host $host): static
|
|
{
|
|
$this->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'] ?? '',
|
|
];
|
|
}
|
|
}
|