diff --git a/app/__pycache__/app.cpython-312.pyc b/app/__pycache__/app.cpython-312.pyc index 1656844..fcb7531 100644 Binary files a/app/__pycache__/app.cpython-312.pyc and b/app/__pycache__/app.cpython-312.pyc differ diff --git a/app/app.py b/app/app.py index 83dc115..fc331af 100644 --- a/app/app.py +++ b/app/app.py @@ -240,12 +240,6 @@ def collect_all(): # Update database (all in main collector thread) with app.app_context(): - # Remove servers no longer in config - config_keys = {(e['username'], e['hostname']) for e in entries} - for server in Server.query.all(): - if (server.username, server.hostname) not in config_keys: - db.session.delete(server) - for key, (entry, result) in results.items(): server = Server.query.filter_by( username=entry['username'], @@ -289,12 +283,23 @@ def collector_loop(): @app.route('/') def index(): + # Get group order from config file and filter to configured servers only + config_entries = parse_infrastructure_conf() + config_keys = {(e['username'], e['hostname']) for e in config_entries} + group_order = [] + for e in config_entries: + g = e['group'] + if g not in group_order: + group_order.append(g) + all_servers = Server.query.order_by(Server.group_name, Server.primary_ip).all() - # Separate parents and children + # Separate parents and children (only servers in current config) children_map = {} # parent_hostname -> [child_servers] parents = [] for s in all_servers: + if (s.username, s.hostname) not in config_keys: + continue if s.parent_hostname: children_map.setdefault(s.parent_hostname, []).append(s) else: @@ -304,14 +309,6 @@ def index(): for hostname in children_map: children_map[hostname].sort(key=lambda s: _ip_sort_key(s.primary_ip)) - # Get group order from config file - config_entries = parse_infrastructure_conf() - group_order = [] - for e in config_entries: - g = e['group'] - if g not in group_order: - group_order.append(g) - # Group parents, preserving config order groups = {} for s in parents: @@ -430,11 +427,6 @@ def api_refresh_stream(): # Update database with app.app_context(): - config_keys = {(e['username'], e['hostname']) for e in entries} - for server in Server.query.all(): - if (server.username, server.hostname) not in config_keys: - db.session.delete(server) - for key, (entry, result) in results.items(): server = Server.query.filter_by( username=entry['username'], hostname=entry['hostname'], @@ -530,19 +522,19 @@ def api_refresh_one_stream(server_id): t.start() # Stream progress while collecting - while not host_done.is_set(): + result = None + while not host_done.is_set() or not progress_q.empty(): try: kind, val = progress_q.get(timeout=0.3) if kind == 'progress': yield f"data: {val}\n\n" elif kind == 'result': - break + result = val except _queue.Empty: continue t.join() - # Drain remaining messages - result = None + # Drain anything remaining while not progress_q.empty(): kind, val = progress_q.get_nowait() if kind == 'progress': @@ -592,18 +584,18 @@ def api_refresh_one_stream(server_id): ct = threading.Thread(target=_collect_child) ct.start() - while not child_done.is_set(): + child_result = None + while not child_done.is_set() or not child_q.empty(): try: kind, val = child_q.get(timeout=0.3) if kind == 'progress': yield f"data: {val}\n\n" elif kind == 'result': - break + child_result = val except _queue.Empty: continue ct.join() - child_result = None while not child_q.empty(): kind, val = child_q.get_nowait() if kind == 'progress': @@ -655,6 +647,33 @@ def api_update_links(server_id): return jsonify({'ok': True}) +@app.route('/api/all-notes') +def api_all_notes(): + servers = Server.query.filter(Server.notes != '', Server.notes != None).all() + return jsonify([{ + 'id': s.id, + 'hostname': s.hostname, + 'group_name': s.group_name, + 'notes': s.notes, + } for s in servers]) + + +@app.route('/api/all-links') +def api_all_links(): + servers = Server.query.filter(Server.links != None, Server.links != '[]').all() + result = [] + for s in servers: + links = s.links or [] + if links: + result.append({ + 'id': s.id, + 'hostname': s.hostname, + 'group_name': s.group_name, + 'links': links, + }) + return jsonify(result) + + def _ip_sort_key(ip_str): if not ip_str: return [999, 999, 999, 999] diff --git a/app/static/style.css b/app/static/style.css index 02b32ed..5cfa83c 100644 --- a/app/static/style.css +++ b/app/static/style.css @@ -31,6 +31,22 @@ header h1 { color: #64748b; } +.header-btn { + background: #334155; + color: #94a3b8; + border: 1px solid #475569; + border-radius: 6px; + padding: 4px 12px; + font-size: 0.8rem; + cursor: pointer; + transition: all 0.2s; +} + +.header-btn:hover { + background: #475569; + color: #e2e8f0; +} + .refresh-btn { margin-left: auto; background: #334155; diff --git a/app/templates/index.html b/app/templates/index.html index 7ec0a70..e5691f5 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -10,6 +10,8 @@

Infrastructure Map

Auto-refreshes every 60s | Built: {{ build_date }} + +
@@ -502,7 +504,64 @@ + + +