Commit Graph

1 Commits (8ca0d4d257c267bcd2d71ade2aa05fa4be8b736e)

Author SHA1 Message Date
nexxo b3081a544b Let the export have as many destinations as somebody wants
tests / pest (push) Failing after 7m35s Details
tests / assets (push) Successful in 21s Details
tests / release (push) Has been skipped Details
One path and one exported_at could not say "on the office NAS, not yet on the
off-site box" — which is the only interesting question once there is more than
one destination, and the reason for a second is precisely that any single one
can fail. A row per destination now, and a row per (invoice, destination) pair.

Two kinds, and the difference is where the protocol lives. 'local' is a
directory this machine can already write to: a NAS mounted over NFS 4.1, a bind
mount, a disk. The application knows nothing about the protocol, the target
changes without code changing with it, and the whole thing stays testable
against a temporary directory. 'sftp' is for something reachable with
credentials — a Hetzner Storage Box being the obvious one — and the password
goes into the secret vault with only its key on the row, because a credential
in a settings table is a credential in every database dump. An empty password
field on an existing target means unchanged, not cleared: the field cannot show
what is stored, so blank-means-delete would wipe it on every unrelated edit.

One job per pair rather than one per invoice. A job writing to both would retry
the one that worked every time the other did not, and report one outcome for
two different things.

The ordering fix the tests found: Laravel's local adapter creates its root when
it is CONSTRUCTED, so the "never create the root" guard was checking a
directory the disk had just made for it. It runs before the disk is built now.
That guard is the difference between an archive and a hole in the ground — mkdir
on an absent mountpoint succeeds, writes onto the container's own disk and
reports success, and an archive that silently is not the archive is worse than
none.

The single path from the previous commit is migrated into a destination called
"Archiv" rather than dropped.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 02:43:15 +02:00