format('Ymd-His').'-'.Str::lower(Str::random(4)); $this->proxmox->createSnapshot((int) $vm->vmid, $name); $hours = (int) (config('hosting.snapshots.retention_hours') ?? 48); return VmSnapshot::query()->create([ 'customer_id' => $vm->id, 'name' => $label ?? $name, 'proxmox_snapshot_id' => $name, 'auto_created' => $auto, 'expires_at' => now()->addHours($hours), ]); } public function autoBeforeDestructive(Customer $vm, User $user, string $reason): void { if (! config('hosting.snapshots.auto_before_destructive', true)) { return; } $this->create($vm, $user, true, "auto-{$reason}"); } public function rollback(Customer $vm, VmSnapshot $snapshot): void { $this->proxmox->rollbackSnapshot((int) $vm->vmid, $snapshot->proxmox_snapshot_id); } public function delete(Customer $vm, VmSnapshot $snapshot): void { $this->proxmox->deleteSnapshot((int) $vm->vmid, $snapshot->proxmox_snapshot_id); $snapshot->delete(); } public function pruneExpired(): int { $count = 0; $expired = VmSnapshot::query()->where('expires_at', '<=', now())->with('vm')->get(); foreach ($expired as $snapshot) { if ($snapshot->vm?->vmid) { try { $this->proxmox->deleteSnapshot((int) $snapshot->vm->vmid, $snapshot->proxmox_snapshot_id); } catch (\Throwable) { // snapshot may already be gone in Proxmox } } $snapshot->delete(); $count++; } return $count; } }