diff --git a/web_interface/app.py b/web_interface/app.py index 42568ce0..cf2a58a3 100644 --- a/web_interface/app.py +++ b/web_interface/app.py @@ -726,4 +726,6 @@ def check_health_monitor(): _threading.Thread(target=_run_startup_reconciliation, daemon=True).start() if __name__ == '__main__': - app.run(host='0.0.0.0', port=5000, debug=True) + # threaded=True is Flask's default since 1.0 but stated explicitly so that + # long-lived /api/v3/stream/* SSE connections don't starve other requests. + app.run(host='0.0.0.0', port=5000, debug=True, threaded=True) diff --git a/web_interface/start.py b/web_interface/start.py index c2562954..885bd136 100644 --- a/web_interface/start.py +++ b/web_interface/start.py @@ -120,7 +120,11 @@ def main(): # Run the web server with error handling for client disconnections try: - app.run(host='0.0.0.0', port=5000, debug=False) + # threaded=True is Flask's default since 1.0, but set it explicitly + # so it's self-documenting: the two /api/v3/stream/* SSE endpoints + # hold long-lived connections and would starve other requests under + # a single-threaded server. + app.run(host='0.0.0.0', port=5000, debug=False, threaded=True) except (OSError, BrokenPipeError) as e: # Suppress non-critical socket errors (client disconnections) if isinstance(e, OSError) and e.errno in (113, 32, 104): # No route to host, Broken pipe, Connection reset diff --git a/web_interface/static/v3/plugins_manager.js b/web_interface/static/v3/plugins_manager.js index e75aee32..0f15b33c 100644 --- a/web_interface/static/v3/plugins_manager.js +++ b/web_interface/static/v3/plugins_manager.js @@ -7161,6 +7161,13 @@ window.getSchemaProperty = getSchemaProperty; window.escapeHtml = escapeHtml; window.escapeAttribute = escapeAttribute; +// Expose GitHub install handlers. These must be assigned inside the IIFE — +// from outside the IIFE, `typeof attachInstallButtonHandler` evaluates to +// 'undefined' and the fallback path at the bottom of this file fires a +// [FALLBACK] attachInstallButtonHandler not available on window warning. +window.attachInstallButtonHandler = attachInstallButtonHandler; +window.setupGitHubInstallHandlers = setupGitHubInstallHandlers; + })(); // End IIFE // Functions to handle array-of-objects @@ -7390,16 +7397,8 @@ if (typeof loadInstalledPlugins !== 'undefined') { if (typeof renderInstalledPlugins !== 'undefined') { window.renderInstalledPlugins = renderInstalledPlugins; } -// Expose GitHub install handlers for debugging and manual testing -if (typeof setupGitHubInstallHandlers !== 'undefined') { - window.setupGitHubInstallHandlers = setupGitHubInstallHandlers; - console.log('[GLOBAL] setupGitHubInstallHandlers exposed to window'); -} -if (typeof attachInstallButtonHandler !== 'undefined') { - window.attachInstallButtonHandler = attachInstallButtonHandler; - console.log('[GLOBAL] attachInstallButtonHandler exposed to window'); -} -// searchPluginStore is now exposed inside the IIFE after its definition +// GitHub install handlers are now exposed inside the IIFE (see above). +// searchPluginStore is also exposed inside the IIFE after its definition. // Verify critical functions are available if (_PLUGIN_DEBUG_EARLY) { diff --git a/web_interface/templates/v3/base.html b/web_interface/templates/v3/base.html index e3da43d3..e4dbf5ff 100644 --- a/web_interface/templates/v3/base.html +++ b/web_interface/templates/v3/base.html @@ -786,56 +786,25 @@ })(); - - + +