feat: ✨ device tags
This commit is contained in:
+23
-1
@@ -21,7 +21,7 @@
|
||||
{% endif %}
|
||||
|
||||
<!-- Quick Links -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-8">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mb-8">
|
||||
<a href="/audit" class="bg-gray-200 dark:bg-zinc-800 hover:bg-gray-300 dark:hover:bg-zinc-700 p-6 rounded-lg shadow-md flex items-center justify-between transition-colors">
|
||||
<div class="flex items-center space-x-4">
|
||||
<i class="fas fa-clipboard-list text-3xl text-gray-600 dark:text-gray-400"></i>
|
||||
@@ -42,6 +42,28 @@
|
||||
</div>
|
||||
<i class="fas fa-chevron-right text-gray-400"></i>
|
||||
</a>
|
||||
{% if has_permission('view_tags') %}
|
||||
<a href="/tags" class="bg-gray-200 dark:bg-zinc-800 hover:bg-gray-300 dark:hover:bg-zinc-700 p-6 rounded-lg shadow-md flex items-center justify-between transition-colors">
|
||||
<div class="flex items-center space-x-4">
|
||||
<i class="fas fa-tags text-3xl text-gray-600 dark:text-gray-400"></i>
|
||||
<div>
|
||||
<h3 class="text-lg font-bold">Tag Management</h3>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">Manage device tags</p>
|
||||
</div>
|
||||
</div>
|
||||
<i class="fas fa-chevron-right text-gray-400"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
<a href="/api-docs" class="bg-gray-200 dark:bg-zinc-800 hover:bg-gray-300 dark:hover:bg-zinc-700 p-6 rounded-lg shadow-md flex items-center justify-between transition-colors">
|
||||
<div class="flex items-center space-x-4">
|
||||
<i class="fas fa-code text-3xl text-gray-600 dark:text-gray-400"></i>
|
||||
<div>
|
||||
<h3 class="text-lg font-bold">API Documentation</h3>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">Interactive API reference</p>
|
||||
</div>
|
||||
</div>
|
||||
<i class="fas fa-chevron-right text-gray-400"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Subnet Management Section -->
|
||||
|
||||
@@ -0,0 +1,330 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>API Documentation - IPAM</title>
|
||||
<link rel="icon" type="image/png" href="{{ LOGO_PNG }}">
|
||||
<link href="/static/css/output.css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css" rel="stylesheet">
|
||||
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
|
||||
</head>
|
||||
<body class="bg-gray-300 text-gray-900 dark:bg-zinc-900 dark:text-gray-100 min-h-screen flex flex-col">
|
||||
{% include 'header.html' %}
|
||||
<div class="flex-1 mx-4 py-8 pt-20">
|
||||
<div class="container max-w-7xl mx-auto">
|
||||
<h1 class="text-3xl font-bold mb-8 text-center">API Documentation</h1>
|
||||
|
||||
<!-- Authentication Section -->
|
||||
<div class="bg-gray-200 dark:bg-zinc-800 rounded-2xl shadow-lg p-8 mb-8">
|
||||
<h2 class="text-2xl font-semibold mb-4 border-b border-gray-300 dark:border-zinc-600 pb-2">Authentication</h2>
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<p class="mb-4">All API requests require authentication using an API key. You can provide the API key in one of three ways:</p>
|
||||
<ul class="list-disc list-inside space-y-2 ml-4">
|
||||
<li><strong>Header:</strong> <code class="bg-gray-300 dark:bg-zinc-700 px-2 py-1 rounded text-sm">X-API-Key: your_api_key</code></li>
|
||||
<li><strong>Authorization Header:</strong> <code class="bg-gray-300 dark:bg-zinc-700 px-2 py-1 rounded text-sm">Authorization: Bearer your_api_key</code></li>
|
||||
<li><strong>Query Parameter:</strong> <code class="bg-gray-300 dark:bg-zinc-700 px-2 py-1 rounded text-sm">?api_key=your_api_key</code></li>
|
||||
</ul>
|
||||
<p class="mt-4"><strong>Base URL:</strong> <code class="bg-gray-300 dark:bg-zinc-700 px-2 py-1 rounded text-sm">/api/v1</code></p>
|
||||
</div>
|
||||
<div class="bg-gray-300 dark:bg-zinc-700 rounded-lg p-4">
|
||||
<h3 class="font-semibold mb-2">Your API Key</h3>
|
||||
<div class="flex items-center space-x-2">
|
||||
<input type="text" id="apiKey" value="{{ api_key or '' }}" readonly
|
||||
class="flex-1 px-3 py-2 bg-gray-100 dark:bg-zinc-600 border border-gray-400 dark:border-zinc-500 rounded text-sm font-mono"
|
||||
placeholder="API key not found">
|
||||
<button onclick="testConnection()" class="px-4 py-2 bg-gray-500 hover:bg-gray-600 dark:bg-zinc-600 dark:hover:bg-zinc-500 text-white rounded text-sm transition-colors">
|
||||
<i class="fas fa-plug mr-2"></i>Test
|
||||
</button>
|
||||
</div>
|
||||
<div id="connectionStatus" class="mt-2 text-sm"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Interactive Endpoints -->
|
||||
<div class="bg-gray-200 dark:bg-zinc-800 rounded-2xl shadow-lg p-8 mb-8">
|
||||
<h2 class="text-2xl font-semibold mb-4 border-b border-gray-300 dark:border-zinc-600 pb-2">
|
||||
<i class="fas fa-play-circle mr-2"></i>Interactive Testing
|
||||
</h2>
|
||||
<p class="text-gray-600 dark:text-gray-300 mb-6">Test GET endpoints directly in your browser. Other methods are documented below.</p>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
<!-- GET /devices -->
|
||||
<div class="bg-gray-300 dark:bg-zinc-700 rounded-lg p-4">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<div class="flex items-center space-x-2">
|
||||
<span class="px-2 py-1 bg-green-600 text-white rounded text-xs font-mono">GET</span>
|
||||
<code class="text-sm">/api/v1/devices</code>
|
||||
</div>
|
||||
<button onclick="tryEndpoint('GET', '/api/v1/devices', null, 'devices-list')"
|
||||
class="px-3 py-1 bg-gray-500 hover:bg-gray-600 dark:bg-zinc-600 dark:hover:bg-zinc-500 text-white rounded text-xs transition-colors">
|
||||
<i class="fas fa-play mr-1"></i>Try
|
||||
</button>
|
||||
</div>
|
||||
<p class="text-xs text-gray-600 dark:text-gray-300 mb-2">List all devices</p>
|
||||
<div id="devices-list-response" class="hidden">
|
||||
<pre class="bg-gray-100 dark:bg-zinc-600 p-2 rounded text-xs overflow-x-auto max-h-32" id="devices-list"></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- GET /devices/{id} -->
|
||||
<div class="bg-gray-300 dark:bg-zinc-700 rounded-lg p-4">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<div class="flex items-center space-x-2">
|
||||
<span class="px-2 py-1 bg-green-600 text-white rounded text-xs font-mono">GET</span>
|
||||
<code class="text-sm">/api/v1/devices/{id}</code>
|
||||
</div>
|
||||
<div class="flex items-center space-x-1">
|
||||
<input type="number" id="device-id" placeholder="ID" class="px-2 py-1 border rounded text-xs w-16">
|
||||
<button onclick="tryEndpointWithId('GET', '/api/v1/devices/', 'device-id', 'device-detail')"
|
||||
class="px-3 py-1 bg-gray-500 hover:bg-gray-600 dark:bg-zinc-600 dark:hover:bg-zinc-500 text-white rounded text-xs transition-colors">
|
||||
<i class="fas fa-play mr-1"></i>Try
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-xs text-gray-600 dark:text-gray-300 mb-2">Get device by ID</p>
|
||||
<div id="device-detail-response" class="hidden">
|
||||
<pre class="bg-gray-100 dark:bg-zinc-600 p-2 rounded text-xs overflow-x-auto max-h-32" id="device-detail"></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- GET /devices/by-tag/{tag} -->
|
||||
<div class="bg-gray-300 dark:bg-zinc-700 rounded-lg p-4">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<div class="flex items-center space-x-2">
|
||||
<span class="px-2 py-1 bg-green-600 text-white rounded text-xs font-mono">GET</span>
|
||||
<code class="text-sm">/api/v1/devices/by-tag/{tag}</code>
|
||||
</div>
|
||||
<div class="flex items-center space-x-1">
|
||||
<input type="text" id="tag-name" placeholder="Tag" class="px-2 py-1 border rounded text-xs w-20">
|
||||
<button onclick="tryEndpointWithId('GET', '/api/v1/devices/by-tag/', 'tag-name', 'devices-by-tag')"
|
||||
class="px-3 py-1 bg-gray-500 hover:bg-gray-600 dark:bg-zinc-600 dark:hover:bg-zinc-500 text-white rounded text-xs transition-colors">
|
||||
<i class="fas fa-play mr-1"></i>Try
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-xs text-gray-600 dark:text-gray-300 mb-2">Filter devices by tag</p>
|
||||
<div id="devices-by-tag-response" class="hidden">
|
||||
<pre class="bg-gray-100 dark:bg-zinc-600 p-2 rounded text-xs overflow-x-auto max-h-32" id="devices-by-tag"></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- GET /tags -->
|
||||
<div class="bg-gray-300 dark:bg-zinc-700 rounded-lg p-4">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<div class="flex items-center space-x-2">
|
||||
<span class="px-2 py-1 bg-green-600 text-white rounded text-xs font-mono">GET</span>
|
||||
<code class="text-sm">/api/v1/tags</code>
|
||||
</div>
|
||||
<button onclick="tryEndpoint('GET', '/api/v1/tags', null, 'tags-list')"
|
||||
class="px-3 py-1 bg-gray-500 hover:bg-gray-600 dark:bg-zinc-600 dark:hover:bg-zinc-500 text-white rounded text-xs transition-colors">
|
||||
<i class="fas fa-play mr-1"></i>Try
|
||||
</button>
|
||||
</div>
|
||||
<p class="text-xs text-gray-600 dark:text-gray-300 mb-2">List all tags</p>
|
||||
<div id="tags-list-response" class="hidden">
|
||||
<pre class="bg-gray-100 dark:bg-zinc-600 p-2 rounded text-xs overflow-x-auto max-h-32" id="tags-list"></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Complete API Documentation -->
|
||||
<div class="space-y-6">
|
||||
<!-- Devices Section -->
|
||||
<div class="bg-gray-200 dark:bg-zinc-800 rounded-2xl shadow-lg p-8">
|
||||
<h2 class="text-2xl font-semibold mb-4 border-b border-gray-300 dark:border-zinc-600 pb-2">
|
||||
<i class="fas fa-server mr-2"></i>Devices
|
||||
</h2>
|
||||
<div class="space-y-4">
|
||||
<div class="bg-gray-300 dark:bg-zinc-700 rounded-lg p-4">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<h4 class="font-semibold mb-2">Read Operations</h4>
|
||||
<ul class="list-disc list-inside space-y-1 text-sm">
|
||||
<li><code class="bg-gray-400 dark:bg-zinc-600 px-2 py-1 rounded text-xs">GET /api/v1/devices</code> - List all devices</li>
|
||||
<li><code class="bg-gray-400 dark:bg-zinc-600 px-2 py-1 rounded text-xs">GET /api/v1/devices/{id}</code> - Get device details</li>
|
||||
<li><code class="bg-gray-400 dark:bg-zinc-600 px-2 py-1 rounded text-xs">GET /api/v1/devices/by-tag/{tag}</code> - Get devices by tag</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="font-semibold mb-2">Write Operations</h4>
|
||||
<ul class="list-disc list-inside space-y-1 text-sm">
|
||||
<li><code class="bg-orange-200 dark:bg-orange-800 px-2 py-1 rounded text-xs">POST /api/v1/devices</code> - Create device</li>
|
||||
<li><code class="bg-blue-200 dark:bg-blue-800 px-2 py-1 rounded text-xs">PUT /api/v1/devices/{id}</code> - Update device</li>
|
||||
<li><code class="bg-red-200 dark:bg-red-800 px-2 py-1 rounded text-xs">DELETE /api/v1/devices/{id}</code> - Delete device</li>
|
||||
<li><code class="bg-orange-200 dark:bg-orange-800 px-2 py-1 rounded text-xs">POST /api/v1/devices/{id}/ips</code> - Add IP to device</li>
|
||||
<li><code class="bg-red-200 dark:bg-red-800 px-2 py-1 rounded text-xs">DELETE /api/v1/devices/{id}/ips/{ip_id}</code> - Remove IP</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Subnets Section -->
|
||||
<div class="bg-gray-200 dark:bg-zinc-800 rounded-2xl shadow-lg p-8">
|
||||
<h2 class="text-2xl font-semibold mb-4 border-b border-gray-300 dark:border-zinc-600 pb-2">
|
||||
<i class="fas fa-network-wired mr-2"></i>Subnets
|
||||
</h2>
|
||||
<div class="space-y-4">
|
||||
<div class="bg-gray-300 dark:bg-zinc-700 rounded-lg p-4">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<h4 class="font-semibold mb-2">Read Operations</h4>
|
||||
<ul class="list-disc list-inside space-y-1 text-sm">
|
||||
<li><code class="bg-gray-400 dark:bg-zinc-600 px-2 py-1 rounded text-xs">GET /api/v1/subnets</code> - List all subnets</li>
|
||||
<li><code class="bg-gray-400 dark:bg-zinc-600 px-2 py-1 rounded text-xs">GET /api/v1/subnets/{id}</code> - Get subnet details</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="font-semibold mb-2">Write Operations</h4>
|
||||
<ul class="list-disc list-inside space-y-1 text-sm">
|
||||
<li><code class="bg-orange-200 dark:bg-orange-800 px-2 py-1 rounded text-xs">POST /api/v1/subnets</code> - Create subnet</li>
|
||||
<li><code class="bg-blue-200 dark:bg-blue-800 px-2 py-1 rounded text-xs">PUT /api/v1/subnets/{id}</code> - Update subnet</li>
|
||||
<li><code class="bg-red-200 dark:bg-red-800 px-2 py-1 rounded text-xs">DELETE /api/v1/subnets/{id}</code> - Delete subnet</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Racks Section -->
|
||||
<div class="bg-gray-200 dark:bg-zinc-800 rounded-2xl shadow-lg p-8">
|
||||
<h2 class="text-2xl font-semibold mb-4 border-b border-gray-300 dark:border-zinc-600 pb-2">
|
||||
<i class="fas fa-building mr-2"></i>Racks
|
||||
</h2>
|
||||
<div class="space-y-4">
|
||||
<div class="bg-gray-300 dark:bg-zinc-700 rounded-lg p-4">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<h4 class="font-semibold mb-2">Read Operations</h4>
|
||||
<ul class="list-disc list-inside space-y-1 text-sm">
|
||||
<li><code class="bg-gray-400 dark:bg-zinc-600 px-2 py-1 rounded text-xs">GET /api/v1/racks</code> - List all racks</li>
|
||||
<li><code class="bg-gray-400 dark:bg-zinc-600 px-2 py-1 rounded text-xs">GET /api/v1/racks/{id}</code> - Get rack details</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="font-semibold mb-2">Write Operations</h4>
|
||||
<ul class="list-disc list-inside space-y-1 text-sm">
|
||||
<li><code class="bg-orange-200 dark:bg-orange-800 px-2 py-1 rounded text-xs">POST /api/v1/racks</code> - Create rack</li>
|
||||
<li><code class="bg-red-200 dark:bg-red-800 px-2 py-1 rounded text-xs">DELETE /api/v1/racks/{id}</code> - Delete rack</li>
|
||||
<li><code class="bg-orange-200 dark:bg-orange-800 px-2 py-1 rounded text-xs">POST /api/v1/racks/{id}/devices</code> - Add device to rack</li>
|
||||
<li><code class="bg-red-200 dark:bg-red-800 px-2 py-1 rounded text-xs">DELETE /api/v1/racks/{id}/devices/{device_id}</code> - Remove device</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tags Section -->
|
||||
<div class="bg-gray-200 dark:bg-zinc-800 rounded-2xl shadow-lg p-8">
|
||||
<h2 class="text-2xl font-semibold mb-4 border-b border-gray-300 dark:border-zinc-600 pb-2">
|
||||
<i class="fas fa-tags mr-2"></i>Tags
|
||||
</h2>
|
||||
<div class="space-y-4">
|
||||
<div class="bg-gray-300 dark:bg-zinc-700 rounded-lg p-4">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<h4 class="font-semibold mb-2">Read Operations</h4>
|
||||
<ul class="list-disc list-inside space-y-1 text-sm">
|
||||
<li><code class="bg-gray-400 dark:bg-zinc-600 px-2 py-1 rounded text-xs">GET /api/v1/tags</code> - List all tags</li>
|
||||
<li><code class="bg-gray-400 dark:bg-zinc-600 px-2 py-1 rounded text-xs">GET /api/v1/tags?format=simple</code> - List tags in simple format</li>
|
||||
<li><code class="bg-gray-400 dark:bg-zinc-600 px-2 py-1 rounded text-xs">GET /api/v1/tags/{id}</code> - Get tag details</li>
|
||||
<li><code class="bg-gray-400 dark:bg-zinc-600 px-2 py-1 rounded text-xs">GET /api/v1/devices/{id}/tags</code> - Get device tags</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="font-semibold mb-2">Write Operations</h4>
|
||||
<ul class="list-disc list-inside space-y-1 text-sm">
|
||||
<li><code class="bg-orange-200 dark:bg-orange-800 px-2 py-1 rounded text-xs">POST /api/v1/tags</code> - Create tag</li>
|
||||
<li><code class="bg-blue-200 dark:bg-blue-800 px-2 py-1 rounded text-xs">PUT /api/v1/tags/{id}</code> - Update tag</li>
|
||||
<li><code class="bg-red-200 dark:bg-red-800 px-2 py-1 rounded text-xs">DELETE /api/v1/tags/{id}</code> - Delete tag</li>
|
||||
<li><code class="bg-orange-200 dark:bg-orange-800 px-2 py-1 rounded text-xs">POST /api/v1/devices/{id}/tags</code> - Assign tag to device</li>
|
||||
<li><code class="bg-red-200 dark:bg-red-800 px-2 py-1 rounded text-xs">DELETE /api/v1/devices/{id}/tags/{tag_id}</code> - Remove tag</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Additional Endpoints -->
|
||||
<div class="bg-gray-200 dark:bg-zinc-800 rounded-2xl shadow-lg p-8">
|
||||
<h2 class="text-2xl font-semibold mb-4 border-b border-gray-300 dark:border-zinc-600 pb-2">
|
||||
<i class="fas fa-cogs mr-2"></i>Additional Endpoints
|
||||
</h2>
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
<div class="bg-gray-300 dark:bg-zinc-700 rounded-lg p-4">
|
||||
<h4 class="font-semibold mb-3"><i class="fas fa-info-circle mr-2"></i>System Information</h4>
|
||||
<ul class="list-disc list-inside space-y-1 text-sm">
|
||||
<li><code class="bg-gray-400 dark:bg-zinc-600 px-2 py-1 rounded text-xs">GET /api/v1/info</code> - System information</li>
|
||||
<li><code class="bg-gray-400 dark:bg-zinc-600 px-2 py-1 rounded text-xs">GET /api/v1/device-types</code> - List device types</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="bg-gray-300 dark:bg-zinc-700 rounded-lg p-4">
|
||||
<h4 class="font-semibold mb-3"><i class="fas fa-dharmachakra mr-2"></i>DHCP Management</h4>
|
||||
<ul class="list-disc list-inside space-y-1 text-sm">
|
||||
<li><code class="bg-gray-400 dark:bg-zinc-600 px-2 py-1 rounded text-xs">GET /api/v1/subnets/{id}/dhcp</code> - Get DHCP config</li>
|
||||
<li><code class="bg-orange-200 dark:bg-orange-800 px-2 py-1 rounded text-xs">POST /api/v1/subnets/{id}/dhcp</code> - Generate DHCP config</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="bg-gray-300 dark:bg-zinc-700 rounded-lg p-4">
|
||||
<h4 class="font-semibold mb-3"><i class="fas fa-users mr-2"></i>User & Role Management</h4>
|
||||
<ul class="list-disc list-inside space-y-1 text-sm">
|
||||
<li><code class="bg-gray-400 dark:bg-zinc-600 px-2 py-1 rounded text-xs">GET /api/v1/users</code> - List users</li>
|
||||
<li><code class="bg-gray-400 dark:bg-zinc-600 px-2 py-1 rounded text-xs">GET /api/v1/roles</code> - List roles</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="bg-gray-300 dark:bg-zinc-700 rounded-lg p-4">
|
||||
<h4 class="font-semibold mb-3"><i class="fas fa-clipboard-list mr-2"></i>Audit Log</h4>
|
||||
<ul class="list-disc list-inside space-y-1 text-sm">
|
||||
<li><code class="bg-gray-400 dark:bg-zinc-600 px-2 py-1 rounded text-xs">GET /api/v1/audit</code> - List audit entries</li>
|
||||
</ul>
|
||||
<p class="text-xs text-gray-600 dark:text-gray-400 mt-2">Supports filtering with query parameters</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Response Format & Permissions -->
|
||||
<div class="bg-gray-200 dark:bg-zinc-800 rounded-2xl shadow-lg p-8">
|
||||
<h2 class="text-2xl font-semibold mb-4 border-b border-gray-300 dark:border-zinc-600 pb-2">
|
||||
<i class="fas fa-info-circle mr-2"></i>Response Format & Permissions
|
||||
</h2>
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
<div class="bg-gray-300 dark:bg-zinc-700 rounded-lg p-4">
|
||||
<h3 class="text-lg font-semibold mb-3">Success Responses</h3>
|
||||
<p class="mb-3 text-sm">All API responses are in JSON format. Successful requests return:</p>
|
||||
<ul class="list-disc list-inside space-y-1 text-sm">
|
||||
<li><code class="bg-green-200 dark:bg-green-800 px-2 py-1 rounded text-xs">200 OK</code> - Request successful</li>
|
||||
<li><code class="bg-green-200 dark:bg-green-800 px-2 py-1 rounded text-xs">201 Created</code> - Resource created</li>
|
||||
<li><code class="bg-green-200 dark:bg-green-800 px-2 py-1 rounded text-xs">204 No Content</code> - Success with no response body</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="bg-gray-300 dark:bg-zinc-700 rounded-lg p-4">
|
||||
<h3 class="text-lg font-semibold mb-3">Error Responses</h3>
|
||||
<p class="mb-3 text-sm">Error responses include descriptive messages:</p>
|
||||
<ul class="list-disc list-inside space-y-1 text-sm">
|
||||
<li><code class="bg-red-200 dark:bg-red-800 px-2 py-1 rounded text-xs">400 Bad Request</code> - Invalid request data</li>
|
||||
<li><code class="bg-red-200 dark:bg-red-800 px-2 py-1 rounded text-xs">401 Unauthorized</code> - Missing or invalid API key</li>
|
||||
<li><code class="bg-red-200 dark:bg-red-800 px-2 py-1 rounded text-xs">403 Forbidden</code> - Insufficient permissions</li>
|
||||
<li><code class="bg-red-200 dark:bg-red-800 px-2 py-1 rounded text-xs">404 Not Found</code> - Resource not found</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-6 bg-gray-300 dark:bg-zinc-700 rounded-lg p-4">
|
||||
<h3 class="text-lg font-semibold mb-3"><i class="fas fa-shield-alt mr-2"></i>Permissions</h3>
|
||||
<p class="text-sm">API endpoints respect the same role-based permissions as the web interface. Users can only perform actions that their role allows. If a user lacks the required permission, the API will return a <code class="bg-red-200 dark:bg-red-800 px-2 py-1 rounded text-xs">403 Forbidden</code> error with details about the missing permission.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/static/js/api_docs.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
+44
-1
@@ -60,7 +60,7 @@
|
||||
<button type="submit" class="bg-gray-200 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 hover:cursor-pointer px-4 py-2 rounded-lg w-full">Add IP</button>
|
||||
</div>
|
||||
</form>
|
||||
<div class="allocated-ips">
|
||||
<div class="allocated-ips mb-6">
|
||||
<h3 class="text-lg font-bold mb-2">Allocated IPs:</h3>
|
||||
<ul class="space-y-2">
|
||||
{% for ip in device_ips %}
|
||||
@@ -74,6 +74,49 @@
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Tags Section -->
|
||||
<div class="tags-section mb-6">
|
||||
<h3 class="text-lg font-bold mb-2">Tags:</h3>
|
||||
<div class="flex flex-wrap gap-2 mb-4">
|
||||
{% if device_tags %}
|
||||
{% for tag in device_tags %}
|
||||
<div class="flex items-center space-x-1 px-3 py-1 rounded-full text-sm" style="background-color: {{ tag.color }}20; border: 1px solid {{ tag.color }}">
|
||||
<div class="w-2 h-2 rounded-full" style="background-color: {{ tag.color }}"></div>
|
||||
<span>{{ tag.name }}</span>
|
||||
{% if can_remove_device_tag %}
|
||||
<form action="/device/{{ device.id }}/remove_tag" method="POST" class="inline">
|
||||
<input type="hidden" name="tag_id" value="{{ tag.id }}">
|
||||
<button type="submit" class="ml-1 text-red-500 hover:text-red-700 text-xs" title="Remove tag">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<span class="text-gray-500">No tags assigned</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if can_assign_device_tag and all_tags %}
|
||||
<form action="/device/{{ device.id }}/assign_tag" method="POST" class="flex gap-2">
|
||||
<select name="tag_id" class="border p-2 rounded-lg bg-gray-200 dark:bg-zinc-800 border-gray-600 flex-1" required>
|
||||
<option value="" disabled selected>Select a tag to assign...</option>
|
||||
{% for tag in all_tags %}
|
||||
{% set already_assigned = device_tags|selectattr('id', 'equalto', tag.id)|list|length > 0 %}
|
||||
{% if not already_assigned %}
|
||||
<option value="{{ tag.id }}">{{ tag.name }}</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
<button type="submit" class="bg-gray-200 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 hover:cursor-pointer px-4 py-2 rounded-lg">
|
||||
<i class="fas fa-plus mr-1"></i>Assign Tag
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<form action="/update_device_description" method="POST" class="mb-6 mt-4">
|
||||
<input type="hidden" name="device_id" value="{{ device.id }}">
|
||||
<label for="description" class="block mb-2 text-lg font-bold">Description</label>
|
||||
|
||||
+47
-13
@@ -18,8 +18,28 @@
|
||||
<a href="/add_device" class="text-center bg-gray-200 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 px-4 py-2 rounded-lg">Add New Device</a>
|
||||
<a href="/device_type_stats" class="text-center bg-gray-200 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 px-4 py-2 rounded-lg">View Device Stats</a>
|
||||
</div>
|
||||
<div class="mb-6">
|
||||
|
||||
<!-- Filters Section -->
|
||||
<div class="mb-6 space-y-4">
|
||||
<input type="text" id="search" placeholder="Search devices or IPs..." class="border p-3 rounded-lg bg-gray-200 dark:bg-zinc-800 border-gray-600 w-full">
|
||||
|
||||
<!-- Tag Filter -->
|
||||
{% if all_tag_names %}
|
||||
<div class="flex items-center space-x-2">
|
||||
<label class="text-sm font-medium">Filter by tag:</label>
|
||||
<select id="tag-filter" class="border p-2 rounded-lg bg-gray-200 dark:bg-zinc-800 border-gray-600">
|
||||
<option value="">All devices</option>
|
||||
{% for tag_name in all_tag_names %}
|
||||
<option value="{{ tag_name }}" {% if current_tag_filter == tag_name %}selected{% endif %}>{{ tag_name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% if current_tag_filter %}
|
||||
<a href="/devices" class="text-red-500 hover:text-red-700 text-sm">
|
||||
<i class="fas fa-times"></i> Clear filter
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div id="site-list" class="space-y-6">
|
||||
{% for site, devices in sites_devices.items() %}
|
||||
@@ -33,18 +53,32 @@
|
||||
<ul class="device-list hidden px-6 pb-4">
|
||||
{% for device in devices %}
|
||||
<li class="my-2">
|
||||
<a href="/device/{{ device.id }}" class="flex items-center justify-between bg-gray-300 hover:bg-gray-100 dark:bg-zinc-900 dark:hover:bg-gray-700 dark:text-gray-200 font-semibold rounded-lg px-4 py-2 shadow transition-colors duration-150">
|
||||
<span><i class="fas {{ device.icon_class or 'fa-server' }} mr-2"></i>{{ device.name }}</span>
|
||||
{% set ips = device_ips.get(device.id, []) %}
|
||||
<span class="flex flex-row flex-wrap justify-end items-center ml-4 text-xs text-blue-300 font-normal align-middle">
|
||||
{% if ips|length > 0 %}
|
||||
{% for ip in ips %}
|
||||
<span class="inline-block bg-gray-200 dark:bg-zinc-800 text-gray-900 dark:text-white rounded px-2 py-1 ml-1 font-mono">{{ ip[1] }}</span>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<span class="text-gray-400">No IPs</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
<a href="/device/{{ device.id }}" class="block bg-gray-300 hover:bg-gray-100 dark:bg-zinc-900 dark:hover:bg-gray-700 dark:text-gray-200 font-semibold rounded-lg px-4 py-2 shadow transition-colors duration-150">
|
||||
<div class="flex items-center justify-between">
|
||||
<span><i class="fas {{ device.icon_class or 'fa-server' }} mr-2"></i>{{ device.name }}</span>
|
||||
{% set ips = device_ips.get(device.id, []) %}
|
||||
<span class="flex flex-row flex-wrap justify-end items-center ml-4 text-xs text-blue-300 font-normal align-middle">
|
||||
{% if ips|length > 0 %}
|
||||
{% for ip in ips %}
|
||||
<span class="inline-block bg-gray-200 dark:bg-zinc-800 text-gray-900 dark:text-white rounded px-2 py-1 ml-1 font-mono">{{ ip[1] }}</span>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<span class="text-gray-400">No IPs</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
<!-- Tags -->
|
||||
{% set tags = device_tags.get(device.id, []) %}
|
||||
{% if tags %}
|
||||
<div class="flex flex-wrap gap-1 mt-2">
|
||||
{% for tag in tags %}
|
||||
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs" style="background-color: {{ tag.color }}20; border: 1px solid {{ tag.color }}; color: {{ tag.color }}">
|
||||
<div class="w-1.5 h-1.5 rounded-full mr-1" style="background-color: {{ tag.color }}"></div>
|
||||
{{ tag.name }}
|
||||
</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
||||
+34
-80
@@ -10,10 +10,11 @@
|
||||
</head>
|
||||
<body class="bg-gray-300 text-gray-900 dark:bg-zinc-900 dark:text-gray-100 min-h-screen flex flex-col">
|
||||
{% include 'header.html' %}
|
||||
<div class="flex-1 flex items-center justify-center mx-4">
|
||||
<div class="container py-8 max-w-2xl pt-20">
|
||||
<div class="flex-1 mx-4 py-8 pt-20">
|
||||
<div class="container max-w-full mx-auto lg:px-32">
|
||||
<h1 class="text-3xl font-bold mb-8 text-center">Help & User Guide</h1>
|
||||
<div class="space-y-10 text-lg">
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-6 mb-8">
|
||||
<div class="bg-gray-200 dark:bg-zinc-800 rounded-2xl shadow-lg p-8">
|
||||
<h2 class="text-2xl font-semibold mb-4 border-b border-gray-700 pb-2">Subnets & Devices</h2>
|
||||
<div class="space-y-4">
|
||||
@@ -52,6 +53,35 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-gray-200 dark:bg-zinc-800 rounded-2xl shadow-lg p-8">
|
||||
<h2 class="text-2xl font-semibold mb-4 border-b border-gray-700 pb-2">Device Tags</h2>
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<h3 class="text-xl font-semibold mb-1">Managing Tags</h3>
|
||||
<p>Tags help you organize and categorize your devices. Administrators can manage tags from the <a href="/tags" class="text-blue-600 dark:text-blue-400 hover:underline">Tag Management</a> page accessible from the Admin panel. Each tag has a name, customizable colour, and optional description.</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-xl font-semibold mb-1">Creating Tags</h3>
|
||||
<p>Click <span class="bg-gray-300 dark:bg-zinc-700 px-2 py-1 rounded">Add Tag</span> to create a new tag. Choose a descriptive name (e.g., "Production", "Development", "Critical") and select a colour to visually distinguish the tag. Tags can be used to group devices by environment, importance, location, or any other criteria.</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-xl font-semibold mb-1">Assigning Tags to Devices</h3>
|
||||
<p>From any device's detail page, you can assign multiple tags using the tag assignment dropdown. Each device can have unlimited tags, and removing a tag is as simple as clicking the × button next to the tag name.</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-xl font-semibold mb-1">Filtering Devices by Tags</h3>
|
||||
<p>On the <a href="/devices" class="text-blue-600 dark:text-blue-400 hover:underline">Devices</a> page, use the tag filter dropdown to view only devices with a specific tag. This makes it easy to focus on devices in a particular environment or category.</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-xl font-semibold mb-1">Tag Colours and Visual Organisation</h3>
|
||||
<p>Tags appear as coloured badges throughout the interface. Use consistent colour schemes (e.g., red for production, blue for development) to create visual patterns that help users quickly identify device categories.</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-xl font-semibold mb-1">Tag Permissions</h3>
|
||||
<p>Tag management respects role-based permissions. Users need appropriate permissions to view, create, edit, delete tags, or assign/remove tags from devices. View-only users can see tags but cannot modify them.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-gray-200 dark:bg-zinc-800 rounded-2xl shadow-lg p-8">
|
||||
<h2 class="text-2xl font-semibold mb-4 border-b border-gray-700 pb-2">User Management & Audit</h2>
|
||||
<div class="space-y-4">
|
||||
@@ -86,84 +116,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-gray-200 dark:bg-zinc-800 rounded-2xl shadow-lg p-8">
|
||||
<h2 class="text-2xl font-semibold mb-4 border-b border-gray-700 pb-2">API Documentation</h2>
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<h3 class="text-xl font-semibold mb-1">Authentication</h3>
|
||||
<p>All API requests require authentication using an API key. You can provide the API key in one of three ways:</p>
|
||||
<ul class="list-disc list-inside mt-2 space-y-1 ml-4">
|
||||
<li><strong>Header:</strong> <code class="bg-gray-300 dark:bg-zinc-700 px-2 py-1 rounded text-sm">X-API-Key: your_api_key</code></li>
|
||||
<li><strong>Authorization Header:</strong> <code class="bg-gray-300 dark:bg-zinc-700 px-2 py-1 rounded text-sm">Authorization: Bearer your_api_key</code></li>
|
||||
<li><strong>Query Parameter:</strong> <code class="bg-gray-300 dark:bg-zinc-700 px-2 py-1 rounded text-sm">?api_key=your_api_key</code></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-xl font-semibold mb-1">Base URL</h3>
|
||||
<p>All API endpoints are prefixed with <code class="bg-gray-300 dark:bg-zinc-700 px-2 py-1 rounded text-sm">/api/v1</code></p>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-xl font-semibold mb-1">Available Endpoints</h3>
|
||||
<div class="space-y-3 mt-2">
|
||||
<div>
|
||||
<h4 class="font-semibold">Devices</h4>
|
||||
<ul class="list-disc list-inside ml-4 text-sm space-y-1">
|
||||
<li><code class="bg-gray-300 dark:bg-zinc-700 px-1 rounded">GET /api/v1/devices</code> - List all devices</li>
|
||||
<li><code class="bg-gray-300 dark:bg-zinc-700 px-1 rounded">GET /api/v1/devices/{id}</code> - Get device details</li>
|
||||
<li><code class="bg-gray-300 dark:bg-zinc-700 px-1 rounded">POST /api/v1/devices</code> - Create device</li>
|
||||
<li><code class="bg-gray-300 dark:bg-zinc-700 px-1 rounded">PUT /api/v1/devices/{id}</code> - Update device</li>
|
||||
<li><code class="bg-gray-300 dark:bg-zinc-700 px-1 rounded">DELETE /api/v1/devices/{id}</code> - Delete device</li>
|
||||
<li><code class="bg-gray-300 dark:bg-zinc-700 px-1 rounded">POST /api/v1/devices/{id}/ips</code> - Add IP to device</li>
|
||||
<li><code class="bg-gray-300 dark:bg-zinc-700 px-1 rounded">DELETE /api/v1/devices/{id}/ips/{ip_id}</code> - Remove IP from device</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="font-semibold">Subnets</h4>
|
||||
<ul class="list-disc list-inside ml-4 text-sm space-y-1">
|
||||
<li><code class="bg-gray-300 dark:bg-zinc-700 px-1 rounded">GET /api/v1/subnets</code> - List all subnets</li>
|
||||
<li><code class="bg-gray-300 dark:bg-zinc-700 px-1 rounded">GET /api/v1/subnets/{id}</code> - Get subnet details</li>
|
||||
<li><code class="bg-gray-300 dark:bg-zinc-700 px-1 rounded">POST /api/v1/subnets</code> - Create subnet</li>
|
||||
<li><code class="bg-gray-300 dark:bg-zinc-700 px-1 rounded">PUT /api/v1/subnets/{id}</code> - Update subnet</li>
|
||||
<li><code class="bg-gray-300 dark:bg-zinc-700 px-1 rounded">DELETE /api/v1/subnets/{id}</code> - Delete subnet</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="font-semibold">Racks</h4>
|
||||
<ul class="list-disc list-inside ml-4 text-sm space-y-1">
|
||||
<li><code class="bg-gray-300 dark:bg-zinc-700 px-1 rounded">GET /api/v1/racks</code> - List all racks</li>
|
||||
<li><code class="bg-gray-300 dark:bg-zinc-700 px-1 rounded">GET /api/v1/racks/{id}</code> - Get rack details</li>
|
||||
<li><code class="bg-gray-300 dark:bg-zinc-700 px-1 rounded">POST /api/v1/racks</code> - Create rack</li>
|
||||
<li><code class="bg-gray-300 dark:bg-zinc-700 px-1 rounded">DELETE /api/v1/racks/{id}</code> - Delete rack</li>
|
||||
<li><code class="bg-gray-300 dark:bg-zinc-700 px-1 rounded">POST /api/v1/racks/{id}/devices</code> - Add device to rack</li>
|
||||
<li><code class="bg-gray-300 dark:bg-zinc-700 px-1 rounded">DELETE /api/v1/racks/{id}/devices/{rack_device_id}</code> - Remove device from rack</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="font-semibold">Other</h4>
|
||||
<ul class="list-disc list-inside ml-4 text-sm space-y-1">
|
||||
<li><code class="bg-gray-300 dark:bg-zinc-700 px-1 rounded">GET /api/v1/info</code> - Get API info and user details</li>
|
||||
<li><code class="bg-gray-300 dark:bg-zinc-700 px-1 rounded">GET /api/v1/device-types</code> - List device types</li>
|
||||
<li><code class="bg-gray-300 dark:bg-zinc-700 px-1 rounded">GET /api/v1/subnets/{id}/dhcp</code> - Get DHCP pools</li>
|
||||
<li><code class="bg-gray-300 dark:bg-zinc-700 px-1 rounded">POST /api/v1/subnets/{id}/dhcp</code> - Configure DHCP pools</li>
|
||||
<li><code class="bg-gray-300 dark:bg-zinc-700 px-1 rounded">GET /api/v1/audit</code> - Get audit log</li>
|
||||
<li><code class="bg-gray-300 dark:bg-zinc-700 px-1 rounded">GET /api/v1/users</code> - List users (admin only)</li>
|
||||
<li><code class="bg-gray-300 dark:bg-zinc-700 px-1 rounded">GET /api/v1/roles</code> - List roles (admin only)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-xl font-semibold mb-1">Permissions</h3>
|
||||
<p>API endpoints respect the same role-based permissions as the web interface. Users can only perform actions that their role allows. If a user lacks the required permission, the API will return a <code class="bg-gray-300 dark:bg-zinc-700 px-2 py-1 rounded text-sm">403 Forbidden</code> error.</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-xl font-semibold mb-1">Response Format</h3>
|
||||
<p>All API responses are in JSON format. Successful requests return <code class="bg-gray-300 dark:bg-zinc-700 px-2 py-1 rounded text-sm">200 OK</code> or <code class="bg-gray-300 dark:bg-zinc-700 px-2 py-1 rounded text-sm">201 Created</code> with the requested data. Errors return appropriate HTTP status codes with an error message in the response body.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -0,0 +1,180 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Tag Management</title>
|
||||
<link rel="icon" type="image/png" href="{{ LOGO_PNG }}">
|
||||
<link href="/static/css/output.css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body class="bg-gray-300 text-gray-900 dark:bg-zinc-900 dark:text-gray-100 min-h-screen flex flex-col">
|
||||
{% include 'header.html' %}
|
||||
<div class="flex-1 mx-4 py-8 pt-20">
|
||||
<div class="container max-w-7xl mx-auto">
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<h1 class="text-3xl font-bold">Tag Management</h1>
|
||||
{% if can_add_tag %}
|
||||
<button onclick="showAddTagModal()" class="bg-gray-200 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 hover:cursor-pointer px-6 py-2 rounded-lg font-medium">
|
||||
<i class="fas fa-plus mr-2"></i>Add Tag
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if error %}
|
||||
<div class="bg-red-200 dark:bg-red-800 text-red-800 dark:text-red-200 p-4 rounded-lg mb-6">
|
||||
{{ error }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if tags %}
|
||||
<div class="bg-gray-200 dark:bg-zinc-800 p-6 rounded-lg shadow-md">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full">
|
||||
<thead>
|
||||
<tr class="border-b border-gray-600">
|
||||
<th class="text-left p-3">Name</th>
|
||||
<th class="text-left p-3">Colour</th>
|
||||
<th class="text-left p-3">Description</th>
|
||||
<th class="text-center p-3">Devices</th>
|
||||
<th class="text-center p-3">Created</th>
|
||||
<th class="text-center p-3">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for tag in tags %}
|
||||
<tr class="border-b border-gray-600 hover:bg-gray-300 dark:hover:bg-zinc-700">
|
||||
<td class="p-3">
|
||||
<div class="flex items-center space-x-2">
|
||||
<div class="w-4 h-4 rounded-full border border-gray-600" style="background-color: {{ tag.color }}"></div>
|
||||
<span class="font-medium">{{ tag.name }}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="p-3">
|
||||
<span class="font-mono text-sm">{{ tag.color }}</span>
|
||||
</td>
|
||||
<td class="p-3">
|
||||
<span class="text-sm">{{ tag.description or '-' }}</span>
|
||||
</td>
|
||||
<td class="p-3 text-center">
|
||||
{% if tag.device_count > 0 %}
|
||||
<a href="/api/v1/devices/by-tag/{{ tag.name }}" class="text-blue-400 hover:text-blue-600">
|
||||
{{ tag.device_count }}
|
||||
</a>
|
||||
{% else %}
|
||||
<span class="text-gray-500">0</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="p-3 text-center text-sm">
|
||||
{{ tag.created_at.strftime('%Y-%m-%d') if tag.created_at else '-' }}
|
||||
</td>
|
||||
<td class="p-3 text-center">
|
||||
<div class="flex items-center justify-center space-x-2">
|
||||
{% if can_edit_tag %}
|
||||
<button class="edit-tag-btn text-gray-900 hover:text-gray-600 dark:text-gray-100 dark:hover:text-gray-300 hover:cursor-pointer"
|
||||
title="Edit Tag"
|
||||
data-tag-id="{{ tag.id }}"
|
||||
data-tag-name="{{ tag.name }}"
|
||||
data-tag-color="{{ tag.color }}"
|
||||
data-tag-description="{{ tag.description or '' }}">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if can_delete_tag %}
|
||||
<form action="/tags" method="POST" onsubmit="return confirm('Are you sure you want to delete this tag? This will remove it from all devices.');" class="inline">
|
||||
<input type="hidden" name="action" value="delete_tag">
|
||||
<input type="hidden" name="tag_id" value="{{ tag.id }}">
|
||||
<button type="submit" class="text-gray-900 hover:text-gray-600 dark:text-gray-100 dark:hover:text-gray-300 hover:cursor-pointer" title="Delete Tag">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="bg-gray-200 dark:bg-zinc-800 p-6 rounded-lg shadow-md">
|
||||
<div class="text-center py-8 text-gray-500">
|
||||
<i class="fas fa-tags text-4xl mb-4"></i>
|
||||
<p>No tags found. Add your first tag to get started.</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add Tag Modal -->
|
||||
<div id="add-tag-modal" class="hidden fixed inset-0 bg-black/30 backdrop-blur-md flex items-center justify-center z-50">
|
||||
<div class="bg-gray-200 dark:bg-zinc-800 p-6 rounded-lg shadow-lg max-w-md w-full mx-4">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h2 class="text-xl font-bold">Add New Tag</h2>
|
||||
<button onclick="closeAddTagModal()" class="text-gray-500 hover:text-gray-700">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
<form action="/tags" method="POST">
|
||||
<input type="hidden" name="action" value="add_tag">
|
||||
<div class="space-y-4">
|
||||
<input type="text" name="name" id="add-tag-name" placeholder="Tag Name"
|
||||
class="border p-3 rounded-lg bg-gray-300 text-gray-900 dark:bg-zinc-900 dark:text-gray-100 border-gray-600 w-full" required>
|
||||
<div class="flex items-center space-x-2">
|
||||
<label class="text-sm font-medium">Colour:</label>
|
||||
<input type="color" name="color" id="add-tag-color" value="#6B7280"
|
||||
class="w-12 h-10 border border-gray-600 rounded cursor-pointer">
|
||||
<span id="add-color-preview" class="text-sm font-mono">#6B7280</span>
|
||||
</div>
|
||||
<textarea name="description" id="add-tag-description" placeholder="Description (optional)" rows="3"
|
||||
class="border p-3 rounded-lg bg-gray-300 text-gray-900 dark:bg-zinc-900 dark:text-gray-100 border-gray-600 w-full resize-y"></textarea>
|
||||
</div>
|
||||
<div class="flex justify-end space-x-2 mt-6">
|
||||
<button type="button" onclick="closeAddTagModal()"
|
||||
class="px-4 py-2 bg-gray-300 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 hover:cursor-pointer rounded-lg">Cancel</button>
|
||||
<button type="submit"
|
||||
class="px-4 py-2 bg-gray-300 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 hover:cursor-pointer rounded-lg">Add Tag</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Edit Tag Modal -->
|
||||
<div id="edit-tag-modal" class="hidden fixed inset-0 bg-black/30 backdrop-blur-md flex items-center justify-center z-50">
|
||||
<div class="bg-gray-200 dark:bg-zinc-800 p-6 rounded-lg shadow-lg max-w-md w-full mx-4">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h2 class="text-xl font-bold">Edit Tag</h2>
|
||||
<button onclick="closeEditTagModal()" class="text-gray-500 hover:text-gray-700">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
<form action="/tags" method="POST">
|
||||
<input type="hidden" name="action" value="edit_tag">
|
||||
<input type="hidden" name="tag_id" id="edit-tag-id">
|
||||
<div class="space-y-4">
|
||||
<input type="text" name="name" id="edit-tag-name" placeholder="Tag Name"
|
||||
class="border p-3 rounded-lg bg-gray-300 text-gray-900 dark:bg-zinc-900 dark:text-gray-100 border-gray-600 w-full" required>
|
||||
<div class="flex items-center space-x-2">
|
||||
<label class="text-sm font-medium">Colour:</label>
|
||||
<input type="color" name="color" id="edit-tag-color"
|
||||
class="w-12 h-10 border border-gray-600 rounded cursor-pointer">
|
||||
<span id="edit-color-preview" class="text-sm font-mono"></span>
|
||||
</div>
|
||||
<textarea name="description" id="edit-tag-description" placeholder="Description (optional)" rows="3"
|
||||
class="border p-3 rounded-lg bg-gray-300 text-gray-900 dark:bg-zinc-900 dark:text-gray-100 border-gray-600 w-full resize-y"></textarea>
|
||||
</div>
|
||||
<div class="flex justify-end space-x-2 mt-6">
|
||||
<button type="button" onclick="closeEditTagModal()"
|
||||
class="px-4 py-2 bg-gray-200 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 hover:cursor-pointer rounded-lg">Cancel</button>
|
||||
<button type="submit"
|
||||
class="px-4 py-2 bg-gray-200 hover:bg-gray-400 dark:bg-zinc-700 dark:hover:bg-zinc-600 hover:cursor-pointer rounded-lg">Save Changes</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/static/js/tags.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user