Let a super admin read the database from the app
A new super admin settings page, /admin/settings/database, lists every table with its record count and size, and one page of the open table's records newest key first. Values are cut inside SQL (400 characters, with a flag saying so) and a record opens whole in a modal, so a table of long texts reads without shipping it all. A search matches every column's text, word by word or as a phrase, which is the one thing that also works on a virtual table. Nothing in it writes: the catalog, the records and the single record are all reads, GET only.
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
import BunSQLite from "@moduletrace/bun-sqlite";
|
||||
import AppError from "@/src/utils/app-error";
|
||||
import buildRecordRow from "./build-record-row";
|
||||
import { buildRecordSql, parseRecordKey } from "./build-records-sql";
|
||||
import grabTableCatalogEntry from "./grab-table-catalog-entry";
|
||||
import type { DatabaseRecordPayload } from "@/src/types";
|
||||
import { AppData } from "@/src/data/app-data";
|
||||
|
||||
type Params = {
|
||||
table?: string | null;
|
||||
/**
|
||||
* The record's key, as it came in the query string.
|
||||
*/
|
||||
key: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* One record of one table, in full for its modal: every value read whole
|
||||
* (up to `AppData.DatabaseBrowserValueMaxChars`, since a megabyte of blob
|
||||
* or checkpoint is not something to render), so a cell cut short in the
|
||||
* table can still be read to its end.
|
||||
*/
|
||||
export default async function grabTableRecord({ table, key }: Params): Promise<DatabaseRecordPayload> {
|
||||
const entry = await grabTableCatalogEntry({ table });
|
||||
|
||||
if (entry.key.kind == "none") {
|
||||
throw new AppError(`"${entry.name}" has no key to open a single record by.`);
|
||||
}
|
||||
|
||||
const columns = entry.columns.map((column) => column.name);
|
||||
const query = buildRecordSql({
|
||||
table: entry.name,
|
||||
columns,
|
||||
key: entry.key,
|
||||
max_chars: AppData.DatabaseBrowserValueMaxChars,
|
||||
value: parseRecordKey({ value: key }),
|
||||
});
|
||||
const res = await BunSQLite.sql<Record<string, unknown>>(query);
|
||||
|
||||
if (!res.success) {
|
||||
throw new AppError(`Couldn't read that record of "${entry.name}".`);
|
||||
}
|
||||
|
||||
const row = res.payload?.[0];
|
||||
|
||||
if (!row) {
|
||||
throw new AppError(`That record isn't in "${entry.name}" any more.`);
|
||||
}
|
||||
|
||||
return {
|
||||
table: entry,
|
||||
record: buildRecordRow({
|
||||
row,
|
||||
columns: entry.columns,
|
||||
max_chars: AppData.DatabaseBrowserValueMaxChars,
|
||||
}),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user