mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-23 07:26:30 +00:00
5f17a5df50
The repositories typed their Kysely statements against 159 table interfaces written by hand, one or more per repository. Nothing compared them with the schema: a migration that renamed a column or changed its type left them stale while tsc stayed green, and several had already drifted (reservations.accommodation_id is TEXT, not INTEGER). scripts/generate-db-types.ts migrates a throwaway database with the real migrations, reads every table back and writes one interface per table into src/db/kysely/ plus the DB map in db.ts. Every hand-written interface is now Pick<DB, ...> of that type, and npm run check:db-types fails when the committed files no longer match the migrations. A column an insert may leave out (rowid, DEFAULT, nullable) is InsertOptional<T>, declared as ColumnType<T, T | void, T>. The server compiles without strictNullChecks, where Kysely's own Generated<T> still demands the column; that is why the old code kept separate insert-only interfaces. The ids callers pass as number | string are cast to the column type at the bind, so every statement binds the same value as before.
57 lines
2.8 KiB
TypeScript
57 lines
2.8 KiB
TypeScript
import type { PluginErrorLog } from '../entities/PluginErrorLog.entity';
|
|
import { TrekRepository } from './_shared/trek-repository';
|
|
import type { DB } from '../kysely/db';
|
|
|
|
/** PR2 delivery row / plugins.service.ts `errors()`'s own projection. */
|
|
export interface PluginErrorLogRow {
|
|
ts: string;
|
|
level: string;
|
|
message: string;
|
|
}
|
|
|
|
/** The tables `pruneKeepingRecent`'s own `this.kysely()` escape hatch (`_shared/trek-repository.ts`) reads, for its correlated subquery on `id`/`plugin_id`. */
|
|
type PluginErrorLogKyselyDB = Pick<DB, 'plugin_error_log'>;
|
|
|
|
export class PluginErrorLogRepository extends TrekRepository<PluginErrorLog> {
|
|
/** PR2 — `INSERT INTO plugin_error_log (plugin_id, level, message) VALUES (?, ?, ?)` (the supervisor's `onLog` callback — §4a, lazily resolved, own try/catch swallow unchanged in the caller). */
|
|
async insertLog(pluginId: string, level: string, message: string): Promise<void> {
|
|
await this.insert({ plugin_id: pluginId, level, message });
|
|
}
|
|
|
|
/**
|
|
* PR3 — `` DELETE FROM plugin_error_log WHERE plugin_id = ? AND id NOT IN (
|
|
* SELECT id FROM plugin_error_log WHERE plugin_id = ? ORDER BY id DESC LIMIT ${retention}
|
|
* ) `` (`pruneErrorLog`'s retention trim). The `NOT IN (subquery … ORDER BY … LIMIT)`
|
|
* shape has no `nativeDelete`/`FilterQuery` equivalent (no correlated-subquery support),
|
|
* so this is one of the few statements per repository that goes through `this.kysely()`
|
|
* (`_shared/trek-repository.ts`'s escape hatch) rather than the QueryBuilder — the
|
|
* `retention` count is a `?`-bound `limit()` call, never interpolated into SQL text.
|
|
*/
|
|
async pruneKeepingRecent(pluginId: string, retention: number): Promise<void> {
|
|
const db = this.kysely<PluginErrorLogKyselyDB>();
|
|
await db
|
|
.deleteFrom('plugin_error_log')
|
|
.where('plugin_id', '=', pluginId)
|
|
.where(
|
|
'id',
|
|
'not in',
|
|
db.selectFrom('plugin_error_log').select('id').where('plugin_id', '=', pluginId).orderBy('id', 'desc').limit(retention),
|
|
)
|
|
.execute();
|
|
}
|
|
|
|
/** PR38 (uninstall cascade) / plugins.service.ts's `clearErrors` — `DELETE FROM plugin_error_log WHERE plugin_id = ?` (identical text, two call sites). */
|
|
async deleteAllForPlugin(pluginId: string): Promise<void> {
|
|
await this.nativeDelete({ plugin_id: pluginId });
|
|
}
|
|
|
|
/** plugins.service.ts `errors()` — `SELECT ts, level, message FROM plugin_error_log WHERE plugin_id = ? ORDER BY ts DESC, id DESC LIMIT 200`. */
|
|
async listRecent(pluginId: string, limit = 200): Promise<PluginErrorLogRow[]> {
|
|
const rows = await this.find(
|
|
{ plugin_id: pluginId },
|
|
{ fields: ['ts', 'level', 'message'], orderBy: [{ ts: 'desc' }, { id: 'desc' }], limit },
|
|
);
|
|
return rows.map((r) => ({ ts: r.ts ?? '', level: r.level, message: r.message ?? '' }));
|
|
}
|
|
}
|