mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-04 14:25:08 +00:00
refactor(web): read plugins through a PluginCatalog; only the display runs them (#688)
The web process built its own PluginManager and loaded plugins into itself: store installs and updates loaded or reloaded a web-side copy, and config saves and enable/disable called on_config_change, on_enable and on_disable on it. None of that reached the panel, and /plugins/installed reported runtime state from those copies. - Add PluginCatalog (src/plugin_system/plugin_catalog.py): manifests, directories, display modes, installed version, schema and config reads, with no way to run a plugin. app.py and both blueprints use it; the plugin_manager blueprint attribute is gone. - Remove every lifecycle call from the web routes. Config changes already reach the display through ConfigService (on_config_change) and the enabled-set reconcile. - Health and metrics readers move to api_v3.health_tracker / resource_monitor. /plugins/installed reports loaded/state/error_info as null (the display does not publish them) and enabled by the display's rule. - Store install, update and uninstall answer restart_required when the running display will not pick the change up by itself (display_restart_required). The restart banner follows the flag via window.noteRestartRequired instead of the /config/main URL heuristic; /config/main now sends restart_required: true. - The one remaining in-process import of plugin code (Starlark helper modules, oauth_flow action scripts) goes through _import_plugin_code_in_web_process() until a web-entry contract. - /plugins/installed reports vegas_participation (from #682) from the user's setting or the manifest, with vegas_participation_source; when only the plugin's code decides it, null with source 'runtime', since the web process no longer has plugin instances to ask. - Check & Update All keeps its restart flags when the final list refresh fails, and asks for a restart when an enabled plugin's first request got no answer and the re-sent one found it up to date. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,8 @@ so their endpoint names do not depend on which module they live in.
|
||||
"""
|
||||
from web_interface.blueprints.api_v3 import (
|
||||
ErrorCode, OperationType, Path, _do_transactional_uninstall,
|
||||
_non_plugin_id_error, _get_plugin_version, _plugin_directory, api_v3,
|
||||
_non_plugin_id_error, _get_plugin_version, _plugin_directory,
|
||||
_plugin_enabled_in_config, _store_restart_fields, api_v3,
|
||||
datetime, error_response, exception_error_response, json, jsonify, logger,
|
||||
request, success_response, validate_request_json,
|
||||
)
|
||||
@@ -228,11 +229,12 @@ def update_plugin():
|
||||
if api_v3.schema_manager:
|
||||
api_v3.schema_manager.invalidate_cache(plugin_id)
|
||||
|
||||
# Rediscover plugins
|
||||
if api_v3.plugin_manager:
|
||||
api_v3.plugin_manager.discover_plugins()
|
||||
if plugin_id in api_v3.plugin_manager.plugins:
|
||||
api_v3.plugin_manager.reload_plugin(plugin_id)
|
||||
# Rediscover plugins. The web process runs no plugin code, so
|
||||
# there is nothing here to reload: the display keeps running the
|
||||
# version it loaded until it restarts, which restart_required
|
||||
# below asks for.
|
||||
if api_v3.plugin_catalog:
|
||||
api_v3.plugin_catalog.discover_plugins()
|
||||
|
||||
# Update state and history
|
||||
if api_v3.plugin_state_manager:
|
||||
@@ -261,7 +263,10 @@ def update_plugin():
|
||||
'commit': updated_commit,
|
||||
'update_status': update_status
|
||||
},
|
||||
message=message
|
||||
message=message,
|
||||
extra=_store_restart_fields(
|
||||
'update', _plugin_enabled_in_config(plugin_id),
|
||||
changed=update_status == 'updated'),
|
||||
)
|
||||
else:
|
||||
refusal = _compatibility_refusal(plugin_id)
|
||||
@@ -341,6 +346,8 @@ def uninstall_plugin():
|
||||
if api_v3.operation_queue:
|
||||
def uninstall_callback(operation):
|
||||
"""Callback to execute plugin uninstallation via transactional helper."""
|
||||
# Read before the uninstall removes the config section.
|
||||
was_enabled = _plugin_enabled_in_config(plugin_id)
|
||||
success, error_msg = _do_transactional_uninstall(plugin_id, preserve_config)
|
||||
if not success:
|
||||
if api_v3.operation_history:
|
||||
@@ -358,7 +365,9 @@ def uninstall_plugin():
|
||||
status="success",
|
||||
details={"preserve_config": preserve_config}
|
||||
)
|
||||
return {'success': True, 'message': 'Plugin uninstalled successfully'}
|
||||
return {'success': True, 'message': 'Plugin uninstalled successfully',
|
||||
**_store_restart_fields('uninstall', was_enabled,
|
||||
preserve_config=preserve_config)}
|
||||
|
||||
# Enqueue operation
|
||||
operation_id = api_v3.operation_queue.enqueue_operation(
|
||||
@@ -373,6 +382,7 @@ def uninstall_plugin():
|
||||
)
|
||||
else:
|
||||
# Direct (non-queued) transactional uninstall
|
||||
was_enabled = _plugin_enabled_in_config(plugin_id)
|
||||
success, error_msg = _do_transactional_uninstall(plugin_id, preserve_config)
|
||||
|
||||
if success:
|
||||
@@ -383,7 +393,10 @@ def uninstall_plugin():
|
||||
status="success",
|
||||
details={"preserve_config": preserve_config}
|
||||
)
|
||||
return success_response(message='Plugin uninstalled successfully')
|
||||
return success_response(
|
||||
message='Plugin uninstalled successfully',
|
||||
extra=_store_restart_fields('uninstall', was_enabled,
|
||||
preserve_config=preserve_config))
|
||||
else:
|
||||
if api_v3.operation_history:
|
||||
api_v3.operation_history.record_operation(
|
||||
@@ -448,10 +461,11 @@ def install_plugin():
|
||||
if api_v3.schema_manager:
|
||||
api_v3.schema_manager.invalidate_cache(plugin_id)
|
||||
|
||||
# Discover and load the new plugin
|
||||
if api_v3.plugin_manager:
|
||||
api_v3.plugin_manager.discover_plugins()
|
||||
api_v3.plugin_manager.load_plugin(plugin_id)
|
||||
# List the new plugin. The display loads it, from disk, when
|
||||
# it is enabled; see restart_required below for one that
|
||||
# already is.
|
||||
if api_v3.plugin_catalog:
|
||||
api_v3.plugin_catalog.discover_plugins()
|
||||
|
||||
# Update state manager
|
||||
if api_v3.plugin_state_manager:
|
||||
@@ -468,7 +482,9 @@ def install_plugin():
|
||||
)
|
||||
|
||||
branch_msg = f" (branch: {branch})" if branch else ""
|
||||
return {'success': True, 'message': f'Plugin {plugin_id} installed successfully{branch_msg}'}
|
||||
return {'success': True,
|
||||
'message': f'Plugin {plugin_id} installed successfully{branch_msg}',
|
||||
**_store_restart_fields('install', _plugin_enabled_in_config(plugin_id))}
|
||||
else:
|
||||
error_msg = f'Failed to install plugin {plugin_id}'
|
||||
if branch:
|
||||
@@ -511,9 +527,8 @@ def install_plugin():
|
||||
if success:
|
||||
if api_v3.schema_manager:
|
||||
api_v3.schema_manager.invalidate_cache(plugin_id)
|
||||
if api_v3.plugin_manager:
|
||||
api_v3.plugin_manager.discover_plugins()
|
||||
api_v3.plugin_manager.load_plugin(plugin_id)
|
||||
if api_v3.plugin_catalog:
|
||||
api_v3.plugin_catalog.discover_plugins()
|
||||
if api_v3.plugin_state_manager:
|
||||
api_v3.plugin_state_manager.set_plugin_installed(plugin_id)
|
||||
if api_v3.operation_history:
|
||||
@@ -526,7 +541,9 @@ def install_plugin():
|
||||
)
|
||||
|
||||
branch_msg = f" (branch: {branch})" if branch else ""
|
||||
return success_response(message=f'Plugin installed successfully{branch_msg}')
|
||||
return success_response(
|
||||
message=f'Plugin installed successfully{branch_msg}',
|
||||
extra=_store_restart_fields('install', _plugin_enabled_in_config(plugin_id)))
|
||||
else:
|
||||
error_msg = f'Failed to install plugin {plugin_id}'
|
||||
if branch:
|
||||
@@ -587,10 +604,9 @@ def install_plugin_from_url():
|
||||
if api_v3.schema_manager and installed_plugin_id:
|
||||
api_v3.schema_manager.invalidate_cache(installed_plugin_id)
|
||||
|
||||
# Discover and load the new plugin
|
||||
if api_v3.plugin_manager and installed_plugin_id:
|
||||
api_v3.plugin_manager.discover_plugins()
|
||||
api_v3.plugin_manager.load_plugin(installed_plugin_id)
|
||||
# List the new plugin; the display loads it when it is enabled.
|
||||
if api_v3.plugin_catalog and installed_plugin_id:
|
||||
api_v3.plugin_catalog.discover_plugins()
|
||||
|
||||
branch_msg = f" (branch: {result.get('branch', branch)})" if (result.get('branch') or branch) else ""
|
||||
response_data = {
|
||||
@@ -601,6 +617,9 @@ def install_plugin_from_url():
|
||||
}
|
||||
if result.get('branch'):
|
||||
response_data['branch'] = result.get('branch')
|
||||
if installed_plugin_id:
|
||||
response_data.update(_store_restart_fields(
|
||||
'install', _plugin_enabled_in_config(installed_plugin_id)))
|
||||
return jsonify(response_data)
|
||||
else:
|
||||
return jsonify({
|
||||
|
||||
Reference in New Issue
Block a user