netbox-sqlquery 0.1.2__tar.gz → 0.1.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/PKG-INFO +3 -3
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/README.md +1 -1
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/__init__.py +2 -2
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/migrations/0001_initial.py +2 -2
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/static/netbox_sqlquery/editor.js +1 -27
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/templates/netbox_sqlquery/query.html +8 -4
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/urls.py +1 -0
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/views.py +68 -3
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery.egg-info/PKG-INFO +3 -3
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery.egg-info/SOURCES.txt +0 -1
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/pyproject.toml +2 -2
- netbox_sqlquery-0.1.2/netbox_sqlquery/migrations/0003_tablepermission_groups_to_users_group.py +0 -16
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/LICENSE +0 -0
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/abstract_schema.py +0 -0
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/access.py +0 -0
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/api/__init__.py +0 -0
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/api/serializers.py +0 -0
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/api/urls.py +0 -0
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/api/views.py +0 -0
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/filtersets.py +0 -0
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/forms.py +0 -0
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/management/__init__.py +0 -0
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/management/commands/__init__.py +0 -0
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/management/commands/sqlquery_create_views.py +0 -0
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/migrations/0002_query_permissions.py +0 -0
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/migrations/__init__.py +0 -0
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/models.py +0 -0
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/navigation.py +0 -0
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/preferences.py +0 -0
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/schema.py +0 -0
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/static/netbox_sqlquery/icon.LICENSE +0 -0
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/static/netbox_sqlquery/icon.svg +0 -0
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/tables.py +0 -0
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/templates/netbox_sqlquery/saved_query.html +0 -0
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/tests/__init__.py +0 -0
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/tests/test_access.py +0 -0
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/tests/test_api.py +0 -0
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/tests/test_models.py +0 -0
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/tests/test_views.py +0 -0
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery.egg-info/dependency_links.txt +0 -0
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery.egg-info/top_level.txt +0 -0
- {netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/setup.cfg +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: netbox-sqlquery
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: SQL query interface for NetBox with syntax highlighting, abstract views, and role-based access control
|
|
5
5
|
Author-email: Ravi Pina <ravi@pina.org>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
7
7
|
Project-URL: Homepage, https://github.com/ravinald/netbox-sqlquery
|
|
8
8
|
Project-URL: Issues, https://github.com/ravinald/netbox-sqlquery/issues
|
|
9
|
-
Requires-Python: >=3.
|
|
9
|
+
Requires-Python: >=3.12
|
|
10
10
|
Description-Content-Type: text/markdown
|
|
11
11
|
License-File: LICENSE
|
|
12
12
|
Dynamic: license-file
|
|
@@ -73,7 +73,7 @@ See [COMPATIBILITY.md](COMPATIBILITY.md) for the full version matrix.
|
|
|
73
73
|
|
|
74
74
|
| NetBox version | Python versions |
|
|
75
75
|
|----------------|------------------------------|
|
|
76
|
-
| 4.
|
|
76
|
+
| 4.5+ | 3.12, 3.13, 3.14 |
|
|
77
77
|
|
|
78
78
|
## Installation
|
|
79
79
|
|
|
@@ -60,7 +60,7 @@ See [COMPATIBILITY.md](COMPATIBILITY.md) for the full version matrix.
|
|
|
60
60
|
|
|
61
61
|
| NetBox version | Python versions |
|
|
62
62
|
|----------------|------------------------------|
|
|
63
|
-
| 4.
|
|
63
|
+
| 4.5+ | 3.12, 3.13, 3.14 |
|
|
64
64
|
|
|
65
65
|
## Installation
|
|
66
66
|
|
|
@@ -12,11 +12,11 @@ class NetBoxSQLQueryConfig(PluginConfig):
|
|
|
12
12
|
"SQL query interface for NetBox with syntax highlighting,"
|
|
13
13
|
" abstract views, and role-based access control"
|
|
14
14
|
)
|
|
15
|
-
version = "0.1.
|
|
15
|
+
version = "0.1.3"
|
|
16
16
|
author = "Ravi Pina"
|
|
17
17
|
author_email = "ravi@pina.org"
|
|
18
18
|
base_url = "sqlquery"
|
|
19
|
-
min_version = "4.
|
|
19
|
+
min_version = "4.5.0"
|
|
20
20
|
max_version = None
|
|
21
21
|
required_settings = []
|
|
22
22
|
default_settings = {
|
|
@@ -8,8 +8,8 @@ class Migration(migrations.Migration):
|
|
|
8
8
|
|
|
9
9
|
dependencies = [
|
|
10
10
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
11
|
-
("auth", "0012_alter_user_first_name_max_length"),
|
|
12
11
|
("extras", "0001_initial"),
|
|
12
|
+
("users", "0001_squashed_0011"),
|
|
13
13
|
]
|
|
14
14
|
|
|
15
15
|
operations = [
|
|
@@ -85,7 +85,7 @@ class Migration(migrations.Migration):
|
|
|
85
85
|
("require_staff", models.BooleanField(default=False)),
|
|
86
86
|
("require_superuser", models.BooleanField(default=False)),
|
|
87
87
|
("allow", models.BooleanField(default=True)),
|
|
88
|
-
("groups", models.ManyToManyField(blank=True, to="
|
|
88
|
+
("groups", models.ManyToManyField(blank=True, to="users.group")),
|
|
89
89
|
],
|
|
90
90
|
options={
|
|
91
91
|
"ordering": ["-require_superuser", "-require_staff", "pattern"],
|
{netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/static/netbox_sqlquery/editor.js
RENAMED
|
@@ -552,33 +552,7 @@
|
|
|
552
552
|
});
|
|
553
553
|
|
|
554
554
|
|
|
555
|
-
// CSV download
|
|
556
|
-
var csvBtn = document.getElementById("csv-download");
|
|
557
|
-
if (csvBtn) {
|
|
558
|
-
csvBtn.addEventListener("click", function () {
|
|
559
|
-
var table = document.querySelector(".results-pane table");
|
|
560
|
-
if (!table) return;
|
|
561
|
-
var rows = [];
|
|
562
|
-
table.querySelectorAll("tr").forEach(function (tr) {
|
|
563
|
-
var cells = [];
|
|
564
|
-
tr.querySelectorAll("th, td").forEach(function (cell) {
|
|
565
|
-
var text = cell.textContent;
|
|
566
|
-
if (text.includes(",") || text.includes('"') || text.includes("\n")) {
|
|
567
|
-
text = '"' + text.replace(/"/g, '""') + '"';
|
|
568
|
-
}
|
|
569
|
-
cells.push(text);
|
|
570
|
-
});
|
|
571
|
-
rows.push(cells.join(","));
|
|
572
|
-
});
|
|
573
|
-
var blob = new Blob([rows.join("\n")], { type: "text/csv" });
|
|
574
|
-
var url = URL.createObjectURL(blob);
|
|
575
|
-
var a = document.createElement("a");
|
|
576
|
-
a.href = url;
|
|
577
|
-
a.download = "query_results.csv";
|
|
578
|
-
a.click();
|
|
579
|
-
URL.revokeObjectURL(url);
|
|
580
|
-
});
|
|
581
|
-
}
|
|
555
|
+
// CSV download is now a server-side form POST (no JS needed)
|
|
582
556
|
|
|
583
557
|
function insertAtCursor(text) {
|
|
584
558
|
var start = editor.selectionStart;
|
{netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/templates/netbox_sqlquery/query.html
RENAMED
|
@@ -299,10 +299,14 @@
|
|
|
299
299
|
</tbody>
|
|
300
300
|
</table>
|
|
301
301
|
<div class="results-meta">
|
|
302
|
-
<span>{{ row_count }} row{{ row_count|pluralize }}</span>
|
|
303
|
-
<
|
|
304
|
-
|
|
305
|
-
|
|
302
|
+
<span>{{ row_count }} row{{ row_count|pluralize }}{% if truncated %} <span class="text-warning">(limited to {{ max_rows }} -- add LIMIT/OFFSET to page through results)</span>{% endif %}</span>
|
|
303
|
+
<form method="post" action="{% url 'plugins:netbox_sqlquery:export_csv' %}" style="display:inline;">
|
|
304
|
+
{% csrf_token %}
|
|
305
|
+
<input type="hidden" name="sql" value="{{ sql }}">
|
|
306
|
+
<button type="submit" class="btn btn-sm btn-outline-secondary" title="Download all results as CSV (no row limit)">
|
|
307
|
+
Download CSV
|
|
308
|
+
</button>
|
|
309
|
+
</form>
|
|
306
310
|
</div>
|
|
307
311
|
</div>
|
|
308
312
|
{% endif %}
|
|
@@ -19,4 +19,5 @@ urlpatterns = [
|
|
|
19
19
|
),
|
|
20
20
|
path("ajax/save-query/", views.SavedQueryAjaxSave.as_view(), name="ajax_save_query"),
|
|
21
21
|
path("ajax/list-queries/", views.SavedQueryAjaxList.as_view(), name="ajax_list_queries"),
|
|
22
|
+
path("export-csv/", views.CSVExportView.as_view(), name="export_csv"),
|
|
22
23
|
]
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import csv
|
|
2
|
+
import io
|
|
1
3
|
import json
|
|
2
4
|
import logging
|
|
3
5
|
|
|
4
6
|
from django.contrib.auth.mixins import UserPassesTestMixin
|
|
5
7
|
from django.db import DatabaseError, connection, transaction
|
|
6
|
-
from django.http import JsonResponse
|
|
8
|
+
from django.http import HttpResponse, JsonResponse
|
|
7
9
|
from django.views import View
|
|
8
10
|
from django.views.generic import TemplateView
|
|
9
11
|
from netbox.plugins import get_plugin_config
|
|
@@ -185,10 +187,19 @@ class QueryView(UserPassesTestMixin, TemplateView):
|
|
|
185
187
|
cursor.execute("SET TRANSACTION READ ONLY")
|
|
186
188
|
cursor.execute(sql)
|
|
187
189
|
columns = [col[0] for col in cursor.description]
|
|
188
|
-
rows = cursor.fetchmany(max_rows)
|
|
190
|
+
rows = cursor.fetchmany(max_rows + 1)
|
|
189
191
|
raise _ReadOnlyRollback()
|
|
190
192
|
except _ReadOnlyRollback:
|
|
191
|
-
|
|
193
|
+
truncated = len(rows) > max_rows
|
|
194
|
+
if truncated:
|
|
195
|
+
rows = rows[:max_rows]
|
|
196
|
+
ctx.update(
|
|
197
|
+
columns=columns,
|
|
198
|
+
rows=rows,
|
|
199
|
+
row_count=len(rows),
|
|
200
|
+
truncated=truncated,
|
|
201
|
+
max_rows=max_rows,
|
|
202
|
+
)
|
|
192
203
|
except DatabaseError as exc:
|
|
193
204
|
ctx["error"] = str(exc)
|
|
194
205
|
return ctx
|
|
@@ -351,3 +362,57 @@ class SavedQueryAjaxList(UserPassesTestMixin, View):
|
|
|
351
362
|
]
|
|
352
363
|
}
|
|
353
364
|
)
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
class CSVExportView(UserPassesTestMixin, View):
|
|
368
|
+
"""Export query results as CSV with no row limit."""
|
|
369
|
+
|
|
370
|
+
def test_func(self):
|
|
371
|
+
user = self.request.user
|
|
372
|
+
if not user.is_active:
|
|
373
|
+
return False
|
|
374
|
+
if user.is_superuser:
|
|
375
|
+
return True
|
|
376
|
+
if get_plugin_config("netbox_sqlquery", "require_superuser"):
|
|
377
|
+
return False
|
|
378
|
+
return user.has_perm("netbox_sqlquery.view_querypermission")
|
|
379
|
+
|
|
380
|
+
def post(self, request):
|
|
381
|
+
sql = request.POST.get("sql", "").strip()
|
|
382
|
+
if not sql:
|
|
383
|
+
return HttpResponse("No SQL provided.", status=400)
|
|
384
|
+
|
|
385
|
+
normalized = sql.lstrip().upper()
|
|
386
|
+
if not (normalized.startswith("SELECT") or normalized.startswith("WITH")):
|
|
387
|
+
return HttpResponse("Only SELECT queries can be exported.", status=400)
|
|
388
|
+
|
|
389
|
+
denied = check_access(request.user, extract_tables(sql))
|
|
390
|
+
if denied:
|
|
391
|
+
return HttpResponse(f"Access denied to: {', '.join(sorted(denied))}", status=403)
|
|
392
|
+
|
|
393
|
+
timeout_ms = get_plugin_config("netbox_sqlquery", "statement_timeout_ms")
|
|
394
|
+
|
|
395
|
+
try:
|
|
396
|
+
with transaction.atomic():
|
|
397
|
+
with connection.cursor() as cursor:
|
|
398
|
+
cursor.execute(f"SET LOCAL statement_timeout = '{timeout_ms}'")
|
|
399
|
+
cursor.execute("SET TRANSACTION READ ONLY")
|
|
400
|
+
cursor.execute(sql)
|
|
401
|
+
columns = [col[0] for col in cursor.description]
|
|
402
|
+
rows = cursor.fetchall()
|
|
403
|
+
raise _ReadOnlyRollback()
|
|
404
|
+
except _ReadOnlyRollback:
|
|
405
|
+
pass
|
|
406
|
+
except DatabaseError as exc:
|
|
407
|
+
return HttpResponse(f"Query error: {exc}", status=400)
|
|
408
|
+
|
|
409
|
+
output = io.StringIO()
|
|
410
|
+
writer = csv.writer(output)
|
|
411
|
+
writer.writerow(columns)
|
|
412
|
+
writer.writerows(rows)
|
|
413
|
+
|
|
414
|
+
response = HttpResponse(output.getvalue(), content_type="text/csv")
|
|
415
|
+
response["Content-Disposition"] = 'attachment; filename="query_results.csv"'
|
|
416
|
+
|
|
417
|
+
_record_query(request.user, sql)
|
|
418
|
+
return response
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: netbox-sqlquery
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: SQL query interface for NetBox with syntax highlighting, abstract views, and role-based access control
|
|
5
5
|
Author-email: Ravi Pina <ravi@pina.org>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
7
7
|
Project-URL: Homepage, https://github.com/ravinald/netbox-sqlquery
|
|
8
8
|
Project-URL: Issues, https://github.com/ravinald/netbox-sqlquery/issues
|
|
9
|
-
Requires-Python: >=3.
|
|
9
|
+
Requires-Python: >=3.12
|
|
10
10
|
Description-Content-Type: text/markdown
|
|
11
11
|
License-File: LICENSE
|
|
12
12
|
Dynamic: license-file
|
|
@@ -73,7 +73,7 @@ See [COMPATIBILITY.md](COMPATIBILITY.md) for the full version matrix.
|
|
|
73
73
|
|
|
74
74
|
| NetBox version | Python versions |
|
|
75
75
|
|----------------|------------------------------|
|
|
76
|
-
| 4.
|
|
76
|
+
| 4.5+ | 3.12, 3.13, 3.14 |
|
|
77
77
|
|
|
78
78
|
## Installation
|
|
79
79
|
|
|
@@ -26,7 +26,6 @@ netbox_sqlquery/management/commands/__init__.py
|
|
|
26
26
|
netbox_sqlquery/management/commands/sqlquery_create_views.py
|
|
27
27
|
netbox_sqlquery/migrations/0001_initial.py
|
|
28
28
|
netbox_sqlquery/migrations/0002_query_permissions.py
|
|
29
|
-
netbox_sqlquery/migrations/0003_tablepermission_groups_to_users_group.py
|
|
30
29
|
netbox_sqlquery/migrations/__init__.py
|
|
31
30
|
netbox_sqlquery/static/netbox_sqlquery/editor.js
|
|
32
31
|
netbox_sqlquery/static/netbox_sqlquery/icon.LICENSE
|
|
@@ -4,11 +4,11 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "netbox-sqlquery"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.3"
|
|
8
8
|
description = "SQL query interface for NetBox with syntax highlighting, abstract views, and role-based access control"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "Apache-2.0"
|
|
11
|
-
requires-python = ">=3.
|
|
11
|
+
requires-python = ">=3.12"
|
|
12
12
|
dependencies = []
|
|
13
13
|
authors = [
|
|
14
14
|
{ name = "Ravi Pina", email = "ravi@pina.org" },
|
netbox_sqlquery-0.1.2/netbox_sqlquery/migrations/0003_tablepermission_groups_to_users_group.py
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
from django.db import migrations, models
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class Migration(migrations.Migration):
|
|
5
|
-
dependencies = [
|
|
6
|
-
("netbox_sqlquery", "0002_query_permissions"),
|
|
7
|
-
("users", "0001_squashed_0011"),
|
|
8
|
-
]
|
|
9
|
-
|
|
10
|
-
operations = [
|
|
11
|
-
migrations.AlterField(
|
|
12
|
-
model_name="tablepermission",
|
|
13
|
-
name="groups",
|
|
14
|
-
field=models.ManyToManyField(blank=True, to="users.group"),
|
|
15
|
-
),
|
|
16
|
-
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/management/commands/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/migrations/0002_query_permissions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/static/netbox_sqlquery/icon.LICENSE
RENAMED
|
File without changes
|
{netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery/static/netbox_sqlquery/icon.svg
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{netbox_sqlquery-0.1.2 → netbox_sqlquery-0.1.3}/netbox_sqlquery.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|