Duriin-API/public/admin/assets/js/sql.js

81 lines
3.1 KiB
JavaScript

// sql console — run ad-hoc queries against archive or intelligence db
// depends on: app.js
async function runSql() {
const sql = document.getElementById("sql-input").value.trim();
if (!sql) return;
const database = document.getElementById("sql-db").value;
const errEl = document.getElementById("sql-error");
const resultsEl = document.getElementById("sql-results");
const elapsedEl = document.getElementById("sql-elapsed");
errEl.style.display = "none";
resultsEl.innerHTML = "";
elapsedEl.textContent = "";
try {
const data = await fetch("/admin/api/sql", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ sql, database }),
}).then(r => r.json());
if (data.error) {
errEl.textContent = data.error;
errEl.style.display = "";
return;
}
elapsedEl.textContent = `${data.elapsed}ms`;
const blocks = [];
for (const r of (data.results || [])) {
if (r.error) {
blocks.push(`<div style="color:#fca5a5; font-size:13px; margin-bottom:12px"><span style="color:var(--muted-dark); font-size:11px; display:block; margin-bottom:4px; font-family:'SF Mono','Fira Code',monospace">${escapeHtml(r.sql.slice(0, 120))}</span>${escapeHtml(r.error)}</div>`);
} else if (r.rows && r.rows.length > 0) {
const cols = Object.keys(r.rows[0]);
blocks.push(`
<div style="margin-bottom:16px">
<div class="table-wrap">
<table>
<thead><tr>${cols.map(c => `<th>${escapeHtml(c)}</th>`).join("")}</tr></thead>
<tbody>${r.rows.map(row =>
`<tr>${cols.map(c => `<td><span class="truncate" style="max-width:300px" title="${escapeHtml(String(row[c] ?? ""))}">${row[c] == null ? '<span style="color:var(--muted-dark)">NULL</span>' : escapeHtml(row[c])}</span></td>`).join("")}</tr>`
).join("")}</tbody>
</table>
</div>
<div style="color:var(--muted-dark); font-size:12px; margin-top:6px">${r.rows.length} row${r.rows.length !== 1 ? "s" : ""}</div>
</div>
`);
} else if (r.rows) {
blocks.push(`<div style="color:var(--muted); font-size:13px; margin-bottom:12px">No rows returned.</div>`);
} else {
blocks.push(`<div style="color:#86efac; font-size:13px; margin-bottom:12px">${r.changes} row${r.changes !== 1 ? "s" : ""} affected.</div>`);
}
}
resultsEl.innerHTML = blocks.join("");
} catch (e) {
errEl.textContent = e.message;
errEl.style.display = "";
}
}
document.addEventListener("DOMContentLoaded", () => {
// restore selected db from url
const urlDb = queryGet("db");
if (urlDb === "archive" || urlDb === "intelligence") {
document.getElementById("sql-db").value = urlDb;
}
document.getElementById("sql-db").addEventListener("change", ev => {
queryWrite({ db: ev.target.value === "archive" ? "" : ev.target.value });
});
document.getElementById("sql-run-btn").onclick = runSql;
document.getElementById("sql-input").addEventListener("keydown", e => {
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) runSql();
});
});