Files
defiQUG 7cd7022f6e Update .gitignore, remove package-lock.json, and enhance Cloudflare and Proxmox adapters
- Added lock file exclusions for pnpm in .gitignore.
- Removed obsolete package-lock.json from the api and portal directories.
- Enhanced Cloudflare adapter with additional interfaces for zones and tunnels.
- Improved Proxmox adapter error handling and logging for API requests.
- Updated Proxmox VM parameters with validation rules in the API schema.
- Enhanced documentation for Proxmox VM specifications and examples.
2025-12-12 19:29:01 -08:00

160 lines
4.5 KiB
Go

package utils
import (
"fmt"
"regexp"
"strconv"
"strings"
)
const (
// VMIDMin is the minimum valid Proxmox VM ID
VMIDMin = 100
// VMIDMax is the maximum valid Proxmox VM ID
VMIDMax = 999999999
// VMMinMemoryMB is the minimum memory for a VM (128MB)
VMMinMemoryMB = 128
// VMMaxMemoryMB is a reasonable maximum memory (2TB)
VMMaxMemoryMB = 2 * 1024 * 1024
// VMMinDiskGB is the minimum disk size (1GB)
VMMinDiskGB = 1
// VMMaxDiskGB is a reasonable maximum disk size (100TB)
VMMaxDiskGB = 100 * 1024
)
// ValidateVMID validates that a VM ID is within valid Proxmox range
func ValidateVMID(vmid int) error {
if vmid < VMIDMin || vmid > VMIDMax {
return fmt.Errorf("VMID %d is out of valid range (%d-%d)", vmid, VMIDMin, VMIDMax)
}
return nil
}
// ValidateVMName validates a VM name according to Proxmox restrictions
// Proxmox VM names must:
// - Be 1-100 characters long
// - Only contain alphanumeric characters, hyphens, underscores, dots, and spaces
// - Not start or end with spaces
func ValidateVMName(name string) error {
if len(name) == 0 {
return fmt.Errorf("VM name cannot be empty")
}
if len(name) > 100 {
return fmt.Errorf("VM name '%s' exceeds maximum length of 100 characters", name)
}
// Proxmox allows: alphanumeric, hyphen, underscore, dot, space
// But spaces cannot be at start or end
name = strings.TrimSpace(name)
if len(name) != len(strings.TrimSpace(name)) {
return fmt.Errorf("VM name cannot start or end with spaces")
}
// Valid characters: alphanumeric, hyphen, underscore, dot, space (but not at edges)
validPattern := regexp.MustCompile(`^[a-zA-Z0-9._-]+( [a-zA-Z0-9._-]+)*$`)
if !validPattern.MatchString(name) {
return fmt.Errorf("VM name '%s' contains invalid characters. Allowed: alphanumeric, hyphen, underscore, dot, space", name)
}
return nil
}
// ValidateMemory validates memory specification
func ValidateMemory(memory string) error {
if memory == "" {
return fmt.Errorf("memory cannot be empty")
}
memoryMB := ParseMemoryToMB(memory)
if memoryMB < VMMinMemoryMB {
return fmt.Errorf("memory %s (%d MB) is below minimum of %d MB", memory, memoryMB, VMMinMemoryMB)
}
if memoryMB > VMMaxMemoryMB {
return fmt.Errorf("memory %s (%d MB) exceeds maximum of %d MB", memory, memoryMB, VMMaxMemoryMB)
}
return nil
}
// ValidateDisk validates disk specification
func ValidateDisk(disk string) error {
if disk == "" {
return fmt.Errorf("disk cannot be empty")
}
diskGB := ParseDiskToGB(disk)
if diskGB < VMMinDiskGB {
return fmt.Errorf("disk %s (%d GB) is below minimum of %d GB", disk, diskGB, VMMinDiskGB)
}
if diskGB > VMMaxDiskGB {
return fmt.Errorf("disk %s (%d GB) exceeds maximum of %d GB", disk, diskGB, VMMaxDiskGB)
}
return nil
}
// ValidateCPU validates CPU count
func ValidateCPU(cpu int) error {
if cpu < 1 {
return fmt.Errorf("CPU count must be at least 1, got %d", cpu)
}
// Reasonable maximum: 1024 cores
if cpu > 1024 {
return fmt.Errorf("CPU count %d exceeds maximum of 1024", cpu)
}
return nil
}
// ValidateNetworkBridge validates network bridge name format
// Network bridges typically follow vmbrX pattern or custom names
func ValidateNetworkBridge(network string) error {
if network == "" {
return fmt.Errorf("network bridge cannot be empty")
}
// Basic validation: alphanumeric, hyphen, underscore (common bridge naming)
validPattern := regexp.MustCompile(`^[a-zA-Z0-9_-]+$`)
if !validPattern.MatchString(network) {
return fmt.Errorf("network bridge name '%s' contains invalid characters", network)
}
return nil
}
// ValidateImageSpec validates image specification format
// Images can be:
// - Numeric VMID (for template cloning): "123"
// - Volid format: "storage:path/to/image"
// - Image name: "ubuntu-22.04-cloud"
func ValidateImageSpec(image string) error {
if image == "" {
return fmt.Errorf("image cannot be empty")
}
// Check if it's a numeric VMID (template)
if vmid, err := strconv.Atoi(image); err == nil {
if err := ValidateVMID(vmid); err != nil {
return fmt.Errorf("invalid template VMID: %w", err)
}
return nil
}
// Check if it's a volid format (storage:path)
if strings.Contains(image, ":") {
parts := strings.SplitN(image, ":", 2)
if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
return fmt.Errorf("invalid volid format '%s', expected 'storage:path'", image)
}
return nil
}
// Otherwise assume it's an image name (validate basic format)
if len(image) > 255 {
return fmt.Errorf("image name '%s' exceeds maximum length of 255 characters", image)
}
return nil
}