b46be5c076
Hop-State: A_06FPC0CGS3F7SJG3BGW0YF0 Hop-Proposal: R_06FPC0BK6YEV7XASNM7C908 Hop-Task: T_06FPAWA3279RBMJAR0BHM10 Hop-Attempt: AT_06FPBZWSBFTB9B5YH9JY9QR
59 lines
1.6 KiB
TypeScript
59 lines
1.6 KiB
TypeScript
import { sql } from 'drizzle-orm';
|
|
import { migrate } from 'drizzle-orm/tursodatabase/migrator';
|
|
import { closeDb, db } from '../src/db';
|
|
|
|
interface DuplicateIdentityRow {
|
|
field: 'did' | 'handle';
|
|
value: string;
|
|
duplicateCount: number;
|
|
}
|
|
|
|
async function assertIdentityUniqueness(): Promise<void> {
|
|
const tables = await db.all<{ name: string }>(sql.raw(
|
|
"SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'users' LIMIT 1",
|
|
));
|
|
if (tables.length === 0) return;
|
|
|
|
const duplicates = await db.all<DuplicateIdentityRow>(sql.raw(`
|
|
SELECT 'did' AS field, did AS value, COUNT(*) AS duplicateCount
|
|
FROM users
|
|
GROUP BY did
|
|
HAVING COUNT(*) > 1
|
|
UNION ALL
|
|
SELECT 'handle' AS field, handle AS value, COUNT(*) AS duplicateCount
|
|
FROM users
|
|
GROUP BY handle
|
|
HAVING COUNT(*) > 1
|
|
LIMIT 20
|
|
`));
|
|
if (duplicates.length === 0) return;
|
|
|
|
const sample = duplicates
|
|
.map((row) => `${row.field}=${JSON.stringify(row.value)} (${row.duplicateCount} rows)`)
|
|
.join(', ');
|
|
throw new Error(
|
|
`Database identity preflight failed: duplicate users must be resolved before migration: ${sample}`,
|
|
);
|
|
}
|
|
|
|
async function main() {
|
|
try {
|
|
await assertIdentityUniqueness();
|
|
const result = await migrate(db, { migrationsFolder: './drizzle' });
|
|
|
|
if (result) {
|
|
throw new Error(`Database migration failed: ${JSON.stringify(result)}`);
|
|
}
|
|
|
|
await db.run(sql.raw('PRAGMA wal_checkpoint(TRUNCATE)'));
|
|
console.log('Database migrations are up to date.');
|
|
} finally {
|
|
await closeDb();
|
|
}
|
|
}
|
|
|
|
main().catch((error) => {
|
|
console.error(error);
|
|
process.exit(1);
|
|
});
|