refactor: 🎨 remove caching #48
@@ -0,0 +1,239 @@
|
||||
# IPAM API Documentation
|
||||
|
||||
REST API for programmatic access to IPAM. All endpoints live under `/api/v1`.
|
||||
|
||||
## Authentication
|
||||
|
||||
Every request requires an API key. Generate or regenerate keys from **Admin → Users** in the web UI.
|
||||
|
||||
Provide the key in one of three ways:
|
||||
|
||||
| Method | Example |
|
||||
|--------|---------|
|
||||
| Header | `X-API-Key: your_api_key` |
|
||||
| Authorization header | `Authorization: Bearer your_api_key` |
|
||||
| Query parameter | `?api_key=your_api_key` |
|
||||
|
||||
Verify credentials with:
|
||||
|
||||
```http
|
||||
GET /api/v1/info
|
||||
X-API-Key: your_api_key
|
||||
```
|
||||
|
||||
## Permissions
|
||||
|
||||
Endpoints use the same role-based permissions as the web UI. If a user lacks the required permission, the API returns `403 Forbidden` with details about the missing permission.
|
||||
|
||||
## Response format
|
||||
|
||||
All responses are JSON.
|
||||
|
||||
**Success**
|
||||
|
||||
| Status | Meaning |
|
||||
|--------|---------|
|
||||
| `200 OK` | Request successful |
|
||||
| `201 Created` | Resource created |
|
||||
| `204 No Content` | Success with no response body |
|
||||
|
||||
**Errors**
|
||||
|
||||
| Status | Meaning |
|
||||
|--------|---------|
|
||||
| `400 Bad Request` | Invalid request data |
|
||||
| `401 Unauthorized` | Missing or invalid API key |
|
||||
| `403 Forbidden` | Insufficient permissions |
|
||||
| `404 Not Found` | Resource not found |
|
||||
|
||||
Error bodies typically include an `error` field with a descriptive message.
|
||||
|
||||
---
|
||||
|
||||
## Devices
|
||||
|
||||
| Method | Endpoint | Permission | Description |
|
||||
|--------|----------|------------|-------------|
|
||||
| GET | `/api/v1/devices` | `view_devices` | List all devices (includes IP addresses, tags, custom fields) |
|
||||
| GET | `/api/v1/devices/{id}` | `view_device` | Get device details |
|
||||
| GET | `/api/v1/devices/by-tag/{tag}` | `view_devices` | List devices by tag name or ID. Use `?format=simple` for `{device, ip}` pairs |
|
||||
| POST | `/api/v1/devices` | `add_device` | Create device |
|
||||
| PUT | `/api/v1/devices/{id}` | `edit_device` | Update device |
|
||||
| DELETE | `/api/v1/devices/{id}` | `delete_device` | Delete device |
|
||||
| POST | `/api/v1/devices/{id}/ips` | `add_device_ip` | Assign IP to device |
|
||||
| DELETE | `/api/v1/devices/{id}/ips/{ip_id}` | `remove_device_ip` | Remove IP from device |
|
||||
|
||||
**Create device** (`POST /api/v1/devices`)
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "server-01",
|
||||
"description": "Optional description",
|
||||
"device_type_id": 1
|
||||
}
|
||||
```
|
||||
|
||||
**Update device** (`PUT /api/v1/devices/{id}`) — include only fields to change: `name`, `description`, `device_type_id`.
|
||||
|
||||
**Add IP** (`POST /api/v1/devices/{id}/ips`)
|
||||
|
||||
```json
|
||||
{
|
||||
"ip_id": 42
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Subnets
|
||||
|
||||
| Method | Endpoint | Permission | Description |
|
||||
|--------|----------|------------|-------------|
|
||||
| GET | `/api/v1/subnets` | `view_subnet` | List all subnets |
|
||||
| GET | `/api/v1/subnets/{id}` | `view_subnet` | Get subnet details |
|
||||
| GET | `/api/v1/subnets/{id}/next_free_ip` | `view_subnet` | Get next available IP |
|
||||
| POST | `/api/v1/subnets` | `add_subnet` | Create subnet |
|
||||
| PUT | `/api/v1/subnets/{id}` | `edit_subnet` | Update subnet |
|
||||
| DELETE | `/api/v1/subnets/{id}` | `delete_subnet` | Delete subnet |
|
||||
|
||||
**Create subnet** (`POST /api/v1/subnets`)
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Office LAN",
|
||||
"cidr": "192.168.1.0/24",
|
||||
"site": "HQ",
|
||||
"vlan_id": 100,
|
||||
"vlan_description": "Optional",
|
||||
"vlan_notes": "Optional"
|
||||
}
|
||||
```
|
||||
|
||||
Subnets must be `/24` or smaller (prefix length ≥ 24).
|
||||
|
||||
---
|
||||
|
||||
## DHCP
|
||||
|
||||
| Method | Endpoint | Permission | Description |
|
||||
|--------|----------|------------|-------------|
|
||||
| GET | `/api/v1/subnets/{id}/dhcp` | `view_dhcp` | Get DHCP pool configuration |
|
||||
| POST | `/api/v1/subnets/{id}/dhcp` | `configure_dhcp` | Create or update DHCP pool |
|
||||
|
||||
**Configure pool** (`POST /api/v1/subnets/{id}/dhcp`)
|
||||
|
||||
```json
|
||||
{
|
||||
"pools": [
|
||||
{
|
||||
"start_ip": "192.168.1.10",
|
||||
"end_ip": "192.168.1.200",
|
||||
"excluded_ips": ["192.168.1.1", "192.168.1.254"]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Remove pool**
|
||||
|
||||
```json
|
||||
{
|
||||
"remove": true
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Racks
|
||||
|
||||
Requires the racks feature to be enabled.
|
||||
|
||||
| Method | Endpoint | Permission | Description |
|
||||
|--------|----------|------------|-------------|
|
||||
| GET | `/api/v1/racks` | `view_racks` | List all racks |
|
||||
| GET | `/api/v1/racks/{id}` | `view_rack` | Get rack details |
|
||||
| POST | `/api/v1/racks` | `add_rack` | Create rack |
|
||||
| DELETE | `/api/v1/racks/{id}` | `delete_rack` | Delete rack |
|
||||
| POST | `/api/v1/racks/{id}/devices` | `add_device_to_rack` | Add device to rack position |
|
||||
| DELETE | `/api/v1/racks/{id}/devices/{rack_device_id}` | `remove_device_from_rack` | Remove device from rack |
|
||||
|
||||
---
|
||||
|
||||
## Tags
|
||||
|
||||
Requires the device tags feature to be enabled.
|
||||
|
||||
| Method | Endpoint | Permission | Description |
|
||||
|--------|----------|------------|-------------|
|
||||
| GET | `/api/v1/tags` | `view_tags` | List all tags |
|
||||
| GET | `/api/v1/tags?format=simple` | `view_tags` | List tags in simple format |
|
||||
| GET | `/api/v1/tags/{id}` | `view_tags` | Get tag details |
|
||||
| POST | `/api/v1/tags` | `add_tag` | Create tag |
|
||||
| PUT | `/api/v1/tags/{id}` | `edit_tag` | Update tag |
|
||||
| DELETE | `/api/v1/tags/{id}` | `delete_tag` | Delete tag |
|
||||
| GET | `/api/v1/devices/{id}/tags` | `view_device` | Get tags for a device |
|
||||
| POST | `/api/v1/devices/{id}/tags` | `assign_device_tag` | Assign tag to device |
|
||||
| DELETE | `/api/v1/devices/{id}/tags/{tag_id}` | `remove_device_tag` | Remove tag from device |
|
||||
|
||||
**Assign tag** (`POST /api/v1/devices/{id}/tags`)
|
||||
|
||||
```json
|
||||
{
|
||||
"tag_id": 3
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Custom fields
|
||||
|
||||
| Method | Endpoint | Permission | Description |
|
||||
|--------|----------|------------|-------------|
|
||||
| GET | `/api/v1/custom_fields/{entity_type}` | `view_custom_fields` | List field definitions (`device` or `subnet`) |
|
||||
| POST | `/api/v1/custom_fields` | `manage_custom_fields` | Create field definition |
|
||||
| PUT | `/api/v1/custom_fields/{id}` | `manage_custom_fields` | Update field definition |
|
||||
| DELETE | `/api/v1/custom_fields/{id}` | `manage_custom_fields` | Delete field definition |
|
||||
|
||||
**Create field** (`POST /api/v1/custom_fields`)
|
||||
|
||||
```json
|
||||
{
|
||||
"entity_type": "device",
|
||||
"name": "Asset tag",
|
||||
"field_key": "asset_tag",
|
||||
"field_type": "text",
|
||||
"required": false,
|
||||
"default_value": "",
|
||||
"help_text": "Optional help text",
|
||||
"display_order": 0,
|
||||
"searchable": true,
|
||||
"validation_rules": {
|
||||
"max_length": 32
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## System & admin
|
||||
|
||||
| Method | Endpoint | Permission | Description |
|
||||
|--------|----------|------------|-------------|
|
||||
| GET | `/api/v1/info` | *(authenticated)* | API version and current user info |
|
||||
| GET | `/api/v1/device-types` | `view_device_types` | List device types |
|
||||
| GET | `/api/v1/audit` | `view_audit` | List audit log entries (`limit`, `offset` query params) |
|
||||
| GET | `/api/v1/users` | `view_users` | List users |
|
||||
| GET | `/api/v1/roles` | `view_users` | List roles with permissions |
|
||||
|
||||
---
|
||||
|
||||
## Example
|
||||
|
||||
```bash
|
||||
curl -H "X-API-Key: YOUR_KEY" https://your-ipam-host/api/v1/devices
|
||||
|
||||
curl -H "X-API-Key: YOUR_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"name": "switch-01", "description": "Core switch"}' \
|
||||
https://your-ipam-host/api/v1/devices
|
||||
```
|
||||
@@ -1796,21 +1796,6 @@ def update_feature_flags():
|
||||
|
||||
return redirect(url_for('admin'))
|
||||
|
||||
@app.route('/api-docs')
|
||||
@permission_required('view_admin')
|
||||
def api_docs():
|
||||
# Get current user's API key
|
||||
from flask import current_app
|
||||
api_key = None
|
||||
if 'user_id' in session:
|
||||
with get_db_connection(current_app) as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('SELECT api_key FROM User WHERE id = %s', (session['user_id'],))
|
||||
result = cursor.fetchone()
|
||||
if result:
|
||||
api_key = result[0]
|
||||
return render_with_user('api_docs.html', api_key=api_key)
|
||||
|
||||
@app.route('/account', methods=['GET'])
|
||||
@login_required
|
||||
def account_settings():
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
// API Documentation Interactive Functions
|
||||
|
||||
function getApiKey() {
|
||||
return document.getElementById('apiKey').value;
|
||||
}
|
||||
|
||||
function showStatus(message, isError = false) {
|
||||
const status = document.getElementById('connectionStatus');
|
||||
status.textContent = message;
|
||||
status.className = `mt-2 text-sm ${isError ? 'text-red-600 dark:text-red-400' : 'text-green-600 dark:text-green-400'}`;
|
||||
}
|
||||
|
||||
async function testConnection() {
|
||||
const apiKey = getApiKey();
|
||||
if (!apiKey) {
|
||||
showStatus('Please enter your API key', true);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.get('/api/v1/devices', {
|
||||
headers: { 'X-API-Key': apiKey }
|
||||
});
|
||||
showStatus('✓ Connection successful');
|
||||
} catch (error) {
|
||||
if (error.response?.status === 401) {
|
||||
showStatus('✗ Invalid API key', true);
|
||||
} else if (error.response?.status === 403) {
|
||||
showStatus('✗ Insufficient permissions', true);
|
||||
} else {
|
||||
showStatus('✗ Connection failed', true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function tryEndpoint(method, url, data, responseId) {
|
||||
const apiKey = getApiKey();
|
||||
if (!apiKey) {
|
||||
showStatus('Please enter your API key first', true);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const config = {
|
||||
method: method,
|
||||
url: url,
|
||||
headers: { 'X-API-Key': apiKey }
|
||||
};
|
||||
|
||||
if (data) {
|
||||
config.data = data;
|
||||
}
|
||||
|
||||
const response = await axios(config);
|
||||
document.getElementById(responseId + '-response').classList.remove('hidden');
|
||||
document.getElementById(responseId).textContent = JSON.stringify(response.data, null, 2);
|
||||
} catch (error) {
|
||||
document.getElementById(responseId + '-response').classList.remove('hidden');
|
||||
const errorMessage = error.response?.data?.error || error.message;
|
||||
document.getElementById(responseId).textContent = `Error (${error.response?.status || 'Network'}): ${errorMessage}`;
|
||||
}
|
||||
}
|
||||
|
||||
async function tryEndpointWithId(method, baseUrl, inputId, responseId) {
|
||||
const id = document.getElementById(inputId).value;
|
||||
if (!id) {
|
||||
alert('Please enter an ID');
|
||||
return;
|
||||
}
|
||||
await tryEndpoint(method, baseUrl + encodeURIComponent(id), null, responseId);
|
||||
}
|
||||
|
||||
// Auto-populate API key if user is logged in
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const apiKeyInput = document.getElementById('apiKey');
|
||||
if (apiKeyInput && apiKeyInput.value) {
|
||||
testConnection();
|
||||
}
|
||||
});
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
function getApiKey(){return document.getElementById("apiKey").value}function showStatus(e,t=!1){let n=document.getElementById("connectionStatus");n.textContent=e,n.className=`mt-2 text-sm ${t?"text-red-600 dark:text-red-400":"text-green-600 dark:text-green-400"}`}async function testConnection(){let e=getApiKey();if(!e){showStatus("Please enter your API key",!0);return}try{await axios.get("/api/v1/devices",{headers:{"X-API-Key":e}}),showStatus("✓ Connection successful")}catch(t){t.response?.status===401?showStatus("✗ Invalid API key",!0):t.response?.status===403?showStatus("✗ Insufficient permissions",!0):showStatus("✗ Connection failed",!0)}}async function tryEndpoint(e,t,n,s){let a=getApiKey();if(!a){showStatus("Please enter your API key first",!0);return}try{let o={method:e,url:t,headers:{"X-API-Key":a}};n&&(o.data=n);let r=await axios(o);document.getElementById(s+"-response").classList.remove("hidden"),document.getElementById(s).textContent=JSON.stringify(r.data,null,2)}catch(i){document.getElementById(s+"-response").classList.remove("hidden");let d=i.response?.data?.error||i.message;document.getElementById(s).textContent=`Error (${i.response?.status||"Network"}): ${d}`}}async function tryEndpointWithId(e,t,n,s){let a=document.getElementById(n).value;if(!a){alert("Please enter an ID");return}await tryEndpoint(e,t+encodeURIComponent(a),null,s)}document.addEventListener("DOMContentLoaded",function(){let e=document.getElementById("apiKey");e&&e.value&&testConnection()});
|
||||
@@ -66,16 +66,6 @@
|
||||
<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>
|
||||
<a href="/backup" 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-database text-3xl text-gray-600 dark:text-gray-400"></i>
|
||||
|
||||
@@ -1,331 +0,0 @@
|
||||
<!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>
|
||||
<li><code class="bg-gray-400 dark:bg-zinc-600 px-2 py-1 rounded text-xs">GET /api/v1/subnets/{id}/next_free_ip</code> - Get next free IP address</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.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user