Add ability to query databases

This commit is contained in:
Peter Stockings
2025-12-24 10:13:25 +11:00
parent 40cb631975
commit 08840b3bc2
2 changed files with 182 additions and 1 deletions

View File

@@ -553,6 +553,44 @@
</div>
</section>
{% endfor %}
<!-- SQL Command Center -->
<section id="sql-center" class="station" data-label="[ STATION: SQL_COMMAND_CENTER ]">
<div class="station-header">
<h2>SQL Query Interface</h2>
<a href="#top" class="back-to-top">[^ BACK_TO_TOP]</a>
</div>
<div class="grid" style="grid-template-columns: 1fr;">
<div class="panel">
<div class="panel-title">Query Console</div>
<div style="display: flex; gap: 10px; margin-bottom: 15px;">
<select id="sql-db-select"
style="background: #000; color: #00ff88; border: 1px solid #30363d; padding: 8px; font-family: inherit; font-size: 12px; flex: 1;">
<option value="">-- SELECT DATABASE CONTAINER --</option>
{% for db in data.databases %}
<option value="{{ db.name }}">{{ db.name }} [{{ db.type|upper }}]</option>
{% endfor %}
</select>
<button onclick="runQuery()"
style="background: #00ff8822; color: #00ff88; border: 1px solid #00ff88; padding: 0 20px; font-family: inherit; font-size: 11px; font-weight: 700; cursor: pointer;">EXECUTE_QUERY</button>
</div>
<textarea id="sql-editor" spellcheck="false" placeholder="SELECT * FROM table_name LIMIT 10;"
style="width: 100%; height: 120px; background: #000; color: #c9d1d9; border: 1px solid #30363d; padding: 12px; font-family: 'JetBrains Mono', monospace; font-size: 13px; resize: vertical;"></textarea>
</div>
<div id="sql-results-panel" class="panel" style="display: none;">
<div class="panel-title">Query Results</div>
<div id="sql-status" style="font-size: 11px; margin-bottom: 10px; color: #8b949e;"></div>
<div style="overflow-x: auto; max-height: 500px;">
<table id="sql-results-table" style="width: 100%; border-collapse: collapse; font-size: 11px;">
<thead id="sql-thead"></thead>
<tbody id="sql-tbody"></tbody>
</table>
</div>
</div>
</div>
</section>
</div>
<script>
@@ -567,6 +605,76 @@
});
}
async function runQuery() {
const container = document.getElementById('sql-db-select').value;
const query = document.getElementById('sql-editor').value;
const resultsPanel = document.getElementById('sql-results-panel');
const status = document.getElementById('sql-status');
const thead = document.getElementById('sql-thead');
const tbody = document.getElementById('sql-tbody');
if (!container || !query) {
alert('Please select a database and enter a query.');
return;
}
resultsPanel.style.display = 'block';
status.innerHTML = '<span style="color: #00d9ff;">EXECUTING...</span>';
thead.innerHTML = '';
tbody.innerHTML = '';
try {
const response = await fetch('/api/sql/query', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ container, query })
});
const result = await response.json();
if (result.error) {
status.innerHTML = `<span style="color: #ff5555;">ERROR: ${result.error}</span>`;
return;
}
status.innerHTML = `<span style="color: #00ff88;">SUCCESS: ${result.count || 0} rows found.</span>`;
if (result.columns && result.columns.length > 0) {
const headerRow = document.createElement('tr');
result.columns.forEach(col => {
const th = document.createElement('th');
th.style.padding = '8px';
th.style.textAlign = 'left';
th.style.border = '1px solid #30363d';
th.style.background = 'rgba(0, 217, 255, 0.1)';
th.style.color = '#00d9ff';
th.innerText = col;
headerRow.appendChild(th);
});
thead.appendChild(headerRow);
}
if (result.rows && result.rows.length > 0) {
result.rows.forEach(row => {
const tr = document.createElement('tr');
row.forEach(cell => {
const td = document.createElement('td');
td.style.padding = '8px';
td.style.border = '1px solid #30363d';
td.innerText = cell;
tr.appendChild(td);
});
tbody.appendChild(tr);
});
} else if (result.message) {
status.innerHTML = `<span style="color: #00ff88;">${result.message}</span>`;
}
} catch (err) {
status.innerHTML = `<span style="color: #ff5555;">CONNECTION ERROR: ${err.message}</span>`;
}
}
// Auto-scroll logic
document.querySelectorAll('.terminal-body').forEach(el => {
el.scrollTop = el.scrollHeight;