django-widget-hub 0.1.0__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.
Files changed (67) hide show
  1. django_widget_hub-0.1.0/.gitignore +16 -0
  2. django_widget_hub-0.1.0/CHANGELOG.md +102 -0
  3. django_widget_hub-0.1.0/LICENSE +21 -0
  4. django_widget_hub-0.1.0/PKG-INFO +541 -0
  5. django_widget_hub-0.1.0/README.md +498 -0
  6. django_widget_hub-0.1.0/example/demo_widgets/tests/__init__.py +0 -0
  7. django_widget_hub-0.1.0/example/demo_widgets/tests/test_widgets.py +168 -0
  8. django_widget_hub-0.1.0/integration/epitech/tests/test_about_identical.py +33 -0
  9. django_widget_hub-0.1.0/integration/epitech/tests/test_data_migration.py +85 -0
  10. django_widget_hub-0.1.0/integration/epitech/tests/test_loc.py +9 -0
  11. django_widget_hub-0.1.0/integration/epitech/tests/test_widgets.py +59 -0
  12. django_widget_hub-0.1.0/pyproject.toml +88 -0
  13. django_widget_hub-0.1.0/src/widget_hub/__init__.py +18 -0
  14. django_widget_hub-0.1.0/src/widget_hub/admin.py +32 -0
  15. django_widget_hub-0.1.0/src/widget_hub/api.py +151 -0
  16. django_widget_hub-0.1.0/src/widget_hub/apps.py +18 -0
  17. django_widget_hub-0.1.0/src/widget_hub/auth.py +68 -0
  18. django_widget_hub-0.1.0/src/widget_hub/checks.py +83 -0
  19. django_widget_hub-0.1.0/src/widget_hub/conf.py +59 -0
  20. django_widget_hub-0.1.0/src/widget_hub/contrib/__init__.py +0 -0
  21. django_widget_hub-0.1.0/src/widget_hub/contrib/drf.py +123 -0
  22. django_widget_hub-0.1.0/src/widget_hub/contrib/ninja.py +151 -0
  23. django_widget_hub-0.1.0/src/widget_hub/credentials.py +83 -0
  24. django_widget_hub-0.1.0/src/widget_hub/discovery.py +62 -0
  25. django_widget_hub-0.1.0/src/widget_hub/errors.py +47 -0
  26. django_widget_hub-0.1.0/src/widget_hub/exceptions.py +63 -0
  27. django_widget_hub-0.1.0/src/widget_hub/fetching.py +256 -0
  28. django_widget_hub-0.1.0/src/widget_hub/migrations/0001_initial.py +68 -0
  29. django_widget_hub-0.1.0/src/widget_hub/migrations/0002_credential.py +50 -0
  30. django_widget_hub-0.1.0/src/widget_hub/migrations/__init__.py +0 -0
  31. django_widget_hub-0.1.0/src/widget_hub/models.py +91 -0
  32. django_widget_hub-0.1.0/src/widget_hub/params.py +331 -0
  33. django_widget_hub-0.1.0/src/widget_hub/py.typed +0 -0
  34. django_widget_hub-0.1.0/src/widget_hub/registry.py +86 -0
  35. django_widget_hub-0.1.0/src/widget_hub/security.py +336 -0
  36. django_widget_hub-0.1.0/src/widget_hub/services.py +228 -0
  37. django_widget_hub-0.1.0/src/widget_hub/static/widget_hub/dashboard.css +664 -0
  38. django_widget_hub-0.1.0/src/widget_hub/static/widget_hub/dashboard.js +1025 -0
  39. django_widget_hub-0.1.0/src/widget_hub/templates/widget_hub/dashboard.html +28 -0
  40. django_widget_hub-0.1.0/src/widget_hub/templatetags/__init__.py +0 -0
  41. django_widget_hub-0.1.0/src/widget_hub/templatetags/widget_hub.py +43 -0
  42. django_widget_hub-0.1.0/src/widget_hub/urls.py +25 -0
  43. django_widget_hub-0.1.0/src/widget_hub/views.py +92 -0
  44. django_widget_hub-0.1.0/src/widget_hub/widgets.py +122 -0
  45. django_widget_hub-0.1.0/tests/__init__.py +0 -0
  46. django_widget_hub-0.1.0/tests/conftest.py +9 -0
  47. django_widget_hub-0.1.0/tests/demoapp/__init__.py +0 -0
  48. django_widget_hub-0.1.0/tests/demoapp/widgets.py +36 -0
  49. django_widget_hub-0.1.0/tests/drf_auth.py +19 -0
  50. django_widget_hub-0.1.0/tests/js/dashboard.test.js +129 -0
  51. django_widget_hub-0.1.0/tests/settings.py +53 -0
  52. django_widget_hub-0.1.0/tests/test_adapters.py +318 -0
  53. django_widget_hub-0.1.0/tests/test_app.py +11 -0
  54. django_widget_hub-0.1.0/tests/test_autodiscover.py +5 -0
  55. django_widget_hub-0.1.0/tests/test_credentials.py +124 -0
  56. django_widget_hub-0.1.0/tests/test_discovery.py +77 -0
  57. django_widget_hub-0.1.0/tests/test_errors.py +103 -0
  58. django_widget_hub-0.1.0/tests/test_fetching.py +232 -0
  59. django_widget_hub-0.1.0/tests/test_front.py +76 -0
  60. django_widget_hub-0.1.0/tests/test_models.py +63 -0
  61. django_widget_hub-0.1.0/tests/test_params_declaration.py +39 -0
  62. django_widget_hub-0.1.0/tests/test_params_validation.py +177 -0
  63. django_widget_hub-0.1.0/tests/test_registry.py +98 -0
  64. django_widget_hub-0.1.0/tests/test_security.py +403 -0
  65. django_widget_hub-0.1.0/tests/test_services.py +171 -0
  66. django_widget_hub-0.1.0/tests/test_single_flight.py +122 -0
  67. django_widget_hub-0.1.0/tests/urls.py +42 -0
