- 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
115 lines
3.5 KiB
PHP
Executable File
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;
|