mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-04 14:25:08 +00:00
refactor(web): split api_v3/plugins.py by area (#658)
* refactor(web): split api_v3/plugins.py by area
web_interface/blueprints/api_v3/plugins.py (3,285 lines) becomes:
- plugins.py: installed list, enable/disable, plugin actions
- plugin_store.py: install, update, uninstall, store, saved repositories
- plugin_config.py: config get/save, schema, reset
- plugin_assets.py: asset uploads and plugin static files
- plugin_health.py: health, metrics, limits
- plugin_operations.py: operation history, state reconciliation
- plugin_calendar.py: calendar credentials and auth
Pure move: all 44 functions and 38 route decorators are byte-identical
(checked with ast), URLs and endpoint names are unchanged (url-map test).
Each module imports only what it uses. Tests and config.py that reached
into plugins.py for moved names now import from the new module.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(web): keep exception text out of calendar responses; annotate moved code
The split made scanners report existing findings in the moved code as new:
- CodeQL: the calendar auth and calendar-list routes returned exception
text (redacted, but still derived from the exception). Both now log the
exception and return a fixed message pointing at the log.
- MD5 in the asset upload only makes a filename unique: usedforsecurity=False.
- pickle reads/writes the calendar plugin's own OAuth token (as before):
annotated. Token-status labels and a log line naming the secrets path are
false positives: annotated with the repo's nosec/nosemgrep convention.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(web): name uploaded assets with SHA-256 instead of MD5
The hash only makes an uploaded image's filename unique. Codacy flags MD5
even with usedforsecurity=False, and SHA-256 does the job as well; existing
files keep their names.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(web): keep the redacted exception detail in calendar errors
Reverts the calendar part of 5e695b7c. The project's policy
(test_no_api_v3_handler_discards_its_exception) is that an API error
carries the redacted exception detail -- describe_exception runs it
through the credential redactor -- so a failure is diagnosable from the web
UI. Dropping it for CodeQL broke that; CodeQL can't see the redaction, so
its two alerts here are false positives, like the existing ones on main.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -158,8 +158,13 @@ everything else through `_reinstall_with_rollback()`.
|
||||
- **API.** [`blueprints/api_v3/`](../web_interface/blueprints/api_v3/) is one
|
||||
blueprint at `/api/v3`, split by area: `backup.py`, `config.py`,
|
||||
`display.py`, `fonts.py`, `misc.py` (health, logs, errors, cache, sync),
|
||||
`plugins.py`, `starlark.py`, `system.py` (service actions, updates, git),
|
||||
`wifi.py`. `__init__.py` defines the blueprint and shared helpers and
|
||||
`starlark.py`, `system.py` (service actions, updates, git), `wifi.py`, and
|
||||
the plugin routes: `plugins.py` (installed list, enable/disable, plugin
|
||||
actions), `plugin_store.py` (install, update, uninstall, store),
|
||||
`plugin_config.py` (config, schema, reset), `plugin_assets.py` (uploads,
|
||||
plugin static files), `plugin_health.py` (health, metrics, limits),
|
||||
`plugin_operations.py` (operation history, state reconciliation) and
|
||||
`plugin_calendar.py`. `__init__.py` defines the blueprint and shared helpers and
|
||||
imports the modules so their routes register. Endpoints are listed in
|
||||
[REST_API_REFERENCE.md](REST_API_REFERENCE.md).
|
||||
- **Front end.** HTMX loads each tab's partial on first open
|
||||
|
||||
@@ -203,7 +203,7 @@ Forms are rendered on the server, not generated in the browser:
|
||||
from the schema (widgets named by `x-widget` are rendered by the scripts in
|
||||
`web_interface/static/v3/js/widgets/`)
|
||||
4. **Save Configuration** posts the form to `/api/v3/plugins/config`
|
||||
(`web_interface/blueprints/api_v3/plugins.py`), which validates it against
|
||||
(`web_interface/blueprints/api_v3/plugin_config.py`), which validates it against
|
||||
the schema, writes `config.json` (secret fields go to
|
||||
`config_secrets.json`) and shows a notification
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
│ • masks x-secret fields │
|
||||
│ • renders partials/plugin_config.html (render_field macros) │
|
||||
│ │
|
||||
│ api_v3 blueprint (blueprints/api_v3/plugins.py) │
|
||||
│ api_v3 blueprint (blueprints/api_v3/plugin_config.py) │
|
||||
│ save_plugin_config() POST /api/v3/plugins/config │
|
||||
│ get_plugin_config() GET /api/v3/plugins/config │
|
||||
│ get_plugin_schema() GET /api/v3/plugins/schema │
|
||||
@@ -91,7 +91,7 @@ validatePluginConfigForm() (client-side checks)
|
||||
POST /api/v3/plugins/config?plugin_id=<id> (form data, all fields of the form)
|
||||
│
|
||||
▼
|
||||
save_plugin_config() (api_v3/plugins.py)
|
||||
save_plugin_config() (api_v3/plugin_config.py)
|
||||
├─→ Start from the stored config.json[<id>]
|
||||
├─→ Apply form fields: dotted names → nested keys, "[]" checkbox
|
||||
│ groups → lists, values coerced to the schema's types
|
||||
@@ -175,7 +175,7 @@ Implement `on_config_change(new_config)` in the plugin (see
|
||||
|---------|------|
|
||||
| Tab partial loader | `web_interface/blueprints/pages_v3.py` (`_load_plugin_config_partial`) |
|
||||
| Form template and field macros | `web_interface/templates/v3/partials/plugin_config.html` |
|
||||
| Save / get / schema / reset handlers | `web_interface/blueprints/api_v3/plugins.py` |
|
||||
| Save / get / schema / reset handlers | `web_interface/blueprints/api_v3/plugin_config.py` |
|
||||
| Schema loading, defaults, validation | `src/plugin_system/schema_manager.py` |
|
||||
| Secret masking and splitting | `src/web_interface/secret_helpers.py` |
|
||||
| Widgets | `web_interface/static/v3/js/widgets/` |
|
||||
|
||||
@@ -40,8 +40,9 @@ the entry below says so.
|
||||
|
||||
> The API blueprint is the `api_v3` package in
|
||||
> `web_interface/blueprints/api_v3/` (one module per area: `config.py`,
|
||||
> `display.py`, `plugins.py`, `system.py`, `backup.py`, `fonts.py`,
|
||||
> `misc.py`, `wifi.py`, `starlark.py`). `web_interface/app.py` registers it
|
||||
> `display.py`, `system.py`, `backup.py`, `fonts.py`, `misc.py`, `wifi.py`,
|
||||
> `starlark.py`, and `plugins.py` plus the `plugin_*.py` modules for the
|
||||
> plugin routes). `web_interface/app.py` registers it
|
||||
> at `/api/v3` (`app.register_blueprint(api_v3, url_prefix='/api/v3')`).
|
||||
> The three SSE endpoints (`/api/v3/stream/*`) are defined directly on the
|
||||
> Flask app in `app.py` (`stream_stats`, `stream_display`, `stream_logs`).
|
||||
|
||||
Reference in New Issue
Block a user