mirror of
https://github.com/eduardogsilva/wireguard_webadmin.git
synced 2026-02-19 19:26:17 +00:00
Add API endpoints for listing peers and retrieving peer details
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
from django.urls import path
|
||||
|
||||
from api_v2.views import view_api_key_list, view_manage_api_key, view_delete_api_key
|
||||
from api_v2.views import view_api_key_list, view_manage_api_key, view_delete_api_key, view_api_docs
|
||||
|
||||
urlpatterns = [
|
||||
path('list/', view_api_key_list, name='api_v2_list'),
|
||||
path('manage/', view_manage_api_key, name='api_v2_manage'),
|
||||
path('delete/<uuid:uuid>/', view_delete_api_key, name='api_v2_delete'),
|
||||
path('docs/', view_api_docs, name='api_v2_docs'),
|
||||
]
|
||||
@@ -94,3 +94,32 @@ def view_delete_api_key(request, uuid):
|
||||
'text': _('Are you sure you want to delete the API Key "%(name)s"?') % {'name': api_key.name}
|
||||
}
|
||||
return render(request, 'generic_delete_confirmation.html', context)
|
||||
|
||||
|
||||
@login_required
|
||||
def view_api_docs(request):
|
||||
from django.urls.resolvers import URLPattern
|
||||
from api_v2 import urls_api
|
||||
|
||||
if not UserAcl.objects.filter(user=request.user).filter(user_level__gte=50).exists():
|
||||
return render(request, 'access_denied.html', {'page_title': _('Access Denied')})
|
||||
|
||||
docs = []
|
||||
# We iterate over the urlpatterns from the api_v2.urls_api module
|
||||
for pattern in urls_api.urlpatterns:
|
||||
if isinstance(pattern, URLPattern):
|
||||
view_func = pattern.callback
|
||||
# The view might be wrapped (e.g. by @csrf_exempt), but @api_doc metadata
|
||||
# should have been preserved on the wrapper or be available on the original function.
|
||||
# verify_api_doc checks handled this.
|
||||
if hasattr(view_func, 'api_doc'):
|
||||
doc_data = view_func.api_doc.copy()
|
||||
doc_data['url_pattern'] = str(pattern.pattern)
|
||||
doc_data['name'] = pattern.name
|
||||
docs.append(doc_data)
|
||||
|
||||
context = {
|
||||
'page_title': _('API Documentation'),
|
||||
'docs': docs
|
||||
}
|
||||
return render(request, 'api_v2/api_documentation.html', context)
|
||||
|
||||
@@ -15,13 +15,14 @@ from wireguard_tools.views import export_wireguard_configuration
|
||||
from .models import ApiKey
|
||||
|
||||
|
||||
def api_doc(*, summary: str, auth: str, params: list, returns: list, examples: Optional[dict] = None):
|
||||
def api_doc(*, summary: str, auth: str, params: list, returns: list, methods: Optional[List[str]] = None, examples: Optional[dict] = None):
|
||||
def decorator(view_func):
|
||||
view_func.api_doc = {
|
||||
"summary": summary,
|
||||
"auth": auth,
|
||||
"params": params,
|
||||
"returns": returns,
|
||||
"methods": methods or ["POST"],
|
||||
"examples": examples or {},
|
||||
}
|
||||
|
||||
@@ -166,6 +167,7 @@ def _get_wireguard_instance(instance_name: str) -> Optional[WireGuardInstance]:
|
||||
@api_doc(
|
||||
summary="Create / Update / Delete a WireGuard peer (and optionally reload the interface)",
|
||||
auth="Header token: <ApiKey.token>",
|
||||
methods=["POST", "PUT", "DELETE"],
|
||||
params=[
|
||||
{"name": "instance", "in": "json", "type": "string", "required": True, "example": "wg0",
|
||||
"description": "Target instance name in the format wg{instance_id} (e.g. wg0, wg1)."},
|
||||
@@ -450,6 +452,7 @@ def api_v2_manage_peer(request):
|
||||
@api_doc(
|
||||
summary="List peers for a specific instance (required)",
|
||||
auth="Header token: <ApiKey.token>",
|
||||
methods=["POST", "GET"],
|
||||
params=[
|
||||
{"name": "instance", "in": "json", "type": "string", "required": True, "example": "wg2",
|
||||
"description": "Required. Target instance name in the format wg{instance_id} (e.g. wg0, wg1)."},
|
||||
@@ -523,6 +526,7 @@ def api_v2_peer_list(request):
|
||||
@api_doc(
|
||||
summary="Peer details for a specific instance (required) by peer_uuid or peer_public_key",
|
||||
auth="Header token: <ApiKey.token>",
|
||||
methods=["POST", "GET"],
|
||||
params=[
|
||||
{"name": "instance", "in": "json", "type": "string", "required": True, "example": "wg2",
|
||||
"description": "Required. Target instance name in the format wg{instance_id} (e.g. wg0, wg1)."},
|
||||
|
||||
89
templates/api_v2/api_documentation.html
Normal file
89
templates/api_v2/api_documentation.html
Normal file
@@ -0,0 +1,89 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h2 class="mb-4">{% trans 'API Documentation' %}</h2>
|
||||
|
||||
{% for doc in docs %}
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">
|
||||
{% for method in doc.methods %}
|
||||
<span class="badge badge-primary mr-2">{{ method }}</span>
|
||||
{% endfor %}
|
||||
<code>/api/v2/{{ doc.url_pattern }}</code>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="lead">{{ doc.summary }}</p>
|
||||
|
||||
<p><strong>{% trans 'Authentication' %}:</strong> {{ doc.auth }}</p>
|
||||
|
||||
{% if doc.params %}
|
||||
<h6 class="mt-3">{% trans 'Parameters' %}</h6>
|
||||
<table class="table table-sm table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans 'Name' %}</th>
|
||||
<th>{% trans 'In' %}</th>
|
||||
<th>{% trans 'Type' %}</th>
|
||||
<th>{% trans 'Required' %}</th>
|
||||
<th>{% trans 'Description' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for param in doc.params %}
|
||||
<tr>
|
||||
<td><code>{{ param.name }}</code></td>
|
||||
<td>{{ param.in }}</td>
|
||||
<td>{{ param.type }}</td>
|
||||
<td>
|
||||
{% if param.required %}
|
||||
<span class="badge badge-danger">{% trans 'Yes' %}</span>
|
||||
{% else %}
|
||||
<span class="badge badge-secondary">{% trans 'No' %}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{{ param.description }}
|
||||
{% if param.example %}
|
||||
<br><small class="text-muted">{% trans 'Example' %}: {{ param.example }}</small>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
{% if doc.returns %}
|
||||
<h6 class="mt-3">{% trans 'Returns' %}</h6>
|
||||
<ul class="list-group">
|
||||
{% for ret in doc.returns %}
|
||||
<li class="list-group-item">
|
||||
<strong>{{ ret.status }}</strong>
|
||||
<pre class="bg-light p-2 mt-2"><code>{{ ret.body|pprint }}</code></pre>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% if doc.examples %}
|
||||
<h6 class="mt-3">{% trans 'Examples' %}</h6>
|
||||
{% for key, example in doc.examples.items %}
|
||||
<div class="card bg-light mb-2">
|
||||
<div class="card-body p-2">
|
||||
<strong>{{ key }}</strong>
|
||||
<pre class="mb-0"><code>{{ example|pprint }}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -79,6 +79,9 @@
|
||||
<a href="/manage_api/v2/manage/" class="btn btn-primary">
|
||||
<i class="fas fa-plus"></i> {% trans 'Add API Key' %}
|
||||
</a>
|
||||
<a href="/manage_api/v2/docs/" class="btn btn-secondary ml-2">
|
||||
<i class="fas fa-book"></i> {% trans 'API Documentation' %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user