fixed
This commit is contained in:
parent
08cf18537b
commit
91d90bd2b6
|
|
@ -157,6 +157,103 @@ async function recreateGroupMembersTable(sqlite) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Sorted column-set keys for each non-PK unique index (matches `PRAGMA index_list` / `index_info`). */
|
||||||
|
async function sqliteUniqueIndexColKeys(sqlite, tableName) {
|
||||||
|
if (!/^[a-z_]+$/.test(tableName)) {
|
||||||
|
throw new Error(`sqliteUniqueIndexColKeys: invalid table name "${tableName}"`);
|
||||||
|
}
|
||||||
|
const list = await sqlite.query(`PRAGMA index_list('${tableName}')`, { type: QueryTypes.SELECT });
|
||||||
|
const keys = [];
|
||||||
|
for (const row of list) {
|
||||||
|
const unique = row.unique === 1 || row.unique === true;
|
||||||
|
if (!unique) continue;
|
||||||
|
const info = await sqlite.query(`PRAGMA index_info('${String(row.name).replace(/'/g, "''")}')`, {
|
||||||
|
type: QueryTypes.SELECT,
|
||||||
|
});
|
||||||
|
const cols = info
|
||||||
|
.slice()
|
||||||
|
.sort((a, b) => (a.seqno ?? 0) - (b.seqno ?? 0))
|
||||||
|
.map((i) => i.name)
|
||||||
|
.filter(Boolean);
|
||||||
|
if (row.origin === 'pk') continue;
|
||||||
|
if (cols.length === 1 && cols[0] === 'id') continue;
|
||||||
|
keys.push(colSetKey(cols));
|
||||||
|
}
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function monthlyDrawsSqliteNeedsRepair(sqlite) {
|
||||||
|
const keys = await sqliteUniqueIndexColKeys(sqlite, 'monthly_draws');
|
||||||
|
const hasComposite = keys.includes('group_id,month,year');
|
||||||
|
const badSingleton = keys.some((k) => k === 'year' || k === 'month' || k === 'group_id');
|
||||||
|
return !hasComposite || badSingleton;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Must match `src/models/MonthlyDraw.js` + Sequelize SQLite DDL (table is empty). */
|
||||||
|
async function recreateMonthlyDrawsTable(sqlite) {
|
||||||
|
await sqlite.query('DROP TABLE IF EXISTS `monthly_draws`');
|
||||||
|
await sqlite.query(`
|
||||||
|
CREATE TABLE \`monthly_draws\` (
|
||||||
|
\`id\` UUID PRIMARY KEY,
|
||||||
|
\`group_id\` UUID NOT NULL REFERENCES \`chit_groups\` (\`id\`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
|
\`month\` INTEGER NOT NULL,
|
||||||
|
\`year\` INTEGER NOT NULL,
|
||||||
|
\`draw_date\` DATETIME NOT NULL,
|
||||||
|
\`eligible_members\` JSON NOT NULL DEFAULT '[]',
|
||||||
|
\`winner_id\` UUID REFERENCES \`users\` (\`id\`) ON DELETE SET NULL ON UPDATE CASCADE,
|
||||||
|
\`prize_amount\` DECIMAL(15,2),
|
||||||
|
\`server_seed\` VARCHAR(255) NOT NULL,
|
||||||
|
\`server_seed_hash\` VARCHAR(255) NOT NULL,
|
||||||
|
\`client_seed\` VARCHAR(255) NOT NULL,
|
||||||
|
\`nonce\` BIGINT NOT NULL,
|
||||||
|
\`result_hash\` VARCHAR(255) NOT NULL,
|
||||||
|
\`status\` TEXT DEFAULT 'pending',
|
||||||
|
\`notes\` TEXT,
|
||||||
|
\`created_at\` DATETIME NOT NULL,
|
||||||
|
\`updated_at\` DATETIME NOT NULL
|
||||||
|
)
|
||||||
|
`);
|
||||||
|
await sqlite.query(
|
||||||
|
'CREATE UNIQUE INDEX `monthly_draws_group_id_month_year` ON `monthly_draws` (`group_id`, `month`, `year`)'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function paymentsSqliteNeedsRepair(sqlite) {
|
||||||
|
const keys = await sqliteUniqueIndexColKeys(sqlite, 'payments');
|
||||||
|
const hasComposite = keys.includes('group_id,month,year,user_id');
|
||||||
|
const badSingleton = keys.some((k) => ['year', 'month', 'group_id', 'user_id'].includes(k));
|
||||||
|
return !hasComposite || badSingleton;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Must match `src/models/Payment.js` + Sequelize SQLite DDL (table is empty). */
|
||||||
|
async function recreatePaymentsTable(sqlite) {
|
||||||
|
await sqlite.query('DROP TABLE IF EXISTS `payments`');
|
||||||
|
await sqlite.query(`
|
||||||
|
CREATE TABLE \`payments\` (
|
||||||
|
\`id\` UUID PRIMARY KEY,
|
||||||
|
\`group_id\` UUID NOT NULL REFERENCES \`chit_groups\` (\`id\`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
|
\`user_id\` UUID NOT NULL REFERENCES \`users\` (\`id\`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
|
\`month\` INTEGER NOT NULL,
|
||||||
|
\`year\` INTEGER NOT NULL,
|
||||||
|
\`amount\` DECIMAL(15,2) NOT NULL,
|
||||||
|
\`payment_method\` TEXT DEFAULT 'upi',
|
||||||
|
\`transaction_id\` VARCHAR(255),
|
||||||
|
\`source\` TEXT NOT NULL DEFAULT 'manual_manager_entry',
|
||||||
|
\`entered_by\` UUID REFERENCES \`users\` (\`id\`) ON DELETE SET NULL ON UPDATE CASCADE,
|
||||||
|
\`idempotency_key\` VARCHAR(128),
|
||||||
|
\`status\` TEXT DEFAULT 'pending',
|
||||||
|
\`paid_at\` DATETIME,
|
||||||
|
\`notes\` TEXT,
|
||||||
|
\`created_at\` DATETIME NOT NULL,
|
||||||
|
\`updated_at\` DATETIME NOT NULL
|
||||||
|
)
|
||||||
|
`);
|
||||||
|
await sqlite.query(
|
||||||
|
'CREATE UNIQUE INDEX `payments_group_id_user_id_month_year` ON `payments` (`group_id`, `user_id`, `month`, `year`)'
|
||||||
|
);
|
||||||
|
await sqlite.query('CREATE UNIQUE INDEX `payments_idempotency_key` ON `payments` (`idempotency_key`)');
|
||||||
|
}
|
||||||
|
|
||||||
async function fetchPostgresTable(pg, table) {
|
async function fetchPostgresTable(pg, table) {
|
||||||
try {
|
try {
|
||||||
return await pg.query(`SELECT * FROM "${table}"`, { type: QueryTypes.SELECT });
|
return await pg.query(`SELECT * FROM "${table}"`, { type: QueryTypes.SELECT });
|
||||||
|
|
@ -253,6 +350,24 @@ async function main() {
|
||||||
console.log(' Done.\n');
|
console.log(' Done.\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (await monthlyDrawsSqliteNeedsRepair(sqlite)) {
|
||||||
|
console.log(
|
||||||
|
'🔧 Fixing `monthly_draws` SQLite schema (wrong UNIQUE on `year` / missing composite on group+month+year).\n' +
|
||||||
|
' Recreating empty table to match current Sequelize model…\n'
|
||||||
|
);
|
||||||
|
await recreateMonthlyDrawsTable(sqlite);
|
||||||
|
console.log(' Done.\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await paymentsSqliteNeedsRepair(sqlite)) {
|
||||||
|
console.log(
|
||||||
|
'🔧 Fixing `payments` SQLite schema (legacy UNIQUE columns vs composite + idempotency_key).\n' +
|
||||||
|
' Recreating empty table to match current Sequelize model…\n'
|
||||||
|
);
|
||||||
|
await recreatePaymentsTable(sqlite);
|
||||||
|
console.log(' Done.\n');
|
||||||
|
}
|
||||||
|
|
||||||
console.log('📥 Copying from PostgreSQL → SQLite…\n');
|
console.log('📥 Copying from PostgreSQL → SQLite…\n');
|
||||||
for (const table of TABLES_ORDER) {
|
for (const table of TABLES_ORDER) {
|
||||||
const rows = await fetchPostgresTable(pg, table);
|
const rows = await fetchPostgresTable(pg, table);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue