mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-20 14:06:27 +00:00
dev
6 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
724c88c201 |
style: format server and shared with prettier
A mechanical prettier --write over server/src, server/tests, server/scripts and shared/src with the workspaces' .prettierrc; nothing but layout and import order changes, and every file keeps the same set of imports. shared is formatted throughout and its format baseline is empty. The server baseline keeps the 35 files that Prettier would grow past their lint:size limit (they need a split first, which is not a formatting change), and the generated entity files stay as their generator writes them. |
||
|
|
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. |
||
|
|
25d4e34959 |
fix(docsync): keep the Synology device token across a restart, tied to its row
The DSM trusted-device token lived in process memory only. After a restart every token was gone and the stored one-time code long spent, so every two-factor NAS binding stopped syncing until its owner typed a fresh code, and nothing on screen said why. The token is now kept in the connection's encrypted secrets, together with a digest of the instance and account it was issued for. A saved connection's ref carries a saveSecret callback that writes through DocSyncConfigService.saveEarnedSecret, which rereads the row inside a transaction so a sync run holding an old ref cannot undo a password the owner changed meanwhile. The key belongs to no form field, so maskSecrets never reports it and mergeSecrets never touches it: the GET and PUT payloads do not carry it, an edit keeps it, and a client cannot plant one. It survives a password edit and a spent or cleared code. A connection repointed at another NAS or account drops it on its next call, and a token DSM refuses with 403 or 404 is dropped in memory and in storage. A probe of form values and an unsaved form may use what is stored but never write. A refused token no longer shadows a fresh code: the code in the form gets its turn in the same login call. In memory the slot is keyed by the connection id together with the row's created_at. A backup restored in place or the demo reset rolls the id sequence back without a restart, and a new connection that reused an id must not log in on the token of the row it replaced. The Synology adapter receives its client through DI like Paperless and WebDAV, instead of building a second instance beside the one the module registers. Two readings of a failed request are settled as well: - INVALID_PURPOSE, INVALID_CA and PATH_LENGTH_EXCEEDED count as an untrusted certificate again, so the settings screen offers the self-signed switch. Papra used to find them through the message text, and the shared classifier reads codes only. - A host that never completes the TCP handshake reads as unreachable on every client. WebDAV said timeout before the shared transport; the change is now documented and tested. |
||
|
|
01a7224f29 |
fix(docsync): key the Synology device token per connection and share one transport
The DSM trusted-device token was cached per NAS and account. A second trip whose owner knew the account password but never had the authenticator could bind the same account and log in on the first trip's token, a two-factor bypass across trip owners. Nothing ever cleared the entry either, so a token DSM stopped trusting went out ahead of any fresh code on every attempt until the process restarted. The token is now filed per connection, with instance and account still in the key. An edit keeps the connection id, so the token survives a password change and a stale code in the form. An unsaved form (connection 0) keeps what its probe earns on the session only, and the saved connection adopts it from there. A token DSM refuses with 403 or 404 is dropped for that connection alone. The four clients each carried their own copy of the transport, and the copies had drifted. provider-http.ts now holds the SSRF-guarded fetch with its timeout and certificate switch, the reading of a request that got no answer, the base status table and the guard on a streamed download. Timeouts, caps, auth headers and each client's error class and wording are unchanged. Where the copies differed by accident they now agree: - Paperless recognises UNABLE_TO_GET_ISSUER_CERT_LOCALLY (a private CA) as tls_untrusted instead of unreachable. - ERR_SSL_* handshake failures read as unreachable on WebDAV too, since the self-signed switch cannot fix them. - Synology maps HTTP 409, 415 and 507 like the others, Paperless maps 415. - Every download is refused on an announced length over its ceiling and counted as it streams. WebDAV and Paperless had no ceiling, Synology only read the header, and Papra's limiter left the source pinned when it tripped. - Papra reported a download without Content-Length as zero bytes, so the size from the listing was never used. PapraProvider and SynologyDriveProvider are renamed PapraDocumentProvider and SynologyDriveDocumentProvider, matching the other adapters. |
||
|
|
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. |
||
|
|
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. |