feat: full api integration

This commit is contained in:
2025-11-06 13:26:19 +00:00
parent fdd8b36fbf
commit c53472c5d7
4 changed files with 740 additions and 15 deletions
+80
View File
@@ -80,6 +80,86 @@
<h3 class="text-xl font-semibold mb-1">Audit & History</h3>
<p>All changes are logged and can be reviewed on the <a href="/audit" class="text-blue-600 dark:text-blue-400 hover:underline">Audit</a> page for accountability and troubleshooting. The audit log shows who made changes, what was changed, and when.</p>
</div>
<div>
<h3 class="text-xl font-semibold mb-1">API Keys</h3>
<p>Each user has a unique API key that can be used to authenticate API requests. API keys can be viewed and regenerated from the <a href="/users" class="text-blue-600 dark:text-blue-400 hover:underline">Users</a> page. Keep your API key secure and never share it publicly.</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">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>
+20 -7
View File
@@ -61,9 +61,9 @@
<tr class="border-b border-gray-600">
<th class="text-left p-2">Name</th>
<th class="text-left p-2">Email</th>
<th class="text-left p-2">Role</th>
<th class="text-center p-2">Role</th>
{% if can_manage_users %}
<th class="text-left p-2">Actions</th>
<th class="text-center p-2">Actions</th>
{% endif %}
</tr>
</thead>
@@ -72,14 +72,21 @@
<tr class="border-b border-gray-600">
<td class="p-2">{{ user[1] }}</td>
<td class="p-2">{{ user[2] }}</td>
<td class="p-2">
<span class="px-2 py-1 bg-blue-200 dark:bg-blue-800 rounded">{{ user[4] or 'No Role' }}</span>
<td class="p-2 text-center">
<span class="px-2 py-1 bg-gray-300 dark:bg-zinc-700 rounded">{{ user[4] or 'No Role' }}</span>
</td>
{% if can_manage_users %}
<td class="p-2">
<button onclick="editUser({{ user[0] }}, '{{ user[1]|replace("'", "\\'") }}', '{{ user[2]|replace("'", "\\'") }}', {{ user[3] if user[3] else 'null' }})" class="text-blue-500 hover:text-blue-700 mr-4 hover:cursor-pointer" title="Edit User">
<td class="p-2 text-center">
<button onclick="editUser({{ user[0] }}, '{{ user[1]|replace("'", "\\'") }}', '{{ user[2]|replace("'", "\\'") }}', {{ user[3] if user[3] else 'null' }}, '{{ user[5]|replace("'", "\\'") if user[5] else '' }}')" class="text-blue-500 hover:text-blue-700 mr-2 hover:cursor-pointer" title="Edit User">
<i class="fas fa-edit"></i>
</button>
<form action="/users" method="POST" onsubmit="return confirm('Are you sure you want to regenerate the API key for this user?');" class="inline mr-2">
<input type="hidden" name="action" value="regenerate_api_key">
<input type="hidden" name="user_id" value="{{ user[0] }}">
<button type="submit" class="text-yellow-500 hover:text-yellow-700 hover:cursor-pointer" title="Regenerate API Key">
<i class="fas fa-key"></i>
</button>
</form>
<form action="/users" method="POST" onsubmit="return confirm('Are you sure you want to delete this user?');" class="inline">
<input type="hidden" name="action" value="delete_user">
<input type="hidden" name="user_id" value="{{ user[0] }}">
@@ -176,6 +183,11 @@
<option value="{{ role[0] }}">{{ role[1] }}</option>
{% endfor %}
</select>
<div class="border p-3 rounded-lg bg-gray-300 dark:bg-zinc-900 border-gray-600 w-full">
<label class="text-sm font-semibold mb-2 block">API Key</label>
<code id="edit-user-api-key" class="text-xs font-mono break-all block bg-gray-200 dark:bg-zinc-800 px-2 py-1 rounded"></code>
<p class="text-xs text-gray-600 dark:text-gray-400 mt-2">Use this API key to authenticate API requests. Keep it secure!</p>
</div>
<div class="flex justify-end space-x-2">
<button type="button" onclick="closeEditUserModal()" 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</button>
@@ -347,12 +359,13 @@
}
}
function editUser(userId, name, email, roleId) {
function editUser(userId, name, email, roleId, apiKey) {
document.getElementById('edit-user-id').value = userId;
document.getElementById('edit-user-name').value = name;
document.getElementById('edit-user-email').value = email;
document.getElementById('edit-user-password').value = '';
document.getElementById('edit-user-role').value = (roleId === null || roleId === 'null') ? '' : roleId;
document.getElementById('edit-user-api-key').textContent = apiKey || 'No API Key';
document.getElementById('edit-user-modal').classList.remove('hidden');
}