Conversation
…in +layout.svelte
…ebar imports Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <copilot@github.com>
…s/utils Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <copilot@github.com>
feat(storage): initial Filebrowser UI implementation
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Copilot <copilot@github.com>
…ation S3 File Browser Page Navigation (Pagination)
* #93 add modal preview for file display * #93 fix broken e2e tests * #93 add preview for parquet files, fix scrollbar visibility in preview, allow ms excel csv data for preview * #93 improve parquet row fetch handling * #93 fix lint * #93 move preview endpoint logic into separate files * #93 disable parquet preview
* refactor(ObjectTable): extract File and Folde rows into seperate components * style: format code * style: use camelCase * style: use camelCase
* feat: implement single-file download functionality and error handling for S3 storage * feat: refactor S3 download endpoint to use new API structure and improve error handling * chore: remove obsolete note about static download route segment in tech debt documentation * style: format code * fix: improve type safety in S3 error handling * fix: preview action * feat: add context menu header and improve file selection handling in FileExplorer * fix: reset selection state on context menu close and refine download action logic * style: format files * style: remove pointer events from Actions row on ContextMenu * #91 fix lint --------- Co-authored-by: F3l1x1vo <fli@ordix.de>
Bohreromir
left a comment
There was a problem hiding this comment.
A lot of the Playwright tests fail for me, do they for you too?
Here is some claude as a starting point:
- storage/preview.spec.ts — PDF preview: timed out waiting for tbody tr or "This bucket is empty" after navigating to a bucket route.
- storage/recent-items.spec.ts — removes a deleted file from Recent Files: timed out (60s, ×3 attempts via retries) waiting for the "Toggle selection mode" button.
- storage/recent-items.spec.ts — removes files/location when a directory is deleted: Error: Channel closed / browser already closed mid-action.
The screenshots are the key clue. In both failures #1 and #2, the captured screenshot shows the Storage overview page (bucket card grid + Recent Files tab) — not the bucket browsing page the test expected. So page.goto(bucketRoute(...)) is redirecting back to /storage instead of opening the bucket.
This is masked by a bug in the test helper waitForObjectsLoaded() (e2e/storage/helpers.ts:185-192): it waits for tbody tr or "This bucket is empty" as a generic "did objects load" signal. But the Storage overview page's Recent Files tab also renders a
, so when the redirect happens and Recent Files has an entry, that locator matches by coincidence — the helper reports success on the wrong page. The test then tries to click "Toggle selection mode" (a bucket-only control), which never appears, and burns the full 60s test.slow() timeout, ×2 retries.
Bohreromir
left a comment
There was a problem hiding this comment.
Please also fix the new lint warnings introduced:
security/detect-non-literal-fs-filename (11)
- src/architecture/code-size-limits.spec.ts:23,38,53
- src/architecture/helpers.ts:57 (plus detect-non-literal-fs-filename for readdirSync at :29)
- src/architecture/i18n-compliance.spec.ts:39
- src/architecture/server-logging.spec.ts:29,56
- src/architecture/ui-pattern-enforcement.spec.ts:35,47,73,91
svelte/no-navigation-without-resolve (8):
- src/lib/components/storage/landing/RecentItems.svelte:132,158,195,203,216
- src/routes/(app)/settings/connections/[id]/edit/+page.svelte:77
- src/routes/(app)/storage/+error.svelte:86
- src/routes/(app)/storage/+layout.svelte:42,45
security/detect-non-literal-regexp (1):
- e2e/storage/archive.spec.ts:174
Sometimes they do fail locally for me when developing under resource strain. This flakiness also happens in the GitHub Actions. I've hopefully improved this in b94619d. |
| /** | ||
| * Shared helpers for architecture fitness tests. | ||
| * | ||
| * ArchUnitTS only scans TypeScript (.ts) source files. Svelte (.svelte) and |
Bohreromir
left a comment
There was a problem hiding this comment.
please do not mark my comments as resolved, I loose track of them otherwise
|
@Bohreromir I've implemented most of your suggestions in 9e2ec68. I have two points to address:
It's the regular bold disabled font we use everywhere. I don't disagree that it appears slightly blurred if you look closely in darkmode but it's clearly legible. This was accepted from the day we first did a mockup with the Stackable Team of the storage browser. |
|
@F3l1x1vo Whoops my bad. I meant using ctrl+v into the field for the new file name |
There's a conflict there with the regular pasting of text. If the user wants to copy and paste a filename he copied from outside the browser for example, a file in the clipboard of the storage browser would be pasted into that field instead of his text. This pasting of filenames by the clipboard is a very niche and weird interaction that I would suggest to leave out. |
| if (!process.env.DATABASE_PASSWORD) { | ||
| log.warn('DATABASE_PASSWORD not set, using default development password'); | ||
| } | ||
| const dbPassword = process.env.DATABASE_PASSWORD || 'cockpit-dev-password'; |
There was a problem hiding this comment.
We should have it fail here in production if the db pw is not set, not fall back to something known
| if (!value.includes('://')) return true; | ||
| try { | ||
| return new URL(v).hostname; | ||
| new URL(value); |
There was a problem hiding this comment.
z.url() should do the trick again
zod even ships ip4/6 validation
| const keysToDelete = [...new Set(succeeded.map((s) => s.sourceKey))]; | ||
| if (keysToDelete.length > 0) { | ||
| await onBeforeDelete?.(keysToDelete); | ||
| const deleteResult = await provider.deleteObjects(keysToDelete); |
There was a problem hiding this comment.
operations.ts:174 + s3-provider.ts:237 — move builds its delete list from succeeded keys, but deleteObjects re-expands any key ending in /. One failed copy in a folder move and that object is deleted anyway.
|
|
||
| const db = drizzle(connectionUri); | ||
| await migrate(db, { | ||
| migrationsFolder: path.resolve('src/lib/server/migrations') |
There was a problem hiding this comment.
Migrations cannot run in the shipped image — src/ is never copied, npm isn't in the image, drizzle-kit and tsx are devDependencies
|
|
||
| // SSL is disabled in development (local k8s), enabled in production | ||
| const isDev = process.env.NODE_ENV !== 'production'; | ||
| const sslMode = isDev ? false : true; |
There was a problem hiding this comment.
where does the actual cert comes from if this is set to true? If its self signed im not sure the app will trust it
| npm run db:studio # Open Drizzle Studio (web-based DB preview/management) | ||
| ``` | ||
|
|
||
| **Override env file**: Use `DRIZZLE_ENV_FILE` to use a different environment file: |
There was a problem hiding this comment.
DRIZZLE_ENV_FILE=.env.production npm run db:generate
That variable is read nowhere. Grep across every .ts/.js/.json outside node_modules returns zero hits.
This is the worse of the two, because it reads as a targeting mechanism. An operator runs DRIZZLE_ENV_FILE=.env.production npm run db:migrate believing they've pointed the migration at production; the variable is ignored, and drizzle-kit migrates whatever process.env happens to hold — which, per the fallbacks above, is localhost with the dev password. It fails silently in the wrong direction: no error, no warning, just a command that didn't do what its own documentation says.
| return { entries, hasMore: false }; | ||
| } catch (err) { | ||
| throw new Error( | ||
| 'RAR support requires the "unrar" command to be installed on the server. ' + |
There was a problem hiding this comment.
Aren't we the ones controlling the server?
Apart from that does the unrar license says:
"UnRAR source code may be used in any software to handle
RAR archives without limitations free of charge, but cannot be
used to develop RAR (WinRAR) compatible archiver and to
re-create RAR compression algorithm, which is proprietary.
Distribution of modified UnRAR source code in separate form
or as a part of other software is permitted, provided that
full text of this paragraph, starting from "UnRAR source code"
words, is included in license, or in documentation if license
is not available, and in source code comments of resulting package."
https://github.com/pmachapman/unrar/blob/master/license.txt
I dont speak lawyer, but that might be something @lfrancke wants to check.
Honestly I'd drop the whole archive thing.
| maxBytes?: number | ||
| ): Promise<ArchiveListing> { | ||
| const bin = await find7zBinary(); | ||
| if (!bin) throw new Error('7z support requires 7-Zip to be installed on the server.'); |
There was a problem hiding this comment.
where is this installed? including it in the build has the same rar problem described above as it includes the rar archiever
| * Delete conflicting destination files before copy/move to prevent | ||
| * the server from auto-renaming them with " (2)" suffix. | ||
| */ | ||
| private async _deleteConflictingDests( |
There was a problem hiding this comment.
cross-bucket paste is offered but unsupported. Replace deletes the destination file, then the copy fails.
This is because both
_performPasteSequential and _performPaste have a _sourceBucket argument but they are simply not used.
| dropSidebarTarget = null; | ||
| } | ||
|
|
||
| function handleSidebarDrop(e: DragEvent, _bucket: string, prefix: string) { |
There was a problem hiding this comment.
nothing uses the _bucket argument so this looks like a drag and drop over two different buckets is possible, but its not as boht move and copy only support a single bucket



Closes #129