Correctif Contrainte de suppression de compte
This commit is contained in:
@@ -2086,4 +2086,69 @@ console.log('[DB] Migrations 2FA OK');
|
||||
}
|
||||
}
|
||||
|
||||
// ── Migration : plateforme_id en CASCADE (au lieu de RESTRICT) sur
|
||||
// depots_retraits et investissements ────────────────────────────────────
|
||||
// RESTRICT empêchait la suppression complète d'un compte (DELETE FROM users) :
|
||||
// la cascade users→plateformes (user_id CASCADE) et users→investisseurs→
|
||||
// depots_retraits/investissements (CASCADE) sont deux branches indépendantes
|
||||
// de l'arbre de suppression ; SQLite peut supprimer une plateforme avant les
|
||||
// lignes qui la référencent encore sur l'autre branche, ce qui déclenche le
|
||||
// RESTRICT (erreur SQLITE_CONSTRAINT_TRIGGER). La protection "impossible de
|
||||
// supprimer une plateforme qui a encore des données" est déplacée dans la
|
||||
// route DELETE /api/plateformes/:id (vérification explicite avec message clair).
|
||||
{
|
||||
const fixPlateformeCascade = (tableName, indexStatements) => {
|
||||
const row = db.prepare(
|
||||
`SELECT sql FROM sqlite_master WHERE type='table' AND name=?`
|
||||
).get(tableName);
|
||||
if (!row || !/REFERENCES\s+plateformes\(id\)\s+ON DELETE RESTRICT/i.test(row.sql)) return;
|
||||
|
||||
const tempName = `__repair_${tableName}`;
|
||||
const nameRe = new RegExp(
|
||||
`CREATE TABLE\\s+(?:IF NOT EXISTS\\s+)?["'\`\\[]?${tableName}["'\`\\]]?`, 'i'
|
||||
);
|
||||
if (!nameRe.test(row.sql)) {
|
||||
console.error(`[DB] migration plateforme_id CASCADE : nom de table non reconnu dans le DDL de "${tableName}", migration ignorée.`);
|
||||
return;
|
||||
}
|
||||
const fixedDdl = row.sql
|
||||
.replace(nameRe, `CREATE TABLE "${tempName}"`)
|
||||
.replace(/REFERENCES\s+plateformes\(id\)\s+ON DELETE RESTRICT/i, 'REFERENCES plateformes(id) ON DELETE CASCADE');
|
||||
|
||||
const colDefs = db.prepare(`PRAGMA table_info("${tableName}")`).all();
|
||||
const colNames = colDefs.map(c => `"${c.name}"`).join(', ');
|
||||
|
||||
const idxs = db.prepare(
|
||||
`SELECT name FROM sqlite_master WHERE type='index' AND tbl_name=? AND sql IS NOT NULL`
|
||||
).all(tableName);
|
||||
|
||||
db.exec('PRAGMA foreign_keys = OFF');
|
||||
db.exec(`DROP TABLE IF EXISTS "${tempName}"`);
|
||||
db.exec(fixedDdl);
|
||||
db.exec(`INSERT INTO "${tempName}" (${colNames}) SELECT ${colNames} FROM "${tableName}"`);
|
||||
for (const idx of idxs) db.exec(`DROP INDEX IF EXISTS "${idx.name}"`);
|
||||
db.exec(`DROP TABLE "${tableName}"`);
|
||||
db.exec('PRAGMA legacy_alter_table = ON');
|
||||
db.exec(`ALTER TABLE "${tempName}" RENAME TO "${tableName}"`);
|
||||
db.exec('PRAGMA legacy_alter_table = OFF');
|
||||
db.exec('PRAGMA foreign_keys = ON');
|
||||
|
||||
for (const stmt of indexStatements) db.exec(stmt);
|
||||
console.log(`[DB] migration : plateforme_id passé en ON DELETE CASCADE sur "${tableName}".`);
|
||||
};
|
||||
|
||||
fixPlateformeCascade('depots_retraits', [
|
||||
'CREATE INDEX IF NOT EXISTS idx_depret_inv ON depots_retraits(investisseur_id)',
|
||||
'CREATE INDEX IF NOT EXISTS idx_depret_plat ON depots_retraits(plateforme_id)',
|
||||
'CREATE INDEX IF NOT EXISTS idx_depret_date ON depots_retraits(date_operation)',
|
||||
]);
|
||||
|
||||
fixPlateformeCascade('investissements', [
|
||||
'CREATE INDEX IF NOT EXISTS idx_inv_inv ON investissements(investisseur_id)',
|
||||
'CREATE INDEX IF NOT EXISTS idx_inv_plat ON investissements(plateforme_id)',
|
||||
'CREATE INDEX IF NOT EXISTS idx_inv_statut ON investissements(statut)',
|
||||
'CREATE INDEX IF NOT EXISTS idx_inv_date ON investissements(date_souscription)',
|
||||
]);
|
||||
}
|
||||
|
||||
export default db;
|
||||
|
||||
Reference in New Issue
Block a user