feat: add DB Explorer for read-only SQLite inspection

Adds /db-explorer page and JSON API to browse all 15 SQLite databases
in data/. Sidebar lists databases with sizes, clicking one renders all
tables as scrollable data tables with row truncation at 200.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Trip T
2026-03-12 10:41:13 -04:00
parent 765e0f79c7
commit bc38fee817
5 changed files with 260 additions and 0 deletions

View File

@@ -78,6 +78,7 @@
<a href="/swarm/events" class="mc-test-link">EVENTS</a>
<a href="/router/status" class="mc-test-link">ROUTER</a>
<a href="/grok/status" class="mc-test-link mc-link-grok">GROK</a>
<a href="/db-explorer" class="mc-test-link">DB EXPLORER</a>
</div>
</div>
<div class="mc-nav-dropdown">
@@ -137,6 +138,7 @@
<a href="/swarm/events" class="mc-mobile-link">EVENTS</a>
<a href="/router/status" class="mc-mobile-link">ROUTER</a>
<a href="/grok/status" class="mc-mobile-link">GROK</a>
<a href="/db-explorer" class="mc-mobile-link">DB EXPLORER</a>
<div class="mc-mobile-section-label">COMMERCE</div>
<a href="/lightning/ledger" class="mc-mobile-link">LEDGER</a>
<a href="/creative/ui" class="mc-mobile-link">CREATIVE</a>

View File

@@ -0,0 +1,82 @@
{% extends "base.html" %}
{% from "macros.html" import panel %}
{% block title %}DB Explorer - Timmy Time{% endblock %}
{% block content %}
<div class="db-explorer-container py-3">
<div class="db-explorer-header">
<div class="db-explorer-title">DB EXPLORER</div>
<span class="db-explorer-subtitle">Read-only view of all SQLite databases in data/</span>
</div>
<div class="row g-3 mt-2">
<!-- Database list sidebar -->
<div class="col-md-3">
{% call panel("DATABASES") %}
<div class="db-list">
{% for db in databases %}
<a href="/db-explorer?db={{ db.name }}"
class="db-list-item {% if selected_db == db.name %}active{% endif %}">
<span class="db-name">{{ db.name }}</span>
<span class="db-size">{{ db.size_kb }} KB</span>
</a>
{% endfor %}
{% if not databases %}
<div class="text-muted p-2">No databases found</div>
{% endif %}
</div>
{% endcall %}
</div>
<!-- Table content -->
<div class="col-md-9">
{% if selected_db %}
{% if tables_data %}
{% for table_name, table in tables_data.items() %}
{% call panel(table_name | upper ~ " (" ~ table.total_count ~ " rows)") %}
{% if table.error %}
<div class="text-danger">Error: {{ table.error }}</div>
{% elif table.rows %}
<div class="table-responsive db-table-wrap">
<table class="table table-sm table-dark db-table">
<thead>
<tr>
{% for col in table.columns %}
<th>{{ col }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for row in table.rows %}
<tr>
{% for col in table.columns %}
<td class="db-cell">{{ row[col] if row[col] is not none else '' }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% if table.truncated %}
<div class="db-truncated">Showing {{ table.rows|length }} of {{ table.total_count }} rows</div>
{% endif %}
{% else %}
<div class="text-muted">Empty table ({{ table.columns|length }} columns: {{ table.columns|join(', ') }})</div>
{% endif %}
{% endcall %}
{% endfor %}
{% else %}
{% call panel("INFO") %}
<div class="text-muted">No tables found in {{ selected_db }}.db</div>
{% endcall %}
{% endif %}
{% else %}
{% call panel("SELECT A DATABASE") %}
<div class="text-muted">Choose a database from the sidebar to inspect its tables.</div>
{% endcall %}
{% endif %}
</div>
</div>
</div>
{% endblock %}