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) {
|
||||
try {
|
||||
return await pg.query(`SELECT * FROM "${table}"`, { type: QueryTypes.SELECT });
|
||||
|
|
@ -253,6 +350,24 @@ async function main() {
|
|||
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');
|
||||
for (const table of TABLES_ORDER) {
|
||||
const rows = await fetchPostgresTable(pg, table);
|
||||
|
|
|
|||
Loading…
Reference in New Issue