fix(plugins): Resolve plugin action button errors and config save permission issues (#162)

* fix(plugins): Resolve plugin ID determination error in action buttons

- Fix server-side template parameter order for executePluginAction
- Add data-plugin-id attributes to action buttons in all templates
- Enhance executePluginAction with comprehensive fallback logic
- Support retrieving pluginId from DOM, Alpine context, and config state
- Fixes 'Unable to determine plugin ID' error for Spotify/YouTube auth

* fix(plugins): Add missing button IDs and status divs in server-side action template

- Add action-{id}-{index} IDs to action buttons
- Add action-status-{id}-{index} status divs for each action
- Match client-side template structure for consistency
- Fixes 'Action elements not found' error

* fix(api): Fix indentation error in execute_plugin_action function

- Fix incorrect else block indentation that caused 500 errors
- Correct indentation for OAuth flow and simple script execution paths
- Resolves syntax error preventing plugin actions from executing

* fix(api): Improve error handling for plugin actions and config saves

- Add better JSON parsing error handling with request details
- Add detailed permission error messages for secrets file saves
- Include file path and permission status in error responses
- Helps diagnose 400 errors on action execution and 500 errors on config saves

* fix(api): Add detailed permission error handling for secrets config saves

- Add PermissionError-specific handling with permission checks
- Include directory and file permission status in error logs
- Provide more helpful error messages with file paths
- Helps diagnose permission issues when saving config_secrets.json

* fix(config): Add permission check and actionable error message for config saves

- Check file writability before attempting write
- Show file owner and current permissions in error message
- Provide exact command to fix permissions (chown + chmod)
- Helps diagnose and resolve permission issues with config_secrets.json

* fix(config): Preserve detailed permission error messages

- Handle PermissionError separately to preserve detailed error messages
- Ensure actionable permission fix commands are included in error response
- Prevents detailed error messages from being lost in exception chain

* fix(config): Remove overly strict pre-write permission check

- Remove pre-write file existence/writability check that was blocking valid writes
- Let actual file write operation determine success/failure
- Provide detailed error messages only when write actually fails
- Fixes regression where config_secrets.json saves were blocked unnecessarily

* fix(config): Use atomic writes for config_secrets.json to handle permission issues

- Write to temp file first, then atomically move to final location
- Works even when existing file isn't writable (as long as directory is writable)
- Matches pattern used elsewhere in codebase (disk_cache, atomic_manager)
- Fixes permission errors when saving secrets configuration

* chore: Update music plugin submodule to include live_priority fix

* fix(plugins): Improve plugin ID determination in dynamic button generation

- Update generateFormFromSchema to pass currentPluginConfig?.pluginId and add data attributes
- Update generateSimpleConfigForm to pass currentPluginConfig?.pluginId and add data attributes
- Scope fallback 6 DOM lookup to button context instead of document-wide search
- Ensures correct plugin tab selection when multiple plugins are present
- Maintains existing try/catch error handling and logging

---------

Co-authored-by: Chuck <chuck@example.com>
This commit is contained in:
Chuck
2025-12-29 22:17:11 -05:00
committed by GitHub
parent 1815a5b791
commit 24c34c5a40
6 changed files with 460 additions and 178 deletions

View File

@@ -283,14 +283,64 @@
{% if web_ui_actions %}
<div class="mt-6 pt-4 border-t border-gray-200">
<h3 class="text-md font-medium text-gray-900 mb-3">Plugin Actions</h3>
<div class="flex flex-wrap gap-2">
{% if web_ui_actions[0].section_description %}
<p class="text-sm text-gray-600 mb-4">{{ web_ui_actions[0].section_description }}</p>
{% endif %}
<div class="space-y-3">
{% for action in web_ui_actions %}
<button type="button"
onclick="executePluginAction('{{ plugin.id }}', '{{ action.id }}')"
class="px-4 py-2 text-sm bg-indigo-600 hover:bg-indigo-700 text-white rounded-md flex items-center gap-2 transition-colors">
{% if action.icon %}<i class="{{ action.icon }}"></i>{% endif %}
{{ action.label or action.id }}
</button>
{% set action_id = "action-" ~ action.id ~ "-" ~ loop.index0 %}
{% set status_id = "action-status-" ~ action.id ~ "-" ~ loop.index0 %}
{% set bg_color = action.color or 'blue' %}
{% if bg_color == 'green' %}
{% set bg_class = 'bg-green-50' %}
{% set border_class = 'border-green-200' %}
{% set text_class = 'text-green-900' %}
{% set text_light_class = 'text-green-700' %}
{% set btn_class = 'bg-green-600 hover:bg-green-700' %}
{% elif bg_color == 'red' %}
{% set bg_class = 'bg-red-50' %}
{% set border_class = 'border-red-200' %}
{% set text_class = 'text-red-900' %}
{% set text_light_class = 'text-red-700' %}
{% set btn_class = 'bg-red-600 hover:bg-red-700' %}
{% elif bg_color == 'yellow' %}
{% set bg_class = 'bg-yellow-50' %}
{% set border_class = 'border-yellow-200' %}
{% set text_class = 'text-yellow-900' %}
{% set text_light_class = 'text-yellow-700' %}
{% set btn_class = 'bg-yellow-600 hover:bg-yellow-700' %}
{% elif bg_color == 'purple' %}
{% set bg_class = 'bg-purple-50' %}
{% set border_class = 'border-purple-200' %}
{% set text_class = 'text-purple-900' %}
{% set text_light_class = 'text-purple-700' %}
{% set btn_class = 'bg-purple-600 hover:bg-purple-700' %}
{% else %}
{% set bg_class = 'bg-blue-50' %}
{% set border_class = 'border-blue-200' %}
{% set text_class = 'text-blue-900' %}
{% set text_light_class = 'text-blue-700' %}
{% set btn_class = 'bg-blue-600 hover:bg-blue-700' %}
{% endif %}
<div class="{{ bg_class }} border {{ border_class }} rounded-lg p-4">
<div class="flex items-center justify-between">
<div class="flex-1">
<h4 class="font-medium {{ text_class }} mb-1">
{% if action.icon %}<i class="{{ action.icon }} mr-2"></i>{% endif %}{{ action.title or action.id }}
</h4>
<p class="text-sm {{ text_light_class }}">{{ action.description or '' }}</p>
</div>
<button type="button"
id="{{ action_id }}"
onclick="executePluginAction('{{ action.id }}', {{ loop.index0 }}, '{{ plugin.id }}')"
data-plugin-id="{{ plugin.id }}"
data-action-id="{{ action.id }}"
class="btn {{ btn_class }} text-white px-4 py-2 rounded-md whitespace-nowrap">
{% if action.icon %}<i class="{{ action.icon }} mr-2"></i>{% endif %}{{ action.button_text or action.title or action.id }}
</button>
</div>
<div id="{{ status_id }}" class="mt-3 hidden"></div>
</div>
{% endfor %}
</div>
</div>