From 3eafc179007f894e1d4dad60bd337181da16cf0f Mon Sep 17 00:00:00 2001 From: Claudio Date: Sun, 10 May 2026 22:43:15 +0200 Subject: [PATCH] docs(mule-image): cascade-delete FK fixes + tz-aware taken_at fix After the hard-remove SourceRoot endpoint shipped, two real-world FK issues surfaced (parent_id self-FK and cross-source-root children) and one tz-aware datetime issue on PATCH /photos/{id}. All three fixed in the same session. Trash icon also picks up a spinner during the multi- second cascade. Deploy.sh's --force-recreate flag isn't actually recreating reliably; flagged as open infra question. Co-Authored-By: Claude Opus 4.7 (1M context) --- containers/120-mule-images.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/containers/120-mule-images.md b/containers/120-mule-images.md index 15bdea7..89318b1 100644 --- a/containers/120-mule-images.md +++ b/containers/120-mule-images.md @@ -80,6 +80,19 @@ For pushes from inside the LXC, gitea creds at `/etc/mule-deploy/git-credentials ## Changelog +### 2026-05-10 — Two cascade-delete + datetime fixes after the hard-remove shipped + +The first cut of `delete_nextcloud_source_root` blew up with `folders_parent_id_fkey` violations on the first real test (`Taco and Muli - 2024 onward`, 35 folders / 4,158 photos). Two iterations to get it right: + +1. **NULL `parent_id` before deleting Folders.** Folders have a self-referential `parent_id` FK with no `ON DELETE` rule; postgres checks the constraint per row regardless of insertion order, so a flat `DELETE FROM folders WHERE id IN (...)` of the whole subtree fails on the parents whose children appear later in the same statement. Fixed with an `UPDATE folders SET parent_id = NULL WHERE id IN (folder_ids)` first. +2. **Widen the NULL UPDATE to cross-source-root children.** A "Leóns 1st Year" SourceRoot at `.../Taco and Muli - 2024 onward/Leóns 1st Year` had its own folder rows whose `parent_id` pointed into the Taco SourceRoot's hierarchy. The first patch only NULLed `parent_id` for folders whose `id` was in the delete set; the Leóns folders weren't in that set so they kept their references and the FK still tripped. Fix: `UPDATE folders SET parent_id = NULL WHERE parent_id IN (folder_ids)` — kills any incoming reference into the delete set, internal or external. + +After both fixes: `DELETE /api/v1/nextcloud/source-roots/{id}` for `Taco and Muli - 2024 onward` cleared 4,158 photos and 35 folders in a single request and returned 200. UI swaps the trash icon for a `Loader2` spinner while the request is in flight (`removeRoot.isPending && removeRoot.variables === r.id`) so the multi-second cascade is visible. + +Separate fix in the same session: `PATCH /api/v1/photos/{id}` returned 500 with `can't subtract offset-naive and offset-aware datetimes` when the frontend sent a tz-aware ISO string for `taken_at` (the datetime-local input is supposed to be naive but real-world locales / paste flows occasionally include `+02:00`). The DB column is `timestamp without time zone`, so asyncpg refused to bind. Normalize on the server with `astimezone(timezone.utc).replace(tzinfo=None)` if `tzinfo is not None`. + +Deploy infra learning: the new `--force-recreate` in `deploy.sh` does NOT reliably recreate containers on every push — saw two consecutive deploys leave the backend at the previous `StartedAt` despite a fresh image. Manual `docker compose up -d --no-deps --force-recreate ` after deploy still races with whatever compose state the auto-deploy left mid-flight, twice landing the stack in a half-broken state (orphaned `_mulita-backend` rename containers, db / redis stopped). Recovery: `docker compose down && docker compose up -d`. Open question — `--force-recreate` may need to be replaced with something more explicit. + ### 2026-05-10 — Hard-remove Nextcloud SourceRoot + reliable delete sync `DELETE /api/v1/nextcloud/source-roots/{id}` was a soft-deactivate (`is_active=false`) — the trash icon in Settings only hid the SourceRoot from active queries while leaving every Folder + Photo row in the DB forever. Re-adding the same path resurrected ghosts; `prune-missing` reported zero deletes for the soft-removed entry because the cleanup code skipped inactive source roots.