@@ -0,0 +1,16 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ .venv/
5
+ venv/
6
+ build/
7
+ dist/
8
+ .pytest_cache/
9
+ .ruff_cache/
10
+ .coverage
11
+ coverage.xml
12
+ htmlcov/
13
+ *.sqlite3
14
+ .env
15
+ .idea/
16
+ .vscode/
@@ -0,0 +1,102 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here.
4
+ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project
5
+ uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.1.0] - 2026-10-16
10
+
11
+ First release.
12
+
13
+ ### Added
14
+
15
+ - Project skeleton: src layout, `widget_hub` app config, pytest-django test suite, demo project
16
+ under `example/`, CI on Django 5.2 / 6.0 / 6.1 with every Python version each one supports.
17
+ - Declaration API: `Service`, `Widget`, typed params (`String`, `Integer`, `Boolean`, `Choice`,
18
+ `Url`), `@register`, and a registry that refuses duplicate service or widget names at startup.
19
+ `<app>/widgets.py` modules are autodiscovered.
20
+ - Parameter validation (`Widget.validate`, `ParamValidationError`) with per-field messages,
21
+ defaults, bounds, choices, URL checks, unknown-key rejection; `Widget.schema()` for clients.
22
+ - `WidgetInstance` model (owner via `AUTH_USER_MODEL`, service, widget, params, refresh rate,
23
+ position, timestamps), its initial migration and admin; `clean()` validates params.
24
+ - Framework-agnostic service layer (`widget_hub.services`): owner-scoped list/get/create/update/
25
+ delete, `fetch_instance_data`, `FetchContext`, `InvalidInput` and `NotFound` exceptions.
26
+ - `WIDGET_HUB` settings dict with system checks; `fetch()` results cached per
27
+ (widget, normalised params) through Django's cache framework, `Widget.cache_per_owner`,
28
+ `MIN_REFRESH_RATE`/`MAX_REFRESH_RATE` enforcement and a per-call `FETCH_TIMEOUT`.
29
+ - Error contract: `WidgetError(message, status)`, generic 503 for crashes/timeouts with details
30
+ only in logs, cached failures, one JSON error shape (`widget_hub.errors`).
31
+ - `MAX_CONCURRENT_FETCHES` caps running `fetch()` calls, timed-out ones included, so a slow
32
+ upstream cannot make the process accumulate threads.
33
+ - Public `GET /about.json` (Epitech Dashboard format) generated from the registry, via
34
+ `include("widget_hub.urls")`; `ABOUT_TRUST_X_FORWARDED_FOR` setting.
35
+ - django-ninja adapter `widget_hub.contrib.ninja.build_router()`; shared HTTP operations in
36
+ `widget_hub.api`; swappable `OWNER_RESOLVER` and `PERMISSION_CHECK`; CSRF enforced for
37
+ cookie-authenticated unsafe requests reaching CSRF-exempt API views.
38
+ - DRF adapter `widget_hub.contrib.drf.build_router()` (`CatalogViewSet`, `WidgetInstanceViewSet`)
39
+ with the same routes, responses and error contract as the ninja adapter.
40
+ - `widget_hub.security.safe_fetch()`: SSRF-safe GET (public addresses only, connection pinned to
41
+ the checked address, re-validated redirects, timeout, size limit) raising `SafeFetchError`.
42
+ - Encrypted credential store (`Credential` model, `widget_hub.credentials`, Fernet with an
43
+ HKDF-derived key from `CREDENTIALS_KEY` or `SECRET_KEY`), `Widget.uses_credential`,
44
+ `FetchContext.credential`, `/credentials` endpoints in both adapters, warning `widget_hub.W001`.
45
+ - New dependency: `cryptography`.
46
+ - Built-in front: `{% load widget_hub %}{% dashboard %}` template tag, dependency-free
47
+ `dashboard.js` (grid, pointer and keyboard move/resize without overlaps, settings forms
48
+ generated from the param schema, per-widget errors with retry, refresh paused on hidden tabs,
49
+ `WidgetHub.registerRenderer`), `dashboard.css` with light/dark themes, and a session-authenticated
50
+ JSON API under `widget-hub/api/` in `widget_hub.urls`. `Widget.schema()` exposes
51
+ `uses_credential`.
52
+ - Example project: weather (public API), RSS (user URL through `safe_fetch`, DTDs refused) and
53
+ GitHub (stored token) widgets with custom renderers and tests.
54
+ - `POSITION_VALIDATOR` setting: position validation is swappable for fronts that do not use the
55
+ built-in grid (found while migrating the Epitech dashboard, whose React front stores pixels).
56
+ - Documentation: full README, CONTRIBUTING, research notes, design decisions.
57
+ - Integration proof (`integration/epitech/`, `docs/migration-epitech.md`): the Epitech dashboard's
58
+ widget layer migrated to the plugin with an identical `/about.json`, a tested data migration and
59
+ reproducible line counts.
60
+ - Packaging: CI job installing the built wheel in a fresh virtualenv and running the example
61
+ against it; `Release` workflow publishing tags to TestPyPI through trusted publishing.
62
+
63
+ ### Changed
64
+
65
+ - Built-in front design pass: consistent SVG icons, ghost icon buttons with hover/active states,
66
+ loading placeholders, params summary under each title, relative "Updated … ago" times, error
67
+ cards with "Try again", empty state with a call to action, dialog with a close button and
68
+ widgets grouped by service, spacing and shadow tokens, 44 px touch targets, reduced-motion
69
+ support. New widgets default to 3 rows so their content fits.
70
+ - The generic 503 message is now "This widget is temporarily unavailable." (the server no longer
71
+ promises client behaviour; the front says it retries).
72
+
73
+ ### Fixed
74
+
75
+ - On small screens, widgets kept their grid rows while stacked in one column and covered each
76
+ other (only 2 of 4 widgets were visible); they now stack in reading order.
77
+
78
+ ### Performance
79
+
80
+ - Concurrent cache misses on the same widget and params are coalesced ("single flight"):
81
+ 20 simultaneous identical requests now make 1 upstream call instead of 20.
82
+ - `fetch()` runs in a shared worker pool instead of a new executor per call (worker hop
83
+ 259 µs → 48 µs median).
84
+
85
+ ### Security
86
+
87
+ - CSRF is now enforced on unsafe requests whenever no framework authenticator accepted the
88
+ request (previously the DRF adapter skipped the check entirely, so a custom `OWNER_RESOLVER`
89
+ reading Django's session could be driven cross-site).
90
+ - CSRF is also enforced when a framework authenticator accepted the request but the browser sent
91
+ Django's session cookie: ninja authenticators that simply return `request.user` no longer
92
+ bypass it. Only requests without the session cookie (token clients) skip the check.
93
+ - `safe_fetch()` hardening: IPv4 addresses embedded in IPv6 (NAT64, 6to4, Teredo) must be public;
94
+ special ranges are rejected explicitly on every Python version; caller headers (not only
95
+ `Authorization`) are dropped on any cross-origin redirect, including https→http on the same
96
+ host; the deadline is enforced by a watchdog so a server trickling bytes cannot hold the call.
97
+ - `safe_fetch()` DNS resolution is bounded by the remaining deadline: `getaddrinfo` has no timeout
98
+ and a slow authoritative DNS server, chosen by whoever controls the URL, extended every hop.
99
+ Pending lookups, abandoned ones included, are capped by `MAX_CONCURRENT_FETCHES`.
100
+
101
+ [Unreleased]: https://github.com/H0ozsed/Plugin-Django-widget/compare/v0.1.0...HEAD
102
+ [0.1.0]: https://github.com/H0ozsed/Plugin-Django-widget/releases/tag/v0.1.0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Hoowsed
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
19
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20
+ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
21
+ OR OTHER DEALINGS IN THE SOFTWARE.