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 <noreply@anthropic.com>
This commit is contained in:
parent
a3ec1a2556
commit
150f58f57a
@ -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 = '<pre>' + content.replace('<', '<') + '</pre>'
|
||||
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']})
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -64,6 +64,13 @@
|
||||
<li><a href="{{ url_for('dashboard.manual_windows') }}" class="{% if request.endpoint == 'dashboard.manual_windows' %}active{% endif %}" style="padding-left:1.5rem;font-size:0.85rem">└ Windows Guide</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div style="padding:6px 12px">
|
||||
<button id="btn-reload-modules" onclick="reloadModules()" class="btn btn-small"
|
||||
style="width:100%;font-size:0.75rem;padding:5px 0;background:var(--bg-input);border:1px solid var(--border);color:var(--text-secondary)"
|
||||
title="Re-scan modules/ directory for new or changed modules">
|
||||
↻ Refresh Modules
|
||||
</button>
|
||||
</div>
|
||||
<div class="sidebar-footer">
|
||||
<span class="admin-name">{{ session.get('user', 'admin') }}</span>
|
||||
<a href="{{ url_for('auth.logout') }}" class="logout-link">Logout</a>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user