mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-20 22:16:26 +00:00
build(server): hold SQLite-only SQL spellings to a shrinking baseline
ESLint keeps raw SQL out of the repositories, but nothing looked at the rest of src/: the entities, the domain services, the src/db lifecycle files or a migration added from now on. scripts/dialect-lint.mjs (npm run lint:dialect) reads every string and template literal under src/, skipping comments, and counts INSERT OR IGNORE/REPLACE, datetime(), strftime, json_extract, GLOB, last_insert_rowid, AUTOINCREMENT, PRAGMA outside the lifecycle files, || concatenation in SQL and .insertId reads. scripts/dialect-baseline.json holds today's thirteen hits; the counts may only go down, like lint:size. The dialect layer is not scanned, and neither are the migrations that have shipped. The four seeders that still spelled INSERT OR IGNORE (addons, photo and document providers, the schema_version marker) are Kysely inserts that do nothing on conflict now, so a re-run still leaves an operator's rows alone. They leave the SQL-string exemption list in eslint.config.mjs and their pins in the selector test.
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import type { EntityManager } from '@mikro-orm/core';
|
||||
import type { SqlEntityManager } from '@mikro-orm/sql';
|
||||
import { Seeder } from '@mikro-orm/seeder';
|
||||
import type { DB } from '../kysely/db';
|
||||
|
||||
interface AddonRow {
|
||||
id: string;
|
||||
@@ -154,18 +156,20 @@ const DEFAULT_ADDONS: AddonRow[] = [
|
||||
/**
|
||||
* Seeds the addon shelf.
|
||||
*
|
||||
* `INSERT OR IGNORE` per row, not a table-level emptiness check: an operator who
|
||||
* has toggled an addon keeps their choice, and a release that adds a new addon
|
||||
* still gets its tile on an existing install.
|
||||
* One insert per row that does nothing when the id is already there, not a
|
||||
* table-level emptiness check: an operator who has toggled an addon keeps their
|
||||
* choice, and a release that adds a new addon still gets its tile on an
|
||||
* existing install.
|
||||
*/
|
||||
export class AddonSeeder extends Seeder {
|
||||
async run(em: EntityManager): Promise<void> {
|
||||
const connection = em.getConnection();
|
||||
const db = (em as SqlEntityManager).getKysely<Pick<DB, 'addons'>>();
|
||||
for (const addon of DEFAULT_ADDONS) {
|
||||
await connection.execute(
|
||||
'INSERT OR IGNORE INTO addons (id, name, description, type, icon, enabled, sort_order) VALUES (?, ?, ?, ?, ?, ?, ?)',
|
||||
[addon.id, addon.name, addon.description, addon.type, addon.icon, addon.enabled, addon.sort_order],
|
||||
);
|
||||
await db
|
||||
.insertInto('addons')
|
||||
.values({ ...addon })
|
||||
.onConflict((oc) => oc.doNothing())
|
||||
.execute();
|
||||
}
|
||||
console.log('Default addons seeded');
|
||||
}
|
||||
|
||||
@@ -1,32 +1,45 @@
|
||||
import { DOCUMENT_PROVIDERS, DOCUMENT_PROVIDER_FIELDS } from '../document-provider-seed';
|
||||
import type { EntityManager } from '@mikro-orm/core';
|
||||
import type { SqlEntityManager } from '@mikro-orm/sql';
|
||||
import { Seeder } from '@mikro-orm/seeder';
|
||||
import type { DB } from '../kysely/db';
|
||||
|
||||
/**
|
||||
* Seeds the document providers.
|
||||
*
|
||||
* The rows come from `document-provider-seed.ts`, the same constants the
|
||||
* migration that introduces the tables uses, so a fresh install and an upgraded
|
||||
* one end up with identical rows.
|
||||
* one end up with identical rows. A row that is already there is left alone
|
||||
* (the insert does nothing on conflict), so an operator's edits survive.
|
||||
*/
|
||||
export class DocumentProviderSeeder extends Seeder {
|
||||
async run(em: EntityManager): Promise<void> {
|
||||
const connection = em.getConnection();
|
||||
const db = (em as SqlEntityManager).getKysely<Pick<DB, 'document_providers' | 'document_provider_fields'>>();
|
||||
|
||||
for (const p of DOCUMENT_PROVIDERS) {
|
||||
await connection.execute(
|
||||
'INSERT OR IGNORE INTO document_providers (id, name, description, icon, enabled, sort_order) VALUES (?, ?, ?, ?, 0, ?)',
|
||||
[p.id, p.name, p.description, p.icon, p.sort_order],
|
||||
);
|
||||
await db
|
||||
.insertInto('document_providers')
|
||||
.values({ id: p.id, name: p.name, description: p.description, icon: p.icon, enabled: 0, sort_order: p.sort_order })
|
||||
.onConflict((oc) => oc.doNothing())
|
||||
.execute();
|
||||
}
|
||||
|
||||
for (const f of DOCUMENT_PROVIDER_FIELDS) {
|
||||
await connection.execute(
|
||||
`INSERT OR IGNORE INTO document_provider_fields
|
||||
(provider_id, field_key, label, input_type, placeholder, hint, required, secret, sort_order)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[f.provider_id, f.field_key, f.label, f.input_type, f.placeholder, f.hint, f.required, f.secret, f.sort_order],
|
||||
);
|
||||
await db
|
||||
.insertInto('document_provider_fields')
|
||||
.values({
|
||||
provider_id: f.provider_id,
|
||||
field_key: f.field_key,
|
||||
label: f.label,
|
||||
input_type: f.input_type,
|
||||
placeholder: f.placeholder,
|
||||
hint: f.hint,
|
||||
required: f.required,
|
||||
secret: f.secret,
|
||||
sort_order: f.sort_order,
|
||||
})
|
||||
.onConflict((oc) => oc.doNothing())
|
||||
.execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import type { EntityManager } from '@mikro-orm/core';
|
||||
import type { SqlEntityManager } from '@mikro-orm/sql';
|
||||
import { Seeder } from '@mikro-orm/seeder';
|
||||
import type { DB } from '../kysely/db';
|
||||
|
||||
interface PhotoProviderRow {
|
||||
id: string;
|
||||
@@ -157,37 +159,37 @@ const FIELDS: PhotoProviderFieldRow[] = [
|
||||
},
|
||||
];
|
||||
|
||||
/** Seeds the photo providers and the fields their connection form asks for. */
|
||||
/** Seeds the photo providers and the fields their connection form asks for, leaving a row that is already there alone. */
|
||||
export class PhotoProviderSeeder extends Seeder {
|
||||
async run(em: EntityManager): Promise<void> {
|
||||
const connection = em.getConnection();
|
||||
const db = (em as SqlEntityManager).getKysely<Pick<DB, 'photo_providers' | 'photo_provider_fields'>>();
|
||||
|
||||
for (const p of PROVIDERS) {
|
||||
await connection.execute(
|
||||
'INSERT OR IGNORE INTO photo_providers (id, name, description, icon, enabled, sort_order) VALUES (?, ?, ?, ?, ?, ?)',
|
||||
[p.id, p.name, p.description, p.icon, p.enabled, p.sort_order],
|
||||
);
|
||||
await db
|
||||
.insertInto('photo_providers')
|
||||
.values({ id: p.id, name: p.name, description: p.description, icon: p.icon, enabled: p.enabled, sort_order: p.sort_order })
|
||||
.onConflict((oc) => oc.doNothing())
|
||||
.execute();
|
||||
}
|
||||
|
||||
for (const f of FIELDS) {
|
||||
await connection.execute(
|
||||
`INSERT OR IGNORE INTO photo_provider_fields
|
||||
(provider_id, field_key, label, input_type, placeholder, hint, required, secret, settings_key, payload_key, sort_order)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[
|
||||
f.provider_id,
|
||||
f.field_key,
|
||||
f.label,
|
||||
f.input_type,
|
||||
f.placeholder,
|
||||
f.hint,
|
||||
f.required,
|
||||
f.secret,
|
||||
f.settings_key,
|
||||
f.payload_key,
|
||||
f.sort_order,
|
||||
],
|
||||
);
|
||||
await db
|
||||
.insertInto('photo_provider_fields')
|
||||
.values({
|
||||
provider_id: f.provider_id,
|
||||
field_key: f.field_key,
|
||||
label: f.label,
|
||||
input_type: f.input_type,
|
||||
placeholder: f.placeholder,
|
||||
hint: f.hint,
|
||||
required: f.required,
|
||||
secret: f.secret,
|
||||
settings_key: f.settings_key,
|
||||
payload_key: f.payload_key,
|
||||
sort_order: f.sort_order,
|
||||
})
|
||||
.onConflict((oc) => oc.doNothing())
|
||||
.execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import type { EntityManager } from '@mikro-orm/core';
|
||||
import type { SqlEntityManager } from '@mikro-orm/sql';
|
||||
import { Seeder } from '@mikro-orm/seeder';
|
||||
import type { DB } from '../kysely/db';
|
||||
|
||||
/**
|
||||
* The version the hand-written `db/migrations.ts` chain ended on — the highest
|
||||
@@ -16,17 +18,17 @@ const LEGACY_SCHEMA_VERSION = 258;
|
||||
* `mikro_orm_migrations` now — but it is still an entity and still carries the
|
||||
* row an installation upgraded from the hand-written chain would have.
|
||||
*
|
||||
* `INSERT OR IGNORE`, like every other seeder here: seeding runs again whenever
|
||||
* An insert that does nothing on conflict, like every other seeder here: seeding runs again whenever
|
||||
* the schema bootstrap does, and a backup restore re-runs it against a database
|
||||
* that already has this row.
|
||||
*/
|
||||
export class SchemaVersionSeeder extends Seeder {
|
||||
async run(em: EntityManager): Promise<void> {
|
||||
const connection = em.getConnection();
|
||||
|
||||
await connection.execute('INSERT OR IGNORE INTO schema_version (id, version) VALUES (?, ?)', [
|
||||
1,
|
||||
LEGACY_SCHEMA_VERSION,
|
||||
]);
|
||||
await (em as SqlEntityManager)
|
||||
.getKysely<Pick<DB, 'schema_version'>>()
|
||||
.insertInto('schema_version')
|
||||
.values({ id: 1, version: LEGACY_SCHEMA_VERSION })
|
||||
.onConflict((oc) => oc.doNothing())
|
||||
.execute();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user