mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-04 14:25:08 +00:00
fix(web): widget cache-busting, dead frontend code, and dependency pins (#656)
- Plugin-supplied widgets load as /static/plugin-widgets/...js?v=<plugin version>, so an update isn't hidden behind the year-long immutable cache. - Fire-and-forget loadInstalledPlugins() calls catch the rejection it has already reported, so the global handler no longer adds a second toast. - Timezone picker renders again when the General partial is re-injected. - Remove dead code: executePluginAction's six plugin-id fallbacks and [DEBUG] logging, window.currentPluginConfig and every read of it, the file-upload JSON delete branch, unused PluginAPI / PluginInstallManager / PluginStateManager helpers, loadPluginWidgetsFromManifest, the stale install_manager.js and LEDVisibility fallbacks, error_handler.js's global escapeHtml, 13 unused CSS rules, and stale comments/no-op returns. - pytz < 2027, psutil < 7 in requirements-test.txt, pytest-cov < 8. - Pin anthropics/claude-code-action to the commit v1 resolves to. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -31,8 +31,6 @@ const RequestThrottler = {
|
||||
|
||||
debugLog('[RequestThrottler] Creating new request for:', key);
|
||||
|
||||
// Create throttled request with abort support
|
||||
let abortController = null;
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
@@ -54,22 +52,8 @@ const RequestThrottler = {
|
||||
this.pending.delete(key);
|
||||
}
|
||||
}, delay);
|
||||
|
||||
// Store abort controller if available
|
||||
if (fn.abort && typeof fn.abort === 'function') {
|
||||
abortController = fn.abort;
|
||||
}
|
||||
});
|
||||
|
||||
// Add abort method if available
|
||||
if (abortController) {
|
||||
promise.abort = () => {
|
||||
debugLog('[RequestThrottler] Aborting request for:', key);
|
||||
abortController.abort();
|
||||
this.pending.delete(key);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
this.pending.set(key, promise);
|
||||
return promise;
|
||||
},
|
||||
@@ -83,18 +67,6 @@ const RequestThrottler = {
|
||||
} else {
|
||||
this.cache.clear();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get statistics about pending requests and cache
|
||||
*/
|
||||
getStats() {
|
||||
return {
|
||||
pendingCount: this.pending.size,
|
||||
cacheSize: this.cache.size,
|
||||
pendingKeys: Array.from(this.pending.keys()),
|
||||
cacheKeys: Array.from(this.cache.keys())
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -225,18 +197,6 @@ const PluginAPI = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Batch multiple requests together for better performance
|
||||
*
|
||||
* @param {Array} requests - Array of {endpoint, method, data} objects
|
||||
* @returns {Promise<Array>} Array of response data
|
||||
*/
|
||||
async batch(requests) {
|
||||
return Promise.all(requests.map(req =>
|
||||
this.request(req.endpoint, req.method || 'GET', req.data || null, false)
|
||||
));
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear API cache
|
||||
*/
|
||||
@@ -308,34 +268,6 @@ const PluginAPI = {
|
||||
return await this.request(`/plugins/config/reset?plugin_id=${encodeURIComponent(pluginId)}`, 'POST');
|
||||
},
|
||||
|
||||
/**
|
||||
* Get plugin schema.
|
||||
*
|
||||
* @param {string} pluginId - Plugin identifier
|
||||
* @returns {Promise<Object>} Plugin schema
|
||||
*/
|
||||
async getPluginSchema(pluginId) {
|
||||
const response = await this.request(`/plugins/schema?plugin_id=${encodeURIComponent(pluginId)}`);
|
||||
return response.data?.schema || null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Install plugin from store.
|
||||
*
|
||||
* @param {string} pluginId - Plugin identifier
|
||||
* @param {string} branch - Optional branch name to install from
|
||||
* @returns {Promise<Object>} Response data
|
||||
*/
|
||||
async installPlugin(pluginId, branch = null) {
|
||||
const data = {
|
||||
plugin_id: pluginId
|
||||
};
|
||||
if (branch) {
|
||||
data.branch = branch;
|
||||
}
|
||||
return await this.request('/plugins/install', 'POST', data);
|
||||
},
|
||||
|
||||
/**
|
||||
* Update plugin.
|
||||
*
|
||||
@@ -348,28 +280,6 @@ const PluginAPI = {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Uninstall plugin.
|
||||
*
|
||||
* @param {string} pluginId - Plugin identifier
|
||||
* @returns {Promise<Object>} Response data
|
||||
*/
|
||||
async uninstallPlugin(pluginId) {
|
||||
return await this.request('/plugins/uninstall', 'POST', {
|
||||
plugin_id: pluginId
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Get plugin store.
|
||||
*
|
||||
* @returns {Promise<Array>} List of available plugins
|
||||
*/
|
||||
async getPluginStore() {
|
||||
const response = await this.request('/plugins/store/list');
|
||||
return response.data || [];
|
||||
},
|
||||
|
||||
/**
|
||||
* Get plugin health.
|
||||
*
|
||||
|
||||
@@ -1,83 +1,11 @@
|
||||
/**
|
||||
* Plugin installation and update management.
|
||||
*
|
||||
* Handles plugin installation, updates, and uninstallation operations.
|
||||
* Handles Check & Update All: which installed plugins it sends, retrying
|
||||
* across a web-service restart, and summarising the results.
|
||||
*/
|
||||
|
||||
const PluginInstallManager = {
|
||||
/**
|
||||
* Install a plugin.
|
||||
*
|
||||
* @param {string} pluginId - Plugin identifier
|
||||
* @param {string} branch - Optional branch name to install from
|
||||
* @returns {Promise<Object>} Installation result
|
||||
*/
|
||||
async install(pluginId, branch = null) {
|
||||
try {
|
||||
const result = await window.PluginAPI.installPlugin(pluginId, branch);
|
||||
|
||||
// Refresh installed plugins list
|
||||
if (window.PluginStateManager) {
|
||||
await window.PluginStateManager.loadInstalledPlugins();
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
if (window.errorHandler) {
|
||||
window.errorHandler.displayError(error, `Failed to install plugin ${pluginId}`);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Update a plugin.
|
||||
*
|
||||
* @param {string} pluginId - Plugin identifier
|
||||
* @returns {Promise<Object>} Update result
|
||||
*/
|
||||
async update(pluginId) {
|
||||
try {
|
||||
const result = await window.PluginAPI.updatePlugin(pluginId);
|
||||
|
||||
// Refresh installed plugins list
|
||||
if (window.PluginStateManager) {
|
||||
await window.PluginStateManager.loadInstalledPlugins();
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
if (window.errorHandler) {
|
||||
window.errorHandler.displayError(error, `Failed to update plugin ${pluginId}`);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Uninstall a plugin.
|
||||
*
|
||||
* @param {string} pluginId - Plugin identifier
|
||||
* @returns {Promise<Object>} Uninstall result
|
||||
*/
|
||||
async uninstall(pluginId) {
|
||||
try {
|
||||
const result = await window.PluginAPI.uninstallPlugin(pluginId);
|
||||
|
||||
// Refresh installed plugins list
|
||||
if (window.PluginStateManager) {
|
||||
await window.PluginStateManager.loadInstalledPlugins();
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
if (window.errorHandler) {
|
||||
window.errorHandler.displayError(error, `Failed to uninstall plugin ${pluginId}`);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Whether POST /plugins/update can update this installed-list entry.
|
||||
*
|
||||
|
||||
@@ -9,12 +9,7 @@ const PluginStateManager = {
|
||||
* Installed plugins state.
|
||||
*/
|
||||
installedPlugins: [],
|
||||
|
||||
/**
|
||||
* Current plugin configuration state.
|
||||
*/
|
||||
currentConfig: null,
|
||||
|
||||
|
||||
/**
|
||||
* Load installed plugins.
|
||||
*
|
||||
@@ -32,83 +27,6 @@ const PluginStateManager = {
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get plugin by ID.
|
||||
*
|
||||
* @param {string} pluginId - Plugin identifier
|
||||
* @returns {Object|null} Plugin object or null
|
||||
*/
|
||||
getPlugin(pluginId) {
|
||||
return this.installedPlugins.find(p => p.id === pluginId) || null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Update plugin state.
|
||||
*
|
||||
* @param {string} pluginId - Plugin identifier
|
||||
* @param {Object} updates - State updates
|
||||
*/
|
||||
updatePlugin(pluginId, updates) {
|
||||
const plugin = this.getPlugin(pluginId);
|
||||
if (plugin) {
|
||||
Object.assign(plugin, updates);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Set plugin enabled state.
|
||||
*
|
||||
* @param {string} pluginId - Plugin identifier
|
||||
* @param {boolean} enabled - Whether plugin is enabled
|
||||
*/
|
||||
setPluginEnabled(pluginId, enabled) {
|
||||
this.updatePlugin(pluginId, { enabled });
|
||||
},
|
||||
|
||||
/**
|
||||
* Get current plugin configuration.
|
||||
*
|
||||
* @param {string} pluginId - Plugin identifier
|
||||
* @returns {Promise<Object>} Plugin configuration
|
||||
*/
|
||||
async getPluginConfig(pluginId) {
|
||||
try {
|
||||
const config = await window.PluginAPI.getPluginConfig(pluginId);
|
||||
this.currentConfig = { pluginId, config };
|
||||
return config;
|
||||
} catch (error) {
|
||||
if (window.errorHandler) {
|
||||
window.errorHandler.displayError(error, `Failed to load config for ${pluginId}`);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Save plugin configuration.
|
||||
*
|
||||
* @param {string} pluginId - Plugin identifier
|
||||
* @param {Object} config - Configuration data
|
||||
* @returns {Promise<Object>} Save result
|
||||
*/
|
||||
async savePluginConfig(pluginId, config) {
|
||||
try {
|
||||
const result = await window.PluginAPI.savePluginConfig(pluginId, config);
|
||||
|
||||
// Update local state
|
||||
if (this.currentConfig && this.currentConfig.pluginId === pluginId) {
|
||||
this.currentConfig.config = config;
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
if (window.errorHandler) {
|
||||
window.errorHandler.displayError(error, `Failed to save config for ${pluginId}`);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user