Correctif Contrainte de suppression de compte

This commit is contained in:
2026-07-05 00:54:40 +02:00
parent 5e775da711
commit 1f75878d87
4 changed files with 83 additions and 2 deletions
+65
View File
@@ -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;