mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-04 22:35: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:
@@ -452,40 +452,9 @@ def save_plugin_config():
|
||||
status_code=500
|
||||
)
|
||||
|
||||
# If the plugin is loaded, notify it of the config change with merged config
|
||||
try:
|
||||
if api_v3.plugin_manager:
|
||||
plugin_instance = api_v3.plugin_manager.get_plugin(plugin_id)
|
||||
if plugin_instance:
|
||||
# Reload merged config (includes secrets) and pass the plugin-specific section
|
||||
merged_config = api_v3.config_manager.load_config()
|
||||
plugin_full_config = _pkg._prepared_plugin_config(
|
||||
plugin_id, merged_config.get(plugin_id, {}))
|
||||
if hasattr(plugin_instance, 'on_config_change'):
|
||||
plugin_instance.on_config_change(plugin_full_config)
|
||||
|
||||
# Update plugin state manager and call lifecycle methods based on enabled state
|
||||
# This ensures the plugin state is synchronized with the config
|
||||
enabled = plugin_full_config.get('enabled', plugin_instance.enabled)
|
||||
|
||||
# Update state manager if available
|
||||
if api_v3.plugin_state_manager:
|
||||
api_v3.plugin_state_manager.set_plugin_enabled(plugin_id, enabled)
|
||||
|
||||
# Call lifecycle methods to ensure plugin state matches config
|
||||
try:
|
||||
if enabled:
|
||||
if hasattr(plugin_instance, 'on_enable'):
|
||||
plugin_instance.on_enable()
|
||||
else:
|
||||
if hasattr(plugin_instance, 'on_disable'):
|
||||
plugin_instance.on_disable()
|
||||
except Exception as lifecycle_error:
|
||||
# Log the error but don't fail the save - config is already saved
|
||||
logger.warning("Lifecycle method error for %s: %s", plugin_id, lifecycle_error, exc_info=True)
|
||||
except Exception as hook_err:
|
||||
# Do not fail the save if hook fails; just log
|
||||
logger.warning("on_config_change failed: %s", hook_err)
|
||||
# The running plugin hears about this from the display process: its
|
||||
# config watcher calls on_config_change with the prepared section, and
|
||||
# loads or unloads the plugin if "enabled" changed.
|
||||
|
||||
secret_count = len(secrets_config)
|
||||
message = f'Plugin {plugin_id} configuration saved successfully'
|
||||
@@ -543,12 +512,7 @@ def _prepare_plugin_config_for_save(plugin_id, plugin_config, schema, schema_mgr
|
||||
current_config = api_v3.config_manager.load_config()
|
||||
if plugin_id in current_config and 'enabled' in current_config[plugin_id]:
|
||||
plugin_config['enabled'] = current_config[plugin_id]['enabled']
|
||||
elif api_v3.plugin_manager:
|
||||
# Fallback to plugin instance if config doesn't have it
|
||||
plugin_instance = api_v3.plugin_manager.get_plugin(plugin_id)
|
||||
if plugin_instance:
|
||||
plugin_config['enabled'] = plugin_instance.enabled
|
||||
# Final fallback: default to True if plugin is loaded (matches BasePlugin default)
|
||||
# Fallback: default to True (matches BasePlugin default)
|
||||
if 'enabled' not in plugin_config:
|
||||
plugin_config['enabled'] = True
|
||||
except Exception as e:
|
||||
@@ -977,18 +941,8 @@ def reset_plugin_config():
|
||||
if default_secrets or not preserve_secrets:
|
||||
api_v3.config_manager.save_raw_file_content('secrets', current_secrets)
|
||||
|
||||
# Notify plugin of config change if loaded
|
||||
try:
|
||||
if api_v3.plugin_manager:
|
||||
plugin_instance = api_v3.plugin_manager.get_plugin(plugin_id)
|
||||
if plugin_instance:
|
||||
merged_config = api_v3.config_manager.load_config()
|
||||
plugin_full_config = _pkg._prepared_plugin_config(
|
||||
plugin_id, merged_config.get(plugin_id, {}))
|
||||
if hasattr(plugin_instance, 'on_config_change'):
|
||||
plugin_instance.on_config_change(plugin_full_config)
|
||||
except Exception as hook_err:
|
||||
logger.warning("on_config_change failed: %s", hook_err)
|
||||
# The display's config watcher passes the reset config to the running
|
||||
# plugin (on_config_change); nothing to notify in this process.
|
||||
|
||||
return jsonify({
|
||||
'status': 'success',
|
||||
|
||||
Reference in New Issue
Block a user