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.
- django_widget_hub-0.1.0/.gitignore +16 -0
- django_widget_hub-0.1.0/CHANGELOG.md +102 -0
- django_widget_hub-0.1.0/LICENSE +21 -0
- django_widget_hub-0.1.0/PKG-INFO +541 -0
- django_widget_hub-0.1.0/README.md +498 -0
- django_widget_hub-0.1.0/example/demo_widgets/tests/__init__.py +0 -0
- django_widget_hub-0.1.0/example/demo_widgets/tests/test_widgets.py +168 -0
- django_widget_hub-0.1.0/integration/epitech/tests/test_about_identical.py +33 -0
- django_widget_hub-0.1.0/integration/epitech/tests/test_data_migration.py +85 -0
- django_widget_hub-0.1.0/integration/epitech/tests/test_loc.py +9 -0
- django_widget_hub-0.1.0/integration/epitech/tests/test_widgets.py +59 -0
- django_widget_hub-0.1.0/pyproject.toml +88 -0
- django_widget_hub-0.1.0/src/widget_hub/__init__.py +18 -0
- django_widget_hub-0.1.0/src/widget_hub/admin.py +32 -0
- django_widget_hub-0.1.0/src/widget_hub/api.py +151 -0
- django_widget_hub-0.1.0/src/widget_hub/apps.py +18 -0
- django_widget_hub-0.1.0/src/widget_hub/auth.py +68 -0
- django_widget_hub-0.1.0/src/widget_hub/checks.py +83 -0
- django_widget_hub-0.1.0/src/widget_hub/conf.py +59 -0
- django_widget_hub-0.1.0/src/widget_hub/contrib/__init__.py +0 -0
- django_widget_hub-0.1.0/src/widget_hub/contrib/drf.py +123 -0
- django_widget_hub-0.1.0/src/widget_hub/contrib/ninja.py +151 -0
- django_widget_hub-0.1.0/src/widget_hub/credentials.py +83 -0
- django_widget_hub-0.1.0/src/widget_hub/discovery.py +62 -0
- django_widget_hub-0.1.0/src/widget_hub/errors.py +47 -0
- django_widget_hub-0.1.0/src/widget_hub/exceptions.py +63 -0
- django_widget_hub-0.1.0/src/widget_hub/fetching.py +256 -0
- django_widget_hub-0.1.0/src/widget_hub/migrations/0001_initial.py +68 -0
- django_widget_hub-0.1.0/src/widget_hub/migrations/0002_credential.py +50 -0
- django_widget_hub-0.1.0/src/widget_hub/migrations/__init__.py +0 -0
- django_widget_hub-0.1.0/src/widget_hub/models.py +91 -0
- django_widget_hub-0.1.0/src/widget_hub/params.py +331 -0
- django_widget_hub-0.1.0/src/widget_hub/py.typed +0 -0
- django_widget_hub-0.1.0/src/widget_hub/registry.py +86 -0
- django_widget_hub-0.1.0/src/widget_hub/security.py +336 -0
- django_widget_hub-0.1.0/src/widget_hub/services.py +228 -0
- django_widget_hub-0.1.0/src/widget_hub/static/widget_hub/dashboard.css +664 -0
- django_widget_hub-0.1.0/src/widget_hub/static/widget_hub/dashboard.js +1025 -0
- django_widget_hub-0.1.0/src/widget_hub/templates/widget_hub/dashboard.html +28 -0
- django_widget_hub-0.1.0/src/widget_hub/templatetags/__init__.py +0 -0
- django_widget_hub-0.1.0/src/widget_hub/templatetags/widget_hub.py +43 -0
- django_widget_hub-0.1.0/src/widget_hub/urls.py +25 -0
- django_widget_hub-0.1.0/src/widget_hub/views.py +92 -0
- django_widget_hub-0.1.0/src/widget_hub/widgets.py +122 -0
- django_widget_hub-0.1.0/tests/__init__.py +0 -0
- django_widget_hub-0.1.0/tests/conftest.py +9 -0
- django_widget_hub-0.1.0/tests/demoapp/__init__.py +0 -0
- django_widget_hub-0.1.0/tests/demoapp/widgets.py +36 -0
- django_widget_hub-0.1.0/tests/drf_auth.py +19 -0
- django_widget_hub-0.1.0/tests/js/dashboard.test.js +129 -0
- django_widget_hub-0.1.0/tests/settings.py +53 -0
- django_widget_hub-0.1.0/tests/test_adapters.py +318 -0
- django_widget_hub-0.1.0/tests/test_app.py +11 -0
- django_widget_hub-0.1.0/tests/test_autodiscover.py +5 -0
- django_widget_hub-0.1.0/tests/test_credentials.py +124 -0
- django_widget_hub-0.1.0/tests/test_discovery.py +77 -0
- django_widget_hub-0.1.0/tests/test_errors.py +103 -0
- django_widget_hub-0.1.0/tests/test_fetching.py +232 -0
- django_widget_hub-0.1.0/tests/test_front.py +76 -0
- django_widget_hub-0.1.0/tests/test_models.py +63 -0
- django_widget_hub-0.1.0/tests/test_params_declaration.py +39 -0
- django_widget_hub-0.1.0/tests/test_params_validation.py +177 -0
- django_widget_hub-0.1.0/tests/test_registry.py +98 -0
- django_widget_hub-0.1.0/tests/test_security.py +403 -0
- django_widget_hub-0.1.0/tests/test_services.py +171 -0
- django_widget_hub-0.1.0/tests/test_single_flight.py +122 -0
- django_widget_hub-0.1.0/tests/urls.py +42 -0
|
@@ -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.
|