mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-19 21:46:49 +00:00
0887f251cece4b29a4be60fcdc8b68e1a6bb1393
7 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ed18c37903 |
fix: the findings of the final 4.3.0 audit
Every file the release touches was read once more, and what survived an independent check against the code is fixed here, with a test for each. Data and credentials - A document sync connection edited to a new address no longer carries the stored token along: a blank form under another origin is refused, the same rule the probe route already had. Dawarich and AirTrail testConnection send the stored key only to the host it was stored for. - The key rotation script now covers document_connections.secrets and trip_document_links.webhook_secret, which it had skipped. - Turning a booked night into a pause in the road trip popup asks first and says that the booking and its expense go with it. Deleting a place says the same when a night is booked there, on the desktop and on the phone. - A moved version marker over unchanged bytes no longer replaces the TREK file and its attachments; a real revision carries the attachments over. Planner - A drop in the day plan names the row it lands ahead of in the full day, so a hidden hotel stop no longer shifts it one slot early; the store sorts before it splices. Via re-anchoring, the booked-night guard and the sidebar's day membership read the stored day, the index space the server uses. - A hotel stop placed mid-day re-pins the road trip vias of that day, and the booking session receives the new stop order too. The Public API and the iCal feed list a booked hotel once. A plugin can no longer write a stay against another trip's day. Road trip and map - The route overview paces its requests to the public routers and retries a rate limit, and it says which legs it could not route instead of quietly reporting a shorter trip. A click on a via handle no longer drops a second via; handles hide when zooming out. Read-only members cannot edit the driving limits. The phone shows a way back when no day is picked and prints the night drive departure in the chosen clock format. Integrations and search - Google transit keeps conventional trains when the train chip is on. TREK_PLACES_ENABLED=false stops the details and enrichment calls too, and accepts the whole boolean family. A caller-supplied description no longer lands in the instance-wide enrichment cache. Nominatim searches carry a timeout. An OSM place id is checked for shape before it reaches Overpass. - Dawarich puts a stay on the trip whose dates hold it, the Atlas hand-off knows what the Atlas already shows, and a host change with a new key clears the old instance's state. - Document sync: a webhook address pasted by hand works on the token alone, deleting a connection unregisters its subscriptions, Nextcloud under a sub-path connects, Synology answers a name collision before uploading and verifies a move into the bin, Paperless titles are capped at 128 characters, a DNS failure reads as unreachable rather than as a policy verdict, videos from the store are accepted like uploads, and remote_missing has a way out. Conflict buttons only for the owner, a refused choice is reported, a refused credential gets a Reconnect button, disconnecting asks first on the desktop as it does on the phone, and the folder picker no longer spins forever. Release - The server holds the release notice back from a bundle that cannot draw it, so the previous shell served from the service worker cache after an update no longer shows bare keys and no longer lets a click use the notice up. - Import into an existing list gets the same body limit as a new list. Chat images say when the limit is reached and the new controls are translated. The transit backend switch reports a failed save. The Helm chart carries the Amap credentials in its Secret; the shipped templates document the Places API and Amap variables and the Overpass default. The wiki follows. |
||
|
|
233f46575c |
fix(docsync): make the admin switch pause a binding, and retry a failed update
The check that was meant to stand a binding down when its provider is switched off asked AddonsService.isAddonEnabled(link.provider_id). The addons table has no rows for paperless, papra and the other document providers, so it answered "off" for every binding, and every run came back disabled before it listed anything. The unit test stubbed that answer and the e2e never runs a sync, so nothing caught it. isSwitchedOff() now asks the two things an admin actually switches: the Documents addon, and document_providers.enabled for the binding's provider. dueLinks leaves such bindings out in SQL, since one that never runs never gets a last_sync_at and twenty of them would take every slot of a tick. A run, the webhook on arrival and when its timer fires, "Sync now" and the MCP sync tool all refuse before anything is written, so a paused binding keeps its state and its documents' attempt counters and resumes where it stopped. The REST route answers 409 provider_disabled, the MCP tool reports the same code, status() carries providerOff per link, and both shells show one line saying the binding is paused because its provider is switched off, in all 23 languages. A failed pull_update also left the row stuck. upsertItem wrote the listing's version, size and time next to the old bytes, so the next run saw no upstream change, planned touch, which leaves error alone, and the document stayed in error on stale bytes for good, "Sync now" included. A failed transfer now leaves the pairing describing the copy TREK holds; a new row keeps what pairs it but no version, so it is fetched again. |
||
|
|
9de8490176 |
style(docsync): drop dash punctuation from the added comments and test titles
The lines this branch adds used em and en dashes as sentence punctuation across comments, JSDoc, CSS comments and test titles. Each one is rewritten with a comma, a colon, parentheses or a second sentence, keeping what the sentence said. Only lines this branch adds are touched; everything that already exists on dev stays as it is. No logic, error code or logged message changes. The only string literals affected are describe and it titles, which now read "X: topic" like the rest of the provider tests. |
||
|
|
e6a5ca1521 |
fix(docsync): stop a push from stealing another file's pairing
Papra answers a push of bytes it already holds with the document that has them. Two trip files with the same content therefore pushed to one document, and the unique index on (link_id, remote_id) aborted that run and every run after it with a constraint error. The pairing now stays with the file that owns it and the second file is recorded as a conflict. Also in this pass: - Paperless no longer adopts a same-bytes document that lacks the scope's tag when a consume outlives the poll budget; that document belongs to someone else's filing and delete-through would have trashed it. - The pull path writes the file row, the retirement of the copy it replaces and the pairing in one transaction, and announces them only once committed. - PATCH links stops accepting remoteRootId/remoteRootPath, which it read and silently dropped. - The MCP sync tool clears shelved items before running, as the REST route does. |
||
|
|
7237a1881d |
fix(docsync): switches that did not switch off, a webhook nobody could authenticate, and a counter that only grew
More from the audit, each checked against the code before it was touched. The webhook obeyed neither the Documents addon nor the app_settings kill switch, so "switch document sync off" stopped the poll while every provider holding a webhook carried on driving full runs. Both are now read on arrival and again when the debounce timer fires, so a switch thrown mid-window still takes effect. Nextcloud's subscription registered its secret under `X-TREK-Docsync-Signature` while the endpoint reads `x-trek-docsync-secret`: the subscription existed, the calls arrived, and every one of them was dropped for carrying no secret the controller could find. The two names now agree, and the test pins the one the endpoint actually reads. A WebDAV create was an unconditional PUT, so a name already taken upstream was silently overwritten — two trips bound to one folder, or a `receipt.pdf` a person put there by hand, is enough. Creates now send `If-None-Match: *`. The per-item retry bookkeeping was stuck at both ends: the backoff was computed at step 1 every time, so three of the curve's four steps were unreachable, and the attempt counter only ever grew, which shelved a row that failed once a month after six months of otherwise healthy syncing. A successful pass now clears it, which is what makes the limit mean six failures in a row. Also: a manual run on an orphaned binding is refused with 409 (its credential belongs to somebody no longer on the trip, and the scheduler already refuses it, so the button was a way around that); a document deleted while a run is in flight is no longer uploaded anyway; a path-shaped WebDAV id containing `..` is rejected instead of addressing a file outside the bound folder; and Papra's multipart part header no longer takes an unescaped media type, which a CR could end early. Client: three writes in useDocSync swallowed their failure into `void` call sites, a load failure blanked the panel to "no providers available" whatever the cause, and overlapping loads let the slowest response win. Writes now report, only 403/404 blanks the panel, and a generation stamp keeps the newest answer. The dead lastRun state went with them. |
||
|
|
1430056d24 |
fix(docsync): read the retry limit back out, debounce the webhook, and cover the UI
The attempt counter and the backoff were written on every failure and never consulted when planning the next run: rows were found in a live database at 46 attempts against a limit of 6, so a document a provider refuses — one it reads as corrupt, a type it will not take, a full quota — was re-uploaded on every cron tick forever. planReconcile now skips a row that is shelved or still inside its window, and a manual run clears the counters, which is what "waits for a person" has to mean. The webhook had the same shape of bug: WEBHOOK_NUDGE_DEBOUNCE_SECONDS existed, with a comment explaining it, and nothing read it. Providers fire once per document, so twenty files dropped into a watched folder were twenty runs — the in-flight guard answered `busy` to nineteen of them, throwing away nineteen announcements, and the one run that did start had begun before most of the changes landed. A burst now folds into one trailing run, and the binding is re-read when the timer fires so a sync switched off in between does not get a last run out of a stale row. Four defects the new suites turned up, all fixed: the sync button lost its accessible name below sm, where its label is hidden; the stored address was never shown in the connect dialog although the form kept submitting it; the allow-self-signed switch only read the connection at mount, so a stored "on" showed as off and the next save wrote that back; and a refused folder create escaped as an unhandled rejection with nothing said to anybody. Also here: the phone shell gets the same feature (one sheet, four views, the same hook as the desktop), the marks come from the product's own icon files rather than redrawn approximations, and the 22 non-English locales carry the new keys. ADMIN-SVC-069 asserted that `documents` has no MCP surface. It does now, so the test takes its example from MCP_GATED_ADDON_IDS instead of naming one. |
||
|
|
2d7cb124b3 |
feat(documents): two-way sync with Paperless, Papra, Nextcloud, OpenCloud and Synology
A trip can be bound to a folder, tag or space in a self-hosted document store, and documents then move in both directions: what someone uploads in TREK appears there, and what someone files there appears in TREK. Three decisions shape the rest. The connection belongs to the TRIP, not to a person. Every other integration here is per user, and photos are shared only through an opt-in flag; neither can deliver "everyone on the trip sees the same documents", because visibility would depend on whose credentials fetched a file, and a member without their own Paperless account would see nothing. So the trip owner binds the trip once, the server talks to the provider under that one identity, and TREK's own membership decides who sees what. The provider never learns that TREK has members. When the credential holder leaves the trip the binding goes to `orphaned` rather than quietly carrying on with an ex-member's token. TREK keeps the bytes. A synced document is an ordinary trip_files row, so download, trash, offline cache, PDF export and backup keep working untouched, and a provider outage does not blank out a trip that starts tomorrow. Deletion never crosses the boundary on its own. A document that disappears upstream is recorded as missing and waits for a human, and a run that sees more than 40 percent of its known documents vanish at once refuses the whole listing rather than acting on it: an unmounted share answers exactly like a mass delete. The core reconciles by enumerating both sides, because the weakest provider sets the model. A changed-since query cannot see a deletion, Papra does not move updatedAt when a tag changes, a Paperless bulk edit leaves `modified` alone, and Synology FileStation has no change feed at all. A webhook is therefore only ever "look now", never a source of truth. Echo is caught by comparing content, not timestamps: pushed_sha256 records what TREK itself wrote, so a provider reporting TREK's own upload is not mistaken for a stranger's edit. Provider differences are answered by capability data rather than by branching on an id: stable ids where they exist (Paperless document id, oc:fileid, driveId) and a normalised path where they do not, replace-in-place where the API offers it, and a content hash from the listing only where the provider really supplies one. Admins switch providers on under Admin, Addons, Documents, mirroring how photo providers hang off Journey, including the cascade and the 409 when the parent addon is off. The per-trip setup lives in the file manager, where the person has the context that makes the folder choice obvious. |