Repository navigation
S3 File Browser v2 - #289
Merged
Merged
Conversation
F3l1x1vo
marked this pull request as draft
August 13, 2026 10:13
* feat: Add basic search Modal * fix: Update cleanup keys for cut and paste test to include destination file
* feat: Add basic search Modal * feat: overhaul Storage Search Modal * Search Regex and more backend functions (#291) * #180 move filters to backend, restrict regex, move regex button, stream search results * fix lint * #180 add date and size filter to search * #180 autofocus search bar, cancel search, dead i18n keys, session shortcut hint, userside regex validation * fix arch * #180 use daisyui dropdown, show datepicker below input * add left padding to select * fix lint * make the input field as tall as the search select * test: keep search modal open when closing date picker --------- Co-authored-by: F3l1x1vo <fli@ordix.de> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
* feat: Add basic search Modal * feat: overhaul Storage Search Modal * feat: improve recent searches tab * style: use text-white for open search button * fix keybind targeting in search modal * fix merge * fix merge * fix merge --------- Co-authored-by: F3l1x1vo <fli@ordix.de>
* #124 add multipart download, add archived download * #124 fix progress display, add download again button * #124 download again only if cached archive not expired * fix lint * fix lint * #124 restrict downloads to zip format * #124 fix cancelled jobs running checks, show compression stage notice * #128 enable download on right clicking a folder, fix worker display of multidownload * #124 use noncompressed archive directly to browser instead of multipart download to server * feat: remove duplicate code and improve download logic * feat: remove duplicate code and improve download logic * fix: changes to download history behaviour * #124 use chevrons instead of text, hide folders, improve error messages * #124 transmit final size to browser, link browser download to job * #124 ui job improvements * remove deprecated download history unit test * pull from v2 --------- Co-authored-by: dklOrdix <dkl@ordix.de>
…history now overflows, fix TSV preview
…at/s3-file-browser-v2 # Conflicts: # e2e/storage/permissions.spec.ts # src/lib/components/storage/explorer/OperationsButton.svelte # src/routes/(app)/settings/connections/+page.server.ts
Bohreromir
requested changes
Oct 8, 2026
Member
There was a problem hiding this comment.
Due to time constrains I can only supply you with a spot checked by me, clauded review for now.
Feel free to push back on any hallucinated ones ;)
High
-
src/lib/server/storage/operations.ts:121– WithdestinationKeyset (used by rename), onlysourceKeys[0]is copied, so renaming a folder only moves its/marker and leaves the contents behind. Expand directory keys here the waycomputeDestinationsdoes. -
src/lib/server/storage/operations.ts:198– Moves filter out keys ending in/before deleting, so the source folder marker survives and an empty folder stays visible. Delete the markers once all their children have moved. -
src/lib/server/storage/preview/csv.ts:112,125,141– Line offsets use JS string length (UTF-16) instead of UTF-8 byte length, so any non-ASCII character shifts every later offset and garbles rows. Count withTextEncoder().encode(line).lengthor scan the raw bytes for\n. -
src/lib/server/auth.ts:97– Regression:email: profile.email || ''dropped thepreferred_usernamefallback. better-auth rejects an empty email (email_is_missing), so OIDC users without an email claim can't log in any more.
Medium
-
src/lib/server/storage/operations.ts:141–uniqueDestKeyruns per expanded child, not once for the folder root. Copyingdocs/wheredocs/already exists gives an emptydocs (1)/, with the files landing indocs/asa (1).txt. -
src/lib/server/storage/preview/csv.ts:225– Cached entries are never checked against the file's ETag or size, and nothing clears them. After a save or re-upload the preview reads the wrong ranges until the TTL expires. -
src/lib/server/storage/connections-db.ts:21– The fingerprint leaves out port, TLS and access style, so connecting to the same host on another port silently reuses the old connection. Add them to the hash. -
src/routes/(app)/settings/connections/[id]/edit/+page.server.ts:170– An edit re-encrypts the payload but never updateshash. A later connect with the old settings then matches the edited connection. -
src/lib/storage/upload.ts:42– Regression:checkObjectExistsreturnsfalseon any non-2xx or network error, so a 401, 403 or 5xx skips the overwrite prompt. Restore the main behaviour: 404 means false, everything else throws (the docstring still says it throws). -
src/lib/server/storage/download-manifests.ts:320– The single-file download pipes intocontroller.enqueuewith no backpressure, so a large file to a slow client is buffered in Node memory. Returndownload.streamdirectly or usepull. -
src/routes/(app)/api/storage/download/manifests/[manifestId]/[part]/+server.ts:31– The raw filename goes intofilename="...", so non-Latin-1 names cause a 500 and a"breaks the header. ReusecontentDispositionFilenamefromdownload/+server.ts. -
src/lib/server/storage/streaming.ts:57– IfoperationPromiserejects (for examplelistAllKeysfails), the job is never failed and staysrunninguntil it expires. Wrap the call in try/catch →failJob, then error the stream.
Low
-
src/routes/(app)/settings/connections/[id]/edit/+page.server.ts:92– If only one of access key or secret is filled in, the change is dropped and the page still reports success. Return a validation error instead. -
src/routes/(app)/settings/connections/[id]/edit/+page.server.ts:155,162–'Connection not found'and'Failed to read existing credentials'are hardcoded user-visible strings. Move them tomessages/*.json. -
src/lib/server/storage/download-manifests.ts:73–expandKeyssends onegetMetadataHEAD per key throughPromise.allwith no limit, so a large folder fires thousands at once. Use the sizes from the listing or cap concurrency. -
src/lib/server/storage/connection.ts:38– A connection-ID header that isn't a valid UUID reaches Postgres and returns 500. Validate it as a UUID first and return 400/401.
Renaming a pasted folder passed a destination key without a trailing slash, so children were written to "<name><child>" and the existence check matched unrelated sibling keys.
Repetitions inside groups and large bounded repetitions were not counted, so patterns like "(.*)(.*)(.*)!" passed and blocked the event loop for seconds per key. Estimate the backtracking width per alternative instead.
pollJob threw on the 404 for a missing job, so the not_found branch was unreachable and downloads waiting on a save dialog were marked as failed. Return not_found instead and keep polling for a 60 s grace period, after which the job counts as expired.
Date filters were built in local time but evaluated on the server, usually in UTC, shifting day boundaries by the user's UTC offset. The client now sends its IANA time zone and the server computes boundaries in it.
An empty name was saved as-is, and renaming to an existing name hit the unique constraint and returned a 500. Blank names now keep the current one, and a duplicate name is shown as a form error.
Exceeding 10000 results was reported with code "unknown", so the UI told the user to retry. Send a dedicated too_many_results code with its own message, log it at info level and count it as a separate outcome.
For entries needing both ZIP64 sizes and offset, zipStreamSize counted two extra-field headers while the central directory writes one, so the advertised Content-Length was 4 bytes too large and downloads failed. Derive all record lengths from the record builders instead.
Modal handles Escape in the capture phase, so pressing Escape with the search filter date picker open closed the whole search modal and aborted running searches. Leave Escape to the browser while an auto popover inside the dialog is open.
The unique constraint over the raw search parameters exceeded the btree index row limit for long queries and exclude patterns, so such searches failed to save. Store a SHA-256 search key and enforce uniqueness on it.
Changing only the access key skipped the credential check and stored the new key with the old secret. Require the matching secret key instead, and show field errors for the connection name, which were never rendered.
A non-UUID manifest id reached the uuid column comparison, which Postgres rejects, so the download and redownload routes returned 500 instead of 404.
Browser retries of a manifest download reuse the URL and its job id, which createJob rejected with 409 until the job expired. Let the same user restart a job that is no longer running; other users and running jobs still get 409.
The history list is scoped to the active connection, but clearing it deleted the user's manifests on every connection. Scope the delete to the connection sent with the request.
A stream cut off by a proxy timeout or restart ended without its complete event, and the partial results were shown as a finished search. Require the complete event and report the bucket as failed otherwise.
"date = 2026-08-17 14:30" compared to the exact millisecond and matched practically nothing. Match the whole minute, or second when given, as a date-only value matches the whole day; "<" and ">" are unchanged.
The depth field accepted values the server rejects (outside 1–20 or not whole numbers), so every bucket request failed without a hint. Show a field error and disable searching until the value is valid.
A new S3Client with its own keep-alive agent was created per request, and its idle sockets were never closed or reused. Over time this exhausted file descriptors (the e2e Garage server crashed at ~1000 connections). Share pooled agents across clients and close idle sockets after 30 s.
The test was placed after the closing of its describe block and missed the beforeEach that skips the suite when no Garage instance is configured, so it failed instead of being skipped.
Object keys are untrusted: anyone with write access to a shared bucket can create keys such as `../../.bashrc` or `/etc/profile`, which naive extractors would write outside the target directory. Name ZIP entries relative to the download prefix and drop empty, `.` and `..` segments, leading slashes, backslashes and drive letters. Files that collide after sanitising get a numeric suffix.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #124 #180 #216