feat: implement server tags management with Proxmox-style color generation and syncing

This commit is contained in:
2025-12-18 20:38:12 -05:00
parent b7547fd01b
commit bdd168d26d
6 changed files with 407 additions and 1 deletions

View File

@@ -0,0 +1,155 @@
<?php
namespace Paymenter\Extensions\Servers\Proxmox\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Paymenter\Extensions\Servers\Proxmox\Models\Server;
use Paymenter\Extensions\Servers\Proxmox\Proxmox;
class ServerTagController
{
/**
* Proxmox-style tag colors
* Based on Proxmox VE color generation for tags
*/
private static $proxmoxTagColors = [
'#e74c3c', // Red
'#e67e22', // Orange
'#f1c40f', // Yellow
'#2ecc71', // Green
'#1abc9c', // Teal
'#3498db', // Blue
'#9b59b6', // Purple
'#e91e63', // Pink
'#00bcd4', // Cyan
'#8bc34a', // Light Green
'#ff5722', // Deep Orange
'#607d8b', // Blue Grey
'#795548', // Brown
'#9e9e9e', // Grey
'#673ab7', // Deep Purple
'#4caf50', // Green Material
];
/**
* Generate a consistent color for a tag based on its text
* Uses the same algorithm as Proxmox VE
*/
public static function generateTagColor(string $tagText): string
{
// Simple hash function to get a consistent color for each tag
$hash = 0;
for ($i = 0; $i < strlen($tagText); $i++) {
$hash = ord($tagText[$i]) + (($hash << 5) - $hash);
$hash = $hash & $hash; // Convert to 32-bit integer
}
$index = abs($hash) % count(self::$proxmoxTagColors);
return self::$proxmoxTagColors[$index];
}
/**
* Get tags for a server
*/
public function show($serverId)
{
$server = Server::where('id', $serverId)
->where('user_id', Auth::id())
->firstOrFail();
return response()->json([
'success' => true,
'tags' => $server->tags ?? [],
]);
}
/**
* Update tags for a server
*/
public function update(Request $request, $serverId)
{
$request->validate([
'tags' => 'nullable|array|max:3',
'tags.*.text' => 'required|string|max:32|regex:/^[a-zA-Z0-9_-]+$/',
'tags.*.color' => 'nullable|string|max:16',
]);
$server = Server::where('id', $serverId)
->where('user_id', Auth::id())
->firstOrFail();
$tags = $request->input('tags', []);
// Limit to 3 tags
$tags = array_slice($tags, 0, 3);
// Process tags and auto-generate colors if not provided
$processedTags = [];
foreach ($tags as $tag) {
$text = trim($tag['text']);
if (empty($text)) continue;
// Convert to lowercase and sanitize (Proxmox requires this)
$text = strtolower(preg_replace('/[^a-zA-Z0-9_-]/', '', $text));
if (empty($text)) continue;
$processedTags[] = [
'text' => $text,
'color' => $tag['color'] ?? self::generateTagColor($text),
];
}
// Update local database
$server->tags = $processedTags;
$server->save();
// Sync to Proxmox
$proxmoxSuccess = $this->syncTagsToProxmox($server, $processedTags);
return response()->json([
'success' => true,
'tags' => $processedTags,
'proxmox_synced' => $proxmoxSuccess,
]);
}
/**
* Sync tags to Proxmox VM
*/
private function syncTagsToProxmox(Server $server, array $tags): bool
{
try {
$proxmox = new Proxmox;
// Convert tags array to Proxmox format (semicolon-separated)
// Proxmox stores tags as a simple semicolon-delimited string
$tagString = implode(';', array_column($tags, 'text'));
// Update VM config with tags
$proxmox->request('/nodes/' . $server->node->name . '/qemu/' . $server->vm_id . '/config', 'put', [
'tags' => $tagString,
]);
return true;
} catch (\Exception $e) {
// Log the error but don't fail the request
\Log::error('Failed to sync tags to Proxmox for server ' . $server->id, [
'error' => $e->getMessage(),
'server_id' => $server->id,
]);
return false;
}
}
/**
* Get available tag colors
*/
public function colors()
{
return response()->json([
'success' => true,
'colors' => self::$proxmoxTagColors,
]);
}
}