Files
phpsite/send.php
alex d629689473 Redesign as VM webshop with NetBox virtualization integration
- Split single-page into 3 pages:
  - index.php: product catalog with VM size cards
  - create.php: order form (name, domain, description)
  - requests.php: request history with pagination
- Rewrite NetBox integration to create Virtual Machines
  (/virtualization/virtual-machines/) instead of DCIM devices
- Add netbox_integration/netbox_api.php: NetBoxClient with
  createVirtualMachine(), findCluster(), findTag(), getList()
- Generate new sleek product-card images (420x280) with
  aligned spec boxes (vcpus/ram/disk)
- Add webshop CSS: product cards, nav links, hero section,
  order layout, responsive grid
- Update redirects: send.php -> requests.php, cancel.php -> requests.php
- Update E2E test for new page structure
- Add .gitignore, deploy-local.sh, setup_database.sql to repo
2026-05-09 23:49:02 +02:00

115 lines
3.5 KiB
PHP
Executable File

<?php
declare(strict_types=1);
require __DIR__ . '/config.php';
require_login();
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo 'Method not allowed.';
exit;
}
if (!verify_csrf($_POST['csrf_token'] ?? null)) {
http_response_code(419);
echo 'Invalid CSRF token.';
exit;
}
$config = require __DIR__ . '/sizes.php';
$sizeMapping = $config['sizes'];
$allowedDomains = $config['domains'];
$vmName = trim((string) ($_POST['VM_Name'] ?? ''));
$size = trim((string) ($_POST['size'] ?? ''));
$domain = trim((string) ($_POST['domain'] ?? ''));
$description = trim((string) ($_POST['description'] ?? ''));
$errors = [];
if (!preg_match('/^[A-Za-z0-9][A-Za-z0-9._-]{1,63}$/', $vmName)) {
$errors[] = 'VM name must be 2-64 characters and use letters, numbers, dots, dashes, or underscores.';
}
if (!isset($sizeMapping[$size])) {
$errors[] = 'Invalid VM size.';
}
if (!in_array($domain, $allowedDomains, true)) {
$errors[] = 'Invalid domain.';
}
if ($errors) {
header('Location: /create.php?size=' . urlencode($size) . '&error=' . urlencode(implode(' ', $errors)));
exit;
}
$resources = $sizeMapping[$size];
try {
$stmt = db()->prepare(
'INSERT INTO vm_requests
(vm_name, domain_name, size, vcpus, ram_gb, disk_gb, requested_by, status, description)
VALUES
(:vm_name, :domain_name, :size, :vcpus, :ram_gb, :disk_gb, :requested_by, :status, :description)'
);
$stmt->execute([
'vm_name' => $vmName,
'domain_name' => $domain,
'size' => $size,
'vcpus' => $resources['vcpus'],
'ram_gb' => $resources['ram_gb'],
'disk_gb' => $resources['disk_gb'],
'requested_by' => $_SESSION['user'] ?? 'unknown',
'status' => 'queued',
'description' => $description,
]);
// Get the inserted request ID
$requestId = db()->lastInsertId();
// Create NetBox Virtual Machine for this request
try {
$netboxClient = getNetBoxClient();
// Find the first cluster (proxmox-cluster)
$cluster = $netboxClient->findCluster('proxmox-cluster');
if (!$cluster) {
throw new Exception('No NetBox cluster found.');
}
$vm = $netboxClient->createVirtualMachine([
'name' => $vmName,
'cluster_id' => $cluster['id'],
'vcpus' => $resources['vcpus'],
'ram_gb' => $resources['ram_gb'],
'disk_gb' => $resources['disk_gb'],
'domain' => $domain,
'description' => $description ?: ('Requested by ' . ($_SESSION['user'] ?? 'unknown')),
]);
$netboxVmId = $vm['id'] ?? null;
// Update the request with NetBox VM ID
$stmt = db()->prepare(
'UPDATE vm_requests SET status = :status, netbox_device_id = :netbox_vm_id WHERE id = :id'
);
$stmt->execute([
'status' => 'deployed_to_netbox',
'netbox_vm_id' => $netboxVmId,
'id' => $requestId,
]);
} catch (Exception $e) {
error_log("NetBox integration failed: " . $e->getMessage());
// Request stays in 'queued' — admin can retry later
}
} catch (PDOException $exception) {
if ($exception->getCode() !== '23000') {
throw $exception;
}
header('Location: /create.php?size=' . urlencode($size) . '&error=' . urlencode('A VM request with that name already exists.'));
exit;
}
header('Location: /requests.php?created=1');
exit;