Files
cobble/src/functions/backend/database/build-record-row.ts
T
tben 41bb4aaac2 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.
2026-10-23 06:03:42 +01:00

44 lines
1.4 KiB
TypeScript

import { metaColumnAlias, RecordKeyAlias } from "./build-records-sql";
import toDatabaseCell from "./to-database-cell";
import type { DatabaseRecord, DatabaseTableColumn } from "@/src/types";
type Params = {
/**
* The row as SQLite returned it, its meta columns included.
*/
row: Record<string, unknown>;
columns: DatabaseTableColumn[];
max_chars: number;
};
/**
* One row of a records query as a record the browser can draw: its key,
* and every column's value as a cell. The meta columns the query added
* (`typeof` and `length` per column) are read and dropped here, so they
* never reach the client.
*/
export default function buildRecordRow({ row, columns, max_chars }: Params): DatabaseRecord {
const cells: Record<string, DatabaseRecord["cells"][string]> = {};
for (const column of columns) {
cells[column.name] = toDatabaseCell({
value: row[column.name],
type: String(row[metaColumnAlias({ name: column.name, kind: "type" })] ?? "null"),
length: Number(row[metaColumnAlias({ name: column.name, kind: "length" })] ?? 0),
max_chars,
});
}
const key = row[RecordKeyAlias];
return {
key:
typeof key == "string" || typeof key == "number"
? key
: key === null || key === undefined
? null
: String(key),
cells,
};
}