Add page to list all flask endpoints with filter
This commit is contained in:
43
app.py
43
app.py
@@ -534,6 +534,49 @@ def plot_query(query_id):
|
|||||||
plot_div = generate_plot(results_df, title)
|
plot_div = generate_plot(results_df, title)
|
||||||
return plot_div
|
return plot_div
|
||||||
|
|
||||||
|
def get_routes():
|
||||||
|
|
||||||
|
return routes
|
||||||
|
|
||||||
|
@app.route('/endpoints')
|
||||||
|
def list_endpoints():
|
||||||
|
"""
|
||||||
|
Lists all API endpoints available in the Flask application.
|
||||||
|
|
||||||
|
This endpoint retrieves all registered routes, excluding static routes,
|
||||||
|
and displays their details such as endpoint name, URL, allowed HTTP methods,
|
||||||
|
view function name, and a brief description.
|
||||||
|
"""
|
||||||
|
routes = []
|
||||||
|
for rule in app.url_map.iter_rules():
|
||||||
|
if rule.endpoint == 'static':
|
||||||
|
continue
|
||||||
|
methods = ', '.join(sorted(rule.methods - {'HEAD', 'OPTIONS'}))
|
||||||
|
route_info = {
|
||||||
|
'endpoint': rule.endpoint,
|
||||||
|
'url': rule.rule,
|
||||||
|
'methods': methods,
|
||||||
|
'view_func': app.view_functions[rule.endpoint].__name__,
|
||||||
|
'doc': app.view_functions[rule.endpoint].__doc__
|
||||||
|
}
|
||||||
|
routes.append(route_info)
|
||||||
|
|
||||||
|
search = request.args.get('search', '').lower()
|
||||||
|
if search:
|
||||||
|
routes = [
|
||||||
|
route for route in routes
|
||||||
|
if search in route['endpoint'].lower()
|
||||||
|
or search in route['url'].lower()
|
||||||
|
or search in route['methods'].lower()
|
||||||
|
or search in route['view_func'].lower()
|
||||||
|
or (route['doc'] and search in route['doc'].lower())
|
||||||
|
]
|
||||||
|
|
||||||
|
if htmx:
|
||||||
|
return render_template('partials/endpoints_table.html', routes=routes)
|
||||||
|
|
||||||
|
return render_template('endpoints.html', routes=routes)
|
||||||
|
|
||||||
@app.teardown_appcontext
|
@app.teardown_appcontext
|
||||||
def closeConnection(exception):
|
def closeConnection(exception):
|
||||||
db.close_connection()
|
db.close_connection()
|
||||||
|
|||||||
@@ -108,26 +108,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
<div class="space-y-2 pt-2">
|
|
||||||
<a hx-get="{{ url_for('settings') }}" hx-push-url="true" hx-target="#container"
|
|
||||||
class="text-base text-gray-900 font-normal rounded-lg hover:bg-gray-100 group transition duration-75 flex items-center p-2 cursor-pointer {{ is_selected_page(url_for('settings')) }} page-link"
|
|
||||||
_="on click add .hidden to #sidebar then remove .ml-64 from #main
|
|
||||||
on htmx:afterRequest go to the top of the body">
|
|
||||||
<svg class="w-6 h-6 text-gray-500 group-hover:text-gray-900 transition duration-75"
|
|
||||||
fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"
|
|
||||||
data-darkreader-inline-fill="" style="--darkreader-inline-fill:currentColor;">
|
|
||||||
<path fill-rule="evenodd"
|
|
||||||
d="M11.49 3.17c-.38-1.56-2.6-1.56-2.98 0a1.532 1.532 0 01-2.286.948c-1.372-.836-2.942.734-2.106 2.106.54.886.061 2.042-.947 2.287-1.561.379-1.561 2.6 0 2.978a1.532 1.532 0 01.947 2.287c-.836 1.372.734 2.942 2.106 2.106a1.532 1.532 0 012.287.947c.379 1.561 2.6 1.561 2.978 0a1.533 1.533 0 012.287-.947c1.372.836 2.942-.734 2.106-2.106a1.533 1.533 0 01.947-2.287c1.561-.379 1.561-2.6 0-2.978a1.532 1.532 0 01-.947-2.287c.836-1.372-.734-2.942-2.106-2.106a1.532 1.532 0 01-2.287-.947zM10 13a3 3 0 100-6 3 3 0 000 6z"
|
|
||||||
clip-rule="evenodd"></path>
|
|
||||||
</svg>
|
|
||||||
<span class="ml-3">Settings</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="space-y-2 pt-2">
|
<div class="space-y-2 pt-2">
|
||||||
<a hx-get="{{ url_for('sql_explorer') }}" hx-push-url="true" hx-target="#container"
|
<a hx-get="{{ url_for('sql_explorer') }}" hx-push-url="true" hx-target="#container"
|
||||||
class="text-base text-gray-900 font-normal rounded-lg hover:bg-gray-100 group transition duration-75 flex items-center p-2 cursor-pointer {{ is_selected_page(url_for('sql_explorer')) }} page-link"
|
class="text-base text-gray-900 font-normal rounded-lg hover:bg-gray-100 group transition duration-75 flex items-center p-2 cursor-pointer {{ is_selected_page(url_for('sql_explorer')) }} page-link"
|
||||||
_="on click add .hidden to #sidebar then remove .ml-64 from #main
|
_="on click add .hidden to #sidebar then remove .ml-64 from #main
|
||||||
on htmx:afterRequest go to the top of the body">
|
on htmx:afterRequest go to the top of the body">
|
||||||
<svg class="w-6 h-6 text-gray-500 group-hover:text-gray-900 transition duration-75"
|
<svg class="w-6 h-6 text-gray-500 group-hover:text-gray-900 transition duration-75"
|
||||||
fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path
|
<path
|
||||||
@@ -138,6 +123,33 @@
|
|||||||
</svg>
|
</svg>
|
||||||
<span class="ml-3">SQL Explorer</span>
|
<span class="ml-3">SQL Explorer</span>
|
||||||
</a>
|
</a>
|
||||||
|
<a hx-get="{{ url_for('list_endpoints') }}" hx-push-url="true" hx-target="#container"
|
||||||
|
class="text-base text-gray-900 font-normal rounded-lg hover:bg-gray-100 group transition duration-75 flex items-center p-2 cursor-pointer {{ is_selected_page(url_for('list_endpoints')) }} page-link"
|
||||||
|
_="on click add .hidden to #sidebar then remove .ml-64 from #main
|
||||||
|
on htmx:afterRequest go to the top of the body">
|
||||||
|
<!-- Server Icon from Heroicons -->
|
||||||
|
<svg class="w-6 h-6 text-gray-500 group-hover:text-gray-900 transition duration-75"
|
||||||
|
fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M4 5a1 1 0 011-1h10a1 1 0 011 1v1H4V5z" />
|
||||||
|
<path fill-rule="evenodd"
|
||||||
|
d="M3 7a1 1 0 011-1h12a1 1 0 011 1v6a1 1 0 01-1 1H4a1 1 0 01-1-1V7zm1 2v4h12V9H4z"
|
||||||
|
clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
<span class="ml-3">Endpoints</span>
|
||||||
|
</a>
|
||||||
|
<a hx-get="{{ url_for('settings') }}" hx-push-url="true" hx-target="#container"
|
||||||
|
class="text-base text-gray-900 font-normal rounded-lg hover:bg-gray-100 group transition duration-75 flex items-center p-2 cursor-pointer {{ is_selected_page(url_for('settings')) }} page-link"
|
||||||
|
_="on click add .hidden to #sidebar then remove .ml-64 from #main
|
||||||
|
on htmx:afterRequest go to the top of the body">
|
||||||
|
<svg class="w-6 h-6 text-gray-500 group-hover:text-gray-900 transition duration-75"
|
||||||
|
fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
data-darkreader-inline-fill="" style="--darkreader-inline-fill:currentColor;">
|
||||||
|
<path fill-rule="evenodd"
|
||||||
|
d="M11.49 3.17c-.38-1.56-2.6-1.56-2.98 0a1.532 1.532 0 01-2.286.948c-1.372-.836-2.942.734-2.106 2.106.54.886.061 2.042-.947 2.287-1.561.379-1.561 2.6 0 2.978a1.532 1.532 0 01.947 2.287c-.836 1.372.734 2.942 2.106 2.106a1.532 1.532 0 012.287.947c.379 1.561 2.6 1.561 2.978 0a1.533 1.533 0 012.287-.947c1.372.836 2.942-.734 2.106-2.106a1.533 1.533 0 01.947-2.287c1.561-.379 1.561-2.6 0-2.978a1.532 1.532 0 01-.947-2.287c.836-1.372-.734-2.942-2.106-2.106a1.532 1.532 0 01-2.287-.947zM10 13a3 3 0 100-6 3 3 0 000 6z"
|
||||||
|
clip-rule="evenodd"></path>
|
||||||
|
</svg>
|
||||||
|
<span class="ml-3">Settings</span>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
23
templates/endpoints.html
Normal file
23
templates/endpoints.html
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container mx-auto">
|
||||||
|
<!-- Title Section -->
|
||||||
|
<div class="mb-6">
|
||||||
|
<h1 class="text-4xl font-extrabold text-gray-800">API Endpoints Overview</h1>
|
||||||
|
<p class="mt-2 text-lg text-gray-600">{{ routes|length }} Routes Available</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Optional: Search or Filter functionality with HTMX -->
|
||||||
|
<div class="mb-4">
|
||||||
|
<input type="text" id="search" name="search" placeholder="Search endpoints..."
|
||||||
|
class="w-full p-2 border border-gray-300 rounded" hx-get="/endpoints"
|
||||||
|
hx-trigger="keyup changed delay:500ms, search" hx-push-url="true" hx-target="#endpoints-table"
|
||||||
|
hx-include="[name='search']">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="endpoints-table">
|
||||||
|
{{ render_partial('partials/endpoints_table.html', routes=routes) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
94
templates/partials/endpoints_table.html
Normal file
94
templates/partials/endpoints_table.html
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
<div class="overflow-x-auto">
|
||||||
|
<!-- Column Toggle Checkboxes -->
|
||||||
|
<div class="mb-4 flex flex-wrap items-center space-x-4">
|
||||||
|
<label class="inline-flex items-center">
|
||||||
|
<input type="checkbox" checked class="form-checkbox h-5 w-5 text-blue-600" hx-target=".col-endpoint"
|
||||||
|
hx-trigger="change" _="
|
||||||
|
on change if me.checked then
|
||||||
|
remove .hidden from .col-endpoint
|
||||||
|
else
|
||||||
|
add .hidden to .col-endpoint
|
||||||
|
">
|
||||||
|
<span class="ml-2 text-gray-700">Endpoint</span>
|
||||||
|
</label>
|
||||||
|
<label class="inline-flex items-center">
|
||||||
|
<input type="checkbox" checked class="form-checkbox h-5 w-5 text-blue-600" hx-target=".col-url"
|
||||||
|
hx-trigger="change" _="
|
||||||
|
on change if me.checked then
|
||||||
|
remove .hidden from .col-url
|
||||||
|
else
|
||||||
|
add .hidden to .col-url
|
||||||
|
">
|
||||||
|
<span class="ml-2 text-gray-700">URL</span>
|
||||||
|
</label>
|
||||||
|
<label class="inline-flex items-center">
|
||||||
|
<input type="checkbox" checked class="form-checkbox h-5 w-5 text-blue-600" hx-target=".col-methods"
|
||||||
|
hx-trigger="change" _="
|
||||||
|
on change if me.checked then
|
||||||
|
remove .hidden from .col-methods
|
||||||
|
else
|
||||||
|
add .hidden to .col-methods
|
||||||
|
">
|
||||||
|
<span class="ml-2 text-gray-700">Methods</span>
|
||||||
|
</label>
|
||||||
|
<label class="inline-flex items-center">
|
||||||
|
<input type="checkbox" checked class="form-checkbox h-5 w-5 text-blue-600" hx-target=".col-view_func"
|
||||||
|
hx-trigger="change" _="
|
||||||
|
on change if me.checked then
|
||||||
|
remove .hidden from .col-view_func
|
||||||
|
else
|
||||||
|
add .hidden to .col-view_func
|
||||||
|
">
|
||||||
|
<span class="ml-2 text-gray-700">View Function</span>
|
||||||
|
</label>
|
||||||
|
<label class="inline-flex items-center">
|
||||||
|
<input type="checkbox" checked class="form-checkbox h-5 w-5 text-blue-600" hx-target=".col-description"
|
||||||
|
hx-trigger="change" _="
|
||||||
|
on change if me.checked then
|
||||||
|
remove .hidden from .col-description
|
||||||
|
else
|
||||||
|
add .hidden to .col-description
|
||||||
|
">
|
||||||
|
<span class="ml-2 text-gray-700">Description</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Endpoints Table -->
|
||||||
|
<table class="min-w-full bg-white rounded-lg shadow">
|
||||||
|
<thead class="bg-gray-200">
|
||||||
|
<tr>
|
||||||
|
<th class="py-2 px-4 border-b text-left text-sm font-medium text-gray-700 col-endpoint">Endpoint</th>
|
||||||
|
<th class="py-2 px-4 border-b text-left text-sm font-medium text-gray-700 col-url">URL</th>
|
||||||
|
<th class="py-2 px-4 border-b text-left text-sm font-medium text-gray-700 col-methods">Methods</th>
|
||||||
|
<th class="py-2 px-4 border-b text-left text-sm font-medium text-gray-700 col-view_func">View Function
|
||||||
|
</th>
|
||||||
|
<th class="py-2 px-4 border-b text-left text-sm font-medium text-gray-700 col-description">Description
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for route in routes %}
|
||||||
|
<tr class="hover:bg-gray-100 even:bg-gray-50">
|
||||||
|
<td class="py-2 px-4 border-b text-sm text-gray-800 col-endpoint">{{ route.endpoint }}</td>
|
||||||
|
<td class="py-2 px-4 border-b text-sm text-gray-800 col-url">{{ route.url }}</td>
|
||||||
|
<td class="py-2 px-4 border-b text-sm text-gray-800 col-methods">{{ route.methods }}</td>
|
||||||
|
<td class="py-2 px-4 border-b text-sm text-gray-800 col-view_func">{{ route.view_func }}</td>
|
||||||
|
<td class="py-2 px-4 border-b text-sm text-gray-800 col-description">
|
||||||
|
{% if route.doc %}
|
||||||
|
<details class="group">
|
||||||
|
<summary class="cursor-pointer text-blue-600 hover:underline">
|
||||||
|
{{ route.doc.split('\n')[0] }}{% if route.doc.count('\n') > 0 %}...{% endif %}
|
||||||
|
</summary>
|
||||||
|
<div class="mt-2 text-gray-700">
|
||||||
|
{{ route.doc }}
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
{% else %}
|
||||||
|
N/A
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
Reference in New Issue
Block a user