feat(admin): deleting an account stops deleting other people's work
`tasks.created_by` was ON DELETE CASCADE, so retiring a colleague removed every task they had ever written — including in projects somebody else owns. The interface said so in plain words before you pressed the button, which is a warning rather than a fix. It was the last remaining way to lose other people's work by accident. SET NULL instead: the task stays and loses its author, the same shape `assignee_id` and `completed_by` already have. SQLite cannot alter a foreign key action, so the table is rebuilt — and with foreign keys on, the drop in the middle of a rebuild is a cascading delete that takes every row referencing `tasks` with it. `PRAGMA foreign_keys` is a no-op inside a transaction, so migrations now run on a connection of their own, and a migration can opt out of enforcement with `-- verdande:rebuild-tables`. `PRAGMA foreign_key_check` runs before the commit, so a rebuild that leaves a dangling reference fails the migration rather than writing it. The test fills a database at 0007, upgrades it, and counts. Without the marker it loses a task, a label link and a comment — which is exactly what it is for. The admin list now says two numbers instead of one: what disappears, and what stays behind unattributed. They are different sentences and the second is a reassurance. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
a29534c811
commit
ebdc8eacf2
@@ -68,18 +68,26 @@
|
||||
/**
|
||||
* Deleting an account is the only thing in this app that cannot be undone.
|
||||
*
|
||||
* Everything else is soft-deleted with a trash behind it. This is not, and it
|
||||
* takes more than the person's own work: `tasks.created_by` cascades, so what
|
||||
* they wrote in projects somebody else owns goes too. The confirmation says the
|
||||
* numbers out loud, which is why the list carries them.
|
||||
* Everything else is soft-deleted with a trash behind it. This is not: the
|
||||
* person's own projects go, and every task in them. What they wrote in somebody
|
||||
* else's project stays and loses its author — `tasks.created_by` is ON DELETE
|
||||
* SET NULL since migration 0008. Both numbers are said out loud, because they
|
||||
* are two different sentences and the second one is a reassurance rather than a
|
||||
* warning.
|
||||
*/
|
||||
async function remove(user) {
|
||||
const parts = [`Slet ${user.name} <${user.email}> for altid?`, ''];
|
||||
if (user.project_count || user.task_count) {
|
||||
parts.push(
|
||||
`Det sletter ${count(user.project_count, 'projekt', 'projekter')} og ` +
|
||||
`${count(user.task_count, 'opgave', 'opgaver')} — også opgaver, personen har ` +
|
||||
`skrevet i projekter, andre ejer.`,
|
||||
`${count(user.task_count, 'opgave', 'opgaver')} i dem.`,
|
||||
''
|
||||
);
|
||||
}
|
||||
if (user.authored_elsewhere) {
|
||||
parts.push(
|
||||
`${count(user.authored_elsewhere, 'opgave', 'opgaver')} i andres projekter ` +
|
||||
`bliver stående uden forfatter.`,
|
||||
''
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user