From 150f58f57a64172f604c2f17f797dd5d8023ce94 Mon Sep 17 00:00:00 2001 From: DigiJ Date: Mon, 2 Mar 2026 21:12:56 -0800 Subject: [PATCH] Add Refresh Modules button to sidebar for hot-reloading modules - Sidebar button at bottom re-scans modules/ directory on click - POST /api/modules/reload endpoint returns updated counts and module list - Button shows success/failure feedback, auto-reloads category pages - Enables hot-dropping new modules without restarting the server Co-Authored-By: Claude Opus 4.6 --- web/routes/dashboard.py | 26 +++++++++++++++++++++++++- web/static/js/app.js | 38 ++++++++++++++++++++++++++++++++++++++ web/templates/base.html | 7 +++++++ 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/web/routes/dashboard.py b/web/routes/dashboard.py index 8322869..828aa04 100644 --- a/web/routes/dashboard.py +++ b/web/routes/dashboard.py @@ -6,7 +6,7 @@ import socket import time from datetime import datetime from pathlib import Path -from flask import Blueprint, render_template, current_app +from flask import Blueprint, render_template, current_app, jsonify from markupsafe import Markup from web.auth import login_required @@ -116,3 +116,27 @@ def manual_windows(): except ImportError: html = '
' + content.replace('<', '<') + '
' return render_template('manual.html', manual_html=Markup(html)) + + +@dashboard_bp.route('/api/modules/reload', methods=['POST']) +@login_required +def reload_modules(): + """Re-scan modules directory and return updated counts + module list.""" + from core.menu import MainMenu + menu = MainMenu() + menu.load_modules() + + counts = {} + modules = [] + for name, info in menu.modules.items(): + cat = info.category + counts[cat] = counts.get(cat, 0) + 1 + modules.append({ + 'name': name, + 'category': cat, + 'description': info.description, + 'version': info.version, + }) + counts['total'] = len(menu.modules) + + return jsonify({'counts': counts, 'modules': modules, 'total': counts['total']}) diff --git a/web/static/js/app.js b/web/static/js/app.js index cf47366..7363d71 100644 --- a/web/static/js/app.js +++ b/web/static/js/app.js @@ -41,6 +41,44 @@ function renderOutput(elementId, text) { if (el) el.textContent = text; } +/* ── Refresh Modules ─────────────────────────────────────────── */ +function reloadModules() { + var btn = document.getElementById('btn-reload-modules'); + if (!btn) return; + var origText = btn.innerHTML; + btn.innerHTML = '↻ Reloading...'; + btn.disabled = true; + btn.style.color = 'var(--accent)'; + + postJSON('/api/modules/reload', {}).then(function(data) { + var total = data.total || 0; + btn.innerHTML = '✓ ' + total + ' modules loaded'; + btn.style.color = 'var(--success)'; + + // If on a category page, reload to reflect changes + var path = window.location.pathname; + var isCategoryPage = /^\/(defense|offense|counter|analyze|osint|simulate)\/?$/.test(path) + || path === '/'; + if (isCategoryPage) { + setTimeout(function() { window.location.reload(); }, 800); + } else { + setTimeout(function() { + btn.innerHTML = origText; + btn.style.color = 'var(--text-secondary)'; + btn.disabled = false; + }, 2000); + } + }).catch(function() { + btn.innerHTML = '✗ Reload failed'; + btn.style.color = 'var(--danger)'; + setTimeout(function() { + btn.innerHTML = origText; + btn.style.color = 'var(--text-secondary)'; + btn.disabled = false; + }, 2000); + }); +} + function showTab(tabGroup, tabId) { document.querySelectorAll('[data-tab-group="' + tabGroup + '"].tab').forEach(function(t) { t.classList.toggle('active', t.dataset.tab === tabId); diff --git a/web/templates/base.html b/web/templates/base.html index ed07364..47fc59d 100644 --- a/web/templates/base.html +++ b/web/templates/base.html @@ -64,6 +64,13 @@
  • └ Windows Guide
  • +
    + +